diff --git a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/permission.stack.handler.js b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/permission.stack.handler.js index 14a8bdddfd9a3526662a003c79ae4817b2577c75..67c03f68c1632490c81477cdc2222ef36e8bda29 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/permission.stack.handler.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/permission.stack.handler.js @@ -68,7 +68,7 @@ const PermissionStackUtil = { permissionStack['scheduleunit'] ={ create: putAccesss, edit: patchAccess, delete: deleteAccess, list: getAccesss, createsub: putAccesss, autodeletion:patchAccess, copysu:putAccesss, excelview:getAccesss, - cleanuptask:true, cancelsu:true, viewworkflow:true }; + cleanuptask:true, cancelsu:true, viewworkflow:true, create_rerun: true, update_sub: true }; permissionStack[module] = { create: putAccesss, edit: patchAccess, delete: deleteAccess, list: getAccesss, add: postAccess } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js index 9d081a10cc2562ee31f8a6ef4ce7089bada529c5..a4c8693984b36a286a682d6408816b90dff1e0a0 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js @@ -206,6 +206,12 @@ class SchedulingUnitList extends Component{ this.confirmAutoDeletion = this.confirmAutoDeletion.bind(this); this.setAutoDeletion = this.setAutoDeletion.bind(this); this.getAutoDeletionDialogContent = this.getAutoDeletionDialogContent.bind(this); + this.confirmCreateRerunPipeline = this.confirmCreateRerunPipeline.bind(this); + this.getCreateRerunPipelineDialogContent = this.getCreateRerunPipelineDialogContent.bind(this); + this.createRerunPipeline = this.createRerunPipeline.bind(this); + this.confirmUpdateSUBlueprint = this.confirmUpdateSUBlueprint.bind(this); + this.getUpdateSUBDialogContent = this.getUpdateSUBDialogContent.bind(this); + this.updateSUB = this.updateSUB.bind(this); } /** @@ -1489,12 +1495,174 @@ class SchedulingUnitList extends Component{ } /** - * Get current user role permission for Schedule Unit List - */ - // getUserRolePermission = async() => { - // await userrole.dispatch({ type: 'scheduleunit' }); - // this.setState({userrole: userrole.getState()}); - // } + * Function to invoke form UI to create Rerun Pipeline Task(s) + */ + async confirmCreateRerunPipeline(e) { + e.preventDefault(); + this.pageUpdated = false; + if (this.selectedRows.length > 0) { + this.suBlueprintList = _.filter(this.selectedRows, (schedulingUnit) => { return schedulingUnit.status === "error" + || schedulingUnit.status === "cancelled"}); + if (this.suBlueprintList.length === 0) { + appGrowl.show({severity: 'info', summary: 'Select Row', detail: 'Please select one or more Scheduling Unit Blueprint(s) which has the status "Error/Cancelled"'}); + } else { + let dialog = this.state.dialog; + dialog.type = "confirmation"; + dialog.header= "Confirm to Create Rerun Pipeline Task(s)"; + dialog.detail = "Do you want to create Rerun Pipeline Task(s) for selected Scheduling Unit Blueprint(s)?"; + dialog.content = this.getCreateRerunPipelineDialogContent; + dialog.actions = [{id: 'yes', title: 'Yes', callback: this.createRerunPipeline, className:(this.props.project)?"dialog-btn": ""}, + {id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.onSubmit = this.createRerunPipeline; + dialog.width = '55vw'; + dialog.showIcon = false; + this.setState({dialog: dialog, dialogVisible: true}); + } + } else { + appGrowl.show({severity: 'info', summary: 'Select Row', detail: 'Please select one or more Scheduling Unit Blueprint(s)'}); + } + } + + /** + * Prepare dialog content with SU List for create Rerun Pipeline Task(s) + * @returns : dialog content + */ + getCreateRerunPipelineDialogContent() { + let selectedSchedulingUnits = []; + for(const su of this.suBlueprintList) { + selectedSchedulingUnits.push({suId: su.id, suName: su.name, suStatus: su.status}); + } + return <> + {selectedSchedulingUnits.length > 0 && + <div style={{marginTop: '1em'}}> + <b>Scheduling Unit(s) list to create Rerun Pipeline task(s)</b> + <DataTable value={selectedSchedulingUnits} resizableColumns columnResizeMode="expand" className="card" style={{paddingLeft: '0em'}}> + <Column field="suId" header="Id"></Column> + <Column field="suName" header="Name"></Column> + <Column field="suStatus" header="Status"></Column> + </DataTable> + </div> + } + </> + } + + /** + * Function to create Rerun Pipeline Task(s) for each selected SU blueprint + */ + async createRerunPipeline() { + this.setState({dialogVisible: false, showSpinner: true}); + this.successSchedulingUnits = []; + this.failedSchedulingUnits = []; + for(const su of this.suBlueprintList) { + try{ + let response = null; + response = await ScheduleService.createRerunPipeline(su); + if(response.message === 'Success') { + this.successSchedulingUnits.push({suId: su.id, suName: su.name, status: 'Success'}); + } else { + this.failedSchedulingUnits.push({suId: su.id, suName: su.name, status: 'Failed'}); + } + } catch(error) { + this.failedSchedulingUnits.push({suId: su.id, suName: su.name, status: 'Failed'}); + } + } + this.setState({showSpinner: false}); + let dialog = this.state.dialog; + dialog.type = "success"; + dialog.header= "Create Rerun Pipeline Task(s) Status"; + dialog.detail = ""; + dialog.content = this.getStatusContent; + dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.onSubmit = this.closeDialog; + dialog.width = '55vw'; + dialog.showIcon = false; + this.setState({dialog: dialog, dialogVisible: true}); + } + + /** + * Function to invoke form UI to update SU Blueprint(s) + */ + async confirmUpdateSUBlueprint(e) { + e.preventDefault(); + this.pageUpdated = false; + if (this.selectedRows.length > 0) { + this.suBlueprintList = _.filter(this.selectedRows, (schedulingUnit) => { return schedulingUnit.status === "error" + || schedulingUnit.status === "cancelled"}); + if (this.suBlueprintList.length === 0) { + appGrowl.show({severity: 'info', summary: 'Select Row', detail: 'Please select one or more Scheduling Unit Blueprint(s)'}); + } else { + let dialog = this.state.dialog; + dialog.type = "confirmation"; + dialog.header= "Confirm to Update Scheduling Unit Blueprint"; + dialog.detail = "Do you want to update the selected Scheduling Unit Blueprint(s)?"; + dialog.content = this.getUpdateSUBDialogContent; + dialog.actions = [{id: 'yes', title: 'Yes', callback: this.updateSUB, className:(this.props.project)?"dialog-btn": ""}, + {id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.onSubmit = this.updateSUB; + dialog.width = '55vw'; + dialog.showIcon = false; + this.setState({dialog: dialog, dialogVisible: true}); + } + } else { + appGrowl.show({severity: 'info', summary: 'Select Row', detail: 'Please select one or more Scheduling Unit Blueprint(s)'}); + } + } + + /** + * Prepare dialog content with SU List for Blueprint(s) + * @returns : dialog content + */ + getUpdateSUBDialogContent() { + let selectedSchedulingUnits = []; + for(const su of this.suBlueprintList) { + selectedSchedulingUnits.push({suId: su.id, suName: su.name, suStatus: su.status}); + } + return <> + {selectedSchedulingUnits.length > 0 && + <div style={{marginTop: '1em'}}> + <b>Scheduling Unit(s) list to Blueprint(s)</b> + <DataTable value={selectedSchedulingUnits} resizableColumns columnResizeMode="expand" className="card" style={{paddingLeft: '0em'}}> + <Column field="suId" header="Id"></Column> + <Column field="suName" header="Name"></Column> + <Column field="suStatus" header="Status"></Column> + </DataTable> + </div> + } + </> + } + + /** + * Function to update selected SU blueprint + */ + async updateSUB() { + this.setState({dialogVisible: false, showSpinner: true}); + this.successSchedulingUnits = []; + this.failedSchedulingUnits = []; + for(const su of this.suBlueprintList) { + try{ + let response = null; + response = await ScheduleService.updateSUBlueprint(su); + if(response.message === 'Success') { + this.successSchedulingUnits.push({suId: su.id, suName: su.name, status: 'Success'}); + } else { + this.failedSchedulingUnits.push({suId: su.id, suName: su.name, status: 'Failed'}); + } + } catch(error) { + this.failedSchedulingUnits.push({suId: su.id, suName: su.name, status: 'Failed'}); + } + } + this.setState({showSpinner: false}); + let dialog = this.state.dialog; + dialog.type = "success"; + dialog.header= "Update Scheduling Unit Blueprint(s) Status"; + dialog.detail = ""; + dialog.content = this.getStatusContent; + dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.onSubmit = this.closeDialog; + dialog.width = '55vw'; + dialog.showIcon = false; + this.setState({dialog: dialog, dialogVisible: true}); + } render(){ if (this.state.isLoading) { @@ -1546,6 +1714,16 @@ class SchedulingUnitList extends Component{ } {this.state.suType === 'Blueprint' && <> + <a href="#" style={{marginLeft: "5px"}} onClick={(e) => this.confirmCreateRerunPipeline(e)} + title={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.create_rerun ? "Create Rerun Pipeline":`${this.access_denied_message} to create Rerun Pipeline`} > + <i class={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.create_rerun?"fa fa-retweet":"fa fa-disabled fa-retweet"} + aria-hidden="true" ></i> + </a> + <a href="#" style={{marginLeft: "5px"}} onClick={(e) => this.confirmUpdateSUBlueprint(e)} + title={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.update_sub ? "Update Scheduling Unit Blueprint(s)":`${this.access_denied_message} to update Scheduling Unit Blueprint(s)`} > + <i class={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.update_sub?"fa fa-certificate":"fa fa-disabled fa-certificate"} + aria-hidden="true" ></i> + </a> <a href="#" style={{marginLeft: "5px"}} onClick={(e) => this.cleanUpSUTask(e)} title={this.state.userrole && this.state.userrole.userRolePermission.task_blueprint && this.state.userrole.userRolePermission.task_blueprint.canceltask ? "Create Clean-up Task(s)":`${this.access_denied_message} to create Clean-up Task(s)`} > <i class={this.state.userrole && this.state.userrole.userRolePermission.task_blueprint && this.state.userrole.userRolePermission.task_blueprint.canceltask?"fa fa-recycle":"fa fa-disabled fa-recycle"} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js index 72ce758af08ab032531e8171001976040a5b9db4..199e5a5984c34c795b7e612bc4a7043a7e9df458 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js @@ -206,6 +206,11 @@ class ViewSchedulingUnit extends Component { this.confirmAutoDeletion = this.confirmAutoDeletion.bind(this); this.cancelView = this.cancelView.bind(this); this.getFilterOptions = this.getFilterOptions.bind(this); + this.confirmCreateRerunPipeline = this.confirmCreateRerunPipeline.bind(this); + this.getCreateRerunPipelineDialogContent = this.getCreateRerunPipelineDialogContent.bind(this); + this.createRerunPipeline = this.createRerunPipeline.bind(this); + this.confirmUpdateSUBlueprint = this.confirmUpdateSUBlueprint.bind(this); + this.updateSUB = this.updateSUB.bind(this); } componentDidUpdate(prevProps, prevState) { @@ -459,6 +464,20 @@ class ViewSchedulingUnit extends Component { disabled: userPermissions.scheduleunit_blueprint?!userPermissions.scheduleunit_blueprint.add:true, actOn: 'click', props: { callback: this.cleanUpSUTask } }); + this.actions.unshift({ + icon: 'fa fa-certificate', + title: userPermissions.scheduleunit && userPermissions.scheduleunit.update_sub?'Update Scheduling Unit Blueprint':`${this.access_denied_message} to update Scheduling Unit Blueprint`, + type: 'button', + disabled: userPermissions.scheduleunit?!userPermissions.scheduleunit.update_sub:true, + actOn: 'click', props: { callback: this.confirmUpdateSUBlueprint } + }); + this.actions.unshift({ + icon: 'fa fa-retweet', + title: userPermissions.scheduleunit && userPermissions.scheduleunit.create_rerun?'Create Rerun Pipeline Task':`${this.access_denied_message} to create Rerun Pipeline Task`, + type: 'button', + disabled: userPermissions.scheduleunit && userPermissions.scheduleunit.create_rerun?!(this.state.scheduleunit.status === 'error' || this.state.scheduleunit.status === 'cancelled'):true, + actOn: 'click', props: { callback: this.confirmCreateRerunPipeline } + }); this.actions.unshift({ icon: 'fa-sitemap', title: userPermissions.scheduleunit && userPermissions.scheduleunit.viewworkflow?'View Workflow':`${this.access_denied_message} to view Workflow`, @@ -1126,7 +1145,7 @@ class ViewSchedulingUnit extends Component { cancelledTasks.push({ id: task.id, suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name, taskId: task.id, controlId: task.subTaskID, taskName: task.name, - status: task.status.toLowerCase()==='cancelled'?'Cancelled': 'Error Occured' + status: task.status === undefined || task.status.toLowerCase()==='cancelled'?'Cancelled': 'Error Occured' }); } } @@ -1281,12 +1300,109 @@ class ViewSchedulingUnit extends Component { } /** - * Get current user role permission for selected Schedule Unit + * Function to show confirmation before create Rerun Pipeline + */ + async confirmCreateRerunPipeline() { + let dialog = this.state.dialog; + dialog.type = "confirmation"; + dialog.header= "Confirm to Create Rerun Pipeline"; + dialog.detail = "Do you want to create Rerun Pipeline for this Scheduling Unit Blueprint?"; + dialog.content = this.getCreateRerunPipelineDialogContent; + dialog.actions = [{id: 'yes', title: 'Yes', callback: this.createRerunPipeline}, + {id: 'no', title: 'No', callback: this.closeDialog}]; + dialog.onSubmit = this.createRerunPipeline; + dialog.width = '55vw'; + dialog.showIcon = false; + this.setState({dialog: dialog, dialogVisible: true}); + } + + /** + * Prepare dialog content before create Rerun Pipeline task + * Also same function used for Update SUB + * @returns : return dialog content + */ + getCreateRerunPipelineDialogContent() { + let selectedSchedulingUnits = [{suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name, suStatus: this.state.scheduleunit.status}]; + return <> + {selectedSchedulingUnits.length > 0 && + <div style={{marginTop: '1em'}}> + <DataTable value={selectedSchedulingUnits} resizableColumns columnResizeMode="expand" className="card" style={{paddingLeft: '0em'}}> + <Column field="suId" header="Id"></Column> + <Column field="suName" header="Name"></Column> + <Column field="suStatus" header="Status"></Column> + </DataTable> + </div> + } + </> + } + + /** + * Function to create Rerun Pipeline */ - // getUserRolePermission = async () => { - // await userPermission.dispatch({ type: 'scheduleunit' }); - // this.setState({userPermission: userPermission.getState()}); - // } + async createRerunPipeline() { + let dialog = this.state.dialog; + this.statusUpdate = null; + let response = null; + try{ + response = await ScheduleService.createRerunPipeline(this.state.scheduleunit); + this.statusUpdate = response.message === 'Success'? "Success":"Failed"; + } catch(error) { + this.statusUpdate = "Failed"; + console.log('Error While creating Rerun Pipeline', error); + } + dialog.detail = ''; + dialog.type = response.message === 'Success' ? "success":"warning"; + dialog.header= "Create Rerun Pipeline Status"; + dialog.content = this.getStatusContent; + dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog}]; + dialog.onSubmit = this.closeDialog; + dialog.width = '55vw'; + dialog.showIcon = false; + this.setState({dialog: dialog, dialogVisible: true}); + } + + + /** + * Function to show confirmation before update Scheduling Unit Blueprint + */ + async confirmUpdateSUBlueprint() { + let dialog = this.state.dialog; + dialog.type = "confirmation"; + dialog.header= "Confirm to Update Scheduling Unit Blueprint"; + dialog.detail = "Do you want to update this Scheduling Unit Blueprint?"; + dialog.content = this.getCreateRerunPipelineDialogContent; + dialog.actions = [{id: 'yes', title: 'Yes', callback: this.updateSUB}, + {id: 'no', title: 'No', callback: this.closeDialog}]; + dialog.onSubmit = this.updateSUB; + dialog.width = '55vw'; + dialog.showIcon = false; + this.setState({dialog: dialog, dialogVisible: true}); + } + + /** + * Function to update SUB + */ + async updateSUB() { + let dialog = this.state.dialog; + this.statusUpdate = null; + let response = null; + try{ + response = await ScheduleService.updateSUBlueprint(this.state.scheduleunit); + this.statusUpdate = response.message === 'Success'? "Success":"Failed"; + } catch(error) { + this.statusUpdate = "Failed"; + console.log('Error While creating Rerun Pipeline', error); + } + dialog.detail = ''; + dialog.type = response.message === 'Success' ? "success":"warning"; + dialog.header= "Create Rerun Pipeline Status"; + dialog.content = this.getStatusContent; + dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog}]; + dialog.onSubmit = this.closeDialog; + dialog.width = '55vw'; + dialog.showIcon = false; + this.setState({dialog: dialog, dialogVisible: true}); + } render() { if (this.state.redirect) { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js index 754be5119c8b4abe6af04cfbca511db1e577f179..ee4dcedc054e328c52b194b3a6ab02f81a672aa2 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js @@ -1035,7 +1035,46 @@ const ScheduleService = { console.error('[schedule.services.getSUDataproduct]', error); return null; } - } + }, + copySpecificationDocForFailedTasks: async(schedulingUnit) => { + let response = {}; + try { + const url = `/api/scheduling_unit_blueprint/${schedulingUnit.id}/copy_specifications_doc_including_copies_for_failed_tasks_back_into_draft/`; + response = (await axios.post(url, {})); + response['message'] = 'Success'; + } catch(error) { + const errorData = error.response.data; + response['message'] = `Failed due to error - ${errorData.substring(0,errorData.indexOf('Request Method:'))}`; + console.error('[schedule.services.createRerunPipeline]',"Mistake", error); + } + return response; + }, + createRerunPipeline: async(schedulingUnit) => { + let response = {}; + try { + const url = `/api/scheduling_unit_blueprint/${schedulingUnit.id}/create_copies_of_failed_tasks_via_draft/`; + response = (await axios.post(url, {})); + response['message'] = 'Success'; + } catch(error) { + const errorData = error.response.data; + response['message'] = `Failed due to error - ${errorData.substring(0,errorData.indexOf('Request Method:'))}`; + console.error('[schedule.services.createRerunPipeline]',"Mistake", error); + } + return response; + }, + updateSUBlueprint: async(schedulingUnit) => { + let response = {}; + try { + const url = `/api/scheduling_unit_blueprint/${schedulingUnit.id}/update_task_blueprints_and_subtasks_graph_from_draft/`; + response = (await axios.post(url, {})); + response['message'] = 'Success'; + } catch(error) { + const errorData = error.response.data; + response['message'] = `Failed due to error - ${errorData.substring(0,errorData.indexOf('Request Method:'))}`; + console.error('[schedule.services.updateSUBlueprint]',"Mistake", error); + } + return response; + }, }