diff --git a/package-lock.json b/package-lock.json index ea15b9dfa926211638476a33fa3293e52c589561..f26b9ce326900be7eca94c42daaa71ebb38b972d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@ghsc/disagg-d3": "^0.13.0", "@ghsc/nshmp-lib-ng": "^19.8.0", "@ghsc/nshmp-template": "^19.5.0", - "@ghsc/nshmp-utils-ts": "^3.14.0", + "@ghsc/nshmp-utils-ts": "^3.14.1", "angular-plotly.js": "^6.0.0", "d3": "^7.9.0", "d3-array": "^3.2.4", @@ -4113,9 +4113,9 @@ } }, "node_modules/@ghsc/nshmp-utils-ts": { - "version": "3.14.0", - "resolved": "https://code.usgs.gov/api/v4/projects/1414/packages/npm/@ghsc/nshmp-utils-ts/-/@ghsc/nshmp-utils-ts-3.14.0.tgz", - "integrity": "sha1-5FmlxItHqbEAqmll6f+dzuCGBQ0=", + "version": "3.14.1", + "resolved": "https://code.usgs.gov/api/v4/projects/1414/packages/npm/@ghsc/nshmp-utils-ts/-/@ghsc/nshmp-utils-ts-3.14.1.tgz", + "integrity": "sha1-XsX0tIiXh1Zb5S5FDQLAZ6Nf3tc=", "dependencies": { "@types/d3-format": "^3.0.4", "@types/geojson": "^7946.0.14", diff --git a/package.json b/package.json index 201d21133324f43bfad9b52cf251e82308567cda..3d7022c3585e947b0f8d2b138b0dd8e420164e96 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@ghsc/disagg-d3": "^0.13.0", "@ghsc/nshmp-lib-ng": "^19.8.0", "@ghsc/nshmp-template": "^19.5.0", - "@ghsc/nshmp-utils-ts": "^3.14.0", + "@ghsc/nshmp-utils-ts": "^3.14.1", "angular-plotly.js": "^6.0.0", "d3": "^7.9.0", "d3-array": "^3.2.4", diff --git a/projects/nshmp-apps/src/app/internal/aws/check-haz-jobs/components/content/content.component.ts b/projects/nshmp-apps/src/app/internal/aws/check-haz-jobs/components/content/content.component.ts index a93f976ca5b4efa9f97c19fce7e80770eed352c2..84e75b4b65a3a7a345af8dd5a060d0b49b771db8 100644 --- a/projects/nshmp-apps/src/app/internal/aws/check-haz-jobs/components/content/content.component.ts +++ b/projects/nshmp-apps/src/app/internal/aws/check-haz-jobs/components/content/content.component.ts @@ -19,6 +19,7 @@ import {MatSnackBar} from '@angular/material/snack-bar'; import {MatToolbar, MatToolbarRow} from '@angular/material/toolbar'; import {MatTooltip} from '@angular/material/tooltip'; import {JobStatus} from '@ghsc/nshmp-lib-ng/aws'; +import {NshmpService} from '@ghsc/nshmp-lib-ng/nshmp'; import {NshmpTemplateService} from '@ghsc/nshmp-template'; import {GetLogsResponseData} from '../../models/get-logs.model'; @@ -67,18 +68,16 @@ export class ContentComponent implements OnDestroy, AfterViewInit { public templateServie: NshmpTemplateService, private service: AppService, private snackBar: MatSnackBar, + private nshmpService: NshmpService, ) { - effect( - () => { - if (this.service.serviceResponse()) { - this.scrollBottom(); - this.checkLogs(this.service.serviceResponse().response); - } - }, - { - allowSignalWrites: true, - }, - ); + effect(() => { + const serviceResponse = this.service.serviceResponse(); + + if (serviceResponse) { + this.scrollBottom(); + this.checkLogs(serviceResponse.response); + } + }); } ngOnDestroy(): void { @@ -126,17 +125,19 @@ export class ContentComponent implements OnDestroy, AfterViewInit { * @param response The logs service response */ private checkLogs(response: GetLogsResponseData): void { - if (response.jobStatus.toString() === JobStatus.FAILED.toString()) { - alert('nshmp-haz failed, check email for more information'); - } - - if (response.results) { - // Call to get last logs - const lastLog = [...response.logs].pop(); - if (!lastLog.message.includes('Finished')) { - this.service.callService(false); + if ( + response.jobStatus === JobStatus.FAILED && + this.service.logHasForSupport(response) + ) { + if (response.results) { + this.snackBar.openFromComponent(SnackBarComponent, { + data: response.results, + }); } - + this.nshmpService.throwError$( + new Error('nshmp-haz failed, check email for more information'), + ); + } else if (response.results && this.service.logHasFinished(response)) { this.snackBar.openFromComponent(SnackBarComponent, { data: response.results, }); diff --git a/projects/nshmp-apps/src/app/internal/aws/check-haz-jobs/services/app.service.ts b/projects/nshmp-apps/src/app/internal/aws/check-haz-jobs/services/app.service.ts index c4af8f414df8a4bff8f71d78cc561ef44edbd6fe..c991579c15de11d20d9a000511c556436def7b79 100644 --- a/projects/nshmp-apps/src/app/internal/aws/check-haz-jobs/services/app.service.ts +++ b/projects/nshmp-apps/src/app/internal/aws/check-haz-jobs/services/app.service.ts @@ -2,6 +2,7 @@ import {Location as LocationService} from '@angular/common'; import {HttpParams} from '@angular/common/http'; import {computed, Injectable, Signal, signal} from '@angular/core'; import {AbstractControl, FormBuilder, Validators} from '@angular/forms'; +import {MatDialogRef} from '@angular/material/dialog'; import {ActivatedRoute} from '@angular/router'; import { AwsService, @@ -9,7 +10,11 @@ import { JobStatus, TerminateJobRequestData, } from '@ghsc/nshmp-lib-ng/aws'; -import {NshmpService, SpinnerService} from '@ghsc/nshmp-lib-ng/nshmp'; +import { + NshmpLibNgSpinnerComponent, + NshmpService, + SpinnerService, +} from '@ghsc/nshmp-lib-ng/nshmp'; import {DynamoDBItem} from '@ghsc/nshmp-utils-ts/libs/aws/run-nshmp-haz'; import {Response} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils'; import {EC2} from 'aws-sdk'; @@ -126,9 +131,10 @@ export class AppService implements AppServiceModel<AppState, ControlForm> { */ callService(showSpinner = true): void { const {id} = this.formGroup.getRawValue(); + let spinnerRef: MatDialogRef<NshmpLibNgSpinnerComponent> = undefined; if (showSpinner) { - this.spinnerService.show(`Getting logs for job ${id}`); + spinnerRef = this.spinnerService.show(`Getting logs for job ${id}`); } const url = `${this.baseUrl}${this.hazardLogsService}/${id}`; @@ -139,9 +145,10 @@ export class AppService implements AppServiceModel<AppState, ControlForm> { this.nshmpService .callService$<Response<GetLogsRequestData, GetLogsResponseData>>(url) .pipe(catchError((error: Error) => this.nshmpService.throwError$(error))) - .subscribe(serviceResponse => - this.handleServiceResponse(serviceResponse, showSpinner), - ); + .subscribe(serviceResponse => { + this.handleServiceResponse(serviceResponse); + spinnerRef?.close(); + }); } defaultFormValues(): ControlForm { @@ -178,6 +185,14 @@ export class AppService implements AppServiceModel<AppState, ControlForm> { }; } + logHasFinished(response: GetLogsResponseData): boolean { + return this.logHasMessage(response, 'Finished'); + } + + logHasForSupport(response: GetLogsResponseData): boolean { + return this.logHasMessage(response, 'For support contact'); + } + /** * Update state. * @@ -212,29 +227,33 @@ export class AppService implements AppServiceModel<AppState, ControlForm> { */ private handleServiceResponse( serviceResponse: Response<GetLogsRequestData, GetLogsResponseData>, - showSpinner: boolean, ): void { const {autoRefresh} = this.formGroup.getRawValue(); const status = serviceResponse.response.jobStatus; + const response = serviceResponse.response; if ( - (status === JobStatus.RUNNING || status === JobStatus.CREATED) && + status !== JobStatus.TERMINATED && + !this.logHasFinished(response) && + !this.logHasForSupport(response) && autoRefresh ) { setTimeout(() => this.callService(false), 3000); } - if (showSpinner) { - this.spinnerService.remove(); - } - this.spinnerService.remove(); - this.updateState({ serviceLoading: false, serviceResponse, }); } + private logHasMessage( + response: GetLogsResponseData, + message: string, + ): boolean { + return response.logs.some(log => log.message.includes(message)); + } + /** * Update URL based on form values. */ diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/config/config.component.html b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/config/config.component.html index afa8e7dcf0a5819bdca7f50671050f6e3e3e8624..4f1dca60d5b11c5ebac27be3ded2f1d629763202 100644 --- a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/config/config.component.html +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/config/config.component.html @@ -351,6 +351,22 @@ <mat-divider /> + <!-- region --> + <div class="parameter createMap"> + <code class="parameter-title">createMap</code> + + <div class="parameter-info"> + <div class="parameter-description"> + <div>Whether to create a hazard map from results.</div> + </div> + + <div>Required: no</div> + <div>Default: true</div> + </div> + </div> + + <mat-divider /> + <!-- region --> <div class="parameter region"> <code class="parameter-title">region</code> diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/config/config.component.ts b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/config/config.component.ts index f1efc56dc5812fed710b9f24832f573c9827f698..06eb33b46cf327723e571fc4d634896180e5f472 100644 --- a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/config/config.component.ts +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/config/config.component.ts @@ -122,17 +122,17 @@ export class ConfigComponent { }, { indent: true, - name: '.region', + name: '.createMap', type: 'string', }, { indent: true, - name: '.title', + name: '.region', type: 'string', }, { indent: true, - name: '.vs30', + name: '.title', type: 'string', }, ]; diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.html b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.html index 55a9eb56b157e280d6264ee3070108216a48c477..5e5e5a71b32720feca40510789eb48bcb94cdb26 100644 --- a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.html +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.html @@ -426,6 +426,14 @@ <mat-divider /> <div class="settings-subsection"> + <mat-label class="settings-subsection--label"> + <mat-slide-toggle + [formControl]="form.controls.mapConfig.controls.createMap" + class="lib-toggle" + > + Create Hazard Map + </mat-slide-toggle> + </mat-label> <div class="settings-subsection--section"> <!-- Map config: Title --> <mat-form-field class="grid-col-12"> @@ -491,7 +499,7 @@ /> </div> - <div class="grid-col-12 padding-y-2"> + <div class="grid-col-12 padding-y-2 calc-config"> <mat-expansion-panel [disabled]="calcConfig() === null" [expanded]="calcConfig() !== null" diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.scss b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.scss index 0695887375fb137083214ec5116668fd80cfa59e..0d57f27d523871a72fc174abbcc8352f003e4835 100644 --- a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.scss +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.scss @@ -1,3 +1,6 @@ +@use '@angular/material' as mat; +@use '@ghsc/nshmp-template/styles/variables'; + :host { height: 100%; width: 100%; @@ -54,3 +57,11 @@ mat-expansion-panel { font-size: clamp(8px, 6px + 1vw, 14px); } } + +.calc-config { + @include mat.expansion-overrides( + ( + container-background-color: variables.$nshmp-surface-container-highest, + ) + ); +} diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.ts b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.ts index a52da6d578fa7617a4cb26369e9c3405cb90426e..fabae1f0973511ad9cb06a3690eaa9dc4063c519 100644 --- a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.ts +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/form/form.component.ts @@ -116,7 +116,7 @@ export class FormComponent implements OnInit, OnDestroy { /** Control form state */ form = this.service.formGroup; - private overrideSubscription = new Subscription(); + private subscriptions: Subscription[] = []; constructor( public service: AppService, @@ -132,9 +132,9 @@ export class FormComponent implements OnInit, OnDestroy { } ngOnInit(): void { - const {nshmpConfig} = this.form.controls; + const {mapConfig, nshmpConfig} = this.form.controls; - this.overrideSubscription = + const overrideSubscription = this.form.controls.overrideNshmpLib.valueChanges.subscribe( overrideNshmpLib => { if (overrideNshmpLib) { @@ -150,10 +150,26 @@ export class FormComponent implements OnInit, OnDestroy { } }, ); + + this.subscriptions.push(overrideSubscription); + + const mapSubscription = mapConfig.controls.createMap.valueChanges.subscribe( + createMap => { + if (createMap) { + mapConfig.controls.region.enable(); + mapConfig.controls.title.enable(); + } else { + mapConfig.controls.region.disable(); + mapConfig.controls.title.disable(); + } + }, + ); + + this.subscriptions.push(mapSubscription); } ngOnDestroy(): void { - this.overrideSubscription.unsubscribe(); + this.subscriptions.forEach(subscription => subscription.unsubscribe()); } /** diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/history/history.component.html b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/history/history.component.html index a9d1ea24a91dc615374d80f49aa03fc4f1d0680f..cd86d3262b04c98a307685c02b72c2c4116112ac 100644 --- a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/history/history.component.html +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/history/history.component.html @@ -45,9 +45,11 @@ </a> </div> - <div>Submitted: {{ serviceResponse.date }}</div> + <div class="padding-bottom-1"> + Submitted: {{ serviceResponse.date }} + </div> - <mat-expansion-panel class="grid-col-12 padding-top-1"> + <mat-expansion-panel class="grid-col-12"> <mat-expansion-panel-header> <mat-panel-title>Configuration</mat-panel-title> </mat-expansion-panel-header> diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/history/history.component.scss b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/history/history.component.scss index 048c1f1df9dedc6b2669012fb99239c7f6236922..1f39dbb77b36625efdf582ad3f93e91abd9cd6ba 100644 --- a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/history/history.component.scss +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/components/history/history.component.scss @@ -1,3 +1,6 @@ +@use '@angular/material' as mat; +@use '@ghsc/nshmp-template/styles/variables'; + .code-block { font-size: 11px; } @@ -5,4 +8,16 @@ .history-cards { overflow: auto; max-height: 150vh; + + @include mat.card-overrides( + ( + outlined-container-color: variables.$nshmp-surface-container-high, + ) + ); + + @include mat.expansion-overrides( + ( + container-background-color: variables.$nshmp-surface-container-highest, + ) + ); } diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/models/post-config.model.ts b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/models/post-config.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..6969c50a6fa2836e234ce9b4c6d56c942f357ef9 --- /dev/null +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/models/post-config.model.ts @@ -0,0 +1,20 @@ +import { + CalcConfig, + CloudConfig, + MapConfig, + NshmpConfig, +} from '@ghsc/nshmp-utils-ts/libs/aws/run-nshmp-haz'; + +/** + * Post config. + */ +export interface PostConfig { + /** Calc config */ + calcConfig: CalcConfig | CalcConfig[]; + /** AWS cloud config */ + cloudConfig: CloudConfig; + /** NSHMP config to run nshmp-haz */ + nshmpConfig: NshmpConfig; + /** Hazard map creation config */ + mapConfig?: MapConfig; +} diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/models/state.model.ts b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/models/state.model.ts index 288c83d310f0e273cfe3b20150e707b375b08535..5b59dcad7dfd2c1d09a47a87abe7934f048d91c4 100644 --- a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/models/state.model.ts +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/models/state.model.ts @@ -5,6 +5,8 @@ import { } from '@ghsc/nshmp-utils-ts/libs/aws/run-nshmp-haz'; import {Response} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils'; +import {PostConfig} from './post-config.model'; + /** * Application state. */ @@ -18,4 +20,6 @@ export interface AppState { RunNshmpHazHttpPostConfig, RunNshmpHazResponseData >[]; + + importedConfig?: PostConfig; } diff --git a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/services/app.service.ts b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/services/app.service.ts index e04af7c39415b859fe9eebbd6a47a9175b0a845a..5ab101a8b6c7437c5cc5daf8b333e3882076e9c6 100644 --- a/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/services/app.service.ts +++ b/projects/nshmp-apps/src/app/internal/aws/submit-haz-jobs/services/app.service.ts @@ -11,8 +11,6 @@ import { import { CalcConfig, CloudConfig, - MapConfig, - NshmpConfig, RunNshmpHazHttpPostConfig, RunNshmpHazResponseData, } from '@ghsc/nshmp-utils-ts/libs/aws/run-nshmp-haz'; @@ -37,22 +35,9 @@ import { import {RunNshmpHazFormGroup} from '../models/form-group.model'; import {GitlabTagItem} from '../models/gitlab-tag-item.model'; import {NshmpHazClass} from '../models/nshmp-haz-class.model'; +import {PostConfig} from '../models/post-config.model'; import {AppState} from '../models/state.model'; -/** - * Post config. - */ -interface PostConfig { - /** Calc config */ - calcConfig: CalcConfig | CalcConfig[]; - /** AWS cloud config */ - cloudConfig: CloudConfig; - /** NSHMP config to run nshmp-haz */ - nshmpConfig: NshmpConfig; - /** Hazard map creation config */ - mapConfig?: MapConfig; -} - /** * Application service. */ @@ -142,14 +127,24 @@ export class AppService implements AppServiceModel<AppState, RunNshmpHazForm> { this.spinnerService.show('Calling nshmp-haz on AWS'); const url = `${this.baseUrl}${environment.webServices.aws.services.runHazardJobs}`; const {cloudConfig, mapConfig, nshmpConfig} = this.formGroup.getRawValue(); + const importedConfig = this.state().importedConfig; const calls = this.state() .calcConfig.map(calcConfig => { const postConfig: RunNshmpHazHttpPostConfig = { calcConfig, - cloudConfig, - mapConfig, - nshmpConfig, + cloudConfig: { + ...importedConfig?.cloudConfig, + ...cloudConfig, + }, + mapConfig: { + ...importedConfig?.mapConfig, + ...mapConfig, + }, + nshmpConfig: { + ...importedConfig?.nshmpConfig, + ...nshmpConfig, + }, }; return postConfig; @@ -199,6 +194,7 @@ export class AppService implements AppServiceModel<AppState, RunNshmpHazForm> { instanceType: 'm7i.4xlarge', }, mapConfig: { + createMap: true, region: null, title: 'Total Hazard Mean', }, @@ -234,6 +230,7 @@ export class AppService implements AppServiceModel<AppState, RunNshmpHazForm> { instanceType: values.cloudConfig.instanceType, }, mapConfig: { + createMap: true, region: 'CONUS', title: 'Total Mean Hazard', }, @@ -579,6 +576,10 @@ export class AppService implements AppServiceModel<AppState, RunNshmpHazForm> { }); } + this.updateState({ + importedConfig: awsConfig, + }); + this.formGroup.markAsDirty(); } catch (e) { const error = e instanceof Error ? e : new Error(e as string);