Issues (208)

server/rules.js (1 issue)

1
var express = require('express');
2
var extend  = require('extend');
3
4
// ------------------------------------------
5
//  CONSTRUCTOR
6
// ------------------------------------------
7
8
var init = function(){
9
  info('Starting RuleEngine ...');
10
  return RuleEngine;
11
}
12
13
// ------------------------------------------
14
//  SCRIPT
15
// ------------------------------------------
16
17
var dispatch = function(name, data, callback){
18
  // 1. Answer now to callback
19
  if (callback) { callback(data); }
20
  
21
  // 2. Then iterate on explicit rules
22
  iterate(name, data, 0);
23
}
24
25
var iterate = function(name, data, i){
26
  var options = data; // backward compatibility
27
  var rules = Config.rules;
28
  if (!rules){ return false; }
29
  
30
  for (; i < rules.length ; i++){
31
    try {
32
      var rule = rules[i];
33
      if (rule['disabled']){ continue; }
34
      if (rule['if'] != name){ continue; }
35
      if (rule['script']){ eval(decode(rule['script'])); }
36
      if (!rule['then']){ continue; }
37
      
38
      info('iterate ', i, rule);
39
      return SARAH.run(rule['then'], data, function(result){
40
        if (result) {
41
          // Handle speech
42
          SARAH.speak(result.tts);
43
          
44
          // Merge resutls
45
          extend(true, data, result);
46
        }
47
        
48
        // Recursive call
49
        iterate(name, data, i+1);
50
      });
51
      
52
    } catch(ex) { 
53
      warn('Rule %s:', name, rule, ex); 
54
      iterate(name, data, i+1);
55
    }
56
  }
57
}
58
59
var decode = function(str){
60
  if (!str){ return str; }
61
  return str.replace(/&quote;/g, '"').replace(/&#10;/g, '\n').trim();
62
}
63
64
// ------------------------------------------
65
//  SORTING MANAGER
66
// ------------------------------------------
67
68
var SEEK   = 1*60*3;  //  3 minutes
69
var SCOPE  = 1*60*10; // 10 minutes
70
var moment = require('moment'); moment.locale('fr');
71
72
var log = function(name, data){
73
  var now = moment();
74
  var day = moment().startOf('day');
75
  
76
  var entry        = {};
77
  entry.name       = name;
78
  entry.data       = data;
79
  entry.timeOfDay  = now.diff(day) / 1000;
80
  entry.dayOfWeek  = now.day();
81
  entry.dayOfMonth = now.date();
82
  entry.location   = 'house';
83
  entry.count      = 0;
84
  
85
  var i = indexOf(entry);
86
  if (i < 0){ 
87
    logs.push(entry); 
88
  } else {  
89
    entry = logs[i];
90
    entry.count++;
91
  }
92
  logs.sort(compare);
93
  
94
  return entry;
95
}
96
97
var indexOf = function(entry){
98
  for (var i = 0 ; i < logs.length; i++){
99
    if (compare(entry, logs[i]) == 0){ return i }
100
  }
101
  return -1;
102
}
103
104
var compare = function(x, y){
105
  var tod = x.timeOfDay  - y.timeOfDay;
106
  var dow = x.dayOfWeek  - y.dayOfWeek;
107
  var dom = x.dayOfMonth - y.dayOfMonth;
108
  var cnt = x.count - y.count;
109
  
110
  if (Math.abs(tod) < SCOPE ){
111
    if (x.name === y.name){ return 0; }
112
    var delta = (Math.abs(dom) + Math.abs(dow) + Math.abs(tod)) / ((Math.abs(cnt)+1) * SCOPE);
113
    if (dom != 0){ return dom>0 ? delta : -delta; }
114
    if (dow != 0){ return dow>0 ? delta : -delta; }
115
    if (cnt != 0){ return cnt>0 ? delta : -delta; }
116
    return tod>0 ? delta : -delta; //x.name.localeCompare(y.name); 
117
  }
118
  else { return tod; }
119
}
120
121
var next = function(entry){
122
  
123
  /*
124
  var i  = indexOf(entry);
125
  var d1 = 1000000;
126
  var e1 = undefined;
127
  
128
  var d2 = 1000000;
129
  var e2 = undefined;
130
  
131
  console.log(i,entry.name,'--------------------');
132
  for (var j in logs){
133
    console.log(j, logs[j].name);
134
  }
135
  
136
  if (i+1 < logs.length){
137
    e1 = logs[i+1];
138
    d1 = compare(e1, entry);
139
    console.log('i+1 <'+logs.length, e1.name, d1);
140
  }
141
  
142
  if (i-1 >= 0){
143
    e2 = logs[i-1];
144
    d2 = compare(entry, e2);
145
    console.log('i-1 >= 0', e2.name, d2);
146
  }
147
  
148
  return d1 < d2 ? e1 : e2;
149
  */
150
}
151
152
var logs = []
153
var getLogs = function(){
154
  return logs;
155
}
156
157
158
// ------------------------------------------
159
//  RULES
160
// ------------------------------------------
161
162
var getRules = function(ifname){
163
  
164
  // Set empty rules
165
  if (!Config.rules){
166
    Config.rules = [
167
      { "if": "before", "then": "", "script" : ""},
168
      { "if": "after",  "then": "", "script" : ""}
169
    ];
170
  }
171
  
172
  // Return all rules if there is no name
173
  if (!ifname){
174
    return Config.rules;
175
  }
176
  
177
  // Filter on name
178
  var tmp = [];
179
  for (var i in Config.rules){
180
    var rule = Config.rules[i];
181
    if (ifname == rule['if']){
182
      tmp.push(rule); 
183
    }
184
  }
185
  return tmp;
186
}
187
188
var save = function(body){
189
  if (!body['if']){ return; }
190
  
191
  var tmp = []
192
  for(var i in body['if']){
193
    var ifname = Helper.parse(body['if'][i]);
194
    if (!ifname) continue;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
195
    
196
    tmp.push({
197
      "if" : ifname,
198
      "then" : Helper.parse(body['then'][i]),
199
      "script" : Helper.parse(body['script'][i]),
200
      "disabled" : Helper.parse(body['disabled'][i]),
201
    });
202
  }
203
  Config.rules = tmp;
204
  SARAH.ConfigManager.save();
205
}
206
207
// ------------------------------------------
208
//  ROUTER
209
// ------------------------------------------
210
211
var Router = express.Router();
212
Router.post('/portal/rules', function(req, res, next) {
213
  var name = req.body.opSave;
214
  if (name){ save(req.body); }
215
  res.redirect('/portal/rules');
216
});
217
218
Router.all('/rules/script', function(req, res, next) {
219
  res.render('rules/rules-script.ejs', {'title' : i18n('modal.rules.script')});
220
});
221
222
// ------------------------------------------
223
//  PUBLIC
224
// ------------------------------------------
225
226
var RuleEngine = {
227
  'init' : init,
228
  'dispatch': dispatch,
229
  'log': log,
230
  'next': next,
231
  'getLogs': getLogs,
232
  'getRules': getRules,
233
  'before': function(){ return getRules('before')[0] },
234
  'after' : function(){ return getRules('after')[0]  },
235
  'Router' : Router
236
}
237
238
// Exports Manager
239
exports.init = RuleEngine.init;