UNPKG

5.63 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 const extendedFormatter = (value) => {
60 // A `null` value corresponds to a null/MVs branch
61 if (_.isNull(value)) {
62 return 'null';
63 }
64 // The empty object `{}` corresponds to an optional branch
65 else if (_.isPlainObject(value) && _.isEmpty(value)) {
66 return 'N/A';
67 }
68 return formatter(value);
69 };
70 if (!_.isUndefined(value)) {
71 return extendedFormatter(value);
72 }
73 return extendedFormatter;
74}
75
76const FORMATTER_FROM_DECISION_RULE = {
77 [OPERATORS.IS]: {
78 [TYPE_ANY]: ({ property, operand, operandFormatter }) => {
79 if (property) {
80 return `'${property}' is ${operandFormatter(operand)}`;
81 }
82 return `is ${operandFormatter(operand)}`;
83 }
84 },
85 [OPERATORS.IN]: {
86 [TYPE_ANY]: ({ property, operand, operandFormatter }) => {
87 if (property) {
88 return `'${property}' in [${operandFormatter(
89 operand[0]
90 )}, ${operandFormatter(operand[1])}[`;
91 }
92 return `[${operandFormatter(operand[0])}, ${operandFormatter(
93 operand[1]
94 )}[`;
95 },
96 [TYPES.day_of_week]: ({ property, operand, operandFormatter }) => {
97 const day_from = Math.floor(operand[0]);
98 const day_to = Math.floor(operand[1]);
99 // If there is only one day in the interval
100 if (day_to - day_from == 1 || (day_from == 6 && day_to == 0)) {
101 if (property) {
102 return `'${property}' is ${operandFormatter(day_from)}`;
103 }
104 return operandFormatter(day_from);
105 }
106 else {
107 if (property) {
108 return `'${property}' from ${operandFormatter(
109 day_from
110 )} to ${operandFormatter((7 + day_to - 1) % 7)}`;
111 }
112 return `${operandFormatter(day_from)} to ${operandFormatter(
113 (7 + day_to - 1) % 7
114 )}`;
115 }
116 },
117 [TYPES.month_of_year]: ({ property, operand, operandFormatter }) => {
118 const month_from = Math.floor(operand[0]);
119 const month_to = Math.floor(operand[1]);
120 if (month_to - month_from == 1 || (month_from == 12 && month_to == 1)) {
121 // One month in the interval
122 if (property) {
123 return `'${property}' is ${operandFormatter(month_from)}`;
124 }
125 return operandFormatter(month_from);
126 }
127 else if (month_to == 1) {
128 // (Excluded) upper bound is january
129 if (property) {
130 return `'${property}' from ${operandFormatter(
131 month_from
132 )} to ${operandFormatter(12)}`;
133 }
134 return `${operandFormatter(month_from)} to ${operandFormatter(12)}`;
135 }
136 else {
137 if (property) {
138 return `'${property}' from ${operandFormatter(
139 month_from
140 )} to ${operandFormatter(month_to - 1)}`;
141 }
142 return `${operandFormatter(month_from)} to ${operandFormatter(
143 month_to - 1
144 )}`;
145 }
146 }
147 },
148 [OPERATORS.GTE]: {
149 [TYPE_ANY]: ({ property, operand, operandFormatter }) => {
150 if (property) {
151 return `'${property}' >= ${operandFormatter(operand)}`;
152 }
153 return `>= ${operandFormatter(operand)}`;
154 }
155 },
156 [OPERATORS.LT]: {
157 [TYPE_ANY]: ({ property, operand, operandFormatter }) => {
158 if (property) {
159 return `'${property}' < ${operandFormatter(operand)}`;
160 }
161 return `< ${operandFormatter(operand)}`;
162 }
163 }
164};
165
166export function formatDecisionRules(decisionRules) {
167 return decisionRules
168 .map(({ property, type, operand, operator }) => {
169 const operatorFormatters = FORMATTER_FROM_DECISION_RULE[operator];
170 if (!operatorFormatters) {
171 throw new Error(
172 `Unable to format the given decision rule: unknown operator '${operator}'.`
173 );
174 }
175 const formatter =
176 operatorFormatters[type] || operatorFormatters[TYPE_ANY];
177 const operandFormatter = formatProperty(type || TYPE_ANY);
178 return formatter({ property, type, operator, operandFormatter, operand });
179 })
180 .join(' and ');
181}