import {IndicatorType, Stakeholder} from "../../../monitor/entities/stakeholder"; import axios from "axios"; import {IndicatorUtils} from "../indicator-utils"; import {Composer} from "../../../utils/email/composer"; import {properties} from "src/environments/environment"; export interface CacheItem { reportId: string, type: IndicatorType, url: string } export class Report { creator: string; name: string; success: number; errors: { url: string, status: number }[]; total: number; completed: boolean; percentage: number constructor(total: number, name: string, creator: string) { this.creator = creator; this.name = name; this.success = 0; this.errors = []; this.total = total; this.completed = false; } setPercentage() { this.percentage = Math.floor((this.success + this.errors.length) / this.total * 100); } } export class CacheIndicators { private static BATCH_SIZE = 10; private reports: Map = new Map(); private queue: CacheItem[] = []; private process: Promise; private isFinished: boolean = true; stakeholderToCacheItems(stakeholder: Stakeholder) { let cacheItems: CacheItem[] = []; let indicatorUtils = new IndicatorUtils(); stakeholder.topics.forEach(topic => { topic.categories.forEach(category => { category.subCategories.forEach(subCategory => { subCategory.numbers.forEach(section => { section.indicators.forEach(indicator => { indicator.indicatorPaths.forEach(indicatorPath => { let url = indicatorUtils.getNumberUrl(indicatorPath.source, indicatorUtils.getFullUrl(stakeholder, indicatorPath)); cacheItems.push({ reportId: stakeholder._id, type: 'number', url: url }); }); }); }); subCategory.charts.forEach(section => { section.indicators.forEach(indicator => { indicator.indicatorPaths.forEach(indicatorPath => { let url = indicatorUtils.getChartUrl(indicatorPath.source, indicatorUtils.getFullUrl(stakeholder, indicatorPath)); cacheItems.push({ reportId: stakeholder._id, type: 'chart', url: url }); }); }); }); }); }); }); return cacheItems; } public exists(id: string) { return this.reports.has(id); } public completed(id: string) { return !this.exists(id) || this.reports.get(id).completed; } public createReport(id: string, cacheItems: CacheItem[], name: string, creator: string) { let report = new Report(cacheItems.length, name, creator); this.reports.set(id, report); this.addItemsToQueue(cacheItems); return report; } public getReport(id: string) { return this.reports.get(id); } private async processQueue() { this.isFinished = false; while (this.queue.length > 0) { let batch = this.queue.splice(0, CacheIndicators.BATCH_SIZE); await this.processBatch(batch); } } private async processBatch(batch: CacheItem[]) { let promises: Promise[] = []; let ids = new Set(); batch.forEach(item => { let promise; ids.add(item.reportId); if (item.type === 'chart') { let [url, json] = item.url.split('?json='); json = decodeURIComponent(json); json = statsToolParser(JSON.parse(json)); promise = axios.post(url, json); } else { promise = axios.get(item.url); } promises.push(promise.then(response => { let report = this.reports.get(item.reportId); if (report) { report.success++; report.setPercentage(); } return response; }).catch(error => { let report = this.reports.get(item.reportId); if (report) { report.errors.push({url: item.url, status: error.response.status}); report.setPercentage(); } return error.response; })); }); await Promise.all(promises); ids.forEach(id => { let report = this.reports.get(id); if (report?.percentage === 100) { report.completed = true; this.sendEmail(report); } }); } private addItemsToQueue(cacheItems: CacheItem[]) { cacheItems.forEach(item => { this.queue.push(item); }); if (this.isFinished) { this.processQueue().then(() => { this.isFinished = true; }); } } sendEmail(report: Report) { let email = Composer.composeEmailToReportCachingProcess(report); axios.post(properties.adminToolsAPIURL + "sendMail/", email).catch(error => { console.error(error); }); } } export function statsToolParser(dataJSONobj: any): any { let RequestInfoObj = Object.assign({}); switch (dataJSONobj.library) { case "GoogleCharts": //Pass the Chart library to ChartDataFormatter RequestInfoObj.library = dataJSONobj.library; RequestInfoObj.orderBy = dataJSONobj.orderBy; //Create ChartInfo Object Array RequestInfoObj.chartsInfo = []; //Create ChartInfo and pass the Chart data queries to ChartDataFormatter //along with the requested Chart type RequestInfoObj.chartsInfo = dataJSONobj.chartDescription.queriesInfo; break; case "eCharts": //Pass the Chart library to ChartDataFormatter RequestInfoObj.library = dataJSONobj.library; RequestInfoObj.orderBy = dataJSONobj.orderBy; //Create ChartInfo Object Array RequestInfoObj.chartsInfo = []; //Create ChartInfo and pass the Chart data queries to ChartDataFormatter //along with the requested Chart type for (let index = 0; index < dataJSONobj.chartDescription.queries.length; index++) { let element = dataJSONobj.chartDescription.queries[index]; var ChartInfoObj = Object.assign({}); if (element.type === undefined) ChartInfoObj.type = dataJSONobj.chartDescription.series[index].type; else ChartInfoObj.type = element.type; if (element.name === undefined) ChartInfoObj.name = null; else ChartInfoObj.name = element.name; ChartInfoObj.query = element.query; RequestInfoObj.chartsInfo.push(ChartInfoObj); } break; case "HighCharts": RequestInfoObj.library = dataJSONobj.library; RequestInfoObj.orderBy = dataJSONobj.orderBy; //Pass the Chart type to ChartDataFormatter var defaultType = dataJSONobj.chartDescription.chart.type; //Create ChartInfo Object Array RequestInfoObj.chartsInfo = []; //Create ChartInfo and pass the Chart data queries to ChartDataFormatter //along with the requested Chart type dataJSONobj.chartDescription.queries.forEach(element => { var ChartInfoObj = Object.assign({}); if (element.type === undefined) ChartInfoObj.type = defaultType; else ChartInfoObj.type = element.type; if (element.name === undefined) ChartInfoObj.name = null; else ChartInfoObj.name = element.name; ChartInfoObj.query = element.query; RequestInfoObj.chartsInfo.push(ChartInfoObj); }); break; case "HighMaps": RequestInfoObj.library = dataJSONobj.library; //Create ChartInfo Object Array RequestInfoObj.chartsInfo = []; //Create ChartInfo and pass the Chart data queries to ChartDataFormatter dataJSONobj.mapDescription.queries.forEach(element => { var ChartInfoObj = Object.assign({}); if (element.name === undefined) ChartInfoObj.name = null; else ChartInfoObj.name = element.name; ChartInfoObj.query = element.query; RequestInfoObj.chartsInfo.push(ChartInfoObj); }); break; default: console.log("Unsupported Library: " + dataJSONobj.library); } return RequestInfoObj; }