diff --git a/server/model/maintenance.js b/server/model/maintenance.js index a507f870..35030801 100644 --- a/server/model/maintenance.js +++ b/server/model/maintenance.js @@ -112,6 +112,40 @@ class Maintenance extends BeanModel { return this.toPublicJSON(timezone); } + getDayOfWeekList() { + log.debug("timeslot", "List: " + this.weekdays); + return JSON.parse(this.weekdays).sort(function (a, b) { + return a - b; + }); + } + + getDayOfMonthList() { + return JSON.parse(this.days_of_month).sort(function (a, b) { + return a - b; + }); + } + + getStartDateTime() { + let startOfTheDay = dayjs.utc(this.start_date).format("HH:mm"); + log.debug("timeslot", "startOfTheDay: " + startOfTheDay); + + // Start Time + let startTimeSecond = dayjs.utc(this.start_time, "HH:mm").diff(dayjs.utc(startOfTheDay, "HH:mm"), "second"); + log.debug("timeslot", "startTime: " + startTimeSecond); + + // Bake StartDate + StartTime = Start DateTime + return dayjs.utc(this.start_date).add(startTimeSecond, "second"); + } + + getDuration() { + let duration = dayjs.utc(this.end_time, "HH:mm").diff(dayjs.utc(this.start_time, "HH:mm"), "second"); + // Add 24hours if it is across day + if (duration < 0) { + duration += 24 * 3600; + } + return duration; + } + static jsonToBean(bean, obj) { if (obj.id) { bean.id = obj.id; diff --git a/server/model/maintenance_timeslot.js b/server/model/maintenance_timeslot.js index 4c13632d..b20b9473 100644 --- a/server/model/maintenance_timeslot.js +++ b/server/model/maintenance_timeslot.js @@ -40,6 +40,7 @@ class MaintenanceTimeslot extends BeanModel { if (maintenance.strategy === "manual") { log.debug("maintenance", "No need to generate timeslot for manual type"); + } else if (maintenance.strategy === "single") { let bean = R.dispense("maintenance_timeslot"); bean.maintenance_id = maintenance.id; @@ -47,74 +48,131 @@ class MaintenanceTimeslot extends BeanModel { bean.end_date = maintenance.end_date; bean.generated_next = true; return await R.store(bean); - } else if (maintenance.strategy === "recurring-interval") { - let bean = R.dispense("maintenance_timeslot"); + } else if (maintenance.strategy === "recurring-interval") { // Prevent dead loop, in case interval_day is not set if (!maintenance.interval_day || maintenance.interval_day <= 0) { maintenance.interval_day = 1; } - let startOfTheDay = dayjs.utc(maintenance.start_date).format("HH:mm"); - log.debug("timeslot", "startOfTheDay: " + startOfTheDay); + return await this.handleRecurringType(maintenance, minDate, (startDateTime) => { + return startDateTime.add(maintenance.interval_day, "day"); + }); - // Start Time - let startTimeSecond = dayjs.utc(maintenance.start_time, "HH:mm").diff(dayjs.utc(startOfTheDay, "HH:mm"), "second"); - log.debug("timeslot", "startTime: " + startTimeSecond); + } else if (maintenance.strategy === "recurring-weekday") { + let dayOfWeekList = maintenance.getDayOfWeekList(); - // Duration - let duration = dayjs.utc(maintenance.end_time, "HH:mm").diff(dayjs.utc(maintenance.start_time, "HH:mm"), "second"); - // Add 24hours if it is across day - if (duration < 0) { - duration += 24 * 3600; + if (dayOfWeekList.length <= 0) { + log.debug("timeslot", "No weekdays selected?"); + return null; } - // Bake StartDate + StartTime = Start DateTime - let startDateTime = dayjs.utc(maintenance.start_date).add(startTimeSecond, "second"); - let endDateTime; + return await this.handleRecurringType(maintenance, minDate, (startDateTime) => { + while (true) { + startDateTime = startDateTime.add(1, "day"); - // Keep generating from the first possible date, until it is ok - while (true) { - log.debug("timeslot", "startDateTime: " + startDateTime.format()); + log.debug("timeslot", "nextDateTime: " + startDateTime); - // Handling out of effective date range - if (startDateTime.diff(dayjs.utc(maintenance.end_date)) > 0) { - log.debug("timeslot", "Out of effective date range"); - return null; + let day = startDateTime.local().day(); + log.debug("timeslot", "nextDateTime.day(): " + day); + + if (dayOfWeekList.includes(day)) { + return startDateTime; + } } + }); - endDateTime = startDateTime.add(duration, "second"); + } else if (maintenance.strategy === "recurring-day-of-month") { + let dayOfMonthList = maintenance.getDayOfMonthList(); + if (dayOfMonthList.length <= 0) { + log.debug("timeslot", "No day selected?"); + return null; + } - // If endDateTime is out of effective date range, use the end datetime from effective date range - if (endDateTime.diff(dayjs.utc(maintenance.end_date)) > 0) { - endDateTime = dayjs.utc(maintenance.end_date); - } + return await this.handleRecurringType(maintenance, minDate, (startDateTime) => { + while (true) { - // If minDate is set, the endDateTime must be bigger than it. - // And the endDateTime must be bigger current time - if ( - (!minDate || endDateTime.diff(minDate) > 0) && - endDateTime.diff(dayjs()) > 0 - ) { - break; - } + startDateTime = startDateTime.add(1, "day"); - startDateTime = startDateTime.add(maintenance.interval_day, "day"); - } + let day = parseInt(startDateTime.local().format("D")); - bean.maintenance_id = maintenance.id; - bean.start_date = localToUTC(startDateTime); - bean.end_date = localToUTC(endDateTime); - bean.generated_next = false; - return await R.store(bean); - } else if (maintenance.strategy === "recurring-weekday") { - // TODO - } else if (maintenance.strategy === "recurring-day-of-month") { - // TODO + log.debug("timeslot", "day: " + day); + + // Check 1-31 + if (dayOfMonthList.includes(day)) { + return startDateTime; + } + + // Check "lastDay1","lastDay2"... + let daysInMonth = startDateTime.daysInMonth(); + let lastDayList = []; + + // Small first, e.g. 28 > 29 > 30 > 31 + for (let i = 4; i >= 1; i--) { + if (dayOfMonthList.includes("lastDay" + i)) { + lastDayList.push(daysInMonth - i + 1); + } + } + log.debug("timeslot", "lastDayList: " + lastDayList); + if (lastDayList.includes(day)) { + return startDateTime; + } + } + }); } else { throw new Error("Unknown maintenance strategy"); } } + + /** + * Generate a next timeslot for all recurring types + * @param maintenance + * @param minDate + * @param nextDayCallback The logic how to get the next possible day + * @returns {Promise} + */ + static async handleRecurringType(maintenance, minDate, nextDayCallback) { + let bean = R.dispense("maintenance_timeslot"); + + let duration = maintenance.getDuration(); + let startDateTime = maintenance.getStartDateTime(); + let endDateTime; + + // Keep generating from the first possible date, until it is ok + while (true) { + log.debug("timeslot", "startDateTime: " + startDateTime.format()); + + // Handling out of effective date range + if (startDateTime.diff(dayjs.utc(maintenance.end_date)) > 0) { + log.debug("timeslot", "Out of effective date range"); + return null; + } + + endDateTime = startDateTime.add(duration, "second"); + + // If endDateTime is out of effective date range, use the end datetime from effective date range + if (endDateTime.diff(dayjs.utc(maintenance.end_date)) > 0) { + endDateTime = dayjs.utc(maintenance.end_date); + } + + // If minDate is set, the endDateTime must be bigger than it. + // And the endDateTime must be bigger current time + if ( + (!minDate || endDateTime.diff(minDate) > 0) && + endDateTime.diff(dayjs()) > 0 + ) { + break; + } + + startDateTime = nextDayCallback(startDateTime); + } + + bean.maintenance_id = maintenance.id; + bean.start_date = localToUTC(startDateTime); + bean.end_date = localToUTC(endDateTime); + bean.generated_next = false; + return await R.store(bean); + } } module.exports = MaintenanceTimeslot; diff --git a/src/languages/en.js b/src/languages/en.js index 7db8147f..187d4810 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -620,6 +620,7 @@ export default { weekdayShortFri: "Fri", weekdayShortSat: "Sat", weekdayShortSun: "Sun", + dayOfWeek: "Day of Week", dayOfMonth: "Day of Month", lastDay: "Last Day", lastDay1: "Last Day of Month", diff --git a/src/pages/EditMaintenance.vue b/src/pages/EditMaintenance.vue index 8cf28dfb..8b2be35a 100644 --- a/src/pages/EditMaintenance.vue +++ b/src/pages/EditMaintenance.vue @@ -91,8 +91,8 @@ - - + + @@ -136,7 +136,7 @@ @@ -297,9 +298,9 @@ export default { value: 6, }, { - id: "weekday7", + id: "weekday0", langKey: "weekdayShortSun", - value: 7, + value: 0, }, ], };