eHealthAfrica/LMIS-Chrome

View on GitHub
app/scripts/services/fixture-loader-service.js

Summary

Maintainability
C
1 day
Test Coverage
'use strict';

angular.module('lmisChromeApp')
  .service('fixtureLoaderService', function($q, $http, locationService, facilityFactory, $rootScope, memoryStorageService, config, storageService, utility, pouchdb, syncService, pouchStorageService) {

    var PATH = 'scripts/fixtures/';
    var REMOTE_URI = config.api.url;
    this.REMOTE_FIXTURES = [
      storageService.UOM,
      storageService.UOM_CATEGORY,
      storageService.PRODUCT_TYPES,
      storageService.PRODUCT_CATEGORY,
      storageService.PRODUCT_PRESENTATION,
      storageService.CCEI,
      storageService.MODE_OF_ADMINISTRATION,
      storageService.PRODUCT_FORMULATIONS,
      storageService.PRODUCT_PROFILES
    ];

    /**
     *  loads database from remote db.
     * @param {String} dbName
     * @returns {*}
     */
    var loadDatabaseFromRemote = function(dbName) {
      //var dbUrl = [REMOTE_URI, '/', dbName].join('');
      return pouchStorageService.getRemoteDB(dbName)
        .then(function(db){
          return db.allDocs({include_docs:true})
            .then(function(res){
              var data = res.rows;
              var dbRecords = [];
              for (var i in data) {
                var record = data[i].doc;
                if(record.uuid){
                  dbRecords.push(record);
                }
              }
              return utility.castArrayToObject(dbRecords, 'uuid');
            });
        });

    };

    /**
     *  This loads databases from remote server.
     *
     * @private
     * @param {Array} dbNames - collection of dbNames to be loaded from remote.
     * @returns {Promise}
     */
    var loadDatabasesFromRemote = function(dbNames) {
      var promises = {};
      for (var i in dbNames) {
        var dbName = dbNames[i];
        promises[dbName] = loadDatabaseFromRemote(dbName);
      }
      return $q.all(promises);
    };

    /**
     * This saves databases to the local storage.
     *
     * @private
     * @param {Object} databases - nested key-value pairs object, where the key is the database name, pair is the database records.
     * @returns {Promise}
     */
    var saveDatabasesToLocalStorage = function(databases) {
      var promises = [], promise, db;
      var dbNames = Object.keys(databases);
      dbNames.forEach(function(dbName) {
        db = databases[dbName];
        // TODO: change callee's return value to an array
        db = utility.values(db);
        promise = storageService.setDatabase(dbName, db);
        promises.push(promise);
      });
      return $q.all(promises);
    };

    this.saveDatabases = function(databases) {
      return saveDatabasesToLocalStorage(databases);
    };

    /**
     * loads given databases into memory store.
     * @private
     * @param {Object} databases - nested object, dbName is the key, values are nested database records.
     */
    var loadDatabasesIntoMemoryStorage = function(databases) {
      if (Object.prototype.toString.call(databases) !== '[object Object]') {
        throw 'databases should be nested object.';
      }
      for (var dbName in databases) {
        var db = databases[dbName];
        memoryStorageService.setDatabase(dbName, db);
      }
    };

    this.loadDatabasesIntoMemoryStorage = function(databases) {
      return loadDatabasesIntoMemoryStorage(databases);
    };

    var loadDataToRemote = function(dbName, batch) {
      //TODO: utility function used to load fixtures into remote DB deprecated if not necessary later.
      var promises = [];
      for (var key in batch) {
        var record = batch[key];
        promises.push(syncService.syncUpRecord(dbName, record, true));
      }
      return $q.all(promises);
    };

    /**
     * loads fixture into memory, then loads the result into remote database.
     * @param {Array} fixtureNames
     * @returns {*|Promise|Promise|!Promise.<R>}
     */
    this.loadFixturesIntoRemoteDb = function(fixtureNames) {
      //TODO: utility function created for loading fixtures into remote db. deprecate when no longer needed.
      var deferred = $q.defer();
      loadFilesIntoCache(fixtureNames)
        .then(function(result) {
          var promises = [];
          for (var dbName in result) {
            var db = result[dbName];
            if (!angular.isArray(db)) {
              db = utility.values(db);
            }
            promises.push(loadDataToRemote(dbName, db));
          }
          $q.all(promises)
            .then(function(result) {
              deferred.resolve(result);
            })
            .catch(function(reason) {
              deferred.reject(reason);
            });
        })
        .catch(function(reason) {
          deferred.reject(reason);
        });
      return deferred.promise;
    };

    // TODO: deprecate. Pouch returns an array of docs by sequence, whereas
    // we're expecting a nested object by ID. Consider rewriting callers, or
    // perhaps using a persisted Pouch view?
    var indexByID = function(results) {
      var table, _results = {}, _table;
      var byID = function(doc) {
        _table[doc.uuid] = doc;
      };
      for (var db in results) {
        table = results[db];
        _table = {};
        table.forEach(byID);
        _results[db] = _table;
      }
      return _results;
    };

    /**
     *  This reads databases from local storage into memory storage.
     *
     * @param {Array} dbNames - array of strings which are databases names.
     * @returns {Promise|*}
     */
    this.loadLocalDatabasesIntoMemory = function(dbNames) {
      var promises = {};
      for (var index in dbNames) {
        var dbName = dbNames[index];
        promises[dbName] = storageService.get(dbName);
      }
      return $q.all(promises)
        .then(indexByID)
        .then(function(results) {
          loadDatabasesIntoMemoryStorage(results);
          return results;
        });
    };

    var loadRemoteAndUpdateStorageAndMemory = function(dbNames) {
      var promises = {};
      //TODO: refactor this later.
      var databases = 'DATABASES';
      var lgas = 'LGAS'
      promises[databases] = loadDatabasesFromRemote(dbNames);
      promises[lgas] = getLgasByState("f87ed3e017cf4f8db26836fd910e4cc8");
      return $q.all(promises)
        .then(function(res) {
          var result = res[databases];
          result[storageService.LOCATIONS] = res[lgas];
          return saveDatabasesToLocalStorage(result)
            .then(function(res) {
              loadDatabasesIntoMemoryStorage(result);
              return res;
            });
        });
    };

    this.loadRemoteAndUpdateStorageAndMemory = function(dbNames) {
      return loadRemoteAndUpdateStorageAndMemory(dbNames);
    };

    /**
     *  This function does the following:
     *  1. loads databases from remote.
     *  2. saves the remote databases to local storage.
     *  3. loads the local databases into memory storage.
     *
     * @param {Array} dbNames - collection of database names(strings).
     * @returns {Promise|*}
     */
    this.setupLocalAndMemoryStore = function(dbNames) {
      $rootScope.$emit('START_LOADING', {started: true});
      return loadRemoteAndUpdateStorageAndMemory(dbNames)
        .finally(function() {
          $rootScope.$emit('LOADING_COMPLETED', {completed: true});
        });
    };

    var loadFilesIntoCache = function(fileNames) {
      //TODO: deprecate ASAP, if we stop using fixture files.
      $rootScope.$emit('START_LOADING', {started: true});
      var deferred = $q.defer();
      var promises = {};
      for (var i in fileNames) {
        var fileName = fileNames[i];
        var fileUrl = [PATH, fileName, '.json'].join('');
        promises[fileName] = $http.get(fileUrl);
      }

      $q.all(promises)
        .then(function(result) {
          var resultSet = {};
          for (var fileName in result) {
            var data = result[fileName].data;
            if (angular.isArray(data)) {
              continue; //skip array fixtures, we don't use them currently
            }
            resultSet[fileName] = data;
            console.log('loaded: ' + fileName);
            memoryStorageService.setDatabase(fileName, data);
          }
          $rootScope.$emit('LOADING_COMPLETED', {completed: true});
          deferred.resolve(resultSet);
        })
        .catch(function(reason) {
          deferred.reject(reason);
        });
      return deferred.promise;
    };

    this.loadFiles = function(fileNames) {
      //TODO: deprecate ASAP
      return loadFilesIntoCache(fileNames);
    };

    var getLgasByState = function(stateId) {
      return pouchStorageService.getRemoteDB(storageService.LOCATIONS)
        .then(function(db){
          var lgaView = 'lga/by_id';
          var options = {
            include_docs: true
          };
          return db.get(stateId)
            .then(function(state) {
              options.keys = state.lgas;
              return db.query(lgaView, options)
                .then(function(res) {
                  return res.rows
                    .map(function(row) {
                      var lga = row.value;
                      if (utility.has(lga, '_id')) {
                        return lga;
                      }
                    });
                });
            });
        });

    };

    this.getLgas = getLgasByState;

    this.getWardsByLgas = function(lgas) {
      return pouchStorageService.getRemoteDB(storageService.LOCATIONS)
        .then(function(db){
          var lgaView = 'lga/by_id';
          var wardView = 'ward/by_id';
          var options = {
            include_docs: true,
            keys: lgas
          };
          var wardIds = [];
          return db.query(lgaView, options)
            .then(function(res) {
              res.rows.forEach(function(row) {
                var lga = row.value;
                if (utility.has(lga, 'wards') && angular.isArray(lga.wards)) {
                  wardIds = wardIds.concat(lga.wards);
                }
              });
              options.keys = wardIds;
              return db.query(wardView, options)
                .then(function(res) {
                  return res.rows
                    .map(function(row) {
                      var ward = row.value;
                      if (utility.has(ward, '_id')) {
                        return ward;
                      }
                    });
                });
            });
        });

    };

    var getFacilities = function(facilityIds) {
      var view = 'facilities/by_id';
      var options = {
        include_docs: true,
        keys: facilityIds
      };
      return pouchStorageService.getRemoteDB('facilities')
        .then(function(db){
          return db.query(view, options)
            .then(function(res) {
              return res.rows
                .map(function(row) {
                  return row.value;
                });
            });
        });
    };

    this.setupWardsAndFacilitesByLgas = function(lgas) {
      return this.getWardsByLgas(lgas)
        .then(function(wards) {
          return locationService.saveBatch(wards)
            .then(function() {
              var wardFacilityIds = [];
              wards.forEach(function(w) {
                if (angular.isArray(w.facilities)) {
                  wardFacilityIds = wardFacilityIds.concat(w.facilities);
                }
              });
              return getFacilities(wardFacilityIds)
                .then(function(facilities){
                  return facilityFactory.saveBatch(facilities);
                });
            });
        });
    };

  });