diff --git a/.vscode/settings.json b/.vscode/settings.json index e6c1e992..9439716a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,6 @@ { "java.configuration.updateBuildConfiguration": "automatic", "cSpell.enabled": false, - "eslint.runtime": "node", // Deno configuration for supabase functions // NOTE - could be refactored to separate code-workspace // https://supabase.com/docs/guides/functions/local-development#setting-up-your-environment diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/admin/admin.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/admin/admin.component.html index f6b29759..aead2cd7 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/admin/admin.component.html +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/admin/admin.component.html @@ -2,7 +2,18 @@

Admin

(WiP) dashboard to allow quick download of Rainfall Summaries to include in the app

- + + + +
Admin [options]="tableOptions" [valueTemplates]="{ csv: csvTemplate }" > - - + + diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/admin/admin.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/admin/admin.component.ts index 7dd67fe3..aa77fb92 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/admin/admin.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/admin/admin.component.ts @@ -4,7 +4,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { IDataTableOptions, PicsaDataTableComponent } from '@picsa/shared/features'; import { SupabaseService } from '@picsa/shared/services/core/supabase'; -import { arrayToHashmapArray } from '@picsa/utils'; +import { _wait, arrayToHashmapArray } from '@picsa/utils'; import download from 'downloadjs'; import JSZip from 'jszip'; import { unparse } from 'papaparse'; @@ -14,6 +14,17 @@ import { ClimateService } from '../../climate.service'; import type { IAnnualRainfallSummariesData, IClimateProductRow, IStationRow } from '../../types'; import { hackConvertAPIDataToLegacyFormat } from '../station-details/components/rainfall-summary/rainfall-summary.utils'; +interface IStationAdminSummary { + station_id: string; + row: IStationRow; + updated_at: string; + rainfall_summary?: IClimateProductRow; + start_year?: number; + end_year?: number; +} + +const DISPLAY_COLUMNS: (keyof IStationAdminSummary)[] = ['station_id', 'updated_at', 'start_year', 'end_year']; + /** * TODOs - See #333 */ @@ -32,8 +43,9 @@ export class ClimateAdminPageComponent { return this.generateTableSummaryData(stations, products); }); public tableOptions: IDataTableOptions = { - displayColumns: ['station_id', 'type', 'updated_at', 'start_year', 'end_year', 'csv'], + displayColumns: DISPLAY_COLUMNS, }; + public refreshCount = signal(-1); private climateProducts = signal([]); @@ -61,7 +73,7 @@ export class ClimateAdminPageComponent { for (const summary of this.tableData()) { const csvData = this.generateStationCSVDownload(summary); if (csvData) { - zip.file(`${summary.station_id}.csv`, csvData); + zip.file(`${summary.row.station_id}.csv`, csvData); } } const blob = await zip.generateAsync({ type: 'blob' }); @@ -69,15 +81,30 @@ export class ClimateAdminPageComponent { download(blob, `${country_code}_rainfall_summaries.zip`); } - public downloadStationCSV(summary) { - const csv = this.generateStationCSVDownload(summary); + public downloadStationCSV(station: IStationAdminSummary) { + const csv = this.generateStationCSVDownload(station); if (csv) { - download(csv, summary.station_id, 'text/csv'); + download(csv, station.row.station_id, 'text/csv'); } } - private generateStationCSVDownload(summary) { - if (summary.data && summary.data.length > 0) { - const csvData = hackConvertAPIDataToLegacyFormat(summary.data); + + public async refreshAllStations() { + this.refreshCount.set(0); + const promises = this.tableData().map(async (station, i) => { + // hack - instead of queueing apply small offset between requests to reduce blocking + await _wait(200 * i); + await this.service.loadFromAPI.rainfallSummaries(station.row); + this.refreshCount.update((v) => v + 1); + }); + await Promise.all(promises); + await this.loadRainfallSummaries(this.deploymentService.activeDeployment()?.country_code as string); + } + + private generateStationCSVDownload(summary: IStationAdminSummary) { + const { rainfall_summary } = summary; + if (rainfall_summary && rainfall_summary.data) { + const data = rainfall_summary.data['data'] as any[]; + const csvData = hackConvertAPIDataToLegacyFormat(data); const columns = Object.keys(csvData[0]); const csv = unparse(csvData, { columns }); return csv; @@ -86,25 +113,21 @@ export class ClimateAdminPageComponent { } private generateTableSummaryData(stations: IStationRow[], products: IClimateProductRow[]) { - if (stations.length > 0 && products.length > 0) { - // NOTE - only single entry for rainfallSummary (not hashmapArray) - const productsHashmap = arrayToHashmapArray(products, 'station_id'); - const summary = stations.map((station) => { - const { station_id } = station; - const rainfallSummary = productsHashmap[station_id]?.find((p) => p.type === 'rainfallSummary'); - if (rainfallSummary) { - const { data, station_id, type } = rainfallSummary; - const entries: IAnnualRainfallSummariesData[] = data?.['data'] || []; - const start_year = entries[0]?.year; - const end_year = entries[entries.length - 1]?.year; - return { station_id, type, updated_at: '', start_year, end_year, csv: '', data: data?.['data'] }; - } else { - return { station_id }; - } - }); + // NOTE - only single entry for rainfallSummary (not hashmapArray) + const productsHashmap = arrayToHashmapArray(products, 'station_id'); + return stations.map((row) => { + const { station_id } = row; + const summary: IStationAdminSummary = { station_id, row, updated_at: '' }; + const rainfallSummary = productsHashmap[station_id]?.find((p) => p.type === 'rainfallSummary'); + if (rainfallSummary) { + summary.rainfall_summary = rainfallSummary; + const { data } = rainfallSummary; + const entries: IAnnualRainfallSummariesData[] = data?.['data'] || []; + summary.start_year = entries[0]?.year; + summary.end_year = entries[entries.length - 1]?.year; + } return summary; - } - return []; + }); } private async loadRainfallSummaries(country_code: string) { diff --git a/apps/picsa-server/supabase/data/deployments_rows.csv b/apps/picsa-server/supabase/data/deployments_rows.csv index 02bf38b0..79520af5 100644 --- a/apps/picsa-server/supabase/data/deployments_rows.csv +++ b/apps/picsa-server/supabase/data/deployments_rows.csv @@ -2,9 +2,9 @@ id,country_code,label,configuration,variant,access_key_md5,public,icon_path demo_dev,demo,Extension,{},extension,,false,global/images/tutorial.svg global_extension,global,Extension,{},extension,,false,global/images/flags/global.svg global_farmer,global,Farmer,{},farmer,,false,global/images/flags/global.svg -mw_extension,mw,Extension,{},extension,,false,global/images/flags/mw.svg mw_farmer,mw,Farmer,{},farmer,,false,global/images/flags/mw.svg -mw_workshop,mw,Malawi Workshop,"{""climate_country_code"":""mw_workshops""}",extension,,true,global/images/flags/mw.svg -zm_extension,zm,Extension,{},extension,,false,global/images/flags/zm.svg zm_farmer,zm,Farmer,{},farmer,,false,global/images/flags/zm.svg -zm_workshop,zm,Zambia Workshop,"{""climate_country_code"":""zm_workshops""}",extension,,true,global/images/flags/zm.svg \ No newline at end of file +mw_extension,mw,Malawi,{},extension,,true,global/images/flags/mw.svg +mw_workshop,mw,Malawi Workshop,"{""climate_country_code"":""mw_workshops""}",extension,,false,global/images/flags/mw.svg +zm_extension,zm,Zambia,{},extension,,true,global/images/flags/zm.svg +zm_workshop,zm,Zambia Workshop,"{""climate_country_code"":""zm_workshops""}",extension,,false,global/images/flags/zm.svg \ No newline at end of file