diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 11341d757780073d68e8580f4ba75dd9d3d0a9d9..85c47d9dcae7913ae3dca475948d7e49842e4ad0 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -33,6 +33,7 @@ import {JobspecTargetComponent} from './jobspecs/jobspec/jobspec-detail/jobspec- import {JobspecInputComponent} from './jobspecs/jobspec/jobspec-detail/jobspec-input/jobspec-input.component'; import {JobspecExecutionComponent} from './jobspecs/jobspec/jobspec-detail/jobspec-execution/jobspec-execution.component'; import {ExecutionDetailComponent} from './executions/execution/execution-detail/execution-detail.component'; +import {ExecutionDetailPlanesComponent} from './executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component'; import {FontAwesomeModule} from "@fortawesome/angular-fontawesome"; import {LoadingComponent} from './loading/loading.component'; import {QueueSettingsComponent} from './settings/queue-settings/queue-settings.component'; @@ -76,6 +77,7 @@ export function init_app(configService: ConfigurationService) { JobspecInputComponent, JobspecExecutionComponent, ExecutionDetailComponent, + ExecutionDetailPlanesComponent, LoadingComponent, QueueSettingsComponent, FutureProductComponent, diff --git a/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.html b/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.html new file mode 100644 index 0000000000000000000000000000000000000000..db3c0c0012d26a9cb7bf1862e902311ba0ebec49 --- /dev/null +++ b/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.html @@ -0,0 +1,13 @@ +<form [formGroup]="planesFormGroup" (ngSubmit)="acceptPlanes()" class="mb-2"> + <h4 class="pt-2 border-top"> + Planes + </h4> + + <div class="w-100" *ngFor="let plane of getPlaneKeys()"> + <label><input type="checkbox" [formControlName]="plane" value="{{plane}}" checked/>{{plane}}</label> + </div> + + <button type="submit" class="btn btn-success btn-sm"> + Accept & Archive Selected Planes + </button> +</form> diff --git a/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.scss b/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.spec.ts b/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f8bab0fdfdd3f5c727fc37f94c6aaf5f97de8f57 --- /dev/null +++ b/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {ExecutionDetailPlanesComponent} from './execution-detail-planes.component'; + +describe('ExecutionDetailPlanesComponent', () => { + let component: ExecutionDetailPlanesComponent; + let fixture: ComponentFixture<ExecutionDetailPlanesComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExecutionDetailPlanesComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExecutionDetailPlanesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.ts b/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..377f8846170ccbf29c665fafcf424ab3d38fe523 --- /dev/null +++ b/src/app/executions/execution/execution-detail/execution-detail-planes/execution-detail-planes.component.ts @@ -0,0 +1,92 @@ +import {Component, EventEmitter, Input, Output, OnDestroy, OnInit} from '@angular/core'; +import {FormControl, FormGroup} from '@angular/forms'; +import {AlertService} from '../../../../services/alert.service'; +import {JobsService} from '../../../../services/jobs.service'; +import {Job} from '../../../../model/job'; +import {Observable} from 'rxjs'; + +@Component({ + selector: 'app-execution-detail-planes', + templateUrl: './execution-detail-planes.component.html', + styleUrls: ['./execution-detail-planes.component.scss'] +}) +export class ExecutionDetailPlanesComponent implements OnInit, OnDestroy { + + @Input() job: Job; + @Output() planesWritten: EventEmitter<any> = new EventEmitter(); + planes: Observable<string>; + planeKeys: string[]; + planesFormGroup: FormGroup; + + constructor( + private jobService: JobsService, + private alertService: AlertService + ) { + // Form group to contain the plane checkboxes + this.planesFormGroup = new FormGroup({}); + this.planeKeys = null; + } + + ngOnInit(): void { + + } + + ngOnDestroy(): void { + + } + + // Reads plane names from a JSON file and returns them in a list + getPlaneKeys(): string[] { + // Return the plane names if we already fetched them + if (this.planeKeys !== null) { + return this.planeKeys; + } + + this.planeKeys = []; + + // Get the plane names from the spectral window numbers in the planes.json file + this.jobService.getPlanes(this.job.job_id).subscribe(response => { + this.planeKeys = Object.keys(response.body); + + // Create a form control for each of the plane checkboxes and add them into the same group + this.planeKeys.forEach(control => this.planesFormGroup.addControl(control, new FormControl(true))); + + // Set the planes data here to signal that the controls are done being added and the HTML can finish loading + this.planes = response.body; + }, + error => { + this.alertService.error('Could not retrieve planes from planes.json. ' + error); + }); + + return this.planeKeys; + } + + // Writes to a file in lustre to flag planes to be accepted + acceptPlanes(): void { + this.alertService.info('Flagging accepted planes to be cached'); + + // Collect the selected planes and write their names separated by a newline + let planesText = ''; + const planes = this.planesFormGroup.value; + Object.keys(planes).forEach(key => { + if (planes[key] === true) { + planesText += key + '\n'; + } + }); + planesText = planesText.trim(); + + // Write out the planes string if any are selected + if (planesText.length > 0) { + this.jobService.writePlanes(this.job.job_id, planesText).subscribe(() => { + // Trigger acceptQa on the parent + this.planesWritten.emit(); + + this.alertService.success('Planes Saved'); + }, + error => { + this.alertService.error('Planes did not save. ' + error); + }); + } + } + +} diff --git a/src/app/executions/execution/execution-detail/execution-detail.component.html b/src/app/executions/execution/execution-detail/execution-detail.component.html index f779a29784c4df7ef8a1f4ae9ba3855d89886c58..9980a990efa8b029a6484e5bd85dfcb78977efc0 100644 --- a/src/app/executions/execution/execution-detail/execution-detail.component.html +++ b/src/app/executions/execution/execution-detail/execution-detail.component.html @@ -1,5 +1,5 @@ <ng-container *ngIf="jobDetail; else loading"> - <div class="row no-gutters" *ngIf="canAcceptArchive(jobDetail.status, jobDetail.archiveStatus)"> + <div class="row no-gutters" *ngIf="jobDetail.queueName !== 'se_coarse_cube_imaging' && canAcceptArchive(jobDetail.status, jobDetail.archiveStatus)"> <div class="col"> <button type="button" class="btn btn-success btn-sm" (click)="acceptQa()">Accept & Archive</button> </div> @@ -43,6 +43,12 @@ formControlName="notes" style="max-height: 400px; overflow-y: scroll; overflow-x: hidden;"></div> </div> </form> + + <app-execution-detail-planes [job]="job" + (planesWritten)="acceptQa()" + *ngIf="jobDetail.queueName === 'se_coarse_cube_imaging' && canAcceptArchive(jobDetail.status, jobDetail.archiveStatus)"> + </app-execution-detail-planes> + <h4 class="pt-2 border-top"> <fa-icon [icon]="faList"></fa-icon> Job diff --git a/src/app/services/jobs.service.ts b/src/app/services/jobs.service.ts index 97bf2c47c3acf027b9b8d80e174044b0d5a4297a..8f7470fe979691f6007264381b50295d4d4446a0 100644 --- a/src/app/services/jobs.service.ts +++ b/src/app/services/jobs.service.ts @@ -163,6 +163,22 @@ export class JobsService { })); } + // Returns a JSON encoded string of plane information + public getPlanes(id: number): Observable<any> { + return this.http.get(this.configService.config.url + this.endPoint + 'jobs/' + id + '/planes', {observe: 'response'}).pipe( + map(response => { + return response; + })); + } + + // Writes a string of plane names + public writePlanes(id: number, planes: string): Observable<any> { + return this.http.put(this.configService.config.url + this.endPoint + 'jobs/' + id + '/writePlanes', {planes}, {observe: 'response'}).pipe( + map(response => { + return status; + })); + } + public performQA(id: number, status: string, queue: string) { return this.http.put(this.configService.config.url + this.endPoint + 'jobs/' + id + '/status?status=' + status + '&queue=' + queue, {}, {observe: "response"}).pipe( switchMap(response => {