UNPKG

5.29 kBJavaScriptView Raw
1import _ from 'lodash';
2import Time from './time';
3import { OPERATORS, TYPE_ANY, TYPES } from './constants';
4
5const DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
6
7const MONTH = [
8 'Jan',
9 'Feb',
10 'Mar',
11 'Apr',
12 'May',
13 'Jun',
14 'Jul',
15 'Aug',
16 'Sep',
17 'Oct',
18 'Nov',
19 'Dec'
20];
21
22const PROPERTY_FORMATTER = {
23 [TYPE_ANY]: (value) => value,
24 [TYPES.continuous]: (number) => `${Math.round(number * 100) / 100}`,
25 [TYPES.time_of_day]: (time) => {
26 const _time = time instanceof Time ? time.time_of_day : time;
27 const hours = Math.floor(_time);
28 const hoursStr = _.padStart(hours, 2, '0');
29 const decMinutes = Math.round((_time - hours) * 60 * 100) / 100;
30 const minutes = Math.floor(decMinutes);
31 const minutesStr = _.padStart(minutes, 2, '0');
32 const seconds = Math.round((decMinutes - minutes) * 60);
33 const secondsStr = _.padStart(seconds, 2, '0');
34
35 if (seconds > 0) {
36 return `${hoursStr}:${minutesStr}:${secondsStr}`;
37 }
38 else {
39 return `${hoursStr}:${minutesStr}`;
40 }
41 },
42 [TYPES.day_of_week]: (day) => {
43 const _day = day instanceof Time ? day.day_of_week : day;
44 return DAYS[_day];
45 },
46 [TYPES.day_of_month]: (day) => {
47 const _day = day instanceof Time ? day.day_of_month : day;
48 return _.padStart(_day, 2, '0');
49 },
50 // Months are in [1; 12] thus -1 to be index month name in [0; 11]
51 [TYPES.month_of_year]: (month) => {
52 const _month = month instanceof Time ? month.month_of_year : month;
53 return MONTH[_month - 1];
54 }
55};
56
57export function formatProperty(type, value = undefined) {
58 const formatter = PROPERTY_FORMATTER[type] || PROPERTY_FORMATTER[TYPE_ANY];
59 if (value !== undefined) {
60 return formatter(value);
61 }
62 return formatter;
63}
64
65const FORMATTER_FROM_DECISION_RULE = {
66 [OPERATORS.IS]: {
67 [TYPE_ANY]: ({ property, operand, operandFormatter }) => {
68 if (property) {
69 return `'${property}' is ${operandFormatter(operand)}`;
70 }
71 return `is ${operandFormatter(operand)}`;
72 }
73 },
74 [OPERATORS.IN]: {
75 [TYPE_ANY]: ({ property, operand, operandFormatter }) => {
76 if (property) {
77 return `'${property}' in [${operandFormatter(
78 operand[0]
79 )}, ${operandFormatter(operand[1])}[`;
80 }
81 return `[${operandFormatter(operand[0])}, ${operandFormatter(
82 operand[1]
83 )}[`;
84 },
85 [TYPES.day_of_week]: ({ property, operand, operandFormatter }) => {
86 const day_from = Math.floor(operand[0]);
87 const day_to = Math.floor(operand[1]);
88 // If there is only one day in the interval
89 if (day_to - day_from == 1 || (day_from == 6 && day_to == 0)) {
90 if (property) {
91 return `'${property}' is ${operandFormatter(day_from)}`;
92 }
93 return operandFormatter(day_from);
94 }
95 else {
96 if (property) {
97 return `'${property}' from ${operandFormatter(
98 day_from
99 )} to ${operandFormatter((7 + day_to - 1) % 7)}`;
100 }
101 return `${operandFormatter(day_from)} to ${operandFormatter(
102 (7 + day_to - 1) % 7
103 )}`;
104 }
105 },
106 [TYPES.month_of_year]: ({ property, operand, operandFormatter }) => {
107 const month_from = Math.floor(operand[0]);
108 const month_to = Math.floor(operand[1]);
109 if (month_to - month_from == 1 || (month_from == 12 && month_to == 1)) {
110 // One month in the interval
111 if (property) {
112 return `'${property}' is ${operandFormatter(month_from)}`;
113 }
114 return operandFormatter(month_from);
115 }
116 else if (month_to == 1) {
117 // (Excluded) upper bound is january
118 if (property) {
119 return `'${property}' from ${operandFormatter(
120 month_from
121 )} to ${operandFormatter(12)}`;
122 }
123 return `${operandFormatter(month_from)} to ${operandFormatter(12)}`;
124 }
125 else {
126 if (property) {
127 return `'${property}' from ${operandFormatter(
128 month_from
129 )} to ${operandFormatter(month_to - 1)}`;
130 }
131 return `${operandFormatter(month_from)} to ${operandFormatter(
132 month_to - 1
133 )}`;
134 }
135 }
136 },
137 [OPERATORS.GTE]: {
138 [TYPE_ANY]: ({ property, operand, operandFormatter }) => {
139 if (property) {
140 return `'${property}' >= ${operandFormatter(operand)}`;
141 }
142 return `>= ${operandFormatter(operand)}`;
143 }
144 },
145 [OPERATORS.LT]: {
146 [TYPE_ANY]: ({ property, operand, operandFormatter }) => {
147 if (property) {
148 return `'${property}' < ${operandFormatter(operand)}`;
149 }
150 return `< ${operandFormatter(operand)}`;
151 }
152 }
153};
154
155export function formatDecisionRules(decisionRules) {
156 return decisionRules
157 .map(({ property, type, operand, operator }) => {
158 const operatorFormatters = FORMATTER_FROM_DECISION_RULE[operator];
159 if (!operatorFormatters) {
160 throw new Error(
161 `Unable to format the given decision rule: unknown operator '${operator}'.`
162 );
163 }
164 const formatter =
165 operatorFormatters[type] || operatorFormatters[TYPE_ANY];
166 const operandFormatter = formatProperty(type || TYPE_ANY);
167 return formatter({ property, type, operator, operandFormatter, operand });
168 })
169 .join(' and ');
170}