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