Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Forecasts Tool #342

Merged
merged 53 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
641d2b3
feat: wip forecast page updates
chrismclarke Oct 25, 2024
6efe06c
chore: update pre-commit hook
chrismclarke Oct 25, 2024
b5e545e
Merge branch 'main' of https://github.com/e-picsa/picsa-apps into fea…
chrismclarke Feb 6, 2025
339cbc5
chore: code tidying
chrismclarke Feb 6, 2025
782bcbd
feat: cliamte forecasts app schema
chrismclarke Feb 6, 2025
19c8ba9
feat: forecast storage backend function
chrismclarke Feb 6, 2025
5f5d2d2
chore: code tidying
chrismclarke Feb 6, 2025
af4da4c
feat: add supabase-storage-download component
chrismclarke Feb 13, 2025
a229de8
feat: add ky lib
chrismclarke Feb 13, 2025
67b7747
feat: wip forecast page
chrismclarke Feb 13, 2025
8aedca7
feat: wip forecasts service
chrismclarke Feb 13, 2025
852f38e
chore: pin capacitor-video-player version
chrismclarke Feb 13, 2025
78e3243
chore: rxdb16 update
chrismclarke Feb 13, 2025
0490946
Merge branch 'core/rxdb-upgrade' of https://github.com/e-picsa/picsa-…
chrismclarke Feb 14, 2025
da257fc
feat: pdf forecast download and view
chrismclarke Feb 14, 2025
3e0dabc
feat: wip forecast page improvements
chrismclarke Feb 15, 2025
fe61596
Merge branch 'core/rxdb-upgrade' of https://github.com/e-picsa/picsa-…
chrismclarke Feb 18, 2025
3d3392d
Merge branch 'main' of https://github.com/e-picsa/picsa-apps into fea…
chrismclarke Feb 19, 2025
25ecd8d
Merge branch 'main' of https://github.com/e-picsa/picsa-apps into fea…
chrismclarke Feb 19, 2025
d230eb0
chore: photo save improvements
chrismclarke Feb 19, 2025
6162ae9
refactor: forecast attachment handling
chrismclarke Feb 19, 2025
fb99047
refactor: attachment uri handling
chrismclarke Feb 19, 2025
3e4e82c
feat: wip geojson updates
chrismclarke Feb 21, 2025
248b547
chore: update boundaries
chrismclarke Feb 21, 2025
d0a5e87
feat: wip forecast page district select
chrismclarke Feb 21, 2025
19081ec
chore: remove boundary json
chrismclarke Feb 21, 2025
25eb1dd
Merge branch 'main' of https://github.com/e-picsa/picsa-apps into fea…
chrismclarke Feb 24, 2025
b7a84e3
refactor: admin_4 locations
chrismclarke Feb 25, 2025
7d83216
feat: user profile district and forecasts
chrismclarke Feb 25, 2025
5050158
feat: mw downscaled forecast integration
chrismclarke Feb 25, 2025
a462704
feat: zm district select and data
chrismclarke Feb 26, 2025
8e36632
feat: location select component
chrismclarke Feb 26, 2025
ee710fc
chore: code tidying
chrismclarke Feb 26, 2025
ac47c04
refactor: forecasts standalone tool
chrismclarke Feb 26, 2025
5e76369
chore: code tidying
chrismclarke Feb 26, 2025
33f9740
chore: code tidying
chrismclarke Feb 26, 2025
dfc096d
chore: code tidying
chrismclarke Feb 26, 2025
3c8cdb4
chore: remove legacy forecast resources
chrismclarke Feb 26, 2025
e52567a
feat: backend function types
chrismclarke Feb 26, 2025
502f250
chore: add resources tool project
chrismclarke Feb 26, 2025
d08cea8
chore: code tidying
chrismclarke Feb 27, 2025
d8f427b
chore: remove legacy weather resources
chrismclarke Feb 27, 2025
8eac2ec
Merge branch 'main' into feat/forecasts-page
chrismclarke Feb 27, 2025
946a60c
Merge branch 'main' of https://github.com/e-picsa/picsa-apps into fea…
chrismclarke Feb 27, 2025
2f0dc1c
Merge branch 'main' into feat/forecasts-page
chrismclarke Feb 27, 2025
4d2584d
Merge branch 'main' into feat/forecasts-page
chrismclarke Feb 27, 2025
c253b1a
chore: climate functions tidying
chrismclarke Feb 28, 2025
f0e44d6
feat: forecast viewer component
chrismclarke Feb 28, 2025
b539fea
chore: code tidying
chrismclarke Feb 28, 2025
59e0a01
chore: code tidying
chrismclarke Feb 28, 2025
35a3ff8
perf: service optimisations
chrismclarke Feb 28, 2025
7154675
chore: close button tidying
chrismclarke Feb 28, 2025
92a345d
chore: build fixes
chrismclarke Feb 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const ApiMapping = (
// setup metadata
const fileBlob = data as any as Blob;
const bucketId = country_code as string;
const folderPath = 'climate/forecasts';
const folderPath = 'forecasts/daily';
// upload to storage
const { fullPath } = await storage.putFile({ bucketId, fileBlob, filename: filepath, folderPath });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export class ClimateForecastPageComponent {
}

private async downloadStorageFile(row: IForecastRow) {
// TODO - invoke cloud function instead of direct
this.activeDownloads.update((v) => ({ ...v, [row.id]: 'pending' }));
const storagePath = await this.service.loadFromAPI.forecast_file(row);
this.activeDownloads.update((v) => ({ ...v, [row.id]: 'complete' }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</div>
</a>
}
<a mat-list-item routerLink="/resources/collection/weatherResources" (click)="drawer.close()">
<a mat-list-item routerLink="/forecasts" (click)="drawer.close()">
<div style="display: flex; align-items: center">
<mat-icon style="margin-right: 8px">cloud</mat-icon>
<span>{{ 'Forecasts' | translate}}</span>
Expand Down
5 changes: 5 additions & 0 deletions apps/picsa-apps/extension-app/src/app/routes/tool-routes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @nx/enforce-module-boundaries */
import { Routes } from '@angular/router';

export const TOOL_ROUTES: Routes = [
Expand All @@ -16,6 +17,10 @@ export const TOOL_ROUTES: Routes = [
loadChildren: () =>
import('@picsa/crop-probability/src/app/app.module-embedded').then((mod) => mod.CropProbabilityToolModule),
},
{
path: 'forecasts',
loadComponent: () => import('@picsa/forecasts/pages/forecast/forecast.page').then((mod) => mod.ForecastComponent),
},
{
path: 'monitoring',
loadChildren: () => import('@picsa/monitoring/src/app/app.module-embedded').then((mod) => mod.MonitoringToolModule),
Expand Down
4 changes: 2 additions & 2 deletions apps/picsa-server/supabase/functions/dashboard/forecast-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ async function getCountryUpdates(country_code: string, query_prefix: string) {
// filter results to only include api forecasts not present on db
const apiForecasts = await getApiForecasts({ country_code, query_prefix });
const dbForecasts = await getDBForecasts({ country_code, query_prefix });

const dbForecastIds = dbForecasts.map((v) => v.id);
const newForecasts = apiForecasts.filter((v) => !dbForecastIds.includes(v.name));
console.log(`${country_code}: ${newForecasts.length} New Forecasts`);
if (newForecasts.length === 0) {
return [];
}
Expand Down Expand Up @@ -92,12 +92,12 @@ async function getApiForecasts(query: { country_code: string; query_prefix?: str
async function getDBForecasts(query: { country_code: string; query_prefix: string }) {
const supabaseClient = getClient();
const { country_code, query_prefix } = query;
console.log('db query', query_prefix, country_code);
const { data, error } = await supabaseClient
.from('forecasts')
.select('*')
.like('id', `${query_prefix}%`)
.eq('country_code', country_code)
.eq('forecast_type', 'daily')
.order('id', { ascending: false });

if (error) {
Expand Down
18 changes: 13 additions & 5 deletions apps/picsa-server/supabase/functions/dashboard/forecast-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,30 @@ class ForecastStorageUpdate {
}

async populateStorageFiles(params: IReqParams) {
const { limit = 5 } = params;
const { limit = 20 } = params;
const pending = await this.listPendingFiles(limit);

const updates: IDBClimateForecastRow[] = [];
const errors: any[] = [];
// TODO - make parallel and allow failure
for (const { country_code, id } of pending) {
const promises = pending.map(async ({ country_code, id }) => {
const { data, error } = await this.storeForecast(country_code, id);
if (error) {
errors.push(error);
}
if (data) {
updates.push(data);
}
});
await Promise.allSettled(promises);
for (const error of errors) {
console.error(error);
}
console.log(`[${updates.length}] storage files populates\n[${errors.length}] errors recorded`);
return { data: updates, error: errors };
}

/** Check all climate forecast db entries for any that are missing corresponding storage files */
private async listPendingFiles(limit = 5) {
private async listPendingFiles(limit: number) {
const query = this.table.select('*').is('storage_file', null).order('id', { ascending: false }).limit(limit);
const { data, error } = await query;
if (error) {
Expand All @@ -78,9 +82,13 @@ class ForecastStorageUpdate {
if (fileData) {
// upload to supabase storage
const contentType = apiResponse.headers.get('content-type') as string;
const year = id.substring(0, 4);
const month = id.substring(4, 6);
const day = id.substring(6, 8);
const filename = id.substring(9);
const { data: uploadData, error: uploadError } = await supabaseClient.storage
.from(country_code)
.upload(`climate/forecasts/${id}`, fileData, { contentType, upsert: true });
.upload(`forecasts/daily/${year}/${month}/${day}/${filename}`, fileData, { contentType, upsert: true });
if (uploadError) {
return { error: uploadError };
}
Expand Down
5 changes: 0 additions & 5 deletions apps/picsa-tools/climate-tool/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ export const ROUTES_COMMON: Routes = [
headerStyle: 'inverted',
},
},
{
path: 'forecast',
loadComponent: () => import('./pages/forecast/forecast.page').then((mod) => mod.ClimateForecastComponent),
title: translateMarker('Forecast'),
},
{
path: 'site',
redirectTo: '',
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { IStationRouteQueryParams } from '../../models';
export class CropProbabilityStationSelectComponent {
@Input() selectedStationId?: string;

// TODO - refactor to use geoLocation data (similar to forecasts page)
// and show fallback if no data available
private stationsByCountry = computed(() => {
const { country_code } = this.configurationService.userSettings();
if (country_code) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ const ADDITIONAL_LINKS: IPageLink[] = [
{
name: translateMarker('Forecasts'),
svgIcon: 'extension_app:forecasts_tool',
// HACK - forecasts currently child resource collection
// TODO - move to standalone tool
url: '/resources/collection/weatherResources',
url: '/forecasts',
tourId: 'forecasts',
},
{
Expand Down
33 changes: 33 additions & 0 deletions apps/picsa-tools/forecasts-tool/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"extends": ["../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "forecast",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "forecast",
"style": "kebab-case"
}
]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@nx/angular-template"],
"rules": {}
}
]
}
21 changes: 21 additions & 0 deletions apps/picsa-tools/forecasts-tool/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default {
displayName: 'forecasts-tool',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../coverage/apps/picsa-tools/forecasts-tool',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};
90 changes: 90 additions & 0 deletions apps/picsa-tools/forecasts-tool/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"name": "forecasts-tool",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/picsa-tools/forecasts-tool/src",
"tags": [],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/picsa-tools/forecasts-tool",
"index": "apps/picsa-tools/forecasts-tool/src/index.html",
"browser": "apps/picsa-tools/forecasts-tool/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/picsa-tools/forecasts-tool/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "apps/picsa-tools/forecasts-tool/public"
}
],
"styles": ["apps/picsa-tools/forecasts-tool/src/styles.scss"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "4mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kb",
"maximumError": "8kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "forecasts-tool:build:production"
},
"development": {
"buildTarget": "forecasts-tool:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "forecasts-tool:build"
}
},
"lint": {
"executor": "@nx/eslint:lint"
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/picsa-tools/forecasts-tool/jest.config.ts"
}
},
"serve-static": {
"executor": "@nx/web:file-server",
"options": {
"buildTarget": "forecasts-tool:build",
"port": 4200,
"staticFilePath": "dist/apps/picsa-tools/forecasts-tool/browser",
"spa": true
}
}
}
}
Binary file not shown.
Empty file.
Empty file.
Loading
Loading