From a25a1bbf67b497a36349bad47af5ad8792e8b03d Mon Sep 17 00:00:00 2001 From: Charlotte Hausman <chausman@nrao.edu> Date: Tue, 20 Jul 2021 15:12:56 +0000 Subject: [PATCH] UI for creating follow on imaging requests from completed calibration requests --- .../request-operations.component.html | 10 ++++ .../request-operations.component.ts | 19 +++++++- .../services/capability-launcher.service.ts | 13 ++++++ ...emove_white_space_from_capability_steps.py | 46 +++++++++++++++++++ schema/versions/87dae5acfd34_.py | 24 ++++++++++ services/capability/capability/routes.py | 5 ++ .../capability/views/capability_request.py | 34 ++++++++++++++ .../capability/test/test_capability_server.py | 1 + .../workflow/services/workflow_service.py | 17 +++++++ 9 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 schema/versions/5ab2fcb61373_remove_white_space_from_capability_steps.py create mode 100644 schema/versions/87dae5acfd34_.py diff --git a/apps/web/src/app/workspaces/components/capability-request/components/request-operations/request-operations.component.html b/apps/web/src/app/workspaces/components/capability-request/components/request-operations/request-operations.component.html index 6589cf74b..744327ace 100644 --- a/apps/web/src/app/workspaces/components/capability-request/components/request-operations/request-operations.component.html +++ b/apps/web/src/app/workspaces/components/capability-request/components/request-operations/request-operations.component.html @@ -7,6 +7,16 @@ [capabilityRequest]="capabilityRequest" ></app-create-new-version-form> </div> + <div class="col-auto d-flex" *ngIf="capabilityRequest.state === 'Complete' && + capabilityRequest.capability_name === 'std_calibration' || capabilityRequest.capability_name === 'restore_cms'"> + <button + id="create-image-request" + type="button" + class="btn btn-warning" + (click)="capabilityLauncherService.createImageRequestFromPreviousCal('std_cms_imaging', capabilityRequest.id).subscribe(followonRequestObserver)"> + <span class="fas fa-camera"></span><span class="pl-2">Create Image Request</span> + </button> + </div> <div class="col-auto d-flex" *ngIf="capabilityRequest.state === 'Created'"> <button id="submit-button" diff --git a/apps/web/src/app/workspaces/components/capability-request/components/request-operations/request-operations.component.ts b/apps/web/src/app/workspaces/components/capability-request/components/request-operations/request-operations.component.ts index a7dc8a97e..9559a9e5a 100644 --- a/apps/web/src/app/workspaces/components/capability-request/components/request-operations/request-operations.component.ts +++ b/apps/web/src/app/workspaces/components/capability-request/components/request-operations/request-operations.component.ts @@ -2,6 +2,7 @@ import { Component, Input, OnInit } from "@angular/core"; import { CapabilityRequest } from "../../../../model/capability-request"; import { CapabilityLauncherService } from "../../../../services/capability-launcher.service"; import { CapabilityExecution } from "../../../../model/capability-execution"; +import {CapabilityRequestService} from "../../../../services/capability-request.service"; @Component({ selector: "app-request-operations", @@ -23,7 +24,23 @@ export class RequestOperationsComponent implements OnInit { }, }; - constructor(public capabilityLauncherService: CapabilityLauncherService) {} + public followonRequestObserver = { + next: (followonResponse: CapabilityRequest) => { + if (followonResponse.id) { + // Capability request created; ID found + // Redirect to request status page + this.capabilityRequestService.redirectToRequestStatusPage(followonResponse.id); + } + }, + error: (error) => { + console.log(error); + }, + }; + + constructor( + public capabilityLauncherService: CapabilityLauncherService, + private capabilityRequestService: CapabilityRequestService, + ) {} ngOnInit(): void {} } diff --git a/apps/web/src/app/workspaces/services/capability-launcher.service.ts b/apps/web/src/app/workspaces/services/capability-launcher.service.ts index b21c71d64..9d7753499 100644 --- a/apps/web/src/app/workspaces/services/capability-launcher.service.ts +++ b/apps/web/src/app/workspaces/services/capability-launcher.service.ts @@ -23,6 +23,19 @@ export class CapabilityLauncherService { return this.httpClient.post<CapabilityRequest>(url, JSON.stringify({ parameters: parameters })); } + /** + * Create standard CMS imaging request from previously exeuted calibration request and send it to capability service + * @param: capabilityName Name of capability to create request for + * @param: requestId Id of calibration request to image + */ + createImageRequestFromPreviousCal( + followonType: string, + requestId: string, + ): Observable<CapabilityRequest> { + const url = this.endpoint + "request/" + requestId + "/followon/" + followonType; + return this.httpClient.post<CapabilityRequest>(url, null); + } + /** * Submit capability request * @param: requestId ID of capability request to submit diff --git a/schema/versions/5ab2fcb61373_remove_white_space_from_capability_steps.py b/schema/versions/5ab2fcb61373_remove_white_space_from_capability_steps.py new file mode 100644 index 000000000..c9b641587 --- /dev/null +++ b/schema/versions/5ab2fcb61373_remove_white_space_from_capability_steps.py @@ -0,0 +1,46 @@ +"""remove white space from capability steps + +Revision ID: 5ab2fcb61373 +Revises: 6508afd4da68 +Create Date: 2021-07-15 14:03:32.460285 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "5ab2fcb61373" +down_revision = "6508afd4da68" +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute( + """ + UPDATE capabilities + SET capability_steps = 'prepare-and-run-workflow restore_cms\nawait-workflow\nawait-qa' WHERE capability_name = 'restore_cms' + """ + ) + op.execute( + """ + UPDATE capabilities + SET capability_steps = 'prepare-and-run-workflow std_restore_imaging\nawait-workflow\nawait-qa' WHERE capability_name = 'std_restore_imaging' + """ + ) + + +def downgrade(): + op.execute( + """ + UPDATE capabilities + SET capability_steps = 'prepare-and-run-workflow restore_cms\nawait-workflow\nawait-qa\n' WHERE capability_name = 'restore_cms' + """ + ) + op.execute( + """ + UPDATE capabilities + SET capability_steps = 'prepare-and-run-workflow std_restore_imaging\nawait-workflow\nawait-qa\n' WHERE capability_name = 'std_restore_imaging' + """ + ) diff --git a/schema/versions/87dae5acfd34_.py b/schema/versions/87dae5acfd34_.py new file mode 100644 index 000000000..204f1c1a5 --- /dev/null +++ b/schema/versions/87dae5acfd34_.py @@ -0,0 +1,24 @@ +"""empty message + +Revision ID: 87dae5acfd34 +Revises: 5ab2fcb61373, 10251d1732a4 +Create Date: 2021-07-20 08:58:26.887779 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '87dae5acfd34' +down_revision = ('5ab2fcb61373', '10251d1732a4') +branch_labels = None +depends_on = None + + +def upgrade(): + pass + + +def downgrade(): + pass diff --git a/services/capability/capability/routes.py b/services/capability/capability/routes.py index 23b1f731d..85efdec58 100644 --- a/services/capability/capability/routes.py +++ b/services/capability/capability/routes.py @@ -69,6 +69,11 @@ def capability_request_routes(config: Configurator): pattern="capability/{capability_name}/request/create", request_method="POST", ) + config.add_route( + name="create_follow_on_capability_request", + pattern=f"{request_url}" + "/followon/{followon_type}", + request_method="POST", + ) config.add_route( name="edit_capability_request", pattern=f"{request_url}", diff --git a/services/capability/capability/views/capability_request.py b/services/capability/capability/views/capability_request.py index 21cbb7938..702cbb50b 100644 --- a/services/capability/capability/views/capability_request.py +++ b/services/capability/capability/views/capability_request.py @@ -109,6 +109,40 @@ def create_capability_request(request: Request) -> Response: return Response(json_body=new_capability_request.__json__()) +@view_config(route_name="create_follow_on_capability_request", renderer="json") +def create_follow_on_capability_request(request: Request) -> Response: + """ + Pyramid view that accepts a request to create a follow-on capability request from a previous request + URL: capability/request/{request_id}/followon/{followon_type} + + :param request: POST request + :return: 200 OK response with JSON-formatted info of newly created capability request + or 400 response (HTTPBadRequest) if expected parameters not given + or 412 response (HTTPPreconditionFailed) if capability with given name does not exist and thus cannot be + requested + """ + + request_id = request.matchdict["request_id"] + followon_type = request.matchdict["followon_type"] + + capability_request = request.capability_info.lookup_capability_request(request_id) + parameters = capability_request.current_execution.parameters + user_email = parameters["user_email"] + + previous_workflow_id = capability_request.current_execution.current_workflow_request_id + + metadata = request.workflow_service.retrieve_file_content( + followon_type, previous_workflow_id, "metadata.json" + ) + cms_path = metadata["cms_path"] + sdm_id = metadata["fileSetIds"] + + new_capability_request = request.capability_service.create_request( + followon_type, parameters={"cmsPath": cms_path, "sdmId": sdm_id, "user_email": user_email} + ) + return Response(json_body=new_capability_request.__json__()) + + @view_config(route_name="edit_capability_request", renderer="json") def edit_capability_request(request: Request) -> Response: """ diff --git a/services/capability/test/test_capability_server.py b/services/capability/test/test_capability_server.py index 4c494c0cf..39ca4d72a 100644 --- a/services/capability/test/test_capability_server.py +++ b/services/capability/test/test_capability_server.py @@ -20,6 +20,7 @@ def capability_routes() -> RouteList: "disable_capability", "view_capability_request", "create_capability_request", + "create_follow_on_capability_request", "edit_capability_request", "submit_capability_request", "cancel_capability_request", diff --git a/shared/workspaces/workspaces/workflow/services/workflow_service.py b/shared/workspaces/workspaces/workflow/services/workflow_service.py index 38f58f0b9..8cb4febba 100644 --- a/shared/workspaces/workspaces/workflow/services/workflow_service.py +++ b/shared/workspaces/workspaces/workflow/services/workflow_service.py @@ -71,6 +71,17 @@ class WorkflowServiceRESTClient(WorkflowServiceIF): ) return response + def retrieve_file_content(self, name: str, request_id: int, filename: str): + """ + Retrieve the conent of a workflow file + :param name: the workflow name + :param request_id: id of parent workflow request + :param filename: file to find + :return: dict containing file content + """ + + requests.get(f"{self.url}/workflow/{name}/requests/{request_id}/files/{filename}") + def create_workflow_request( self, workflow: Union[str, WorkflowIF], argument: Dict ) -> WorkflowRequestIF: @@ -147,6 +158,12 @@ class WorkflowService(WorkflowServiceIF): else: logger.info(f"{filename} is a protected file name.") + def retrieve_file_content(self, request_id: int, filename: str): + wf_req = self.info.lookup_workflow_request(request_id) + for file in wf_req.files: + if file.filename == filename: + return file.content.decode() + def create_workflow_request(self, workflow, argument) -> WorkflowRequestIF: """ Create a workflow request from the supplied arguments. -- GitLab