d0cd9c12ee1228a5cdd86cdc1ca2f8014fd2dfdd
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / services / lovefield.js
1 var osb = lf.schema.create('offline', 2);
2
3 osb.createTable('Object').
4     addColumn('type', lf.Type.STRING).          // class hint
5     addColumn('id', lf.Type.STRING).           // obj id
6     addColumn('object', lf.Type.OBJECT).
7     addPrimaryKey(['type','id']);
8
9 osb.createTable('CacheDate').
10     addColumn('type', lf.Type.STRING).          // class hint
11     addColumn('cachedate', lf.Type.DATE_TIME).  // when was it last updated
12     addPrimaryKey(['type']);
13
14 osb.createTable('Setting').
15     addColumn('name', lf.Type.STRING).
16     addColumn('value', lf.Type.STRING).
17     addPrimaryKey(['name']);
18
19 osb.createTable('StatCat').
20     addColumn('id', lf.Type.INTEGER).
21     addColumn('value', lf.Type.OBJECT).
22     addPrimaryKey(['id']);
23
24 osb.createTable('OfflineXact').
25     addColumn('seq', lf.Type.INTEGER).
26     addColumn('value', lf.Type.OBJECT).
27     addPrimaryKey(['seq'], true);
28
29 osb.createTable('OfflineBlocks').
30     addColumn('barcode', lf.Type.STRING).
31     addColumn('reason', lf.Type.STRING).
32     addPrimaryKey(['barcode']);
33
34 /**
35  * Core Service - egLovefield
36  *
37  * Lovefield wrapper factory for low level offline stuff
38  *
39  */
40 angular.module('egCoreMod')
41
42 .factory('egLovefield', ['$q','$rootScope','egCore','$timeout', 
43                  function($q , $rootScope , egCore , $timeout) { 
44
45     var service = {};
46
47     function connectOrGo() {
48
49         if (lf.offlineDB) { // offline DB connected
50             return $q.when();
51         }
52
53         if (service.cannotConnect) { // connection will never happen
54             return $q.reject();
55         }
56
57         if (service.connectPromise) { // connection in progress
58             return service.connectPromise;
59         }
60
61         // start a new connection attempt
62         
63         var deferred = $q.defer();
64
65         //console.debug('attempting offline DB connection');
66         try {
67             osb.connect().then(
68                 function(db) {
69                     console.debug('successfully connected to offline DB');
70                     service.connectPromise = null;
71                     lf.offlineDB = db;
72                     deferred.resolve();
73                 },
74                 function(err) {
75                     // assumes that a single connection failure means
76                     // a connection will never succeed.
77                     service.cannotConnect = true;
78                     console.error('Cannot connect to offline DB: ' + err);
79                 }
80             );
81         } catch (e) {
82             // .connect() will throw an error if it detects that a connection
83             // attempt is already in progress; this can happen with PhantomJS
84             console.error('Cannot connect to offline DB: ' + e);
85             service.cannotConnect = true;
86         }
87
88         service.connectPromise = deferred.promise;
89         return service.connectPromise;
90     }
91
92     service.isCacheGood = function (type) {
93
94         return connectOrGo().then(function() {
95             var cacheDate = lf.offlineDB.getSchema().table('CacheDate');
96
97             return lf.offlineDB.
98                 select(cacheDate.cachedate).
99                 from(cacheDate).
100                 where(cacheDate.type.eq(type)).
101                 exec().then(function(results) {
102                     if (results.length == 0) {
103                         return $q.when(false);
104                     }
105
106                     var now = new Date();
107     
108                     // hard-coded 1 day offline cache timeout
109                     return $q.when((now.getTime() - results[0]['cachedate'].getTime()) <= 86400000);
110                 })
111         });
112     }
113
114     service.destroyPendingOfflineXacts = function () {
115         return connectOrGo().then(function() {
116             var table = lf.offlineDB.getSchema().table('OfflineXact');
117             return lf.offlineDB.
118                 delete().
119                 from(table).
120                 exec();
121         });
122     }
123
124     service.havePendingOfflineXacts = function () {
125         return connectOrGo().then(function() {
126             var table = lf.offlineDB.getSchema().table('OfflineXact');
127             return lf.offlineDB.
128                 select(table.reason).
129                 from(table).
130                 exec().
131                 then(function(list) {
132                     return $q.when(Boolean(list.length > 0))
133                 });
134         });
135     }
136
137     service.retrievePendingOfflineXacts = function () {
138         return connectOrGo().then(function() {
139             var table = lf.offlineDB.getSchema().table('OfflineXact');
140             return lf.offlineDB.
141                 select(table.value).
142                 from(table).
143                 exec().
144                 then(function(list) {
145                     return $q.when(list.map(function(x) { return x.value }))
146                 });
147         });
148     }
149
150     service.destroyOfflineBlocks = function () {
151         return connectOrGo().then(function() {
152             var table = lf.offlineDB.getSchema().table('OfflineBlocks');
153             return $q.when(
154                 lf.offlineDB.
155                     delete().
156                     from(table).
157                     exec()
158             );
159         });
160     }
161
162     service.addOfflineBlock = function (barcode, reason) {
163         return connectOrGo().then(function() {
164             var table = lf.offlineDB.getSchema().table('OfflineBlocks');
165             return $q.when(
166                 lf.offlineDB.
167                     insertOrReplace().
168                     into(table).
169                     values([ table.createRow({ barcode : barcode, reason : reason }) ]).
170                     exec()
171             );
172         });
173     }
174
175     // Returns a promise with true for blocked, false for not blocked
176     service.testOfflineBlock = function (barcode) {
177         return connectOrGo().then(function() {
178             var table = lf.offlineDB.getSchema().table('OfflineBlocks');
179             return lf.offlineDB.
180                 select(table.reason).
181                 from(table).
182                 where(table.barcode.eq(barcode)).
183                 exec().then(function(list) {
184                     if(list.length > 0) return $q.when(list[0].reason);
185                     return $q.when(null);
186                 });
187         });
188     }
189
190     service.addOfflineXact = function (obj) {
191         return connectOrGo().then(function() {
192             var table = lf.offlineDB.getSchema().table('OfflineXact');
193             return $q.when(
194                 lf.offlineDB.
195                     insertOrReplace().
196                     into(table).
197                     values([ table.createRow({ value : obj }) ]).
198                     exec()
199             );
200         });
201     }
202
203     service.setStatCatsCache = function (statcats) {
204         if (lf.isOffline) return $q.when();
205
206         return connectOrGo().then(function() {
207             var table = lf.offlineDB.getSchema().table('StatCat');
208             var rlist = [];
209
210             angular.forEach(statcats, function (val) {
211                 rlist.push(table.createRow({
212                     id    : val.id(),
213                     value : egCore.idl.toHash(val)
214                 }));
215             });
216             return lf.offlineDB.
217                 insertOrReplace().
218                 into(table).
219                 values(rlist).
220                 exec();
221         });
222     }
223
224     service.getStatCatsCache = function () {
225         return connectOrGo().then(function() {
226
227             var table = lf.offlineDB.getSchema().table('StatCat');
228             var result = [];
229             return lf.offlineDB.
230                 select(table.value).
231                 from(table).
232                 exec().then(function(list) {
233                     angular.forEach(list, function (s) {
234                         var sc = egCore.idl.fromHash('actsc', s.value);
235     
236                         if (angular.isArray(sc.default_entries())) {
237                             sc.default_entries(
238                                 sc.default_entries().map( function (k) {
239                                     return egCore.idl.fromHash('actsced', k);
240                                 })
241                             );
242                         }
243     
244                         if (angular.isArray(sc.entries())) {
245                             sc.entries(
246                                 sc.entries().map( function (k) {
247                                     return egCore.idl.fromHash('actsce', k);
248                                 })
249                             );
250                         }
251     
252                         result.push(sc);
253                     });
254                     return $q.when(result);
255                 });
256     
257         });
258     }
259
260     service.setSettingsCache = function (settings) {
261         if (lf.isOffline) return $q.when();
262
263         return connectOrGo().then(function() {
264
265             var table = lf.offlineDB.getSchema().table('Setting');
266             var rlist = [];
267
268             angular.forEach(settings, function (val, key) {
269                 rlist.push(
270                     table.createRow({
271                         name  : key,
272                         value : JSON.stringify(val)
273                     })
274                 );
275             });
276
277             return lf.offlineDB.
278                 insertOrReplace().
279                 into(table).
280                 values(rlist).
281                 exec();
282         });
283     }
284
285     service.getSettingsCache = function (settings) {
286         return connectOrGo().then(function() {
287
288             var table = lf.offlineDB.getSchema().table('Setting');
289
290             var search_pred = table.name.isNotNull();
291             if (settings && settings.length) {
292                 search_pred = table.name.in(settings);
293             }
294                 
295             return lf.offlineDB.
296                 select(table.name, table.value).
297                 from(table).
298                 where(search_pred).
299                 exec().then(function(list) {
300                     angular.forEach(list, function (s) {
301                         s.value = JSON.parse(s.value)
302                     });
303                     return $q.when(list);
304                 });
305         });
306     }
307
308     service.setListInOfflineCache = function (type, list) {
309         if (lf.isOffline) return $q.when();
310
311         return connectOrGo().then(function() {
312
313             service.isCacheGood(type).then(function(good) {
314                 if (!good) {
315                     var object = lf.offlineDB.getSchema().table('Object');
316                     var cacheDate = lf.offlineDB.getSchema().table('CacheDate');
317                     var pkey = egCore.idl.classes[type].pkey;
318         
319                     angular.forEach(list, function(item) {
320                         var row = object.createRow({
321                             type    : type,
322                             id      : '' + item[pkey](),
323                             object  : egCore.idl.toHash(item)
324                         });
325                         lf.offlineDB.insertOrReplace().into(object).values([row]).exec();
326                     });
327         
328                     var row = cacheDate.createRow({
329                         type      : type,
330                         cachedate : new Date()
331                     });
332         
333                     console.log('egLovefield saving ' + type + ' list');
334                     lf.offlineDB.insertOrReplace().into(cacheDate).values([row]).exec();
335                 }
336             })
337         });
338     }
339
340     service.getListFromOfflineCache = function(type) {
341         return connectOrGo().then(function() {
342
343             var object = lf.offlineDB.getSchema().table('Object');
344
345             return lf.offlineDB.
346                 select(object.object).
347                 from(object).
348                 where(object.type.eq(type)).
349                 exec().then(function(results) {
350                     return $q.when(results.map(function(item) {
351                         return egCore.idl.fromHash(type,item['object'])
352                     }));
353                 });
354         });
355     }
356
357     service.reconstituteList = function(type) {
358         if (lf.isOffline) {
359             console.log('egLovefield reading ' + type + ' list');
360             return service.getListFromOfflineCache(type).then(function (list) {
361                 egCore.env.absorbList(list, type, true)
362                 return $q.when(true);
363             });
364         }
365         return $q.when(false);
366     }
367
368     service.reconstituteTree = function(type) {
369         if (lf.isOffline) {
370             console.log('egLovefield reading ' + type + ' tree');
371
372             var pkey = egCore.idl.classes[type].pkey;
373             var parent_field = 'parent';
374
375             if (type == 'aou') {
376                 parent_field = 'parent_ou';
377             }
378
379             return service.getListFromOfflineCache(type).then(function (list) {
380                 var hash = {};
381                 var top = null;
382                 angular.forEach(list, function (item) {
383
384                     // Special case for aou, to reconstitue ou_type
385                     if (type == 'aou') {
386                         if (item.ou_type()) {
387                             item.ou_type( egCore.idl.fromHash('aout', item.ou_type()) );
388                         }
389                     }
390
391                     hash[''+item[pkey]()] = item;
392                     if (!item[parent_field]()) {
393                         top = item;
394                     } else if (angular.isObject(item[parent_field]())) {
395                         // un-objectify the parent
396                         item[parent_field](
397                             item[parent_field]()[pkey]()
398                         );
399                     }
400                 });
401
402                 angular.forEach(list, function (item) {
403                     item.children([]); // just clear it out if there's junk in there
404
405                     item.children( list.filter(function (kid) {
406                         return kid[parent_field]() == item[pkey]();
407                     }) );
408                 });
409
410                 angular.forEach(list, function (item) {
411                     if (item[parent_field]()) {
412                         item[parent_field]( hash[''+item[parent_field]()] );
413                     }
414                 });
415
416                 egCore.env.absorbTree(top, type, true)
417                 return $q.when(true)
418             });
419         }
420         return $q.when(false);
421     }
422
423     return service;
424 }]);
425