@publidata/utils-data-manager
Version:
Collection of methods to extract data from publidata
480 lines (421 loc) • 14.3 kB
JavaScript
const axios = require("axios");
const { isEmpty } = require("lodash");
const haversine = require("haversine-distance");
const DataSourcePublidata = require("./../Publidata");
const { getId } = require("@publidata/utils-mapper");
const { facilityIconTranslation } = require("@publidata/utils-translation");
const { instanciatePublidataObject } = require("../../types");
const DATA_STORE_COLLECTIONS_MAX_CAPACITY = 100;
const ALLOWED_FACILITY_TYPES = [
71, 85, 86, 87, 88, 89, 91, 413, 458, 459, 496, 497, 559, 562, 566, 600, 726,
772, 777, 784, 785, 790, 799, 801, 802, 804, 808
];
class DataSourcePipedrive {
constructor(uid, instance) {
this.instance = instance;
this.endpoint = uid
? `${instance.custom_settings.url.utils}/api/v1/redis/${uid}`
: `${instance.custom_settings.url.utils}/api/v1/redis/9d60b180-0151-11ea-b0d5-69d50c2027c3`;
this.publidata = new DataSourcePublidata(instance);
this.dataStore = {
collections: {
data: new Map(),
maxCapacity: DATA_STORE_COLLECTIONS_MAX_CAPACITY
}
};
}
verifyDataStoreCapacity(dataStoreType) {
if (dataStoreType.data.length) {
return dataStoreType.data.length < dataStoreType.maxCapacity;
}
return true;
}
removeFirstItemFromDataStore(dataStoreType) {
const keysIterator = dataStoreType.data.keys();
const firstKey = keysIterator.next().value;
dataStoreType.data.delete(firstKey);
}
saveDataInDataStore(uri, value) {
const { collections } = this.dataStore;
if (!this.verifyDataStoreCapacity(collections)) {
this.removeFirstItemFromDataStore(collections);
}
collections.data.set(uri, value);
}
getDataFromDataStore(uri) {
const { collections } = this.dataStore;
const dataInCollections = collections.data.get(uri);
if (dataInCollections) return dataInCollections;
return null;
}
encodeTypes(types) {
return types.toString().replace(",", ":");
}
getObject(params) {
const { types, size } = params;
const encodedTypes = this.encodeTypes(types);
const uri = `${this.endpoint}:${encodedTypes}${
size ? `?size=${size}` : ""
}`;
const storageUri = `${this.endpoint}:${encodedTypes}${
size ? `:${size}` : ""
}`;
const object = this.getDataFromDataStore(storageUri);
if (object)
return new Promise(resolve => {
if (Array.isArray(object)) {
resolve({ data: object });
} else resolve(object);
});
return new Promise((resolve, reject) => {
axios
.get(uri)
.then(response => {
const { data } = response;
const { value } = data;
let returnValue;
if (Array.isArray(value)) returnValue = value;
else returnValue = [value];
this.saveDataInDataStore(storageUri, returnValue);
resolve({ data: returnValue });
})
.catch(err => reject(err));
});
}
getInstance() {
const params = {
types: ["instance"]
};
return this.getObject(params);
}
getDocumentsByInstanceId(params) {
return this.publidata.getDocumentsByInstanceId(params);
}
getDocumentsByIds(params) {
const { instances } = params;
return this.publidata.getDocumentsByIds({ instances });
}
getProceduresByIds(params) {
return this.publidata.getProceduresByIds(params);
}
getCities(params) {
return this.publidata.getCities({
...params,
instances: []
});
}
getSectors(params) {
return this.publidata.getSectors(params);
}
getItems(params) {
return this.publidata.getItems(params);
}
getAlerts(params = {}) {
return this.publidata.getAlerts(params);
}
getAlert(params = {}) {
return this.publidata.getAlert(params);
}
getFacilitiesAlerts(params) {
return this.publidata.getFacilitiesAlerts(params);
}
getWasteCollectionAlerts(params) {
return this.publidata.getWasteCollectionAlerts(params);
}
getNews(params) {
return this.publidata.getNews(params);
}
getNewsTotalPages(params) {
return this.publidata.getNewsTotalPages(params);
}
getData(params) {
return this.publidata.getData(params);
}
//----------------------------------------------------
//-------------- GET: WASTE COLLECTIONS --------------
//----------------------------------------------------
getWasteCollections(params = {}) {
const newParams = {
...params,
types: ["waste_collections"],
size: 100
};
if (!params.ids) return this.getObject(newParams);
return new Promise(resolve => {
this.getObject(newParams).then(({ data }) => {
const { ids } = params;
const normalizeId = id => parseInt(id, 10);
const normalizedIds = ids.map(normalizeId);
const filteredData = data.filter(wasteCollection =>
normalizedIds.includes(normalizeId(getId(wasteCollection)))
);
resolve({ data: filteredData });
});
});
}
getWasteCollection = this.getWasteCollections;
getWasteCollectionById = this.getWasteCollections;
//----------------------------------------------------
//----------------- GET : FACILITIES -----------------
//----------------------------------------------------
getWasteCollectionFacilities(params) {
return new Promise((resolve, reject) => {
const ids = params.services;
this.getWasteCollectionById({ ids })
.then(wasteCollection => {
const publidataWasteCollection = instanciatePublidataObject(
wasteCollection.data[0],
this.instance,
this
);
const [facility_types] = publidataWasteCollection.facilityTypes; // eslint-disable-line camelcase
/* eslint-disable camelcase */
if (facility_types) {
this.getFacilitiesByType({ facility_types, ...params })
.then(response => {
resolve(response);
})
.catch(err => reject(err));
}
})
.catch(err => reject(err));
});
}
getWasteCollectionFacility(params) {
return new Promise((resolve, reject) => {
const ids = params.services;
this.getWasteCollectionById({ ids })
.then(wasteCollection => {
const publidataWasteCollection = instanciatePublidataObject(
wasteCollection.data[0],
this.instance,
this
);
/* eslint-disable camelcase */
const [facility_types] = publidataWasteCollection.facilityTypes;
if (facility_types) {
this.getFacilityByType({ facility_types, ...params })
.then(response => {
resolve(response);
})
.catch(err => reject(err));
} else {
resolve({ data: [] });
}
})
.catch(err => reject(err));
});
}
// ------------------------
// Facility types based getters
// ------------------------
getRecyclingBins(garbageType) {
const newParams = {
types: ["recycling_bins", garbageType],
size: 10000
};
return this.getObject(newParams);
}
getRecyclingCenters() {
const newParams = {
types: ["recycling_centers"],
size: 1000
};
return this.getObject(newParams);
}
getMobiles(garbageType) {
const types = garbageType ? ["mobiles", garbageType] : "mobiles";
const newParams = {
types,
size: 10000
};
return this.getObject(newParams);
}
getReuses(garbageType) {
const types = garbageType ? ["reuses", garbageType] : "reuses";
const newParams = {
types,
size: 10000
};
return this.getObject(newParams);
}
/**
* Get all the facilities of each facility_types
* @param {object} params - { facility_types: [1,2,3]}
*/
getFacilities(params) {
const facilityTypes = params.facility_types || ALLOWED_FACILITY_TYPES;
const promises = facilityTypes.map(facilityType =>
this.getFacilitiesByType({ ...params, facility_types: facilityType })
);
const promise = Promise.all(promises)
.then(
results =>
new Promise(resolve => {
let data = [];
results.forEach(result => (data = [...data, ...result.data]));
// Order by distance
const { geo_point } = params;
data = this.orderFacilitiesByDistance(data, geo_point);
resolve({ data });
})
)
.catch(err => new Promise((resolve, reject) => reject(err)));
return promise;
}
/**
* Order facilities based on distance | Mutates the array
* @param {array} facilities - Array of facilities
* @param {*} geo_point - { lat: 0, lon: 0 }
* @returns {array} - Ordered array of facilities
*/
orderFacilitiesByDistance(facilities = [], geo_point) {
if (!facilities) throw new Error("No facilities provided");
if (!geo_point) return facilities;
facilities.filter(Boolean).forEach(facility => {
if (!facility?._source?.location) return;
const { lat, lon } = facility?._source.location;
facility._source.distance = haversine(
{ latitude: lat, longitude: lon },
{ latitude: geo_point.lat, longitude: geo_point.lon }
);
});
return facilities.sort((a, b) => a._source.distance - b._source.distance);
}
/**
* Get a facility based on its id
* @param {object} params
* @returns {Promise} - Promise of the facility
*/
getFacilityById(params) {
return new Promise((resolve, reject) => {
this.getFacilities(params)
.then(results => {
const { ids } = params;
const resultsFilteredById = results.data.filter(result =>
ids.find(id => `${id}` === `${getId(result)}`)
);
resolve({ data: resultsFilteredById });
})
.catch(err => reject(err));
});
}
/**
* Helper function to determine which method to use to get the facilities
* @param {object} params
* @returns {Promise} - Promise of the facilities
*/
getFacilitiesByTypeMethod(params) {
const facilityTypes = params.facility_types;
let facilityType;
if (Array.isArray(facilityTypes)) [facilityType] = facilityTypes;
else facilityType = facilityTypes;
if (facilityType === 71) return this.getRecyclingCenters();
if (facilityType === 91) return this.getMobiles();
if (facilityType === 778 || facilityType === 777) return this.getReuses();
const garbageType =
facilityType === 562 ? "canin" : facilityIconTranslation(facilityType);
const reuse = [89, 726, 776, 799, 801, 804];
if (reuse.includes(facilityType)) return this.getReuses(garbageType);
if (facilityType === 566) return this.getMobiles(garbageType);
return this.getRecyclingBins(garbageType);
}
/**
* Get all the facilities of a facility_type, check this.getFacilitiesByTypeMethod for more info
* @param {object} params
* @returns {Promise} - Promise of the facilities
*/
getFacilitiesByType(params) {
const method = this.getFacilitiesByTypeMethod(params);
const { geo_point } = params;
return method.then(({ data }) => {
return new Promise(resolve => {
const orderedData = this.orderFacilitiesByDistance(data, geo_point);
resolve({ data: orderedData });
});
});
}
/**
* Get the one facility of a facility_type, check this.getFacilitiesByTypeMethod for more info
* @param {object} params
* @returns {Promise} - Promise of the facility
*/
getFacilityByType(_params) {
const params = { ..._params };
// We first want to get all the facilities of the tye to order them by distance and get the closest one
delete params.size;
return this.getFacilitiesByType(params).then(({ data }) => {
const [facility] = data;
return { data: [facility] };
});
}
/**
* Get the one facility of a facility_type
* @param {object} params
* @returns {Promise} - Promise of the facility
*/
getFacilityByTypeAndId(params) {
return new Promise((resolve, reject) => {
const { ids } = params;
this.getFacilitiesByType(params)
.then(({ data }) => {
if (!isEmpty(data)) {
const facility = data.find(item => {
const publidataFacility = instanciatePublidataObject(
item,
this.instance,
this
);
return publidataFacility.id === ids[0];
});
resolve({ data: [facility] });
}
resolve({ data: [] });
})
.catch(err => reject(err));
});
}
getServiceFacilityAggregations(params) {
return new Promise((resolve, reject) => {
let facilityType;
const ids = params.services;
this.getWasteCollectionById({ ids })
.then(({ data }) => {
facilityType = data.reduce(
(wasteCollectionAccumulator, wasteCollection) => {
const publidataWasteCollection = instanciatePublidataObject(
wasteCollection,
this.instance,
this
);
return wasteCollectionAccumulator.concat(
publidataWasteCollection.facilityTypes
.filter(facilityTypeId =>
ALLOWED_FACILITY_TYPES.includes(facilityTypeId)
)
.reduce(
(publidataWasteCollectionAccumulator, facilityTypeId) =>
publidataWasteCollectionAccumulator.concat([
{
key: facilityTypeId,
doc_count:
publidataWasteCollection._source.serviceables.length
}
]),
[]
)
);
},
[]
);
const total = facilityType.reduce(
(acc, facilityType) => acc + facilityType.doc_count,
0
);
resolve({ data: [], facilityType, total });
})
.catch(err => reject(err));
});
}
}
module.exports = DataSourcePipedrive;