diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/parset.py b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/parset.py index 51d9eaecd1fae359ba05e952ad3714e88191b11a..33606d13965ccece7a67549752bbce396ad98ea8 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/parset.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/parset.py @@ -688,8 +688,8 @@ def _convert_to_parset_dict_for_pulsarpipeline_schema(subtask: models.Subtask) - # Dataproducts. NOTE: The pulsar pipeline doesn't actually use this information, and reads input/writes output as it pleases. - inputs = subtask.inputs.all() - in_dataproducts = sum([list(subtask_input.dataproducts.all()) for subtask_input in inputs], []) + inputs = subtask.inputs.order_by('id').all() + in_dataproducts = sum([list(subtask_input.dataproducts.order_by('id').all()) for subtask_input in inputs], []) coherent_in_dataproducts = [dp for dp in in_dataproducts if dp.specifications_doc["coherent"]] incoherent_in_dataproducts = [dp for dp in in_dataproducts if not dp.specifications_doc["coherent"]] @@ -699,8 +699,8 @@ def _convert_to_parset_dict_for_pulsarpipeline_schema(subtask: models.Subtask) - parset.update(_add_prefix(_dataproduct_parset_subkeys(subtask, incoherent_in_dataproducts), "Observation.DataProducts.Input_IncoherentStokes.")) parset["Observation.DataProducts.Input_IncoherentStokes.identifications"] = ["TMSS_subtask_%s.SAP%03d" % (input.producer.subtask.id, 0) for input in inputs] # needed by ResourceEstimator - subtask_outputs = list(subtask.outputs.all()) - out_dataproducts = sum([list(models.Dataproduct.objects.filter(producer_id=subtask_output.id)) for subtask_output in subtask_outputs], []) # todo, order these correctly? + subtask_outputs = list(subtask.outputs.order_by('id').all()) + out_dataproducts = sum([list(models.Dataproduct.objects.filter(producer_id=subtask_output.id).order_by('id')) for subtask_output in subtask_outputs], []) # todo, order these correctly? parset.update(_add_prefix(_dataproduct_parset_subkeys(subtask, out_dataproducts), "Observation.DataProducts.Output_Pulsar.")) parset["Observation.DataProducts.Output_Pulsar.identifications"] = ["TMSS_subtask_%s.SAP%03d" % (subtask.id, 0)] # todo: find correct SAP id (although this is probably fine since the pulsar pipeline does not use this?) diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/sip.py b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/sip.py index 4ada15f7bb070a6b63227553864bcbce07c4b469..0024bd150c3229ecf16d8bd26ac882123100cbd9 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/sip.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/sip.py @@ -314,7 +314,7 @@ def create_sip_representation_for_subtask(subtask: Subtask, parset_sip_identifie channelwidth_frequency=None, # NA any more ('BlueGene compatibility' see comment in LTA-SIP.xsd) channelwidth_frequencyunit=constants.FREQUENCYUNIT_HZ, # fixed observationdescription=subtask.task_blueprint.description, - channelspersubband=0, # NA any more ('BlueGene compatibility' see comment in LTA-SIP.xsd) + channelspersubband=None, # NA any more ('BlueGene compatibility' see comment in LTA-SIP.xsd) subarraypointings=subarraypointings, transientbufferboardevents=None # fixed ) diff --git a/SAS/TMSS/backend/test/t_adapter.py b/SAS/TMSS/backend/test/t_adapter.py index c2a20bc654879e6e07c85c17cad5e2a35499f774..bc95f17d66a5412053afb4104bdf4e0dc2c95dbf 100755 --- a/SAS/TMSS/backend/test/t_adapter.py +++ b/SAS/TMSS/backend/test/t_adapter.py @@ -356,7 +356,8 @@ class PulsarPipelineParsetAdapterTest(unittest.TestCase): subtask:models.Subtask = models.Subtask.objects.create(**subtask_data) subtask_output = models.SubtaskOutput.objects.create(**SubtaskOutput_test_data(subtask=subtask)) - dataproduct:models.Dataproduct = models.Dataproduct.objects.create(**Dataproduct_test_data(producer=subtask_output)) + for i in range(8): + dataproduct:models.Dataproduct = models.Dataproduct.objects.create(**Dataproduct_test_data(producer=subtask_output, filename='myoutputtestfile_%s' % i)) return subtask def test_pulp(self): @@ -381,12 +382,12 @@ class PulsarPipelineParsetAdapterTest(unittest.TestCase): # generate parset parset = convert_to_parset_dict(subtask) - logger.info("test_pulp parset:", parset) + logger.info("test_pulp parset: %s" % parset) self.assertEqual(True, parset["Observation.DataProducts.Output_Pulsar.enabled"]) self.assertEqual("Test pipeline task_blueprint", parset["Observation.Scheduler.taskName"]) self.assertEqual(subtask_preds_ids, parset["Observation.Scheduler.predecessors"]) - + self.assertEqual(parset['Observation.DataProducts.Output_Pulsar.filenames'], ['myoutputtestfile_%s' % i for i in range(8)]) class SIPadapterTest(unittest.TestCase): def test_simple_sip_generate_from_dataproduct(self): diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js index 893225ed6b5a5ab8bc6b7a290646c0ec7c15ca76..4822167ac20be373b5ee46d1118b420552126307 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js @@ -286,6 +286,8 @@ function Jeditor(props) { } } else if (schema.validationType === "transitOffset") { Validator.validateTransitOffset(schema, value, errors, path); + } else if (schema.validationType === "duration_HHmmss") { + Validator.validateDurationHHmmss(schema, value, errors, path); } else if (schema.validationType === "distanceOnSky") { // To add eventlistener to the sky distance field based on the validation type set if(_.indexOf(skyDistanceProps, path) === -1) { @@ -689,35 +691,54 @@ function Jeditor(props) { } else if (propertyKey.toLowerCase() === 'duration') { let newProperty = { "type": "string", - "format": "time", "title": "Duration", - "description": `${propertyValue.description} (Hours:Minutes:Seconds)`, + "description": `${propertyValue.description.replace("(seconds)","")} (Hours:Minutes:Seconds)`, + 'validationType': 'duration_HHmmss', "options": { "inputAttributes": { - "placeholder": "Enter time" + "placeholder": "HH:mm:ss" }, - "flatpickr": { - "wrap": true, - "showClearButton": false, - "inlineHideInput": true, - "defaultHour": 0, - "defaultMinute": 1, - "enableSeconds": true, - "defaultSecond": 0, - "hourIncrement": 1, - "minuteIncrement": 1, - "secondIncrement": 5, - "time_24hr": true, - "allowInput": true - } - } + }, + "minimum": propertyValue.minimum || 0, + "maximum": propertyValue.maximum || 86400, }; if (props.showInGrid) { newProperty.options["grid_columns"] = 3 } properties[propertyKey] = newProperty; - // durationProps.push(propertyKey); - } else if (propertyKey.toLowerCase() === 'timedelta') { + } + // else if (propertyKey.toLowerCase() === 'duration') { + // let newProperty = { + // "type": "string", + // "title": "Duration", + // "description": `${propertyValue.description} (Hours:Minutes:Seconds)`, + // 'validationType': 'duration_HHmmss', + // "options": { + // "inputAttributes": { + // "placeholder": "HH:mm:ss" + // }, + // "flatpickr": { + // "wrap": true, + // "showClearButton": false, + // "inlineHideInput": true, + // "defaultHour": 0, + // "defaultMinute": 1, + // "enableSeconds": true, + // "defaultSecond": 0, + // "hourIncrement": 1, + // "minuteIncrement": 1, + // "secondIncrement": 5, + // "time_24hr": true, + // "allowInput": true + // } + // } + // }; + // if (props.showInGrid) { + // newProperty.options["grid_columns"] = 3 + // } + // properties[propertyKey] = newProperty; + // } + else if (propertyKey.toLowerCase() === 'timedelta') { // Customization for defintion is removed as the same property type is used in other fields where it is required to be just number only // let newProperty = { // "type": "string", diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js index 5d5743a854296d1c1ff0a01a637d065157308f0e..045172981c181df096ec75dd75e1607263929d1b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js @@ -14,6 +14,7 @@ export default (props) => { const [originIngestRelation, setOriginIngestRelation] = useState(_.cloneDeep(props.ingestGroup)); const [ingestRelation, setIngestRelation] = useState(_.cloneDeep(props.ingestGroup)); const [isSaveDisabled, setSaveDisabled] = useState(true); + const [isBlueprintRelation, setBlueprintRelation] = useState(false); const [stateConfrimDialog, setConfirmDialog] = useState({ dialog: {}, dialogVisible: false }); const [taskRelationDraft, setTaskRelationDraft] = useState([]); const isAllTaskChecked = (groupName) => !ingestRelation[groupName].filter(task => !task.canIngest).length; @@ -86,7 +87,8 @@ export default (props) => { } const originTask = _.find(originIngestRelation[ingestGroup], ['id', ingestTask.id]); if (ingestTask.canIngest !== originTask.canIngest) { - canSave = true; + canSave = ((!tempTaskRelation.canRemove && tempTaskRelation.canIngest) || (tempTaskRelation.canRemove)) ? true : canSave; + setBlueprintRelation(!canSave); tempTaskRelation.toAdd = ingestTask.canIngest; tempTaskRelation.toDelete = !ingestTask.canIngest; } @@ -120,25 +122,48 @@ export default (props) => { const getSUDialogContent = () => { let changedData = []; + let inremovableData = []; const tempIngestRelation = {...ingestRelation}; for (const ingestGroup of _.keys(tempIngestRelation)) { for (const ingestTask of tempIngestRelation[ingestGroup]) { const originTask = _.find(originIngestRelation[ingestGroup], ['id', ingestTask.id]); - if (ingestTask.canIngest !== originTask.canIngest) { + if ((ingestTask.canIngest !== originTask.canIngest) && ((ingestTask.canIngest) || (!ingestTask.canIngest && ingestTask.canRemove)) ) { changedData.push({name: ingestTask.name, displayMessage: ingestTask.canIngest?"Add":"Remove"}); } + else if((ingestTask.canIngest !== originTask.canIngest) && (!ingestTask.canIngest && !ingestTask.canRemove)) { + ingestTask.canIngest = true; + inremovableData.push({name: ingestTask.name, displayMessage: "Cannot Remove"}) + } } } return <> - <div style={{ marginTop: '1em' }}> - <b>Do you want to add/remove dataproducts of tasks to Ingest?</b> - </div> - <div style={{ marginTop: '1em' }}> - <DataTable value={changedData} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> - <Column field="name" header="Task Name"></Column> - <Column field="displayMessage" header="Action"></Column> - </DataTable> - </div> + { changedData.length > 0 && + <> + <div style={{ marginTop: '1em' }}> + <b>Do you want to add/remove dataproducts of tasks to Ingest?</b> + </div> + + <div style={{ marginTop: '1em' }}> + <DataTable value={changedData} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <Column field="name" header="Task Name"></Column> + <Column field="displayMessage" header="Action"></Column> + </DataTable> + </div> + </> + } + { inremovableData.length > 0 && + <> + <div style={{ marginTop: '1em' }}> + <b>Dataproducts of the following task(s) cannot be removed from Ingest as blueprint(s) exists for it/them</b> + </div> + <div style={{ marginTop: '1em' }}> + <DataTable value={inremovableData} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <Column field="name" header="Task Name"></Column> + <Column field="displayMessage" header="Action"></Column> + </DataTable> + </div> + </> + } </> } @@ -221,8 +246,11 @@ export default (props) => { )} </> ))} - - + <div> + {isSaveDisabled && isBlueprintRelation? + <span style={{color: 'red'}}>* Cannot remove if blueprint relation exists</span> : <span></span> + } + </div> </div> </div> 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 6f94b77c5535407f4034adf339d651e6caf77483..43b3ed59f3e860bd705501c2fa3dd462a85e2b7c 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js @@ -41,7 +41,7 @@ class ViewSchedulingUnit extends Component { SU_ACTIVE_STATUSES = ['started', 'observing', 'observed', 'processing', 'processed', 'ingesting']; SU_END_STATUSES = ['finished', 'error', 'cancelled']; TASK_END_STATUSES = ['finished', 'error', 'cancelled']; - SU_BLUEPRINT_EXPAND= 'draft.scheduling_constraints_template,draft,draft.scheduling_set,task_blueprints.specifications_template,task_blueprints,task_blueprints.subtasks,draft.observation_strategy_template' + SU_BLUEPRINT_EXPAND= 'draft.scheduling_constraints_template,draft,draft.scheduling_set,task_blueprints.specifications_template,task_blueprints,task_blueprints.subtasks,draft.observation_strategy_template,task_blueprints.draft' SU_BLUEPRINT_FIELDS= ['id','url','created_at','status', 'unschedulable_reason', 'tags','output_pinned','duration','name','on_sky_start_time','on_sky_stop_time','scheduling_constraints_doc','description', 'updated_at','draft.url','draft.id','draft.name','draft.scheduling_set.url','draft.scheduling_set.name','draft.scheduling_set.project_id','task_blueprints.status', 'task_blueprints.unschedulable_reason', 'draft.rank','draft.priority_queue_value','task_blueprints.subtasks.id','task_blueprints.subtasks.primary', 'task_blueprints.subtasks.total_nr_of_dataproducts', 'task_blueprints.subtasks.total_size_of_dataproducts', 'task_blueprints.subtasks.total_size_of_dataproducts_on_filesysem','task_blueprints.subtasks.specifications_template_id', @@ -51,14 +51,14 @@ class ViewSchedulingUnit extends Component { 'task_blueprints.do_cancel','task_blueprints.obsolete_since','task_blueprints.created_at','task_blueprints.updated_at','task_blueprints.specifications_template.id', 'task_blueprints.draft_id', 'task_blueprints.specifications_template.type_value','task_blueprints.specifications_template.schema', 'draft.scheduling_constraints_template.schema','draft.scheduling_constraints_template.ref_resolved_schema','draft.scheduling_constraints_template.url', - 'task_blueprints.produced_by_ids','task_blueprints.specifications_doc', + 'task_blueprints.produced_by_ids','task_blueprints.specifications_doc', 'task_blueprints.draft', 'draft.observation_strategy_template_id','draft.observation_strategy_template.id','draft.observation_strategy_template.name', 'draft.observation_strategy_template.purpose_value','draft.observation_strategy_template.state_value','draft.observation_strategy_template.version', 'draft.observation_strategy_template.description', 'draft.observation_strategy_template.template'] SU_DRAFT_EXPAND= 'scheduling_constraints_template,scheduling_set,task_drafts.specifications_template,task_drafts,observation_strategy_template,scheduling_unit_blueprints' SU_DRAFT_FIELDS=['id','url','created_at','status','tags','output_pinned','duration','name','on_sky_start_time','on_sky_stop_time','rank','priority_queue_value','description', 'scheduling_constraints_doc','scheduling_constraints_template.schema','scheduling_constraints_template.ref_resolved_schema','scheduling_constraints_template.url', - 'observation_strategy_template_id','task_drafts.url','scheduling_constraints_template_id', + 'observation_strategy_template_id','task_drafts.url','scheduling_constraints_template_id', 'task_drafts.connector_types', 'updated_at','scheduling_set.url','scheduling_set.name','scheduling_set.project_id','task_drafts.status','task_drafts.task_type','task_drafts.id','task_drafts.subtasks_ids', 'task_drafts.name','task_drafts.description','task_drafts.short_description','task_drafts.on_sky_start_time','task_drafts.on_sky_stop_time','task_drafts.process_start_time', 'task_drafts.process_stop_time','task_drafts.duration','task_drafts.relative_start_time','task_drafts.relative_stop_time','task_drafts.tags','task_drafts.do_cancel', @@ -70,7 +70,7 @@ class ViewSchedulingUnit extends Component { constructor(props) { - super(props); + super(props); this.setToggleBySorting(); this.state = { scheduleunit: null, @@ -289,7 +289,7 @@ class ViewSchedulingUnit extends Component { componentDidUpdate(prevProps, prevState) { if (this.state.scheduleunit && this.props.match.params && - (this.state.scheduleunitId.toString() !== this.props.match.params.id || + (this.state.scheduleunitId !== this.props.match.params.id || this.state.scheduleunitType !== this.props.match.params.type)) { this.getSchedulingUnitDetails(this.props.match.params.type, this.props.match.params.id); } @@ -381,7 +381,7 @@ class ViewSchedulingUnit extends Component { .then(async (schedulingUnit) => { if (schedulingUnit) { const permissionById = await AuthUtil.getUserPermissionByModuleId(schedule_type === 'blueprint'? 'scheduling_unit_blueprint': 'scheduling_unit_draft', schedule_id) - this.setState({permissionById: permissionById[schedule_id]}); + this.setState({permissionById: permissionById[schedule_id], scheduleunitType: schedule_type,}); schedulingUnit = this.formatConstraintDocForUI(schedulingUnit); if (schedulingUnit.draft) { schedulingUnit['observation_strategy_template_id'] = schedulingUnit.draft.observation_strategy_template_id; @@ -404,15 +404,15 @@ class ViewSchedulingUnit extends Component { scheduleunit: schedulingUnit, scheduleunitType: schedule_type, constraintTemplate: schedulingUnit.scheduling_constraints_template}) - let ingestGroup; if(this.props.match.params.type === 'draft') { ingestGroup = tasks.map(task => ({ name: task.name, - canIngest: task.canIngest?true:false, type_value: task.type_value, id: task.id })); + canIngest: task.canIngest?true:false, type_value: task.type_value, id: task.id, + canRemove: task.canRemove? true: false })); ingestGroup = _.groupBy(_.sortBy(ingestGroup, ['type_value','name']), 'type_value'); } else if(this.props.match.params.type === 'blueprint') { - ingestGroup = tasks.map(task => ({ name: task.name, + ingestGroup = tasks.map(task => ({ name: task.name, canRemove: true, canIngest: task.canIngest?true:false, type_value: task.task_type, id: task.id })); ingestGroup = _.groupBy(_.sortBy(ingestGroup, ['type_value','name']), 'type_value'); @@ -718,11 +718,13 @@ class ViewSchedulingUnit extends Component { //Ingest Task Relation const ingestTask = scheduletasklist.find(task => task.type_value === 'ingest' && task.tasktype.toLowerCase() === 'draft'); if (ingestTask) { + this.ingestTaskRelations = []; for (const producer_id of ingestTask.produced_by_ids) { const taskRelation = await ScheduleService.getTaskRelation(producer_id); let producerTask = scheduletasklist.find(task => task.id === taskRelation.producer_id && task.tasktype.toLowerCase() === 'draft'); if(producerTask!=undefined){ producerTask.canIngest = true; + producerTask.canRemove = taskRelation? taskRelation.blueprints.length < 1: true; // Store all existing task relations of ingest task this.ingestTaskRelations.push(taskRelation); } @@ -1110,49 +1112,86 @@ class ViewSchedulingUnit extends Component { } } + fetchTaskRelationObj(producerTask, consumerTask, taskDraftRelObj, isDraft, producerRelations) { + let taskRelAddDraftObj = []; + const producerConnectors = _.filter(producerTask.connector_types,[ 'iotype_value', 'output']); + for (const producerConnector of producerConnectors) { + let taskRelObj = _.cloneDeep(taskDraftRelObj) + const consumerConnector = _.find(consumerTask.connector_types, connector => { + return (connector.datatype === producerConnector.datatype + && connector.dataformat === producerConnector.dataformat + && (connector.role === producerConnector.role || + connector.role === 'any')); + }); + if(producerRelations) { + let producerDraftRelation = _.find(producerRelations, ['output_role_id', producerConnector.id]) + taskRelObj.draft = producerDraftRelation.url; + } + if (consumerConnector) { + taskRelObj.name = producerTask.name + taskRelObj.dataformat_value = consumerConnector.dataformat_value; + taskRelObj.consumer = isDraft ? consumerTask.draft.url: consumerTask.url; + taskRelObj.producer = isDraft ? producerTask.draft.url: producerTask.url; + taskRelObj.input_role = `${taskRelObj.input_role}/${consumerConnector.id}`; + taskRelObj.output_role = `${taskRelObj.output_role}/${producerConnector.id}`; + taskRelAddDraftObj.push(taskRelObj); + } + } + return taskRelAddDraftObj + } + async submitTaskRelationToIngest(data) { if(data) { let consumer = data.ingestGroup.ingest[0]; let taskRelationsToAdd = _.filter(data.taskRelationDraft, taskRelDraft => {return taskRelDraft.toAdd === true}); - let taskRelationsToDelete = _.filter(data.taskRelationDraft, taskRelDraft => {return taskRelDraft.toDelete === true}); - const propPromises = [], - taskRelAddDraftObj=[], + let taskRelationsToDelete = _.filter(data.taskRelationDraft, taskRelDraft => {return taskRelDraft.toDelete === true && taskRelDraft.canRemove === true}); + let taskRelAddDraftObj=[]; + const propPromises = [], taskRelObj=ScheduleService.taskRelationDraftAPIReqObj(); let createdTaskRes=[],deletedTaskRes=[],updateTaskRelObj=[]; + let schedulingUnitTaskList = [] + if(this.props.match.params.type == 'draft') { + schedulingUnitTaskList = this.state.scheduleunit.task_drafts; + } else if(this.props.match.params.type == 'blueprint'){ + schedulingUnitTaskList = this.state.scheduleunit.task_blueprints; + } + const consumerTask = _.find(schedulingUnitTaskList, ['id', consumer.id]); + if(this.props.match.params.type == 'blueprint') { + consumerTask.connector_types = consumerTask.draft.connector_types; + } if(taskRelationsToAdd.length) { - // const consumerTask = await ScheduleService.getTaskDraft(consumer.id); - let schedulingUnitTaskList = [] - if(this.props.match.params.type == 'draft') { - schedulingUnitTaskList = this.state.scheduleunit.task_drafts - } else if(this.props.match.params.type == 'blueprint'){ - schedulingUnitTaskList = this.state.scheduleunit.task_blueprints - } - const consumerTask = _.find(schedulingUnitTaskList, ['id', consumer.id]); const producerTasks = _.filter(schedulingUnitTaskList, draft => { const matchingTask = _.find(taskRelationsToAdd, ['id', draft.id]); return (matchingTask !== null && matchingTask !== undefined); }); for (const producerTask of producerTasks) { let taskDraftRelObj = {...taskRelObj}; - const producerConnectors = _.filter(producerTask.connector_types,[ 'iotype', 'output']); - for (const producerConnector of producerConnectors) { - const consumerConnector = _.find(consumerTask.connector_types, connector => { - return (connector.datatype === producerConnector.datatype - && connector.dataformat === producerConnector.dataformat - && (connector.role === producerConnector.role || - connector.role === 'any')); - }); - if (consumerConnector) { - taskDraftRelObj.consumer = consumerTask.url; - taskDraftRelObj.producer = producerTask.url; - taskDraftRelObj.input_role = `${taskDraftRelObj.input_role}/${consumerConnector.id}`; - taskDraftRelObj.output_role = `${taskDraftRelObj.output_role}/${producerConnector.id}`; - taskRelAddDraftObj.push(taskDraftRelObj); - break; + if(this.props.match.params.type == 'blueprint') { + producerTask.connector_types = producerTask.draft.connector_types; + let producerRelations = await ScheduleService.getAllTaskRelation('draft', `producer=${producerTask.draft_id}`); + producerRelations = _.filter(producerRelations, ['consumer_id', consumerTask.draft_id]) + if (producerRelations.length > 0) { + let taskRel = this.fetchTaskRelationObj(producerTask, consumerTask, taskDraftRelObj, false, producerRelations); + taskRelAddDraftObj = [...taskRelAddDraftObj, ...taskRel]; + } + else { + taskDraftRelObj.blueprints = []; + let taskAddRel = this.fetchTaskRelationObj(producerTask, consumerTask, taskDraftRelObj, true); + let createDraftRelation = await ScheduleService.createTaskRelation([...taskAddRel],[producerTask.id], 'draft');//'dataFormat':this.state.dataformat + if(createDraftRelation) { + taskDraftRelObj = {...taskRelObj}; + let taskRel = this.fetchTaskRelationObj(producerTask, consumerTask, taskDraftRelObj, false, _.map(createDraftRelation, 'msg')); + taskRelAddDraftObj = [...taskRelAddDraftObj, ...taskRel]; + } } } + if (this.props.match.params.type == 'draft') { + taskDraftRelObj.blueprints = []; + let taskRel = this.fetchTaskRelationObj(producerTask, consumerTask, taskDraftRelObj); + taskRelAddDraftObj = [...taskRelAddDraftObj, ...taskRel]; + } } - if(taskRelAddDraftObj) { + if(taskRelAddDraftObj) { createdTaskRes = await ScheduleService.createTaskRelation(taskRelAddDraftObj,taskRelationsToAdd, this.props.match.params.type);//'dataFormat':this.state.dataformat // Add newly created task relation to the existing relations list for (const newRelation of createdTaskRes) { @@ -1166,14 +1205,35 @@ class ViewSchedulingUnit extends Component { if(taskRelationsToDelete.length) { // let taskRelationData = await ScheduleService.getAllTaskRelation(this.props.match.params.type); let taskRelationData = this.ingestTaskRelations; - let getDeleteTaskRelation= taskRelationData.filter(obj1 => { + let taskRelationToDelete = taskRelationData; + if(this.props.match.params.type === 'draft') { + taskRelationToDelete = _.uniqBy(taskRelationData.filter(taskRelation => { + if(taskRelation.blueprints) { + return taskRelation.blueprints.length < 1; + } + return false; + }), (taskRelation => { + return taskRelation.id; + })) + } + else { + taskRelationToDelete = _.uniqBy(taskRelationData.filter(taskRelation => { + return !taskRelation.blueprints; + }), (taskRelation => { + return taskRelation.id; + })) + } + let getDeleteTaskRelation= taskRelationToDelete.filter(obj1 => { + obj1.producer = _.find(taskRelationsToDelete, rel => rel.id === obj1.producer_id) return obj1.consumer_id==consumer.id - && (taskRelationsToDelete.some(obj2 => obj1.producer_id == obj2.id)) + && (taskRelationsToDelete.some(obj2 => obj1.producer_id == obj2.id)); } ); deletedTaskRes = await ScheduleService.deleteTaskRelation(getDeleteTaskRelation,taskRelationsToDelete, this.props.match.params.type); // Remove the deleted relations from the existing relations in the local list for (const deletedRelation of deletedTaskRes) { if (deletedRelation.action === 'Removed') { + let draftRelation = _.find(getDeleteTaskRelation, ['id', deletedRelation.msg]); + deletedRelation.data_format = _.find(consumerTask.connector_types, ['id', draftRelation.input_role_id]).dataformat_value _.remove(this.ingestTaskRelations, oldRelation => {return oldRelation.id === deletedRelation.msg}); } } @@ -1191,7 +1251,7 @@ class ViewSchedulingUnit extends Component { let taskStatus = []; updateTaskRelObj.forEach((task)=>{ let tempTask = {}; - tempTask['name'] = task.name; + tempTask['name'] = `${task.name} (${task.data_format})`; tempTask['action'] = task.action; taskStatus.push(tempTask); }); @@ -1229,7 +1289,7 @@ class ViewSchedulingUnit extends Component { <div style={{marginTop: '1em'}}> <b>Data Products to Ingest </b> <DataTable value={taskStatus} resizableColumns columnResizeMode="expand" className="card" style={{paddingLeft: '0em'}}> - <Column field="name" header="Task Name"></Column> + <Column field="name" header="Task Name (Dataformat)"></Column> <Column field="action" header="Action"></Column> </DataTable> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js index 2668cff4dbe627e92f58768eecffbf04bc6aa402..c919f8c9364cbe9fcf80f4fe5cf4a8eb774806dd 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js @@ -1102,7 +1102,9 @@ export class SchedulingSetCreate extends Component { values: schema.definitions.antenna_set.enum, }, }; - this.getColumnDefinition('Duration~Duration', {type: 'string', format: 'hh:mm:ss'}); + this.getColumnDefinition('Duration~Duration', {type: 'string', format: 'hh:mm:ss', + schema: {minimum: schema.properties.Duration.minimum || 0, + maximum: schema.properties.Duration.maximum || 86400}}); cellProps['Duration~Duration'] = { cellEditor:'offsetTimeInputmask'}; this.getColumnDefinition('Beamformers~Beamformers', {type: 'object', format: 'json'}); cellProps['beamformers'] = { cellRenderer: 'beamformersRenderer', cellEditor:'beamformer' }; @@ -1404,8 +1406,9 @@ export class SchedulingSetCreate extends Component { case 'offset_to': this.columnDefinition[key]['format'] = '-hh:mm:ss'; break; - case 'Duration~Duration': + case 'duration~duration': this.columnDefinition[key]['format'] = 'hh:mm:ss'; + this.columnDefinition[key]['schema'] = props.schema; break; case 'timeat': case 'timeafter': @@ -1814,9 +1817,18 @@ export class SchedulingSetCreate extends Component { isValidRow = false; } } else if (defProperty.format === 'hh:mm:ss') { - if (!Validator.isValidHHmmss(rowData[colDefkey],false)) { - errorMsg += column.headerName+", "; - isValidRow = false; + if (defProperty.schema) { + let errors = []; + Validator.validateDurationHHmmss(defProperty.schema, rowData[colDefkey], errors, null); + if (errors.length > 0) { + errorMsg += errors[0].message +", "; + isValidRow = false; + } + } else { + if (!Validator.isValidHHmmss(rowData[colDefkey],false)) { + errorMsg += column.headerName+", "; + isValidRow = false; + } } } else if (defProperty.format === 'dd:mm:ss') { // for angle2 , check the format and implement the validation const result = Validator.isValidHHmmss(rowData[colDefkey],false); 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 71638b7e125d2f485f5fc43cb87e38d79a11145f..c02b31d99e1ed40668ff3232060ca9069b703b3c 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js @@ -563,6 +563,7 @@ const ScheduleService = { status: true, action: 'Added', 'name': name, + 'data_format': params.dataformat_value, 'msg': response.data }; } catch (error) { @@ -1037,13 +1038,13 @@ const ScheduleService = { return res; }, /* get All Task Relation */ - getAllTaskRelation: async function (type) { + getAllTaskRelation: async function (type, filter) { try { let url = '' if(type === 'draft') { - url = '/api/task_relation_draft/'; + url = `/api/task_relation_draft/?${filter}`; } else if(type === 'blueprint') { - url = '/api/task_relation_blueprint/'; + url = `/api/task_relation_blueprint/?${filter}`; } const response = await axios.get(url); return response.data.results; @@ -1057,7 +1058,7 @@ const ScheduleService = { try { if (taskRelDraftObj) { taskRelDraftObj.forEach((tObj, i) => { - taskRelDraftPromises.push(this.addTaskRelation(tObj, obj[i].name, type)); + taskRelDraftPromises.push(this.addTaskRelation(tObj, tObj.name, type)); }); const taskRelationsToAdd = await Promise.all(taskRelDraftPromises); return taskRelationsToAdd; @@ -1073,7 +1074,7 @@ const ScheduleService = { try { if (taskRelDelDraft) { taskRelDelDraft.forEach((tObj, i) => { - taskRelDelDraftPromises.push(this.removeTaskRelation(tObj.id, obj[i].name, type)); + taskRelDelDraftPromises.push(this.removeTaskRelation(tObj.id, tObj.producer.name, type)); }); const taskRelationsToDelete = await Promise.all(taskRelDelDraftPromises); return taskRelationsToDelete; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js index 96cc0862169a6d9324df9305162b9066b17b680d..9242d0922cdc1e3e5e8e2fe927d0e5deb6cfc86a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js @@ -75,6 +75,15 @@ const Validator = { } return isValid; }, + isValidDuration(duration) { + let isValid = false; + if (duration && duration.includes(':')) { + duration = duration.replaceAll("_", "0"); + var timeFormat = /^([0-9]|[0-1][0-9]|[0-2][0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9]) || (24:00:00)$/; + isValid = timeFormat.test(duration); + } + return isValid; + }, /** * Validate Day time * @param {string value with DDD HH:mm:ss format} Days time @@ -130,6 +139,25 @@ const Validator = { } } }, + validateDurationHHmmss(schema, jsonOutput, error, path) { + console.log(schema); + if (!this.isValidDuration(jsonOutput)) { + error.push({ + message:"Invalid duration. Duration should be in 'HH:mm:ss' format and between "+UnitConverter.getSecsToHHmmss(schema.minimum)+' and '+UnitConverter.getSecsToHHmmss(schema.maximum), + path:path, + property:'validationType', + }); + } else { + let value = UnitConverter.getHHmmssToSecs(jsonOutput); + if (isNaN(value) || (value < schema.minimum || value > schema.maximum)) { + error.push({ + message:'Duration must be between '+UnitConverter.getSecsToHHmmss(schema.minimum)+' and '+UnitConverter.getSecsToHHmmss(schema.maximum), + path:path, + property:'validationType', + }); + } + } + } }; export default Validator;