Crowdtech(クラウドテック)の勤怠で不足時間を計算するスクリプト
ソース
javascript:function reportToHash(e){const tds=[];e.querySelectorAll('td').forEach(function(e){tds.push(e)});const rawDay=tds[1].textContent;const dayInfo=rawDay.split(' ');const week=dayInfo[1].replace(/\(|\)/gi,'');const startTime=tds[2].textContent;const endTime=tds[3].textContent;const breakTime=tds[4].textContent;const description=tds[6].textContent;const rawWorkTime=tds[5].textContent;const weekdaysFlag=(week=='土'||week=='日');const vacationFlag=weekdaysFlag&&rawWorkTime=='';if(vacationFlag||rawWorkTime=='-'){return{"workTime":0,"weekdaysFlag":weekdaysFlag,"vacationFlag":true,"description":description,}}var workTime=0;if(rawWorkTime!=''){const times=rawWorkTime.replace(/(\d{1,2})時間(\d{1,2})分/gi,'$1:$2').split(':');workTime=parseInt(times[0]*60)+parseInt(times[1])}return{"day":dayInfo[0].replace(/日/gi,''),"week":week,"start":startTime,"end":endTime,"breakTime":breakTime,"workTime":workTime,"description":description,"weekdaysFlag":weekdaysFlag,"vacationFlag":false,}}function workTime(){const limitTime=document.querySelector('body > div.main.l-main > div.wrapper > div:nth-child(3) > div.d-flex.flex-wrap.justify-content-between > div.col-12.col-md-6.col-lg-4 > table > tbody > tr > td:nth-child(2)').textContent;minAndMax=limitTime.split('~');return[minAndMax[0].replace(/時間/gi,''),minAndMax[1].replace(/時間/gi,'')]}function minWorkMinutes(){return calM(workTime()[0])}function maxWorkMinutes(){return calM(workTime()[1])}function calM(hour){return hour*60}function calMFromD(day){return day*60*8}function calH(minutes){return minutes/60}function calD(hour){return hour/8}function calDFromM(minutes){return(minutes/60)/8}function pushMessage(messages,title,content){messages.push(title+":"+content.toString())}function main(){var weekdaysCount=0;var vacationCount=0;var workedMinutes=0;var workedCount=0;var totalWorkCount=0;document.querySelectorAll('table.table .report-row').forEach(function(e){const report=reportToHash(e);if(report["vacationFlag"]){vacationCount+=1;if(report["weekdaysFlag"]){weekdaysCount+=1}}else{if(report["workTime"]>0){workedCount+=1;workedMinutes+=report["workTime"]}totalWorkCount+=1}});const scheduledMinutes=calMFromD(totalWorkCount-workedCount);const planWorkMinutes=workedMinutes+scheduledMinutes;var messages=[];messages.push("~~~~~~~~~~~~~~~~~");pushMessage(messages,"稼働(時)",Math.floor(calH(workedMinutes)));pushMessage(messages,"予定稼働(時)",Math.floor(calH(planWorkMinutes)));pushMessage(messages,"稼働数(日)",workedCount+" (残:"+(totalWorkCount-workedCount)+")");pushMessage(messages,"稼働予定数(日)",totalWorkCount);pushMessage(messages,"休日数(日)",vacationCount+" (土・日:"+weekdaysCount+", 有給:"+(vacationCount-weekdaysCount)+")");const overWorkMinutes=(workedMinutes+scheduledMinutes)-maxWorkMinutes();if(overWorkMinutes>=60){pushMessage(messages,"★過労時間(時)",Math.floor(calH(overWorkMinutes)));messages.push("~~~~~~~~~~~~~~~~~");messages.push("(・ω・`三´・ω・). < 上限を超えそう!?");return messages}if(workedMinutes-minWorkMinutes()>=0){messages.push("~~~~~~~~~~~~~~~~~");messages.push("ヾ(○⌒∇⌒○)ノ < 目標達成!!!!!");return messages}const mustWorkMinutes=minWorkMinutes()-workedMinutes;if(mustWorkMinutes<scheduledMinutes){if(scheduledMinutes-mustWorkMinutes<60){pushMessage(messages,"★余裕時間(分)",scheduledMinutes-mustWorkMinutes)}else{messages.push("★余裕時間(時)"+Math.floor(calH(scheduledMinutes-mustWorkMinutes)));if(scheduledMinutes-mustWorkMinutes>60*8){pushMessage(messages,"★休み可能日数",Math.floor(calDFromM(scheduledMinutes-mustWorkMinutes)))}}messages.push("~~~~~~~~~~~~~~~~~");messages.push("( ^∀^) < 残業なしで達成可能!");return messages}const lackMinutes=mustWorkMinutes-scheduledMinutes;if(lackMinutes<60){pushMessage(messages,"◉不足時間(分)",lackMinutes)}else{pushMessage(messages,"◉不足時間(時)",Math.ceil(calH(lackMinutes)));const avarageOverMinutes=Math.ceil(lackMinutes/(totalWorkCount-workedCount));if(avarageOverMinutes<60){pushMessage(messages,"◉平均残業","1時間未満")}else{pushMessage(messages,"◉平均残業(時)",Math.ceil(calH(avarageOverMinutes)*10)/10)}}messages.push("~~~~~~~~~~~~~~~~~");messages.push("( ;´・ω・`) < 残業しなきゃ...");return messages};window.alert(main().join("\n"));
/**
* Do compress at https://javascriptcompressor.com/
**/
function reportToHash(e) {
const tds = [];
e.querySelectorAll('td').forEach(function(e) {
tds.push(e);
});
// 1日 (日)
const rawDay = tds[1].textContent;
const dayInfo = rawDay.split(' ');
const week = dayInfo[1].replace(/\(|\)/gi, '');
// 09:34
const startTime = tds[2].textContent;
// 19:11
const endTime = tds[3].textContent;
// 60分
const breakTime = tds[4].textContent;
// 資料まとめ
const description = tds[6].textContent;
// 8時間30分
const rawWorkTime = tds[5].textContent;
const weekdaysFlag = (week == '土' || week == '日');
const vacationFlag = weekdaysFlag && rawWorkTime == '';
if(vacationFlag || rawWorkTime == '-') {
return {
"workTime": 0,
"weekdaysFlag": weekdaysFlag,
"vacationFlag": true,
"description": description,
}
}
var workTime = 0;
if(rawWorkTime != '') {
const times = rawWorkTime.replace(/(\d{1,2})時間(\d{1,2})分/gi, '$1:$2').split(':');
workTime = parseInt(times[0] * 60) + parseInt(times[1]);
}
return {
"day": dayInfo[0].replace(/日/gi, ''),
"week": week,
"start": startTime,
"end": endTime,
"breakTime": breakTime,
"workTime": workTime,
"description": description,
"weekdaysFlag": weekdaysFlag,
"vacationFlag": false,
}
}
function workTime() {
// 140時間~180時間
const limitTime = document.querySelector('body > div.main.l-main > div.wrapper > div:nth-child(3) > div.d-flex.flex-wrap.justify-content-between > div.col-12.col-md-6.col-lg-4 > table > tbody > tr > td:nth-child(2)').textContent;
minAndMax = limitTime.split('~');
return [minAndMax[0].replace(/時間/gi, ''), minAndMax[1].replace(/時間/gi, '')];
}
function minWorkMinutes() {
return calM(workTime()[0]);
}
function maxWorkMinutes() {
return calM(workTime()[1]);
}
function calM(hour) {
return hour * 60;
}
function calMFromD(day) {
return day * 60 * 8;
}
function calH(minutes) {
return minutes / 60;
}
function calD(hour) {
return hour / 8;
}
function calDFromM(minutes) {
return (minutes / 60) / 8;
}
function pushMessage(messages, title, content) {
messages.push(title + ":" + content.toString());
}
function main() {
var weekdaysCount = 0;
var vacationCount = 0;
var workedMinutes = 0;
var workedCount = 0;
var totalWorkCount = 0;
document.querySelectorAll('table.table .report-row').forEach(function(e) {
const report = reportToHash(e);
if(report["vacationFlag"]) {
vacationCount += 1;
if(report["weekdaysFlag"]){
weekdaysCount += 1;
}
} else {
if(report["workTime"] > 0) {
workedCount += 1;
workedMinutes += report["workTime"];
}
totalWorkCount += 1;
}
});
const scheduledMinutes = calMFromD(totalWorkCount - workedCount);
const planWorkMinutes = workedMinutes + scheduledMinutes;
var messages = [];
messages.push("~~~~~~~~~~~~~~~~~");
pushMessage(messages, "稼働(時)", Math.floor(calH(workedMinutes)));
pushMessage(messages, "予定稼働(時)", Math.floor(calH(planWorkMinutes)));
pushMessage(messages, "稼働数(日)", workedCount + " (残:" + (totalWorkCount - workedCount) + ")");
pushMessage(messages, "稼働予定数(日)", totalWorkCount);
pushMessage(messages, "休日数(日)", vacationCount + " (土・日:" + weekdaysCount + ", 有給:" + (vacationCount - weekdaysCount) + ")");
const overWorkMinutes = (workedMinutes + scheduledMinutes) - maxWorkMinutes();
if(overWorkMinutes >= 60) {
pushMessage(messages, "★過労時間(時)", Math.floor(calH(overWorkMinutes)));
messages.push("~~~~~~~~~~~~~~~~~");
messages.push("(・ω・`三´・ω・). < 上限を超えそう!?");
return messages;
}
if(workedMinutes - minWorkMinutes() >= 0) {
messages.push("~~~~~~~~~~~~~~~~~");
messages.push("ヾ(○⌒∇⌒○)ノ < 目標達成!!!!!");
return messages;
}
const mustWorkMinutes = minWorkMinutes() - workedMinutes;
if(mustWorkMinutes < scheduledMinutes) {
if(scheduledMinutes - mustWorkMinutes < 60) {
pushMessage(messages, "★余裕時間(分)", scheduledMinutes - mustWorkMinutes);
} else {
messages.push("★余裕時間(時)" + Math.floor(calH(scheduledMinutes - mustWorkMinutes)));
if(scheduledMinutes - mustWorkMinutes > 60 * 8) {
pushMessage(messages, "★休み可能日数", Math.floor(calDFromM(scheduledMinutes - mustWorkMinutes)));
}
}
messages.push("~~~~~~~~~~~~~~~~~");
messages.push("( ^∀^) < 残業なしで達成可能!");
return messages;
}
const lackMinutes = mustWorkMinutes - scheduledMinutes;
if(lackMinutes < 60) {
pushMessage(messages, "◉不足時間(分)", lackMinutes);
} else {
pushMessage(messages, "◉不足時間(時)", Math.ceil(calH(lackMinutes)));
const avarageOverMinutes = Math.ceil(lackMinutes / (totalWorkCount - workedCount));
if(avarageOverMinutes < 60) {
pushMessage(messages, "◉平均残業", "1時間未満");
} else {
pushMessage(messages, "◉平均残業(時)", Math.ceil(calH(avarageOverMinutes) * 10) / 10);
}
}
messages.push("~~~~~~~~~~~~~~~~~");
messages.push("( ;´・ω・`) < 残業しなきゃ...");
return messages;
};
window.alert(main().join("\n"));
実行方法
Crowdtechの勤怠ページで該当スクリプトを実行する。 下記みたいなメッセージが出力される。
~~~~~~~~~~~~~~~~~
休日数: 11 (土・日:9, 有給:2)
稼働時間(時): 105.75
稼働日数: 14 (残:6)
稼働予定数: 20
☆ 余裕時間(時): 13
☆ 休み可能日数: 1
~~~~~~~~~~~~~~~~~
( ^∀^) < 残業なしで達成可能!