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