|
|
|
const { BeanModel } = require("redbean-node/dist/bean-model");
|
|
|
|
const { parseTimeObject, parseTimeFromTimeObject, utcToLocal, localToUTC, log } = require("../../src/util");
|
|
|
|
const { timeObjectToUTC, timeObjectToLocal } = require("../util-server");
|
|
|
|
const { R } = require("redbean-node");
|
|
|
|
const dayjs = require("dayjs");
|
|
|
|
|
|
|
|
class Maintenance extends BeanModel {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return an object that ready to parse to JSON for public
|
|
|
|
* Only show necessary data to public
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
|
|
|
async toPublicJSON() {
|
|
|
|
|
|
|
|
let dateRange = [];
|
|
|
|
if (this.start_date) {
|
|
|
|
dateRange.push(utcToLocal(this.start_date));
|
|
|
|
if (this.end_date) {
|
|
|
|
dateRange.push(utcToLocal(this.end_date));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let timeRange = [];
|
|
|
|
let startTime = timeObjectToLocal(parseTimeObject(this.start_time));
|
|
|
|
timeRange.push(startTime);
|
|
|
|
let endTime = timeObjectToLocal(parseTimeObject(this.end_time));
|
|
|
|
timeRange.push(endTime);
|
|
|
|
|
|
|
|
let obj = {
|
|
|
|
id: this.id,
|
|
|
|
title: this.title,
|
|
|
|
description: this.description,
|
|
|
|
strategy: this.strategy,
|
|
|
|
intervalDay: this.interval_day,
|
|
|
|
active: !!this.active,
|
|
|
|
dateRange: dateRange,
|
|
|
|
timeRange: timeRange,
|
|
|
|
weekdays: (this.weekdays) ? JSON.parse(this.weekdays) : [],
|
|
|
|
daysOfMonth: (this.days_of_month) ? JSON.parse(this.days_of_month) : [],
|
|
|
|
timeslotList: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
const timeslotList = await this.getTimeslotList();
|
|
|
|
|
|
|
|
for (let timeslot of timeslotList) {
|
|
|
|
obj.timeslotList.push(await timeslot.toPublicJSON());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Array.isArray(obj.weekdays)) {
|
|
|
|
obj.weekdays = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Array.isArray(obj.daysOfMonth)) {
|
|
|
|
obj.daysOfMonth = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Maintenance Status
|
|
|
|
if (!obj.active) {
|
|
|
|
obj.status = "inactive";
|
|
|
|
} else if (obj.strategy === "manual") {
|
|
|
|
obj.status = "under-maintenance";
|
|
|
|
} else if (obj.timeslotList.length > 0) {
|
|
|
|
let currentTimestamp = dayjs().unix();
|
|
|
|
|
|
|
|
for (let timeslot of obj.timeslotList) {
|
|
|
|
if (dayjs.utc(timeslot.startDate).unix() <= currentTimestamp && dayjs.utc(timeslot.endDate).unix() >= currentTimestamp) {
|
|
|
|
log.debug("timeslot", "Timeslot ID: " + timeslot.id);
|
|
|
|
log.debug("timeslot", "currentTimestamp:" + currentTimestamp);
|
|
|
|
log.debug("timeslot", "timeslot.start_date:" + dayjs.utc(timeslot.startDate).unix());
|
|
|
|
log.debug("timeslot", "timeslot.end_date:" + dayjs.utc(timeslot.endDate).unix());
|
|
|
|
|
|
|
|
obj.status = "under-maintenance";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!obj.status) {
|
|
|
|
obj.status = "scheduled";
|
|
|
|
}
|
|
|
|
} else if (obj.timeslotList.length === 0) {
|
|
|
|
obj.status = "ended";
|
|
|
|
} else {
|
|
|
|
obj.status = "unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Only get future or current timeslots only
|
|
|
|
* @returns {Promise<[]>}
|
|
|
|
*/
|
|
|
|
async getTimeslotList() {
|
|
|
|
return R.convertToBeans("maintenance_timeslot", await R.getAll(`
|
|
|
|
SELECT maintenance_timeslot.*
|
|
|
|
FROM maintenance_timeslot, maintenance
|
|
|
|
WHERE maintenance_timeslot.maintenance_id = maintenance.id
|
|
|
|
AND maintenance.id = ?
|
|
|
|
AND ${Maintenance.getActiveAndFutureMaintenanceSQLCondition()}
|
|
|
|
`, [
|
|
|
|
this.id
|
|
|
|
]));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return an object that ready to parse to JSON
|
|
|
|
* @param {string} timezone If not specified, the timeRange will be in UTC
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
|
|
|
async toJSON(timezone = null) {
|
|
|
|
return this.toPublicJSON(timezone);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a list of weekdays that the maintenance is active for
|
|
|
|
* Monday=1, Tuesday=2 etc.
|
|
|
|
* @returns {number[]} Array of active weekdays
|
|
|
|
*/
|
|
|
|
getDayOfWeekList() {
|
|
|
|
log.debug("timeslot", "List: " + this.weekdays);
|
|
|
|
return JSON.parse(this.weekdays).sort(function (a, b) {
|
|
|
|
return a - b;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a list of days in month that maintenance is active for
|
|
|
|
* @returns {number[]} Array of active days in month
|
|
|
|
*/
|
|
|
|
getDayOfMonthList() {
|
|
|
|
return JSON.parse(this.days_of_month).sort(function (a, b) {
|
|
|
|
return a - b;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the start date and time for maintenance
|
|
|
|
* @returns {dayjs.Dayjs} Start date and time
|
|
|
|
*/
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the duraction of maintenance in seconds
|
|
|
|
* @returns {number} Duration of maintenance
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert data from socket to bean
|
|
|
|
* @param {Bean} bean Bean to fill in
|
|
|
|
* @param {Object} obj Data to fill bean with
|
|
|
|
* @returns {Bean} Filled bean
|
|
|
|
*/
|
|
|
|
static jsonToBean(bean, obj) {
|
|
|
|
if (obj.id) {
|
|
|
|
bean.id = obj.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply timezone offset to timeRange, as it cannot apply automatically.
|
|
|
|
if (obj.timeRange[0]) {
|
|
|
|
timeObjectToUTC(obj.timeRange[0]);
|
|
|
|
if (obj.timeRange[1]) {
|
|
|
|
timeObjectToUTC(obj.timeRange[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bean.title = obj.title;
|
|
|
|
bean.description = obj.description;
|
|
|
|
bean.strategy = obj.strategy;
|
|
|
|
bean.interval_day = obj.intervalDay;
|
|
|
|
bean.active = obj.active;
|
|
|
|
|
|
|
|
if (obj.dateRange[0]) {
|
|
|
|
bean.start_date = localToUTC(obj.dateRange[0]);
|
|
|
|
|
|
|
|
if (obj.dateRange[1]) {
|
|
|
|
bean.end_date = localToUTC(obj.dateRange[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bean.start_time = parseTimeFromTimeObject(obj.timeRange[0]);
|
|
|
|
bean.end_time = parseTimeFromTimeObject(obj.timeRange[1]);
|
|
|
|
|
|
|
|
bean.weekdays = JSON.stringify(obj.weekdays);
|
|
|
|
bean.days_of_month = JSON.stringify(obj.daysOfMonth);
|
|
|
|
|
|
|
|
return bean;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SQL conditions for active maintenance
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
static getActiveMaintenanceSQLCondition() {
|
|
|
|
return `
|
|
|
|
(
|
|
|
|
(maintenance_timeslot.start_date <= DATETIME('now')
|
|
|
|
AND maintenance_timeslot.end_date >= DATETIME('now')
|
|
|
|
AND maintenance.active = 1)
|
|
|
|
OR
|
|
|
|
(maintenance.strategy = 'manual' AND active = 1)
|
|
|
|
)
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SQL conditions for active and future maintenance
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
static getActiveAndFutureMaintenanceSQLCondition() {
|
|
|
|
return `
|
|
|
|
(
|
|
|
|
((maintenance_timeslot.end_date >= DATETIME('now')
|
|
|
|
AND maintenance.active = 1)
|
|
|
|
OR
|
|
|
|
(maintenance.strategy = 'manual' AND active = 1))
|
|
|
|
)
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Maintenance;
|