diff --git a/SAS/TMSS/frontend/tmss_webapp/src/__mocks__/scheduleunit.service.data.js b/SAS/TMSS/frontend/tmss_webapp/src/__mocks__/scheduleunit.service.data.js index 684b850628fcc906d259b78e6e1f9dd3a291ae57..e33d5167ad4263425e709ef510f9afd7aaa05082 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/__mocks__/scheduleunit.service.data.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/__mocks__/scheduleunit.service.data.js @@ -3,162 +3,162 @@ import {parseTemplatesToCorrectJSONFormat} from "../services/service.helper"; const fs = require('fs'); const path = require('path'); -const SUServiceMock= { +const SUServiceMock = { scheduleSetList: [ - { - "id": 1, - "url": "http://127.0.0.100:8008/api/scheduling_set/1", - "created_at": "2022-02-03T01:53:42.824670", - "description": "", - "name": "Test Scheduling Set", - "project": "http://127.0.0.100:8008/api/project/high", - "project_id": "high", - "scheduling_unit_drafts": [ - "http://127.0.0.100:8008/api/scheduling_unit_draft/1", - "http://127.0.0.100:8008/api/scheduling_unit_draft/2", - "http://127.0.0.100:8008/api/scheduling_unit_draft/3", - "http://127.0.0.100:8008/api/scheduling_unit_draft/4", - "http://127.0.0.100:8008/api/scheduling_unit_draft/5", - "http://127.0.0.100:8008/api/scheduling_unit_draft/6", - "http://127.0.0.100:8008/api/scheduling_unit_draft/7", - "http://127.0.0.100:8008/api/scheduling_unit_draft/8", - "http://127.0.0.100:8008/api/scheduling_unit_draft/9", - "http://127.0.0.100:8008/api/scheduling_unit_draft/10", - "http://127.0.0.100:8008/api/scheduling_unit_draft/11", - "http://127.0.0.100:8008/api/scheduling_unit_draft/12", - "http://127.0.0.100:8008/api/scheduling_unit_draft/13", - "http://127.0.0.100:8008/api/scheduling_unit_draft/14", - "http://127.0.0.100:8008/api/scheduling_unit_draft/15", - "http://127.0.0.100:8008/api/scheduling_unit_draft/16", - "http://127.0.0.100:8008/api/scheduling_unit_draft/49", - "http://127.0.0.100:8008/api/scheduling_unit_draft/50", - "http://127.0.0.100:8008/api/scheduling_unit_draft/51", - "http://127.0.0.100:8008/api/scheduling_unit_draft/53" - ], - "scheduling_unit_drafts_ids": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 49, - 50, - 51, - 53 - ], - "tags": [], - "updated_at": "2022-02-03T01:53:42.824686" - }, - { - "id": 2, - "url": "http://127.0.0.100:8008/api/scheduling_set/2", - "created_at": "2022-02-03T01:53:42.848197", - "description": "", - "name": "Test Scheduling Set", - "project": "http://127.0.0.100:8008/api/project/normal", - "project_id": "normal", - "scheduling_unit_drafts": [ - "http://127.0.0.100:8008/api/scheduling_unit_draft/17", - "http://127.0.0.100:8008/api/scheduling_unit_draft/18", - "http://127.0.0.100:8008/api/scheduling_unit_draft/19", - "http://127.0.0.100:8008/api/scheduling_unit_draft/20", - "http://127.0.0.100:8008/api/scheduling_unit_draft/21", - "http://127.0.0.100:8008/api/scheduling_unit_draft/22", - "http://127.0.0.100:8008/api/scheduling_unit_draft/23", - "http://127.0.0.100:8008/api/scheduling_unit_draft/24", - "http://127.0.0.100:8008/api/scheduling_unit_draft/25", - "http://127.0.0.100:8008/api/scheduling_unit_draft/26", - "http://127.0.0.100:8008/api/scheduling_unit_draft/27", - "http://127.0.0.100:8008/api/scheduling_unit_draft/28", - "http://127.0.0.100:8008/api/scheduling_unit_draft/29", - "http://127.0.0.100:8008/api/scheduling_unit_draft/30", - "http://127.0.0.100:8008/api/scheduling_unit_draft/31", - "http://127.0.0.100:8008/api/scheduling_unit_draft/32" - ], - "scheduling_unit_drafts_ids": [ - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32 - ], - "tags": [], - "updated_at": "2022-02-03T01:53:42.848209" - }, - { - "id": 3, - "url": "http://127.0.0.100:8008/api/scheduling_set/3", - "created_at": "2022-02-03T01:53:42.866377", - "description": "", - "name": "Test Scheduling Set", - "project": "http://127.0.0.100:8008/api/project/low", - "project_id": "low", - "scheduling_unit_drafts": [ - "http://127.0.0.100:8008/api/scheduling_unit_draft/33", - "http://127.0.0.100:8008/api/scheduling_unit_draft/34", - "http://127.0.0.100:8008/api/scheduling_unit_draft/35", - "http://127.0.0.100:8008/api/scheduling_unit_draft/36", - "http://127.0.0.100:8008/api/scheduling_unit_draft/37", - "http://127.0.0.100:8008/api/scheduling_unit_draft/38", - "http://127.0.0.100:8008/api/scheduling_unit_draft/39", - "http://127.0.0.100:8008/api/scheduling_unit_draft/40", - "http://127.0.0.100:8008/api/scheduling_unit_draft/41", - "http://127.0.0.100:8008/api/scheduling_unit_draft/42", - "http://127.0.0.100:8008/api/scheduling_unit_draft/43", - "http://127.0.0.100:8008/api/scheduling_unit_draft/44", - "http://127.0.0.100:8008/api/scheduling_unit_draft/45", - "http://127.0.0.100:8008/api/scheduling_unit_draft/46", - "http://127.0.0.100:8008/api/scheduling_unit_draft/47", - "http://127.0.0.100:8008/api/scheduling_unit_draft/48", - "http://127.0.0.100:8008/api/scheduling_unit_draft/52" - ], - "scheduling_unit_drafts_ids": [ - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 52 - ], - "tags": [], - "updated_at": "2022-02-03T01:53:42.866395" - } - ], + { + "id": 1, + "url": "http://127.0.0.100:8008/api/scheduling_set/1", + "created_at": "2022-02-03T01:53:42.824670", + "description": "", + "name": "Test Scheduling Set", + "project": "http://127.0.0.100:8008/api/project/high", + "project_id": "high", + "scheduling_unit_drafts": [ + "http://127.0.0.100:8008/api/scheduling_unit_draft/1", + "http://127.0.0.100:8008/api/scheduling_unit_draft/2", + "http://127.0.0.100:8008/api/scheduling_unit_draft/3", + "http://127.0.0.100:8008/api/scheduling_unit_draft/4", + "http://127.0.0.100:8008/api/scheduling_unit_draft/5", + "http://127.0.0.100:8008/api/scheduling_unit_draft/6", + "http://127.0.0.100:8008/api/scheduling_unit_draft/7", + "http://127.0.0.100:8008/api/scheduling_unit_draft/8", + "http://127.0.0.100:8008/api/scheduling_unit_draft/9", + "http://127.0.0.100:8008/api/scheduling_unit_draft/10", + "http://127.0.0.100:8008/api/scheduling_unit_draft/11", + "http://127.0.0.100:8008/api/scheduling_unit_draft/12", + "http://127.0.0.100:8008/api/scheduling_unit_draft/13", + "http://127.0.0.100:8008/api/scheduling_unit_draft/14", + "http://127.0.0.100:8008/api/scheduling_unit_draft/15", + "http://127.0.0.100:8008/api/scheduling_unit_draft/16", + "http://127.0.0.100:8008/api/scheduling_unit_draft/49", + "http://127.0.0.100:8008/api/scheduling_unit_draft/50", + "http://127.0.0.100:8008/api/scheduling_unit_draft/51", + "http://127.0.0.100:8008/api/scheduling_unit_draft/53" + ], + "scheduling_unit_drafts_ids": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 49, + 50, + 51, + 53 + ], + "tags": [], + "updated_at": "2022-02-03T01:53:42.824686" + }, + { + "id": 2, + "url": "http://127.0.0.100:8008/api/scheduling_set/2", + "created_at": "2022-02-03T01:53:42.848197", + "description": "", + "name": "Test Scheduling Set", + "project": "http://127.0.0.100:8008/api/project/normal", + "project_id": "normal", + "scheduling_unit_drafts": [ + "http://127.0.0.100:8008/api/scheduling_unit_draft/17", + "http://127.0.0.100:8008/api/scheduling_unit_draft/18", + "http://127.0.0.100:8008/api/scheduling_unit_draft/19", + "http://127.0.0.100:8008/api/scheduling_unit_draft/20", + "http://127.0.0.100:8008/api/scheduling_unit_draft/21", + "http://127.0.0.100:8008/api/scheduling_unit_draft/22", + "http://127.0.0.100:8008/api/scheduling_unit_draft/23", + "http://127.0.0.100:8008/api/scheduling_unit_draft/24", + "http://127.0.0.100:8008/api/scheduling_unit_draft/25", + "http://127.0.0.100:8008/api/scheduling_unit_draft/26", + "http://127.0.0.100:8008/api/scheduling_unit_draft/27", + "http://127.0.0.100:8008/api/scheduling_unit_draft/28", + "http://127.0.0.100:8008/api/scheduling_unit_draft/29", + "http://127.0.0.100:8008/api/scheduling_unit_draft/30", + "http://127.0.0.100:8008/api/scheduling_unit_draft/31", + "http://127.0.0.100:8008/api/scheduling_unit_draft/32" + ], + "scheduling_unit_drafts_ids": [ + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32 + ], + "tags": [], + "updated_at": "2022-02-03T01:53:42.848209" + }, + { + "id": 3, + "url": "http://127.0.0.100:8008/api/scheduling_set/3", + "created_at": "2022-02-03T01:53:42.866377", + "description": "", + "name": "Test Scheduling Set", + "project": "http://127.0.0.100:8008/api/project/low", + "project_id": "low", + "scheduling_unit_drafts": [ + "http://127.0.0.100:8008/api/scheduling_unit_draft/33", + "http://127.0.0.100:8008/api/scheduling_unit_draft/34", + "http://127.0.0.100:8008/api/scheduling_unit_draft/35", + "http://127.0.0.100:8008/api/scheduling_unit_draft/36", + "http://127.0.0.100:8008/api/scheduling_unit_draft/37", + "http://127.0.0.100:8008/api/scheduling_unit_draft/38", + "http://127.0.0.100:8008/api/scheduling_unit_draft/39", + "http://127.0.0.100:8008/api/scheduling_unit_draft/40", + "http://127.0.0.100:8008/api/scheduling_unit_draft/41", + "http://127.0.0.100:8008/api/scheduling_unit_draft/42", + "http://127.0.0.100:8008/api/scheduling_unit_draft/43", + "http://127.0.0.100:8008/api/scheduling_unit_draft/44", + "http://127.0.0.100:8008/api/scheduling_unit_draft/45", + "http://127.0.0.100:8008/api/scheduling_unit_draft/46", + "http://127.0.0.100:8008/api/scheduling_unit_draft/47", + "http://127.0.0.100:8008/api/scheduling_unit_draft/48", + "http://127.0.0.100:8008/api/scheduling_unit_draft/52" + ], + "scheduling_unit_drafts_ids": [ + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 52 + ], + "tags": [], + "updated_at": "2022-02-03T01:53:42.866395" + } + ], observStrategies: [ - { + { "id": 1, "url": "http://127.0.0.100:8008/api/scheduling_unit_observing_strategy_template/1", "created_at": "2022-02-03T01:54:06.310497", @@ -1508,7 +1508,7 @@ const SUServiceMock= { "updated_at": "2022-02-23T10:43:07.385256", "version": 1 } - ], + ], getObservStrategies: () => { const templates = require("../../../tmss_webapp/src/__mocks__/observing_strategy_templates.json"); return parseTemplatesToCorrectJSONFormat(templates.strategies); @@ -1516,96 +1516,110 @@ const SUServiceMock= { getObservStrategy: (name) => { // const templates = require("../../../tmss_webapp/src/__mocks__/observing_strategy_templates.json"); // return templates.strategies.find(strategy=> { return strategy.name === name }); - return SUServiceMock.getObservStrategies().find(strategy=> { return strategy.name === name }); + return SUServiceMock.getObservStrategies().find(strategy => { + return strategy.name === name + }); }, templatePurposes: [ { - "value": "production", - "url": "http://localhost:3000/api/template_purpose/production" + "value": "production", + "url": "http://localhost:3000/api/template_purpose/production" }, { - "value": "scientific_commissioning", - "url": "http://localhost:3000/api/template_purpose/scientific_commissioning" + "value": "scientific_commissioning", + "url": "http://localhost:3000/api/template_purpose/scientific_commissioning" }, { - "value": "technical_commissioning", - "url": "http://localhost:3000/api/template_purpose/technical_commissioning" + "value": "technical_commissioning", + "url": "http://localhost:3000/api/template_purpose/technical_commissioning" }, { - "value": "system_health", - "url": "http://localhost:3000/api/template_purpose/system_health" + "value": "system_health", + "url": "http://localhost:3000/api/template_purpose/system_health" } - ], + ], templateStates: [ { - "value": "development", - "url": "http://localhost:3000/api/template_state/development" + "value": "development", + "url": "http://localhost:3000/api/template_state/development" }, { - "value": "active", - "url": "http://localhost:3000/api/template_state/active" + "value": "active", + "url": "http://localhost:3000/api/template_state/active" }, { - "value": "legacy", - "url": "http://localhost:3000/api/template_state/legacy" + "value": "legacy", + "url": "http://localhost:3000/api/template_state/legacy" }, { - "value": "obsolete", - "url": "http://localhost:3000/api/template_state/obsolete" + "value": "obsolete", + "url": "http://localhost:3000/api/template_state/obsolete" } ], getSchedulingUnitFromObservStrategy: (observStrategy) => { const strategyId = SUServiceMock.getObservStrategy(observStrategy.name)['id']; - return {data: { - "id": 1, - "url": "http://192.168.99.100:8008/api/scheduling_unit_draft/1", - "created_at": "2020-08-25T13:28:42.092602", - "description": "", - "duration": 30120, - "generator_instance_doc": null, - "name": "UC1 test scheduling unit 1.1", - "observation_strategy_template": `http://192.168.99.100:8008/api/scheduling_unit_observing_strategy_template/${strategyId}`, - "observation_strategy_template_id": strategyId, - "specifications_template": "http://192.168.99.100:8008/api/scheduling_unit_template/1", - "specifications_template_id": 1, - "scheduling_set": "http://192.168.99.100:8008/api/scheduling_set/1", - "scheduling_set_id": 1, - "scheduling_unit_blueprints": [ - "http://192.168.99.100:8008/api/scheduling_unit_blueprint/1" - ], - "scheduling_unit_blueprints_ids": [ - 1 - ], - "tags": [ - "TEST", - "UC1" - ], - "task_drafts": [ - "http://192.168.99.100:8008/api/task_draft/5", - "http://192.168.99.100:8008/api/task_draft/7", - "http://192.168.99.100:8008/api/task_draft/6", - "http://192.168.99.100:8008/api/task_draft/4", - "http://192.168.99.100:8008/api/task_draft/3", - "http://192.168.99.100:8008/api/task_draft/2", - "http://192.168.99.100:8008/api/task_draft/1" - ], - "task_drafts_ids": [ - 5, - 7, - 6, - 4, - 3, - 2, - 1 - ], - "updated_at": "2020-08-25T13:28:42.119417" - }}}, - stations:[ - {"group": "Core", "stations": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS101", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501"]}, - {"group": "Remote", "stations": ["RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"]}, - {"group": "International", "stations": ["DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"]}, - ], - suFilters:{ + return { + data: { + "id": 1, + "url": "http://192.168.99.100:8008/api/scheduling_unit_draft/1", + "created_at": "2020-08-25T13:28:42.092602", + "description": "", + "duration": 30120, + "generator_instance_doc": null, + "name": "UC1 test scheduling unit 1.1", + "observation_strategy_template": `http://192.168.99.100:8008/api/scheduling_unit_observing_strategy_template/${strategyId}`, + "observation_strategy_template_id": strategyId, + "specifications_template": "http://192.168.99.100:8008/api/scheduling_unit_template/1", + "specifications_template_id": 1, + "scheduling_set": "http://192.168.99.100:8008/api/scheduling_set/1", + "scheduling_set_id": 1, + "scheduling_unit_blueprints": [ + "http://192.168.99.100:8008/api/scheduling_unit_blueprint/1" + ], + "scheduling_unit_blueprints_ids": [ + 1 + ], + "tags": [ + "TEST", + "UC1" + ], + "task_drafts": [ + "http://192.168.99.100:8008/api/task_draft/5", + "http://192.168.99.100:8008/api/task_draft/7", + "http://192.168.99.100:8008/api/task_draft/6", + "http://192.168.99.100:8008/api/task_draft/4", + "http://192.168.99.100:8008/api/task_draft/3", + "http://192.168.99.100:8008/api/task_draft/2", + "http://192.168.99.100:8008/api/task_draft/1" + ], + "task_drafts_ids": [ + 5, + 7, + 6, + 4, + 3, + 2, + 1 + ], + "updated_at": "2020-08-25T13:28:42.119417" + } + } + }, + stations: [ + { + "group": "Core", + "stations": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS101", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501"] + }, + { + "group": "Remote", + "stations": ["RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"] + }, + { + "group": "International", + "stations": ["DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"] + }, + ], + suFilters: { "name": "Scheduling Unit Draft Extended List", "description": "", "renders": [ @@ -4042,1529 +4056,1529 @@ const SUServiceMock= { "correlator" ], "additionalProperties": false, - "properties": { - "Target Pointing 1": { - "$id": "#target_pointing", - "$ref": "#/definitions/pointing", - "title": "Target Pointing 1", - "default": { - "angle1": 0.6624317181687094, - "angle2": 1.5579526427549426, - "target": "target1", - "direction_type": "J2000" - } - }, - "Target Pointing 2": { - "$id": "#target_pointing", - "$ref": "#/definitions/pointing", - "title": "Target Pointing 2", - "default": { - "angle1": 0.6624317181687094, - "angle2": 1.5579526427549426, - "target": "target1", - "direction_type": "J2000" - } - }, - "Tile Beam": { - "$ref": "#/definitions/pointing", - "title": "Tile Beam", - "default": { - "angle1": 0.6624317181687094, - "angle2": 1.5579526427549426, - "target": "target1", - "direction_type": "J2000" - }, - "description": "HBA only" - } - }, - "definitions": { - "QA": { - "type": "object", - "title": "QA", - "default": {}, - "required": [ - "file_conversion", - "plots", - "inspection_plots" - ], - "properties": { - "plots": { - "$ref": "#/definitions/plots", - "default": {} - }, - "file_conversion": { - "$ref": "#/definitions/file_conversion", - "default": {} - }, - "inspection_plots": { - "$ref": "#/definitions/inspection_plots", - "default": "msplots" - } - }, - "description": "Perform all Quality Assurance (QA) tasks, including file conversion and plotting.", - "additionalProperties": false - }, - "plots": { - "type": "object", - "title": "Plots", - "default": {}, - "required": [ - "enabled", - "autocorrelation", - "crosscorrelation" - ], - "properties": { - "enabled": { - "type": "boolean", - "title": "enabled", - "default": true, - "description": "Do/Don't create plots from the QA file from the observation" - }, - "autocorrelation": { - "type": "boolean", - "title": "autocorrelation", - "default": true, - "description": "Create autocorrelation plots for all stations" - }, - "crosscorrelation": { - "type": "boolean", - "title": "crosscorrelation", - "default": true, - "description": "Create crosscorrelation plots for all baselines" - } - }, - "description": "Create dynamic spectrum plots", - "additionalProperties": false - }, - "file_conversion": { - "type": "object", - "title": "File Conversion", - "default": {}, - "required": [ - "enabled", - "nr_of_subbands", - "nr_of_timestamps" - ], - "properties": { - "enabled": { - "type": "boolean", - "title": "enabled", - "default": true, - "description": "Do/Don't create a QA file for the observation" - }, - "nr_of_subbands": { - "type": "integer", - "title": "#subbands", - "default": -1, - "description": "Keep this number of subbands from the observation in the QA file, or all if -1" - }, - "nr_of_timestamps": { - "type": "integer", - "title": "#timestamps", - "default": 256, - "minimum": 1, - "description": "Extract this number of timestamps from the observation in the QA file (equidistantanly sampled, no averaging/interpolation)" - } - }, - "description": "Create a QA file for the observation", - "additionalProperties": false - }, - "inspection_plots": { - "enum": [ - "msplots", - "dynspec", - "none" - ], - "type": "string", - "title": "Inspection Plots", - "default": "msplots", - "decription": "Type of inspection plots to run" - }, - "SAPs": { - "type": "array", - "items": { - "type": "object", - "title": "SAP", - "default": {}, - "required": [ - "name", - "digital_pointing", - "subbands" - ], - "properties": { - "name": { - "type": "string", - "title": "Name", - "default": "_SAP_name_", - "minLength": 1, - "description": "Identifier for this beam" - }, - "subbands": { - "type": "array", - "items": { - "type": "integer", - "title": "Subband", - "maximum": 511, - "minimum": 0, - "maxLength": 488, - "minLength": 1 - }, - "title": "Subband list", - "default": [], - "additionalItems": false - }, - "digital_pointing": { - "$id": "#target_pointing", - "$ref": "#/definitions/pointing", - "title": "Digital pointing", - "default": {} - } - }, - "headerTemplate": "{{ i0 }} - {{ self.name }}", - "additionalProperties": false - }, - "title": "SAPs", - "default": [ - {} - ], - "minItems": 0, - "description": "Station beams", - "additionalItems": false - }, - "pointing": { - "type": "object", - "required": [ - "angle1", - "angle2", - "target" - ], - "properties": { - "angle1": { - "type": "number", - "title": "Angle 1", - "default": 0.6624317181687094, - "description": "First angle (e.g. RA)" - }, - "angle2": { - "type": "number", - "title": "Angle 2", - "default": 1.5579526427549426, - "description": "Second angle (e.g. DEC)" - }, - "target": { - "type": "string", - "title": "Target", - "default": "_target_name_", - "minLength": 1, - "description": "Description of where this beam points at" - }, - "direction_type": { - "enum": [ - "J2000", - "AZELGEO", - "LMN", - "SUN", - "MOON", - "MERCURY", - "VENUS", - "MARS", - "JUPITER", - "SATURN", - "URANUS", - "NEPTUNE", - "PLUTO" - ], - "type": "string", - "title": "Reference frame", - "default": "J2000", - "description": "" - } - }, - "additionalProperties": false - }, - "filter": { - "enum": [ - "LBA_10_70", - "LBA_30_70", - "LBA_10_90", - "LBA_30_90", - "HBA_110_190", - "HBA_210_250" - ], - "type": "string", - "title": "Band-pass filter", - "default": "HBA_110_190", - "description": "Must match antenna type" - }, - "timedelta": { - "type": "number", - "default": 0, - "description": "A time duration or delta expressed in seconds" - }, - "antenna_set": { - "enum": [ - "HBA_DUAL", - "HBA_DUAL_INNER", - "HBA_ONE", - "HBA_ONE_INNER", - "HBA_ZERO", - "HBA_ZERO_INNER", - "LBA_INNER", - "LBA_OUTER", - "LBA_SPARSE_EVEN", - "LBA_SPARSE_ODD", - "LBA_ALL" - ], - "type": "string", - "title": "Antenna set", - "default": "HBA_DUAL", - "description": "Fields & antennas to use" - }, - "station_groups": { - "type": "array", - "items": { - "$ref": "#/definitions/station_group" - }, - "title": "Station groups", - "default": [ - { - "stations": [ - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007" - ], - "max_nr_missing": 1 - } - ], - "minItems": 1, - "description": "One or more predefined or custom groups of stations", - "additionalItems": false, - "additionalProperties": false - }, - "station_group": { - "type": "object", - "anyOf": [ - { - "type": "object", - "title": "Superterp", - "default": { - "stations": [ - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007" - ], - "max_nr_missing": 0 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all stations on the Superterp", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" + "properties": { + "Target Pointing 1": { + "$id": "#target_pointing", + "$ref": "#/definitions/pointing", + "title": "Target Pointing 1", + "default": { + "angle1": 0.6624317181687094, + "angle2": 1.5579526427549426, + "target": "target1", + "direction_type": "J2000" + } + }, + "Target Pointing 2": { + "$id": "#target_pointing", + "$ref": "#/definitions/pointing", + "title": "Target Pointing 2", + "default": { + "angle1": 0.6624317181687094, + "angle2": 1.5579526427549426, + "target": "target1", + "direction_type": "J2000" + } + }, + "Tile Beam": { + "$ref": "#/definitions/pointing", + "title": "Tile Beam", + "default": { + "angle1": 0.6624317181687094, + "angle2": 1.5579526427549426, + "target": "target1", + "direction_type": "J2000" + }, + "description": "HBA only" + } + }, + "definitions": { + "QA": { + "type": "object", + "title": "QA", + "default": {}, + "required": [ + "file_conversion", + "plots", + "inspection_plots" + ], + "properties": { + "plots": { + "$ref": "#/definitions/plots", + "default": {} + }, + "file_conversion": { + "$ref": "#/definitions/file_conversion", + "default": {} + }, + "inspection_plots": { + "$ref": "#/definitions/inspection_plots", + "default": "msplots" + } + }, + "description": "Perform all Quality Assurance (QA) tasks, including file conversion and plotting.", + "additionalProperties": false + }, + "plots": { + "type": "object", + "title": "Plots", + "default": {}, + "required": [ + "enabled", + "autocorrelation", + "crosscorrelation" + ], + "properties": { + "enabled": { + "type": "boolean", + "title": "enabled", + "default": true, + "description": "Do/Don't create plots from the QA file from the observation" + }, + "autocorrelation": { + "type": "boolean", + "title": "autocorrelation", + "default": true, + "description": "Create autocorrelation plots for all stations" + }, + "crosscorrelation": { + "type": "boolean", + "title": "crosscorrelation", + "default": true, + "description": "Create crosscorrelation plots for all baselines" + } + }, + "description": "Create dynamic spectrum plots", + "additionalProperties": false + }, + "file_conversion": { + "type": "object", + "title": "File Conversion", + "default": {}, + "required": [ + "enabled", + "nr_of_subbands", + "nr_of_timestamps" + ], + "properties": { + "enabled": { + "type": "boolean", + "title": "enabled", + "default": true, + "description": "Do/Don't create a QA file for the observation" + }, + "nr_of_subbands": { + "type": "integer", + "title": "#subbands", + "default": -1, + "description": "Keep this number of subbands from the observation in the QA file, or all if -1" + }, + "nr_of_timestamps": { + "type": "integer", + "title": "#timestamps", + "default": 256, + "minimum": 1, + "description": "Extract this number of timestamps from the observation in the QA file (equidistantanly sampled, no averaging/interpolation)" + } + }, + "description": "Create a QA file for the observation", + "additionalProperties": false + }, + "inspection_plots": { + "enum": [ + "msplots", + "dynspec", + "none" + ], + "type": "string", + "title": "Inspection Plots", + "default": "msplots", + "decription": "Type of inspection plots to run" + }, + "SAPs": { + "type": "array", + "items": { + "type": "object", + "title": "SAP", + "default": {}, + "required": [ + "name", + "digital_pointing", + "subbands" + ], + "properties": { + "name": { + "type": "string", + "title": "Name", + "default": "_SAP_name_", + "minLength": 1, + "description": "Identifier for this beam" + }, + "subbands": { + "type": "array", + "items": { + "type": "integer", + "title": "Subband", + "maximum": 511, + "minimum": 0, + "maxLength": 488, + "minLength": 1 + }, + "title": "Subband list", + "default": [], + "additionalItems": false + }, + "digital_pointing": { + "$id": "#target_pointing", + "$ref": "#/definitions/pointing", + "title": "Digital pointing", + "default": {} + } + }, + "headerTemplate": "{{ i0 }} - {{ self.name }}", + "additionalProperties": false + }, + "title": "SAPs", + "default": [ + {} + ], + "minItems": 0, + "description": "Station beams", + "additionalItems": false + }, + "pointing": { + "type": "object", + "required": [ + "angle1", + "angle2", + "target" + ], + "properties": { + "angle1": { + "type": "number", + "title": "Angle 1", + "default": 0.6624317181687094, + "description": "First angle (e.g. RA)" + }, + "angle2": { + "type": "number", + "title": "Angle 2", + "default": 1.5579526427549426, + "description": "Second angle (e.g. DEC)" + }, + "target": { + "type": "string", + "title": "Target", + "default": "_target_name_", + "minLength": 1, + "description": "Description of where this beam points at" + }, + "direction_type": { + "enum": [ + "J2000", + "AZELGEO", + "LMN", + "SUN", + "MOON", + "MERCURY", + "VENUS", + "MARS", + "JUPITER", + "SATURN", + "URANUS", + "NEPTUNE", + "PLUTO" + ], + "type": "string", + "title": "Reference frame", + "default": "J2000", + "description": "" + } + }, + "additionalProperties": false + }, + "filter": { + "enum": [ + "LBA_10_70", + "LBA_30_70", + "LBA_10_90", + "LBA_30_90", + "HBA_110_190", + "HBA_210_250" + ], + "type": "string", + "title": "Band-pass filter", + "default": "HBA_110_190", + "description": "Must match antenna type" + }, + "timedelta": { + "type": "number", + "default": 0, + "description": "A time duration or delta expressed in seconds" + }, + "antenna_set": { + "enum": [ + "HBA_DUAL", + "HBA_DUAL_INNER", + "HBA_ONE", + "HBA_ONE_INNER", + "HBA_ZERO", + "HBA_ZERO_INNER", + "LBA_INNER", + "LBA_OUTER", + "LBA_SPARSE_EVEN", + "LBA_SPARSE_ODD", + "LBA_ALL" + ], + "type": "string", + "title": "Antenna set", + "default": "HBA_DUAL", + "description": "Fields & antennas to use" + }, + "station_groups": { + "type": "array", + "items": { + "$ref": "#/definitions/station_group" + }, + "title": "Station groups", + "default": [ + { + "stations": [ + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007" + ], + "max_nr_missing": 1 + } + ], + "minItems": 1, + "description": "One or more predefined or custom groups of stations", + "additionalItems": false, + "additionalProperties": false + }, + "station_group": { + "type": "object", + "anyOf": [ + { + "type": "object", + "title": "Superterp", + "default": { + "stations": [ + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007" + ], + "max_nr_missing": 0 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all stations on the Superterp", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "Core", + "default": { + "stations": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501" + ], + "max_nr_missing": 4 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all Core stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "Remote", + "default": { + "stations": [ + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509" + ], + "max_nr_missing": 4 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all Dutch remote stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "Dutch", + "default": { + "stations": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509" + ], + "max_nr_missing": 4 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all Dutch (Core + Remote) stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "International", + "default": { + "stations": [ + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "DE609", + "FR606", + "SE607", + "UK608", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "max_nr_missing": 2 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "DE609", + "FR606", + "SE607", + "UK608", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all international stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "International required", + "default": { + "stations": [ + "DE601", + "DE605" + ], + "max_nr_missing": 1 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "DE601", + "DE605" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "A subgroup of the international stations which are required when doing observation with international stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "All", + "default": { + "stations": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "DE609", + "FR606", + "SE607", + "UK608", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "max_nr_missing": 6 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "DE609", + "FR606", + "SE607", + "UK608", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all (Core + Remote + International) stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "Custom", + "default": { + "stations": [ + "CS001" + ], + "max_nr_missing": 0 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list" + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "A custom group of stations which can be defined by the user", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + } + ], + "title": "Station group", + "default": { + "stations": [ + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007" + ], + "max_nr_missing": 1 + }, + "description": "A set of predefined list of stations, and a constraint on how many stations are allowed to be missing (due to maintenance for example)" + }, + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + }, + "description": "This schema defines the parameters to setup a target observation task." }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "Core", - "default": { - "stations": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501" - ], - "max_nr_missing": 4 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all Core stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "Remote", - "default": { - "stations": [ - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509" - ], - "max_nr_missing": 4 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all Dutch remote stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "Dutch", - "default": { - "stations": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509" - ], - "max_nr_missing": 4 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all Dutch (Core + Remote) stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "International", - "default": { - "stations": [ - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "DE609", - "FR606", - "SE607", - "UK608", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "max_nr_missing": 2 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "DE609", - "FR606", - "SE607", - "UK608", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all international stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "International required", - "default": { - "stations": [ - "DE601", - "DE605" - ], - "max_nr_missing": 1 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "DE601", - "DE605" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "A subgroup of the international stations which are required when doing observation with international stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "All", - "default": { - "stations": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "DE609", - "FR606", - "SE607", - "UK608", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "max_nr_missing": 6 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "DE609", - "FR606", - "SE607", - "UK608", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all (Core + Remote + International) stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "Custom", - "default": { - "stations": [ - "CS001" - ], - "max_nr_missing": 0 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list" - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "A custom group of stations which can be defined by the user", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - } - ], - "title": "Station group", - "default": { - "stations": [ - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007" - ], - "max_nr_missing": 1 - }, - "description": "A set of predefined list of stations, and a constraint on how many stations are allowed to be missing (due to maintenance for example)" - }, - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - }, - "description": "This schema defines the parameters to setup a target observation task." - }, - "tags": [], - "type": "http://localhost:3000/api/task_type/observation", - "type_value": "observation", - "updated_at": "2022-01-17T03:57:03.765750", - "version": 1 + "tags": [], + "type": "http://localhost:3000/api/task_type/observation", + "type_value": "observation", + "updated_at": "2022-01-17T03:57:03.765750", + "version": 1 } } ], @@ -5913,1535 +5927,1535 @@ const SUServiceMock= { "correlator" ], "additionalProperties": false, - "properties": { - "Target Pointing 1": { - "$id": "#target_pointing", - "$ref": "#/definitions/pointing", - "title": "Target Pointing 1", - "default": { - "angle1": 0.6624317181687094, - "angle2": 1.5579526427549426, - "target": "target1", - "direction_type": "J2000" - } - }, - "Target Pointing 2": { - "$id": "#target_pointing", - "$ref": "#/definitions/pointing", - "title": "Target Pointing 2", - "default": { - "angle1": 0.6624317181687094, - "angle2": 1.5579526427549426, - "target": "target1", - "direction_type": "J2000" + "properties": { + "Target Pointing 1": { + "$id": "#target_pointing", + "$ref": "#/definitions/pointing", + "title": "Target Pointing 1", + "default": { + "angle1": 0.6624317181687094, + "angle2": 1.5579526427549426, + "target": "target1", + "direction_type": "J2000" + } + }, + "Target Pointing 2": { + "$id": "#target_pointing", + "$ref": "#/definitions/pointing", + "title": "Target Pointing 2", + "default": { + "angle1": 0.6624317181687094, + "angle2": 1.5579526427549426, + "target": "target1", + "direction_type": "J2000" + } + }, + "Tile Beam": { + "$ref": "#/definitions/pointing", + "title": "Tile Beam", + "default": { + "angle1": 0.6624317181687094, + "angle2": 1.5579526427549426, + "target": "target1", + "direction_type": "J2000" + }, + "description": "HBA only" + } + }, + "definitions": { + "QA": { + "type": "object", + "title": "QA", + "default": {}, + "required": [ + "file_conversion", + "plots", + "inspection_plots" + ], + "properties": { + "plots": { + "$ref": "#/definitions/plots", + "default": {} + }, + "file_conversion": { + "$ref": "#/definitions/file_conversion", + "default": {} + }, + "inspection_plots": { + "$ref": "#/definitions/inspection_plots", + "default": "msplots" + } + }, + "description": "Perform all Quality Assurance (QA) tasks, including file conversion and plotting.", + "additionalProperties": false + }, + "plots": { + "type": "object", + "title": "Plots", + "default": {}, + "required": [ + "enabled", + "autocorrelation", + "crosscorrelation" + ], + "properties": { + "enabled": { + "type": "boolean", + "title": "enabled", + "default": true, + "description": "Do/Don't create plots from the QA file from the observation" + }, + "autocorrelation": { + "type": "boolean", + "title": "autocorrelation", + "default": true, + "description": "Create autocorrelation plots for all stations" + }, + "crosscorrelation": { + "type": "boolean", + "title": "crosscorrelation", + "default": true, + "description": "Create crosscorrelation plots for all baselines" + } + }, + "description": "Create dynamic spectrum plots", + "additionalProperties": false + }, + "file_conversion": { + "type": "object", + "title": "File Conversion", + "default": {}, + "required": [ + "enabled", + "nr_of_subbands", + "nr_of_timestamps" + ], + "properties": { + "enabled": { + "type": "boolean", + "title": "enabled", + "default": true, + "description": "Do/Don't create a QA file for the observation" + }, + "nr_of_subbands": { + "type": "integer", + "title": "#subbands", + "default": -1, + "description": "Keep this number of subbands from the observation in the QA file, or all if -1" + }, + "nr_of_timestamps": { + "type": "integer", + "title": "#timestamps", + "default": 256, + "minimum": 1, + "description": "Extract this number of timestamps from the observation in the QA file (equidistantanly sampled, no averaging/interpolation)" + } + }, + "description": "Create a QA file for the observation", + "additionalProperties": false + }, + "inspection_plots": { + "enum": [ + "msplots", + "dynspec", + "none" + ], + "type": "string", + "title": "Inspection Plots", + "default": "msplots", + "decription": "Type of inspection plots to run" + }, + "SAPs": { + "type": "array", + "items": { + "type": "object", + "title": "SAP", + "default": {}, + "required": [ + "name", + "digital_pointing", + "subbands" + ], + "properties": { + "name": { + "type": "string", + "title": "Name", + "default": "_SAP_name_", + "minLength": 1, + "description": "Identifier for this beam" + }, + "subbands": { + "type": "array", + "items": { + "type": "integer", + "title": "Subband", + "maximum": 511, + "minimum": 0, + "maxLength": 488, + "minLength": 1 + }, + "title": "Subband list", + "default": [], + "additionalItems": false + }, + "digital_pointing": { + "$id": "#target_pointing", + "$ref": "#/definitions/pointing", + "title": "Digital pointing", + "default": {} + } + }, + "headerTemplate": "{{ i0 }} - {{ self.name }}", + "additionalProperties": false + }, + "title": "SAPs", + "default": [ + {} + ], + "minItems": 0, + "description": "Station beams", + "additionalItems": false + }, + "pointing": { + "type": "object", + "required": [ + "angle1", + "angle2", + "target" + ], + "properties": { + "angle1": { + "type": "number", + "title": "Angle 1", + "default": 0.6624317181687094, + "description": "First angle (e.g. RA)" + }, + "angle2": { + "type": "number", + "title": "Angle 2", + "default": 1.5579526427549426, + "description": "Second angle (e.g. DEC)" + }, + "target": { + "type": "string", + "title": "Target", + "default": "_target_name_", + "minLength": 1, + "description": "Description of where this beam points at" + }, + "direction_type": { + "enum": [ + "J2000", + "AZELGEO", + "LMN", + "SUN", + "MOON", + "MERCURY", + "VENUS", + "MARS", + "JUPITER", + "SATURN", + "URANUS", + "NEPTUNE", + "PLUTO" + ], + "type": "string", + "title": "Reference frame", + "default": "J2000", + "description": "" + } + }, + "additionalProperties": false + }, + "filter": { + "enum": [ + "LBA_10_70", + "LBA_30_70", + "LBA_10_90", + "LBA_30_90", + "HBA_110_190", + "HBA_210_250" + ], + "type": "string", + "title": "Band-pass filter", + "default": "HBA_110_190", + "description": "Must match antenna type" + }, + "timedelta": { + "type": "number", + "default": 0, + "description": "A time duration or delta expressed in seconds" + }, + "antenna_set": { + "enum": [ + "HBA_DUAL", + "HBA_DUAL_INNER", + "HBA_ONE", + "HBA_ONE_INNER", + "HBA_ZERO", + "HBA_ZERO_INNER", + "LBA_INNER", + "LBA_OUTER", + "LBA_SPARSE_EVEN", + "LBA_SPARSE_ODD", + "LBA_ALL" + ], + "type": "string", + "title": "Antenna set", + "default": "HBA_DUAL", + "description": "Fields & antennas to use" + }, + "station_groups": { + "type": "array", + "items": { + "$ref": "#/definitions/station_group" + }, + "title": "Station groups", + "default": [ + { + "stations": [ + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007" + ], + "max_nr_missing": 1 + } + ], + "minItems": 1, + "description": "One or more predefined or custom groups of stations", + "additionalItems": false, + "additionalProperties": false + }, + "station_group": { + "type": "object", + "anyOf": [ + { + "type": "object", + "title": "Superterp", + "default": { + "stations": [ + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007" + ], + "max_nr_missing": 0 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all stations on the Superterp", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "Core", + "default": { + "stations": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501" + ], + "max_nr_missing": 4 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all Core stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "Remote", + "default": { + "stations": [ + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509" + ], + "max_nr_missing": 4 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all Dutch remote stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "Dutch", + "default": { + "stations": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509" + ], + "max_nr_missing": 4 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all Dutch (Core + Remote) stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "International", + "default": { + "stations": [ + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "DE609", + "FR606", + "SE607", + "UK608", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "max_nr_missing": 2 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "DE609", + "FR606", + "SE607", + "UK608", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all international stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "International required", + "default": { + "stations": [ + "DE601", + "DE605" + ], + "max_nr_missing": 1 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "DE601", + "DE605" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "A subgroup of the international stations which are required when doing observation with international stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "All", + "default": { + "stations": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "DE609", + "FR606", + "SE607", + "UK608", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "max_nr_missing": 6 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list", + "enum": [ + [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "DE609", + "FR606", + "SE607", + "UK608", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ] + ] + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "The group of all (Core + Remote + International) stations", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + }, + { + "type": "object", + "title": "Custom", + "default": { + "stations": [ + "CS001" + ], + "max_nr_missing": 0 + }, + "required": [ + "stations", + "max_nr_missing" + ], + "properties": { + "stations": { + "$ref": "#/definitions/station_list" + }, + "max_nr_missing": { + "$ref": "#/definitions/max_number_of_missing_stations" + } + }, + "description": "A custom group of stations which can be defined by the user", + "additionalProperties": false, + "definitions": { + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + } + } + ], + "title": "Station group", + "default": { + "stations": [ + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007" + ], + "max_nr_missing": 1 + }, + "description": "A set of predefined list of stations, and a constraint on how many stations are allowed to be missing (due to maintenance for example)" + }, + "station_list": { + "type": "array", + "items": { + "enum": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509", + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "FR606", + "SE607", + "UK608", + "DE609", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "type": "string", + "title": "Station", + "description": "These are the LOFAR stations" + }, + "default": [], + "minItems": 0, + "uniqueItems": true, + "additionalItems": false, + "additionalProperties": false + }, + "max_number_of_missing_stations": { + "type": "integer", + "title": "Maximum number of stations to omit", + "default": 0, + "minimum": 0, + "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" + } + }, + "description": "This schema defines the parameters to setup a target observation task." + }, + "tags": [], + "type": "http://localhost:3000/api/task_type/observation", + "type_value": "observation", + "updated_at": "2022-01-17T03:57:03.765750", + "version": 1 + } + } + ], + "updated_at": "2022-01-24T05:55:23.369965" } - }, - "Tile Beam": { - "$ref": "#/definitions/pointing", - "title": "Tile Beam", - "default": { - "angle1": 0.6624317181687094, - "angle2": 1.5579526427549426, - "target": "target1", - "direction_type": "J2000" - }, - "description": "HBA only" - } - }, - "definitions": { - "QA": { - "type": "object", - "title": "QA", - "default": {}, - "required": [ - "file_conversion", - "plots", - "inspection_plots" - ], - "properties": { - "plots": { - "$ref": "#/definitions/plots", - "default": {} - }, - "file_conversion": { - "$ref": "#/definitions/file_conversion", - "default": {} - }, - "inspection_plots": { - "$ref": "#/definitions/inspection_plots", - "default": "msplots" - } - }, - "description": "Perform all Quality Assurance (QA) tasks, including file conversion and plotting.", - "additionalProperties": false - }, - "plots": { - "type": "object", - "title": "Plots", - "default": {}, - "required": [ - "enabled", - "autocorrelation", - "crosscorrelation" - ], - "properties": { - "enabled": { - "type": "boolean", - "title": "enabled", - "default": true, - "description": "Do/Don't create plots from the QA file from the observation" - }, - "autocorrelation": { - "type": "boolean", - "title": "autocorrelation", - "default": true, - "description": "Create autocorrelation plots for all stations" - }, - "crosscorrelation": { - "type": "boolean", - "title": "crosscorrelation", - "default": true, - "description": "Create crosscorrelation plots for all baselines" - } - }, - "description": "Create dynamic spectrum plots", - "additionalProperties": false - }, - "file_conversion": { - "type": "object", - "title": "File Conversion", - "default": {}, - "required": [ - "enabled", - "nr_of_subbands", - "nr_of_timestamps" - ], - "properties": { - "enabled": { - "type": "boolean", - "title": "enabled", - "default": true, - "description": "Do/Don't create a QA file for the observation" - }, - "nr_of_subbands": { - "type": "integer", - "title": "#subbands", - "default": -1, - "description": "Keep this number of subbands from the observation in the QA file, or all if -1" - }, - "nr_of_timestamps": { - "type": "integer", - "title": "#timestamps", - "default": 256, - "minimum": 1, - "description": "Extract this number of timestamps from the observation in the QA file (equidistantanly sampled, no averaging/interpolation)" - } - }, - "description": "Create a QA file for the observation", - "additionalProperties": false - }, - "inspection_plots": { - "enum": [ - "msplots", - "dynspec", - "none" - ], - "type": "string", - "title": "Inspection Plots", - "default": "msplots", - "decription": "Type of inspection plots to run" - }, - "SAPs": { - "type": "array", - "items": { - "type": "object", - "title": "SAP", - "default": {}, - "required": [ - "name", - "digital_pointing", - "subbands" - ], - "properties": { - "name": { - "type": "string", - "title": "Name", - "default": "_SAP_name_", - "minLength": 1, - "description": "Identifier for this beam" - }, - "subbands": { - "type": "array", - "items": { - "type": "integer", - "title": "Subband", - "maximum": 511, - "minimum": 0, - "maxLength": 488, - "minLength": 1 - }, - "title": "Subband list", - "default": [], - "additionalItems": false - }, - "digital_pointing": { - "$id": "#target_pointing", - "$ref": "#/definitions/pointing", - "title": "Digital pointing", - "default": {} - } - }, - "headerTemplate": "{{ i0 }} - {{ self.name }}", - "additionalProperties": false - }, - "title": "SAPs", - "default": [ - {} - ], - "minItems": 0, - "description": "Station beams", - "additionalItems": false - }, - "pointing": { - "type": "object", - "required": [ - "angle1", - "angle2", - "target" - ], - "properties": { - "angle1": { - "type": "number", - "title": "Angle 1", - "default": 0.6624317181687094, - "description": "First angle (e.g. RA)" - }, - "angle2": { - "type": "number", - "title": "Angle 2", - "default": 1.5579526427549426, - "description": "Second angle (e.g. DEC)" - }, - "target": { - "type": "string", - "title": "Target", - "default": "_target_name_", - "minLength": 1, - "description": "Description of where this beam points at" - }, - "direction_type": { - "enum": [ - "J2000", - "AZELGEO", - "LMN", - "SUN", - "MOON", - "MERCURY", - "VENUS", - "MARS", - "JUPITER", - "SATURN", - "URANUS", - "NEPTUNE", - "PLUTO" - ], - "type": "string", - "title": "Reference frame", - "default": "J2000", - "description": "" - } - }, - "additionalProperties": false - }, - "filter": { - "enum": [ - "LBA_10_70", - "LBA_30_70", - "LBA_10_90", - "LBA_30_90", - "HBA_110_190", - "HBA_210_250" - ], - "type": "string", - "title": "Band-pass filter", - "default": "HBA_110_190", - "description": "Must match antenna type" - }, - "timedelta": { - "type": "number", - "default": 0, - "description": "A time duration or delta expressed in seconds" - }, - "antenna_set": { - "enum": [ - "HBA_DUAL", - "HBA_DUAL_INNER", - "HBA_ONE", - "HBA_ONE_INNER", - "HBA_ZERO", - "HBA_ZERO_INNER", - "LBA_INNER", - "LBA_OUTER", - "LBA_SPARSE_EVEN", - "LBA_SPARSE_ODD", - "LBA_ALL" - ], - "type": "string", - "title": "Antenna set", - "default": "HBA_DUAL", - "description": "Fields & antennas to use" - }, - "station_groups": { - "type": "array", - "items": { - "$ref": "#/definitions/station_group" - }, - "title": "Station groups", - "default": [ - { - "stations": [ - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007" - ], - "max_nr_missing": 1 - } - ], - "minItems": 1, - "description": "One or more predefined or custom groups of stations", - "additionalItems": false, - "additionalProperties": false - }, - "station_group": { - "type": "object", - "anyOf": [ - { - "type": "object", - "title": "Superterp", - "default": { - "stations": [ - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007" - ], - "max_nr_missing": 0 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all stations on the Superterp", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "Core", - "default": { - "stations": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501" - ], - "max_nr_missing": 4 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all Core stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "Remote", - "default": { - "stations": [ - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509" - ], - "max_nr_missing": 4 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all Dutch remote stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "Dutch", - "default": { - "stations": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509" - ], - "max_nr_missing": 4 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all Dutch (Core + Remote) stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "International", - "default": { - "stations": [ - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "DE609", - "FR606", - "SE607", - "UK608", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "max_nr_missing": 2 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "DE609", - "FR606", - "SE607", - "UK608", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all international stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "International required", - "default": { - "stations": [ - "DE601", - "DE605" - ], - "max_nr_missing": 1 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "DE601", - "DE605" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "A subgroup of the international stations which are required when doing observation with international stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "All", - "default": { - "stations": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "DE609", - "FR606", - "SE607", - "UK608", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "max_nr_missing": 6 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list", - "enum": [ - [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "DE609", - "FR606", - "SE607", - "UK608", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ] - ] - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "The group of all (Core + Remote + International) stations", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - }, - { - "type": "object", - "title": "Custom", - "default": { - "stations": [ - "CS001" - ], - "max_nr_missing": 0 - }, - "required": [ - "stations", - "max_nr_missing" - ], - "properties": { - "stations": { - "$ref": "#/definitions/station_list" - }, - "max_nr_missing": { - "$ref": "#/definitions/max_number_of_missing_stations" - } - }, - "description": "A custom group of stations which can be defined by the user", - "additionalProperties": false, - "definitions": { - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - } - } - ], - "title": "Station group", - "default": { - "stations": [ - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007" - ], - "max_nr_missing": 1 - }, - "description": "A set of predefined list of stations, and a constraint on how many stations are allowed to be missing (due to maintenance for example)" - }, - "station_list": { - "type": "array", - "items": { - "enum": [ - "CS001", - "CS002", - "CS003", - "CS004", - "CS005", - "CS006", - "CS007", - "CS011", - "CS013", - "CS017", - "CS021", - "CS024", - "CS026", - "CS028", - "CS030", - "CS031", - "CS032", - "CS101", - "CS103", - "CS201", - "CS301", - "CS302", - "CS401", - "CS501", - "RS106", - "RS205", - "RS208", - "RS210", - "RS305", - "RS306", - "RS307", - "RS310", - "RS406", - "RS407", - "RS409", - "RS503", - "RS508", - "RS509", - "DE601", - "DE602", - "DE603", - "DE604", - "DE605", - "FR606", - "SE607", - "UK608", - "DE609", - "PL610", - "PL611", - "PL612", - "IE613", - "LV614" - ], - "type": "string", - "title": "Station", - "description": "These are the LOFAR stations" - }, - "default": [], - "minItems": 0, - "uniqueItems": true, - "additionalItems": false, - "additionalProperties": false - }, - "max_number_of_missing_stations": { - "type": "integer", - "title": "Maximum number of stations to omit", - "default": 0, - "minimum": 0, - "description": "Maximum number of stations that can be omitted from a group (due to maintenance for example)" - } - }, - "description": "This schema defines the parameters to setup a target observation task." - }, - "tags": [], - "type": "http://localhost:3000/api/task_type/observation", - "type_value": "observation", - "updated_at": "2022-01-17T03:57:03.765750", - "version": 1 - } - } - ], - "updated_at": "2022-01-24T05:55:23.369965" - } - ]; + ]; }, // getSchedulingConstraintTemplates: [ // { @@ -7859,18 +7873,19 @@ const SUServiceMock= { "version": 1 } ], - getSUCTemplates:async() => { + getSUCTemplates: async () => { async function getTemplateFiles(dir) { let files = await fs.promises.readdir(dir); files = await Promise.all(files.map(async file => { const filePath = path.join(dir, file); const stats = await fs.promises.stat(filePath); if (stats.isDirectory()) return getTemplateFiles(filePath); - else if(stats.isFile()) return filePath; + else if (stats.isFile()) return filePath; })); - + return files.reduce((all, folderContents) => all.concat(folderContents), []); } + const templateFiles = await getTemplateFiles("../tmss_webapp/build/schemas/scheduling_constraints_template"); let constraintTemplates = []; for (const template of templateFiles) { @@ -7879,820 +7894,2489 @@ const SUServiceMock= { return parseTemplatesToCorrectJSONFormat(constraintTemplates); }, getSchedulingUnitFilterDefinition: { - "name": "Scheduling Unit Draft List", - "description": "", - "renders": [ - "application/json", - "text/html" - ], - "parses": [ - "application/json", - "application/x-www-form-urlencoded", - "multipart/form-data" - ], - "actions": { - "POST": { - "id": { - "type": "integer", - "required": false, - "read_only": true, - "label": "ID" - }, - "url": { - "type": "field", - "required": false, - "read_only": true, - "label": "Url" - }, - "created_at": { - "type": "datetime", - "required": false, - "read_only": true, - "label": "Created at", - "help_text": "Moment of object creation." - }, - "description": { - "type": "string", - "required": false, - "read_only": false, - "label": "Description", - "help_text": "A longer description of this object.", - "max_length": 255 - }, - "duration": { - "type": "float", - "required": false, - "read_only": true, - "label": "Duration" - }, - "ingest_permission_required": { - "type": "boolean", - "required": false, - "read_only": false, - "label": "Ingest permission required", - "help_text": "Explicit permission is needed before the task." - }, - "interrupts_telescope": { - "type": "boolean", - "required": false, - "read_only": true, - "label": "Interrupts telescope", - "help_text": "boolean (default FALSE), which indicates whether this observation was triggered (responsive telescope)" - }, - "name": { - "type": "string", - "required": true, - "read_only": false, - "label": "Name", - "help_text": "Human-readable name of this object.", - "max_length": 128 - }, - "observation_strategy_template": { - "type": "field", - "required": false, - "read_only": false, - "label": "Observation strategy template", - "help_text": "Observation Strategy Template used to create the specifications_doc." - }, - "observation_strategy_template_id": { - "type": "field", - "required": false, - "read_only": true, - "label": "Observation strategy template id" - }, - "piggyback_allowed_aartfaac": { - "type": "boolean", - "required": false, - "read_only": false, - "label": "Piggyback allowed aartfaac", - "help_text": "Piggyback key for AARTFAAC." - }, - "piggyback_allowed_tbb": { - "type": "boolean", - "required": false, - "read_only": false, - "label": "Piggyback allowed tbb", - "help_text": "Piggyback key for TBB." - }, - "priority_queue": { - "type": "field", - "required": false, - "read_only": false, - "label": "Priority queue", - "help_text": "Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units." - }, - "priority_queue_value": { - "type": "field", - "required": false, - "read_only": true, - "label": "priority_queue_value" - }, - "priority_rank": { - "type": "float", - "required": false, - "read_only": false, - "label": "Priority rank", - "help_text": "Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project." - }, - "scheduling_constraints_doc": { - "type": "field", - "required": true, - "read_only": false, - "label": "Scheduling constraints doc" - }, - "scheduling_constraints_template": { - "type": "field", - "required": false, - "read_only": false, - "label": "Scheduling constraints template", - "help_text": "Schema used for scheduling_constraints_doc." - }, - "scheduling_constraints_template_id": { - "type": "field", - "required": false, - "read_only": true, - "label": "Scheduling constraints template id" - }, - "scheduling_set": { - "type": "field", - "required": true, - "read_only": false, - "label": "Scheduling set", - "help_text": "Set to which this scheduling unit draft belongs." - }, - "scheduling_set_id": { - "type": "field", - "required": false, - "read_only": true, - "label": "Scheduling set id" - }, - "scheduling_unit_blueprints": { - "type": "field", - "required": true, - "read_only": false, - "label": "Scheduling unit blueprints" - }, - "scheduling_unit_blueprints_ids": { - "type": "field", - "required": false, - "read_only": true, - "label": "scheduling_unit_blueprints_ids" - }, - "specifications_template": { - "type": "field", - "required": true, - "read_only": false, - "label": "Specifications template", - "help_text": "Schema used for specifications_doc." - }, - "specifications_template_id": { - "type": "field", - "required": false, - "read_only": true, - "label": "Specifications template id" - }, - "tags": { - "type": "list", - "required": false, - "read_only": false, - "label": "Tags", - "help_text": "User-defined search keywords for object.", - "child": { - "type": "string", - "required": true, - "read_only": false, - "label": "Tags", - "max_length": 128 - } - }, - "task_drafts": { - "type": "field", - "required": true, - "read_only": false, - "label": "Task drafts" - }, - "task_drafts_ids": { - "type": "field", - "required": false, - "read_only": true, - "label": "task_drafts_ids" - }, - "updated_at": { - "type": "datetime", - "required": false, - "read_only": true, - "label": "Updated at", - "help_text": "Moment of last object update." - } - } - }, - "filters": { - "id": { - "type": "NumberInFilter", - "lookup_types": [ - "exact" - ] - }, - "tags": { - "type": "CharFilter", - "lookup_types": [ - "exact" - ] - }, - "created_at": { - "type": "IsoDateTimeFromToRangeFilter", - "lookup_types": [ - "exact" - ] - }, - "updated_at": { - "type": "IsoDateTimeFromToRangeFilter", - "lookup_types": [ - "exact" - ] - }, - "name": { - "type": "CharFilter", - "lookup_types": [ - "exact" - ] - }, - "description": { - "type": "CharFilter", - "lookup_types": [ - "exact" - ] - }, - "scheduling_set": { - "type": "PropertyCharFilter", - "lookup_types": [ - "exact" - ] - }, - "specifications_template": { - "type": "ModelChoiceFilter", - "lookup_types": [ - "exact" - ] - }, - "observation_strategy_template": { - "type": "ModelChoiceFilter", - "lookup_types": [ - "exact" - ] - }, - "scheduling_constraints_doc": { - "type": "CharFilter", - "lookup_types": [ - "exact" - ] - }, - "scheduling_constraints_template": { - "type": "ModelChoiceFilter", - "lookup_types": [ - "exact" - ] - }, - "ingest_permission_required": { - "type": "BooleanFilter", - "lookup_types": [ - "exact" - ] - }, - "piggyback_allowed_tbb": { - "type": "BooleanFilter", - "lookup_types": [ - "exact" - ] - }, - "piggyback_allowed_aartfaac": { - "type": "BooleanFilter", - "lookup_types": [ - "exact" - ] - }, - "priority_rank": { - "type": "RangeFilter", - "lookup_types": [ - "exact" - ] - }, - "priority_queue": { - "type": "ModelChoiceFilter", - "lookup_types": [ - "exact" - ] - }, - "interrupts_telescope": { - "type": "BooleanFilter", - "lookup_types": [ - "exact" - ] - }, - "project": { - "type": "PropertyCharFilter", - "lookup_types": [ - "exact" - ] - }, - "id_min": { - "type": "NumberFilter", - "lookup_types": [ - "exact" - ] - }, - "id_max": { - "type": "NumberFilter", - "lookup_types": [ - "exact" - ] - }, - "scheduling_unit_blueprints": { - "type": "ModelMultipleChoiceFilter", - "lookup_types": [ - "exact" - ] - }, - "duration_min": { - "type": "PropertyDurationFilter", - "lookup_types": [ - "exact" - ] - }, - "duration_max": { - "type": "PropertyDurationFilter", - "lookup_types": [ - "exact" - ] - }, - "angle1": { - "type": "TargetObservationSpecificationDocFloatRangeFilter", - "lookup_types": [ - "exact" - ] - }, - "angle2": { - "type": "TargetObservationSpecificationDocFloatRangeFilter", - "lookup_types": [ - "exact" - ] - }, - "direction_type": { - "type": "TargetObservationSpecificationFilterCharFilter", - "lookup_types": [ - "exact" - ] - }, - "observation_strategy_template_name": { - "type": "CharFilter", - "lookup_types": [ - "exact" - ] - }, - "observation_strategy_template_description": { - "type": "CharFilter", - "lookup_types": [ - "exact" - ] - } - }, - "ordering": [ - "id", - "tags", - "created_at", - "updated_at", - "name", - "description", - "scheduling_set", - "specifications_template", - "observation_strategy_template", - "scheduling_constraints_doc", - "scheduling_constraints_template", - "ingest_permission_required", - "piggyback_allowed_tbb", - "piggyback_allowed_aartfaac", - "priority_rank", - "priority_queue", - "interrupts_telescope" - ] - }, - getAllTaskRelation: [ - { - "id": 1, - "url": "http://127.0.0.100:8008/api/task_relation_draft/1", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/4" - ], - "blueprints_ids": [ - 4 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/8", - "consumer_id": 8, - "created_at": "2022-01-24T07:40:18.426593", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", - "input_role_id": 10, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/1", - "output_role_id": 1, - "producer": "http://127.0.0.100:8008/api/task_draft/2", - "producer_id": 2, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:18.426609" - }, - { - "id": 2, - "url": "http://127.0.0.100:8008/api/task_relation_draft/2", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/3" - ], - "blueprints_ids": [ - 3 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/7", - "consumer_id": 7, - "created_at": "2022-01-24T07:40:18.476404", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", - "input_role_id": 10, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/1", - "output_role_id": 1, - "producer": "http://127.0.0.100:8008/api/task_draft/4", - "producer_id": 4, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:18.476429" - }, - { - "id": 3, - "url": "http://127.0.0.100:8008/api/task_relation_draft/3", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/2" - ], - "blueprints_ids": [ - 2 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/6", - "consumer_id": 6, - "created_at": "2022-01-24T07:40:18.523693", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", - "input_role_id": 10, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/2", - "output_role_id": 2, - "producer": "http://127.0.0.100:8008/api/task_draft/3", - "producer_id": 3, - "selection_doc": { - "sap": [ - "target1" - ], - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/SAP/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/1", - "selection_template_id": 1, - "tags": [], - "updated_at": "2022-01-24T07:40:18.523730" - }, - { - "id": 4, - "url": "http://127.0.0.100:8008/api/task_relation_draft/4", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/1" - ], - "blueprints_ids": [ - 1 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/5", - "consumer_id": 5, - "created_at": "2022-01-24T07:40:18.562314", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", - "input_role_id": 10, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/2", - "output_role_id": 2, - "producer": "http://127.0.0.100:8008/api/task_draft/3", - "producer_id": 3, - "selection_doc": { - "sap": [ - "target2" - ], - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/SAP/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/1", - "selection_template_id": 1, - "tags": [], - "updated_at": "2022-01-24T07:40:18.562330" - }, - { - "id": 5, - "url": "http://127.0.0.100:8008/api/task_relation_draft/5", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/5" - ], - "blueprints_ids": [ - 5 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/9", - "consumer_id": 9, - "created_at": "2022-01-24T07:40:18.599211", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", - "input_role_id": 12, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", - "output_role_id": 11, - "producer": "http://127.0.0.100:8008/api/task_draft/8", - "producer_id": 8, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:18.599226" - }, - { - "id": 6, - "url": "http://127.0.0.100:8008/api/task_relation_draft/6", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/6" - ], - "blueprints_ids": [ - 6 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/9", - "consumer_id": 9, - "created_at": "2022-01-24T07:40:18.630126", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", - "input_role_id": 12, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", - "output_role_id": 11, - "producer": "http://127.0.0.100:8008/api/task_draft/7", - "producer_id": 7, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:18.630141" - }, - { - "id": 7, - "url": "http://127.0.0.100:8008/api/task_relation_draft/7", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/7" - ], - "blueprints_ids": [ - 7 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/9", - "consumer_id": 9, - "created_at": "2022-01-24T07:40:18.657560", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", - "input_role_id": 12, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", - "output_role_id": 11, - "producer": "http://127.0.0.100:8008/api/task_draft/6", - "producer_id": 6, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:18.657575" - }, - { - "id": 8, - "url": "http://127.0.0.100:8008/api/task_relation_draft/8", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/8" - ], - "blueprints_ids": [ - 8 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/9", - "consumer_id": 9, - "created_at": "2022-01-24T07:40:18.678912", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", - "input_role_id": 12, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", - "output_role_id": 11, - "producer": "http://127.0.0.100:8008/api/task_draft/5", - "producer_id": 5, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:18.678929" - }, - { - "id": 9, - "url": "http://127.0.0.100:8008/api/task_relation_draft/9", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/9" - ], - "blueprints_ids": [ - 9 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/11", - "consumer_id": 11, - "created_at": "2022-01-24T07:40:22.006825", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", - "input_role_id": 10, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/2", - "output_role_id": 2, - "producer": "http://127.0.0.100:8008/api/task_draft/10", - "producer_id": 10, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:22.006859" - }, - { - "id": 10, - "url": "http://127.0.0.100:8008/api/task_relation_draft/10", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/12" - ], - "blueprints_ids": [ - 12 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/13", - "consumer_id": 13, - "created_at": "2022-01-24T07:40:22.119443", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", - "input_role_id": 12, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", - "output_role_id": 11, - "producer": "http://127.0.0.100:8008/api/task_draft/11", - "producer_id": 11, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:22.119469" - }, - { - "id": 11, - "url": "http://127.0.0.100:8008/api/task_relation_draft/11", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/10" - ], - "blueprints_ids": [ - 10 - ], - "consumer": "http://127.0.0.100:8008/api/task_draft/12", - "consumer_id": 12, - "created_at": "2022-01-24T07:40:22.185730", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/30", - "input_role_id": 30, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/2", - "output_role_id": 2, - "producer": "http://127.0.0.100:8008/api/task_draft/10", - "producer_id": 10, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:22.185749" - }, - { - "id": 12, - "url": "http://127.0.0.100:8008/api/task_relation_draft/12", - "blueprints": [ - "http://127.0.0.100:8008/api/task_relation_blueprint/11" + "name": "Scheduling Unit Draft List", + "description": "", + "renders": [ + "application/json", + "text/html" ], - "blueprints_ids": [ - 11 + "parses": [ + "application/json", + "application/x-www-form-urlencoded", + "multipart/form-data" ], - "consumer": "http://127.0.0.100:8008/api/task_draft/12", - "consumer_id": 12, - "created_at": "2022-01-24T07:40:22.268746", - "input_role": "http://127.0.0.100:8008/api/task_connector_type/30", - "input_role_id": 30, - "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", - "output_role_id": 11, - "producer": "http://127.0.0.100:8008/api/task_draft/11", - "producer_id": 11, - "selection_doc": { - "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" - }, - "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", - "selection_template_id": 2, - "tags": [], - "updated_at": "2022-01-24T07:40:22.268769" - }], - getStationGroup:[{ - value: 'Dutch' - }, { - value: 'International' - }, { - value: 'Core' - }, { - value: 'Remote' - }, { - value: 'Superterp' - }], - getStations: { - "group": "dutch", "stations": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS101", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"] - }, - getMyRoles: { - "project_roles": {}, "system_roles": ["superuser"] - }, - getSchedulingBySet: () => { - return []; - /*return { - "id": 1, - "url": "http://127.0.0.100:8008/api/scheduling_unit_draft/1", - "created_at": "2022-02-03T01:54:19.628183", - "description": "Test scheduling unit", - "duration": 7560, - "ingest_permission_required": true, - "interrupts_telescope": false, - "name": "UC1 high 1", - "observation_strategy_template": "http://127.0.0.100:8008/api/scheduling_unit_observing_strategy_template/1", - "observation_strategy_template_id": 1, - "piggyback_allowed_aartfaac": true, - "piggyback_allowed_tbb": true, - "priority_queue": "http://127.0.0.100:8008/api/priority_queue_type/A", - "priority_queue_value": "A", - "priority_rank": 1, - "scheduling_constraints_doc": { - "sky": { - "min_distance": { - "sun": 0.5, - "moon": 0.5, - "jupiter": 0.5 - }, - "min_elevation": { - "target": 0.5, - "calibrator": 0.5 - }, - "transit_offset": { - "to": 7200, - "from": -7200 - } - }, - "time": { - "at": "2022-02-03T02:44:18.280549" - }, - "daily": { - "require_day": false, - "require_night": false, - "avoid_twilight": false - }, - "$schema": "http://127.0.0.1:8008/api/schemas/schedulingconstraintstemplate/constraints/1#", - "scheduler": "fixed_time" - }, - "scheduling_constraints_template": "http://127.0.0.100:8008/api/scheduling_constraints_template/1", - "scheduling_constraints_template_id": 1, - "scheduling_set": "http://127.0.0.100:8008/api/scheduling_set/1", - "scheduling_set_id": 1, - "scheduling_unit_blueprints": [ - "http://127.0.0.100:8008/api/scheduling_unit_blueprint/2" - ], - "scheduling_unit_blueprints_ids": [ - 2 - ], - "specifications_template": "http://127.0.0.100:8008/api/scheduling_unit_template/1", - "specifications_template_id": 1, - "tags": [], - "task_drafts": [ - "http://127.0.0.100:8008/api/task_draft/9", - "http://127.0.0.100:8008/api/task_draft/8", - "http://127.0.0.100:8008/api/task_draft/7", - "http://127.0.0.100:8008/api/task_draft/6", - "http://127.0.0.100:8008/api/task_draft/5", - "http://127.0.0.100:8008/api/task_draft/4", - "http://127.0.0.100:8008/api/task_draft/3", - "http://127.0.0.100:8008/api/task_draft/2" - ], - "task_drafts_ids": [ - 9, - 8, - 7, - 6, - 5, - 4, - 3, - 2 - ], - "updated_at": "2022-02-03T01:54:19.628201" - }*/ - }, + "actions": { + "POST": { + "id": { + "type": "integer", + "required": false, + "read_only": true, + "label": "ID" + }, + "url": { + "type": "field", + "required": false, + "read_only": true, + "label": "Url" + }, + "created_at": { + "type": "datetime", + "required": false, + "read_only": true, + "label": "Created at", + "help_text": "Moment of object creation." + }, + "description": { + "type": "string", + "required": false, + "read_only": false, + "label": "Description", + "help_text": "A longer description of this object.", + "max_length": 255 + }, + "duration": { + "type": "float", + "required": false, + "read_only": true, + "label": "Duration" + }, + "ingest_permission_required": { + "type": "boolean", + "required": false, + "read_only": false, + "label": "Ingest permission required", + "help_text": "Explicit permission is needed before the task." + }, + "interrupts_telescope": { + "type": "boolean", + "required": false, + "read_only": true, + "label": "Interrupts telescope", + "help_text": "boolean (default FALSE), which indicates whether this observation was triggered (responsive telescope)" + }, + "name": { + "type": "string", + "required": true, + "read_only": false, + "label": "Name", + "help_text": "Human-readable name of this object.", + "max_length": 128 + }, + "observation_strategy_template": { + "type": "field", + "required": false, + "read_only": false, + "label": "Observation strategy template", + "help_text": "Observation Strategy Template used to create the specifications_doc." + }, + "observation_strategy_template_id": { + "type": "field", + "required": false, + "read_only": true, + "label": "Observation strategy template id" + }, + "piggyback_allowed_aartfaac": { + "type": "boolean", + "required": false, + "read_only": false, + "label": "Piggyback allowed aartfaac", + "help_text": "Piggyback key for AARTFAAC." + }, + "piggyback_allowed_tbb": { + "type": "boolean", + "required": false, + "read_only": false, + "label": "Piggyback allowed tbb", + "help_text": "Piggyback key for TBB." + }, + "priority_queue": { + "type": "field", + "required": false, + "read_only": false, + "label": "Priority queue", + "help_text": "Priority queue of this scheduling unit. Queues provide a strict ordering between scheduling units." + }, + "priority_queue_value": { + "type": "field", + "required": false, + "read_only": true, + "label": "priority_queue_value" + }, + "priority_rank": { + "type": "float", + "required": false, + "read_only": false, + "label": "Priority rank", + "help_text": "Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project." + }, + "scheduling_constraints_doc": { + "type": "field", + "required": true, + "read_only": false, + "label": "Scheduling constraints doc" + }, + "scheduling_constraints_template": { + "type": "field", + "required": false, + "read_only": false, + "label": "Scheduling constraints template", + "help_text": "Schema used for scheduling_constraints_doc." + }, + "scheduling_constraints_template_id": { + "type": "field", + "required": false, + "read_only": true, + "label": "Scheduling constraints template id" + }, + "scheduling_set": { + "type": "field", + "required": true, + "read_only": false, + "label": "Scheduling set", + "help_text": "Set to which this scheduling unit draft belongs." + }, + "scheduling_set_id": { + "type": "field", + "required": false, + "read_only": true, + "label": "Scheduling set id" + }, + "scheduling_unit_blueprints": { + "type": "field", + "required": true, + "read_only": false, + "label": "Scheduling unit blueprints" + }, + "scheduling_unit_blueprints_ids": { + "type": "field", + "required": false, + "read_only": true, + "label": "scheduling_unit_blueprints_ids" + }, + "specifications_template": { + "type": "field", + "required": true, + "read_only": false, + "label": "Specifications template", + "help_text": "Schema used for specifications_doc." + }, + "specifications_template_id": { + "type": "field", + "required": false, + "read_only": true, + "label": "Specifications template id" + }, + "tags": { + "type": "list", + "required": false, + "read_only": false, + "label": "Tags", + "help_text": "User-defined search keywords for object.", + "child": { + "type": "string", + "required": true, + "read_only": false, + "label": "Tags", + "max_length": 128 + } + }, + "task_drafts": { + "type": "field", + "required": true, + "read_only": false, + "label": "Task drafts" + }, + "task_drafts_ids": { + "type": "field", + "required": false, + "read_only": true, + "label": "task_drafts_ids" + }, + "updated_at": { + "type": "datetime", + "required": false, + "read_only": true, + "label": "Updated at", + "help_text": "Moment of last object update." + } + } + }, + "filters": { + "id": { + "type": "NumberInFilter", + "lookup_types": [ + "exact" + ] + }, + "tags": { + "type": "CharFilter", + "lookup_types": [ + "exact" + ] + }, + "created_at": { + "type": "IsoDateTimeFromToRangeFilter", + "lookup_types": [ + "exact" + ] + }, + "updated_at": { + "type": "IsoDateTimeFromToRangeFilter", + "lookup_types": [ + "exact" + ] + }, + "name": { + "type": "CharFilter", + "lookup_types": [ + "exact" + ] + }, + "description": { + "type": "CharFilter", + "lookup_types": [ + "exact" + ] + }, + "scheduling_set": { + "type": "PropertyCharFilter", + "lookup_types": [ + "exact" + ] + }, + "specifications_template": { + "type": "ModelChoiceFilter", + "lookup_types": [ + "exact" + ] + }, + "observation_strategy_template": { + "type": "ModelChoiceFilter", + "lookup_types": [ + "exact" + ] + }, + "scheduling_constraints_doc": { + "type": "CharFilter", + "lookup_types": [ + "exact" + ] + }, + "scheduling_constraints_template": { + "type": "ModelChoiceFilter", + "lookup_types": [ + "exact" + ] + }, + "ingest_permission_required": { + "type": "BooleanFilter", + "lookup_types": [ + "exact" + ] + }, + "piggyback_allowed_tbb": { + "type": "BooleanFilter", + "lookup_types": [ + "exact" + ] + }, + "piggyback_allowed_aartfaac": { + "type": "BooleanFilter", + "lookup_types": [ + "exact" + ] + }, + "priority_rank": { + "type": "RangeFilter", + "lookup_types": [ + "exact" + ] + }, + "priority_queue": { + "type": "ModelChoiceFilter", + "lookup_types": [ + "exact" + ] + }, + "interrupts_telescope": { + "type": "BooleanFilter", + "lookup_types": [ + "exact" + ] + }, + "project": { + "type": "PropertyCharFilter", + "lookup_types": [ + "exact" + ] + }, + "id_min": { + "type": "NumberFilter", + "lookup_types": [ + "exact" + ] + }, + "id_max": { + "type": "NumberFilter", + "lookup_types": [ + "exact" + ] + }, + "scheduling_unit_blueprints": { + "type": "ModelMultipleChoiceFilter", + "lookup_types": [ + "exact" + ] + }, + "duration_min": { + "type": "PropertyDurationFilter", + "lookup_types": [ + "exact" + ] + }, + "duration_max": { + "type": "PropertyDurationFilter", + "lookup_types": [ + "exact" + ] + }, + "angle1": { + "type": "TargetObservationSpecificationDocFloatRangeFilter", + "lookup_types": [ + "exact" + ] + }, + "angle2": { + "type": "TargetObservationSpecificationDocFloatRangeFilter", + "lookup_types": [ + "exact" + ] + }, + "direction_type": { + "type": "TargetObservationSpecificationFilterCharFilter", + "lookup_types": [ + "exact" + ] + }, + "observation_strategy_template_name": { + "type": "CharFilter", + "lookup_types": [ + "exact" + ] + }, + "observation_strategy_template_description": { + "type": "CharFilter", + "lookup_types": [ + "exact" + ] + } + }, + "ordering": [ + "id", + "tags", + "created_at", + "updated_at", + "name", + "description", + "scheduling_set", + "specifications_template", + "observation_strategy_template", + "scheduling_constraints_doc", + "scheduling_constraints_template", + "ingest_permission_required", + "piggyback_allowed_tbb", + "piggyback_allowed_aartfaac", + "priority_rank", + "priority_queue", + "interrupts_telescope" + ] + }, + getAllTaskRelation: [ + { + "id": 1, + "url": "http://127.0.0.100:8008/api/task_relation_draft/1", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/4" + ], + "blueprints_ids": [ + 4 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/8", + "consumer_id": 8, + "created_at": "2022-01-24T07:40:18.426593", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", + "input_role_id": 10, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/1", + "output_role_id": 1, + "producer": "http://127.0.0.100:8008/api/task_draft/2", + "producer_id": 2, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:18.426609" + }, + { + "id": 2, + "url": "http://127.0.0.100:8008/api/task_relation_draft/2", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/3" + ], + "blueprints_ids": [ + 3 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/7", + "consumer_id": 7, + "created_at": "2022-01-24T07:40:18.476404", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", + "input_role_id": 10, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/1", + "output_role_id": 1, + "producer": "http://127.0.0.100:8008/api/task_draft/4", + "producer_id": 4, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:18.476429" + }, + { + "id": 3, + "url": "http://127.0.0.100:8008/api/task_relation_draft/3", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/2" + ], + "blueprints_ids": [ + 2 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/6", + "consumer_id": 6, + "created_at": "2022-01-24T07:40:18.523693", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", + "input_role_id": 10, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/2", + "output_role_id": 2, + "producer": "http://127.0.0.100:8008/api/task_draft/3", + "producer_id": 3, + "selection_doc": { + "sap": [ + "target1" + ], + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/SAP/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/1", + "selection_template_id": 1, + "tags": [], + "updated_at": "2022-01-24T07:40:18.523730" + }, + { + "id": 4, + "url": "http://127.0.0.100:8008/api/task_relation_draft/4", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/1" + ], + "blueprints_ids": [ + 1 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/5", + "consumer_id": 5, + "created_at": "2022-01-24T07:40:18.562314", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", + "input_role_id": 10, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/2", + "output_role_id": 2, + "producer": "http://127.0.0.100:8008/api/task_draft/3", + "producer_id": 3, + "selection_doc": { + "sap": [ + "target2" + ], + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/SAP/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/1", + "selection_template_id": 1, + "tags": [], + "updated_at": "2022-01-24T07:40:18.562330" + }, + { + "id": 5, + "url": "http://127.0.0.100:8008/api/task_relation_draft/5", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/5" + ], + "blueprints_ids": [ + 5 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/9", + "consumer_id": 9, + "created_at": "2022-01-24T07:40:18.599211", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", + "input_role_id": 12, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", + "output_role_id": 11, + "producer": "http://127.0.0.100:8008/api/task_draft/8", + "producer_id": 8, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:18.599226" + }, + { + "id": 6, + "url": "http://127.0.0.100:8008/api/task_relation_draft/6", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/6" + ], + "blueprints_ids": [ + 6 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/9", + "consumer_id": 9, + "created_at": "2022-01-24T07:40:18.630126", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", + "input_role_id": 12, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", + "output_role_id": 11, + "producer": "http://127.0.0.100:8008/api/task_draft/7", + "producer_id": 7, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:18.630141" + }, + { + "id": 7, + "url": "http://127.0.0.100:8008/api/task_relation_draft/7", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/7" + ], + "blueprints_ids": [ + 7 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/9", + "consumer_id": 9, + "created_at": "2022-01-24T07:40:18.657560", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", + "input_role_id": 12, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", + "output_role_id": 11, + "producer": "http://127.0.0.100:8008/api/task_draft/6", + "producer_id": 6, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:18.657575" + }, + { + "id": 8, + "url": "http://127.0.0.100:8008/api/task_relation_draft/8", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/8" + ], + "blueprints_ids": [ + 8 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/9", + "consumer_id": 9, + "created_at": "2022-01-24T07:40:18.678912", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", + "input_role_id": 12, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", + "output_role_id": 11, + "producer": "http://127.0.0.100:8008/api/task_draft/5", + "producer_id": 5, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:18.678929" + }, + { + "id": 9, + "url": "http://127.0.0.100:8008/api/task_relation_draft/9", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/9" + ], + "blueprints_ids": [ + 9 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/11", + "consumer_id": 11, + "created_at": "2022-01-24T07:40:22.006825", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/10", + "input_role_id": 10, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/2", + "output_role_id": 2, + "producer": "http://127.0.0.100:8008/api/task_draft/10", + "producer_id": 10, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:22.006859" + }, + { + "id": 10, + "url": "http://127.0.0.100:8008/api/task_relation_draft/10", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/12" + ], + "blueprints_ids": [ + 12 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/13", + "consumer_id": 13, + "created_at": "2022-01-24T07:40:22.119443", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/12", + "input_role_id": 12, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", + "output_role_id": 11, + "producer": "http://127.0.0.100:8008/api/task_draft/11", + "producer_id": 11, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:22.119469" + }, + { + "id": 11, + "url": "http://127.0.0.100:8008/api/task_relation_draft/11", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/10" + ], + "blueprints_ids": [ + 10 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/12", + "consumer_id": 12, + "created_at": "2022-01-24T07:40:22.185730", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/30", + "input_role_id": 30, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/2", + "output_role_id": 2, + "producer": "http://127.0.0.100:8008/api/task_draft/10", + "producer_id": 10, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:22.185749" + }, + { + "id": 12, + "url": "http://127.0.0.100:8008/api/task_relation_draft/12", + "blueprints": [ + "http://127.0.0.100:8008/api/task_relation_blueprint/11" + ], + "blueprints_ids": [ + 11 + ], + "consumer": "http://127.0.0.100:8008/api/task_draft/12", + "consumer_id": 12, + "created_at": "2022-01-24T07:40:22.268746", + "input_role": "http://127.0.0.100:8008/api/task_connector_type/30", + "input_role_id": 30, + "output_role": "http://127.0.0.100:8008/api/task_connector_type/11", + "output_role_id": 11, + "producer": "http://127.0.0.100:8008/api/task_draft/11", + "producer_id": 11, + "selection_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/taskrelationselectiontemplate/all/1#" + }, + "selection_template": "http://127.0.0.100:8008/api/task_relation_selection_template/2", + "selection_template_id": 2, + "tags": [], + "updated_at": "2022-01-24T07:40:22.268769" + }], + getStationGroup: [{ + value: 'Dutch' + }, { + value: 'International' + }, { + value: 'Core' + }, { + value: 'Remote' + }, { + value: 'Superterp' + }], + getStations: { + "group": "dutch", + "stations": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS101", "CS103", "CS201", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"] + }, + getMyRoles: { + "project_roles": {}, "system_roles": ["superuser"] + }, + getSchedulingBySet: () => { + return [{ + "id": 1, + "url": "http://127.0.0.100:8008/api/scheduling_unit_draft/1", + "created_at": "2022-02-03T01:54:19.628183", + "description": "Test scheduling unit", + "duration": 7560, + "ingest_permission_required": true, + "interrupts_telescope": false, + "name": "UC1 high 1", + "observation_strategy_template": "http://127.0.0.100:8008/api/scheduling_unit_observing_strategy_template/1", + "observation_strategy_template_id": 1, + "piggyback_allowed_aartfaac": true, + "piggyback_allowed_tbb": true, + "priority_queue": "http://127.0.0.100:8008/api/priority_queue_type/A", + "priority_queue_value": "A", + "priority_rank": 1, + "scheduling_constraints_doc": { + "sky": { + "min_distance": { + "sun": 0.5, + "moon": 0.5, + "jupiter": 0.5 + }, + "min_elevation": { + "target": 0.5, + "calibrator": 0.5 + }, + "transit_offset": { + "to": 7200, + "from": -7200 + } + }, + "time": { + "at": "2022-02-03T02:44:18.280549" + }, + "daily": { + "require_day": false, + "require_night": false, + "avoid_twilight": false + }, + "$schema": "http://127.0.0.1:8008/api/schemas/schedulingconstraintstemplate/constraints/1#", + "scheduler": "fixed_time" + }, + "scheduling_constraints_template": "http://127.0.0.100:8008/api/scheduling_constraints_template/1", + "scheduling_constraints_template_id": 1, + "scheduling_set": "http://127.0.0.100:8008/api/scheduling_set/1", + "scheduling_set_id": 1, + "scheduling_unit_blueprints": [ + "http://127.0.0.100:8008/api/scheduling_unit_blueprint/2" + ], + "scheduling_unit_blueprints_ids": [ + 2 + ], + "specifications_template": "http://127.0.0.100:8008/api/scheduling_unit_template/1", + "specifications_template_id": 1, + "tags": [], + "task_drafts": [ + "http://127.0.0.100:8008/api/task_draft/9", + "http://127.0.0.100:8008/api/task_draft/8", + "http://127.0.0.100:8008/api/task_draft/7", + "http://127.0.0.100:8008/api/task_draft/6", + "http://127.0.0.100:8008/api/task_draft/5", + "http://127.0.0.100:8008/api/task_draft/4", + "http://127.0.0.100:8008/api/task_draft/3", + "http://127.0.0.100:8008/api/task_draft/2" + ], + "task_drafts_ids": [ + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2 + ], + "updated_at": "2022-02-03T01:54:19.628201" + }, + { + "id": 1, + "url": "http://localhost:3000/api/scheduling_unit_draft/3", + "description": "Test scheduling unit", + "name": "BF CV FRB", + "observation_strategy_template": "http://localhost:3000/api/scheduling_unit_observing_strategy_template/3", + "observation_strategy_template_id": 3, + "priority_queue_value": "A", + "rank": 1, + "scheduling_constraints_doc": { + "sky": { + "min_distance": { + "sun": 0.00872665, + "moon": 0.00872665, + "jupiter": 0 + }, + "min_elevation": { + "target": 0.5235987755982988, + "calibrator": 0.5235987755982988 + }, + "transit_offset": { + "to": 21600, + "from": -21600 + } + }, + "$schema": "http://127.0.0.1:8008/api/schemas/schedulingconstraintstemplate/constraints/8/ref_resolved", + "scheduler": "dynamic" + }, + "scheduling_constraints_template": "http://localhost:3000/api/scheduling_constraints_template/3", + "scheduling_constraints_template_id": 3, + "scheduling_set": "http://localhost:3000/api/scheduling_set/1", + "scheduling_unit_blueprints": [ + "http://localhost:3000/api/scheduling_unit_blueprint/3" + ], + "specifications_template": "http://localhost:3000/api/scheduling_unit_template/1", + "task_drafts": [ + "http://localhost:3000/api/task_draft/9", + "http://localhost:3000/api/task_draft/10", + "http://localhost:3000/api/task_draft/11", + "http://localhost:3000/api/task_draft/12" + ], + "taskDrafts": [ + { + "id": 9, + "url": "http://localhost:3000/api/task_draft/9", + "description": "Beamforming observation for default baseband recording with HBA, Full Core, 20 min, 8bit, CV, 1ch/sub, Nsubs=400", + "name": "Observation", + "short_description": "FRB YYYYMMDDA", + "specifications_doc": { + "QA": { + "plots": { + "enabled": false, + "autocorrelation": true, + "crosscorrelation": true + }, + "file_conversion": { + "enabled": false, + "nr_of_subbands": -1, + "nr_of_timestamps": 256 + }, + "inspection_plots": "msplots" + }, + "$schema": "http://127.0.0.1:8008/api/schemas/tasktemplate/beamforming%20observation/8/ref_resolved", + "duration": 120, + "beamformer": { + "ppf": false, + "pipelines": [ + { + "name": "Beamformer CV", + "coherent": { + "SAPs": [ + { + "name": "SAP0", + "tabs": [ + { + "duration": 0, + "pointing": { + "angle1": 0, + "angle2": 0, + "target": "OnTarget", + "direction_type": "J2000" + }, + "relative": true, + "allow_after_previous": true + } + ], + "subbands": { + "list": [ + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450 + ], + "method": "copy" + }, + "tab_rings": { + "count": 0, + "width": 0.01 + } + } + ], + "settings": { + "stokes": "XXYY", + "quantisation": { + "bits": 8, + "enabled": false, + "scale_max": 5, + "scale_min": -5 + }, + "subbands_per_file": 20, + "channels_per_subband": 1, + "time_integration_factor": 1 + } + }, + "flys eye": { + "enabled": false, + "settings": { + "stokes": "I", + "quantisation": { + "bits": 8, + "enabled": false, + "scale_max": 5, + "scale_min": -5 + }, + "subbands_per_file": 488, + "channels_per_subband": 1, + "time_integration_factor": 1 + } + }, + "incoherent": { + "SAPs": [], + "settings": { + "stokes": "I", + "quantisation": { + "bits": 8, + "enabled": false, + "scale_max": 5, + "scale_min": -5 + }, + "subbands_per_file": 488, + "channels_per_subband": 1, + "time_integration_factor": 1 + } + }, + "station_groups": [ + { + "stations": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501" + ], + "max_nr_missing": 4 + } + ] + } + ] + }, + "station_configuration": { + "SAPs": [ + { + "name": "SAP0", + "subbands": [ + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450 + ], + "digital_pointing": { + "angle1": 0.92934186635, + "angle2": 0.952579228492, + "target": "FRB YYYYMMDDA", + "direction_type": "J2000" + } + } + ], + "filter": "HBA_110_190", + "tile_beam": { + "angle1": 0.92934186635, + "angle2": 0.952579228492, + "target": "Tile Beam on SAP center", + "direction_type": "J2000" + }, + "antenna_set": "HBA_DUAL_INNER", + "station_groups": [ + { + "stations": [ + "CS001", + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501" + ], + "max_nr_missing": 4 + } + ] + } + }, + "specifications_template": "http://localhost:3000/api/task_template/29", + "task_type": "observation" + }, + { + "id": 10, + "url": "http://localhost:3000/api/task_draft/10", + "description": "Pulsar Pipeline for FRB observation", + "name": "Pipeline", + "short_description": "FRB YYYYMMDDA/pulp", + "specifications_doc": { + "dspsr": { + "digifil": { + "dm": 0.0001, + "channels_per_part": 320, + "coherent_dedispersion": true, + "integration_time_factor": 4 + }, + "enabled": false, + "filterbank": { + "enabled": false, + "channels_per_part": 120, + "coherent_dedispersion": false + }, + "rfi_excision": true, + "optimise_period_dm": true, + "subintegration_length": -1, + "single_pulse_subintegration": false + }, + "output": { + "quantisation": { + "scale": 5, + "enabled": false + }, + "dynamic_spectrum": { + "enabled": false, + "time_average": 0.5 + } + }, + "presto": { + "input": { + "nr_blocks": 100, + "decode_sigma": 3, + "samples_per_block": 8192 + }, + "rrats": { + "enabled": false, + "dm_range": 5 + }, + "rfifind": { + "blocks": 16 + }, + "prepdata": { + "dm": -1 + }, + "prepfold": false, + "fold_profile": true + }, + "pulsar": { + "name": "B9999+99", + "strategy": "manual" + }, + "$schema": "http://127.0.0.1:8008/api/schemas/tasktemplate/pulsar%20pipeline/6/ref_resolved", + "cluster_resources": { + "where": { + "cluster": "CEP4", + "partition": "cpu" + }, + "cores_per_task": 2, + "parallel_tasks": 20 + }, + "single_pulse_search": true + }, + "specifications_template": "http://localhost:3000/api/task_template/15", + "task_type": "pipeline" + }, + { + "id": 11, + "url": "http://localhost:3000/api/task_draft/11", + "description": "Cleanup all dataproducts from disk", + "name": "Cleanup", + "short_description": "", + "specifications_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/tasktemplate/cleanup/7/ref_resolved" + }, + "specifications_template": "http://localhost:3000/api/task_template/56", + "task_type": "cleanup" + }, + { + "id": 12, + "url": "http://localhost:3000/api/task_draft/12", + "description": "Ingest the pipeline outputs dataproducts", + "name": "Ingest", + "short_description": "", + "specifications_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/tasktemplate/ingest/7/ref_resolved" + }, + "specifications_template": "http://localhost:3000/api/task_template/4", + "task_type": "ingest" + } + ] + }, + { + "id": 1, + "url": "http://localhost:3000/api/scheduling_unit_draft/12", + "description": "Test scheduling unit", + "name": "IM LBA - 1 Beam", + "observation_strategy_template": "http://localhost:3000/api/scheduling_unit_observing_strategy_template/9", + "observation_strategy_template_id": 9, + "priority_queue_value": "A", + "rank": 1, + "scheduling_constraints_doc": { + "sky": { + "min_distance": { + "sun": 0.52359877559, + "moon": 0.52359877559, + "jupiter": 0.52359877559 + }, + "min_elevation": { + "target": 0.69813170079, + "calibrator": 0.52359877559 + }, + "transit_offset": { + "to": 7200, + "from": -7200 + } + }, + "daily": { + "require_day": false, + "require_night": false, + "avoid_twilight": false + }, + "$schema": "http://127.0.0.1:8008/api/schemas/schedulingconstraintstemplate/constraints/8/ref_resolved", + "scheduler": "dynamic" + }, + "scheduling_constraints_template": "http://localhost:3000/api/scheduling_constraints_template/3", + "scheduling_constraints_template_id": 3, + "scheduling_set": "http://localhost:3000/api/scheduling_set/1", + "scheduling_unit_blueprints": [ + "http://localhost:3000/api/scheduling_unit_blueprint/12" + ], + "specifications_template": "http://localhost:3000/api/scheduling_unit_template/1", + "task_drafts": [ + "http://localhost:3000/api/task_draft/53", + "http://localhost:3000/api/task_draft/54", + "http://localhost:3000/api/task_draft/55", + "http://localhost:3000/api/task_draft/56", + "http://localhost:3000/api/task_draft/57" + ], + "taskDrafts": [ + { + "id": 53, + "url": "http://localhost:3000/api/task_draft/53", + "description": "Combined parallel Calibrator & Target Observation for LBA with 1 target beam", + "name": "Combined Observation", + "short_description": "OOO.O _Target_name_", + "specifications_doc": { + "QA": { + "plots": { + "enabled": true, + "autocorrelation": true, + "crosscorrelation": true + }, + "file_conversion": { + "enabled": true, + "nr_of_subbands": -1, + "nr_of_timestamps": 256 + }, + "inspection_plots": "msplots" + }, + "$schema": "http://127.0.0.1:8008/api/schemas/tasktemplate/parallel%20calibrator%20target%20observation/9/ref_resolved", + "duration": 120, + "calibrator": { + "name": "calibrator", + "pointing": { + "angle1": 0.6624317181687094, + "angle2": 1.5579526427549426, + "target": "3Cabc", + "direction_type": "J2000" + }, + "autoselect": false + }, + "correlator": { + "storage_cluster": "CEP4", + "integration_time": 1, + "channels_per_subband": 64, + "topocentric_frequency_correction": false + }, + "station_configuration": { + "SAPs": [ + { + "name": "target1", + "subbands": [ + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353 + ], + "digital_pointing": { + "angle1": 0.6624317181687094, + "angle2": 1.5579526427549426, + "target": "_Target_name_", + "direction_type": "J2000" + } + } + ], + "filter": "LBA_10_90", + "tile_beam": { + "angle1": 0.6624317181687094, + "angle2": 1.5579526427549426, + "target": "_target_name_", + "direction_type": "J2000" + }, + "antenna_set": "LBA_SPARSE_EVEN", + "station_groups": [ + { + "stations": [ + "CS002", + "CS003", + "CS004", + "CS005", + "CS006", + "CS007", + "CS011", + "CS013", + "CS017", + "CS021", + "CS024", + "CS026", + "CS028", + "CS030", + "CS031", + "CS032", + "CS101", + "CS103", + "CS201", + "CS301", + "CS302", + "CS401", + "CS501", + "RS106", + "RS205", + "RS208", + "RS210", + "RS305", + "RS306", + "RS307", + "RS310", + "RS406", + "RS407", + "RS409", + "RS503", + "RS508", + "RS509" + ], + "max_nr_missing": 4 + }, + { + "stations": [ + "RS508", + "RS509" + ], + "max_nr_missing": 1 + }, + { + "stations": [ + "RS310", + "RS210" + ], + "max_nr_missing": 0 + }, + { + "stations": [ + "DE601", + "DE602", + "DE603", + "DE604", + "DE605", + "DE609", + "FR606", + "SE607", + "UK608", + "PL610", + "PL611", + "PL612", + "IE613", + "LV614" + ], + "max_nr_missing": 2 + }, + { + "stations": [ + "DE601", + "DE605" + ], + "max_nr_missing": 1 + } + ] + } + }, + "specifications_template": "http://localhost:3000/api/task_template/21", + "task_type": "observation" + }, + { + "id": 54, + "url": "http://localhost:3000/api/task_draft/54", + "description": "Preprocessing Pipeline for Calibrator Observation", + "name": "Calibrator Pipeline", + "short_description": "oOOO.O 3Cabc", + "specifications_doc": { + "flag": { + "rfi_strategy": "LBAdefault", + "outerchannels": true, + "autocorrelations": true + }, + "demix": { + "sources": [], + "time_steps": 8, + "ignore_target": false, + "frequency_steps": 64 + }, + "$schema": "http://127.0.0.1:8008/api/schemas/tasktemplate/preprocessing%20pipeline/8/ref_resolved", + "average": { + "time_steps": 2, + "frequency_steps": 4 + }, + "storagemanager": "dysco", + "cluster_resources": { + "where": { + "cluster": "CEP4", + "partition": "cpu" + }, + "cores_per_task": 2, + "parallel_tasks": 122 + } + }, + "specifications_template": "http://localhost:3000/api/task_template/17", + "task_type": "pipeline" + }, + { + "id": 55, + "url": "http://localhost:3000/api/task_draft/55", + "description": "Preprocessing Pipeline for Target Observation target beam", + "name": "Pipeline target", + "short_description": "oOOO.O _Target_name_", + "specifications_doc": { + "flag": { + "rfi_strategy": "LBAdefault", + "outerchannels": true, + "autocorrelations": true + }, + "demix": { + "sources": [], + "time_steps": 8, + "ignore_target": false, + "frequency_steps": 64 + }, + "$schema": "http://127.0.0.1:8008/api/schemas/tasktemplate/preprocessing%20pipeline/8/ref_resolved", + "average": { + "time_steps": 2, + "frequency_steps": 4 + }, + "storagemanager": "dysco", + "cluster_resources": { + "where": { + "cluster": "CEP4", + "partition": "cpu" + }, + "cores_per_task": 2, + "parallel_tasks": 122 + } + }, + "specifications_template": "http://localhost:3000/api/task_template/17", + "task_type": "pipeline" + }, + { + "id": 56, + "url": "http://localhost:3000/api/task_draft/56", + "description": "Clean up all dataproducts from disk after ingest", + "name": "Cleanup", + "short_description": "", + "specifications_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/tasktemplate/cleanup/7/ref_resolved" + }, + "specifications_template": "http://localhost:3000/api/task_template/56", + "task_type": "cleanup" + }, + { + "id": 57, + "url": "http://localhost:3000/api/task_draft/57", + "description": "Ingest all preprocessed dataproducts", + "name": "Ingest", + "short_description": "", + "specifications_doc": { + "$schema": "http://127.0.0.1:8008/api/schemas/tasktemplate/ingest/7/ref_resolved" + }, + "specifications_template": "http://localhost:3000/api/task_template/4", + "task_type": "ingest" + } + ] + }] + }, }; export default SUServiceMock; \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/NumericEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/NumericEditor.js new file mode 100644 index 0000000000000000000000000000000000000000..237cd4c1c6817dffa48eb2be7c63dedacb39377c --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/NumericEditor.js @@ -0,0 +1,137 @@ +import React, {Component, createRef} from 'react'; + +// backspace starts the editor on Windows +const KEY_BACKSPACE = 'Backspace'; +const KEY_DELETE = 'Delete'; +const KEY_ENTER = 'Enter'; +const KEY_TAB = 'Tab'; + +//Reference: https://www.ag-grid.com/react-data-grid/cell-editors/ +export default class NumericEditor extends Component { + constructor(props) { + super(props); + + this.inputRef = createRef(null); + + const isCharacter = this.props.eventKey && this.props.eventKey.length === 1; + + this.cancelBeforeStart = isCharacter && '1234567890'.indexOf(this.props.eventKey) < 0; + this.state = this.createInitialState(props); + this.onKeyDown = this.onKeyDown.bind(this); + this.handleChange = this.handleChange.bind(this); + } + + componentDidMount() { + this.setCaret(); + } + + render() { + return ( + <input + ref={this.inputRef} + className="numericAgGridField" + data-testid="numeric-editor-input" + style={{width: '100%'}} + value={this.state.value} + onKeyDown={this.onKeyDown} + onChange={this.handleChange} + /> + ); + } + + /* Component Editor Lifecycle methods */ + + // the final value to send to the grid, on completion of editing + getValue() { + return this.state.value; + } + + // Gets called once before editing starts, to give editor a chance to + // cancel the editing before it even starts. + isCancelBeforeStart() { + return this.cancelBeforeStart; + } + + // Gets called once when editing is finished (eg if Enter is pressed). + // If you return true, then the result of the edit will be ignored. + isCancelAfterEnd() { + // will reject the number if it greater than 1,000,000 + // not very practical, but demonstrates the method. + return this.state.value > 1000000; + } + + /* Utility methods */ + createInitialState(props) { + let startValue; + + if (props.eventKey === KEY_BACKSPACE || props.eventKey === KEY_DELETE) { + startValue = ''; + } else if (props.eventKey && props.eventKey.length === 1) { + startValue = props.eventKey; + } else { + startValue = props.value; + } + + return { + value: startValue, + }; + } + + onKeyDown(event) { + if (this.isLeftOrRight(event) || this.isBackspace(event) || this.isDelete(event)) { + event.stopPropagation(); + return; + } + + if (!this.finishedEditingPressed(event) && !this.isNumericKey(event)) { + if (event.preventDefault) { + event.preventDefault(); + } + } + } + + isLeftOrRight(event) { + return ['ArrowLeft', 'ArrowRight'].indexOf(event.key) > -1; + } + + handleChange(event) { + this.setState({value: event.target.value}); + } + + getCharCodeFromEvent(event) { + return event?.which || event?.keyCode + } + + isCharNumericOrDot(charStr) { + return !Number.isNaN(Number.parseInt(charStr, 10)) || charStr === '.' + } + + isNumericKey(event) { + const charCode = this.getCharCodeFromEvent(event); + const charStr = event.key ? event.key : String.fromCharCode(charCode); + //this check will ensure that you can only enter valid (decimal) numbers as a whole + const containsOneOrZeroDots = `${event.target.value + charStr}`.split('.').length <= 2; + return containsOneOrZeroDots && this.isCharNumericOrDot(charStr); + } + + + isBackspace(event) { + return event.key === KEY_BACKSPACE; + } + + isDelete(event) { + return event.key === KEY_DELETE; + } + + finishedEditingPressed(event) { + const key = event.key; + return key === KEY_ENTER || key === KEY_TAB; + } + + setCaret() { + setTimeout(() => { + const currentInput = this.inputRef.current; + currentInput.focus(); + }); + } +} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/NumericEditor.test.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/NumericEditor.test.js new file mode 100644 index 0000000000000000000000000000000000000000..5dcb8c4585b016759e914b4646553c6add008365 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/NumericEditor.test.js @@ -0,0 +1,51 @@ +import React from 'react'; + +import NumericEditor from './NumericEditor'; +import {fireEvent, render} from "@testing-library/react"; +import {removeReact18ConsoleErrors} from "../../utils/test.helper"; + +removeReact18ConsoleErrors() + +describe('NumericEditor', () => { + let props = { + eventKey: '', + value: '', + }; + + test('set initial state correctly based on props', () => { + const startValue = '123'; + props.value = startValue + const {getByTestId} = render(<NumericEditor {...props} />); + + const inputElement = getByTestId('numeric-editor-input'); + expect(inputElement.value).toBe(startValue); + }); + + test('update state value on input change', () => { + const {getByTestId} = render(<NumericEditor {...props} />); + const inputElement = getByTestId('numeric-editor-input'); + + const newValue = '4.56'; + fireEvent.change(inputElement, {target: {value: newValue}}); + expect(inputElement.value).toBe(newValue); + }); + + //negative, non-numeric, keyboard actions and double dot + test.each(['-', 'A', 'Enter', '.'])(`Prevent invalid key events entries: %s`, (eventKey) => { + const {getByTestId} = render(<NumericEditor {...props} />); + + const inputElement = getByTestId('numeric-editor-input'); + + const preventDefaultMock = jest.fn() + let event = { + preventDefault: preventDefaultMock(), + target: { + value: '1.5', + }, + } + + event.key = eventKey + fireEvent.keyDown(inputElement, {event}); + expect(preventDefaultMock).toHaveBeenCalled(); + }) +}); \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/numericEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/numericEditor.js deleted file mode 100644 index c4a99ae23c2ea7f8a0ef572d86181c824bdc742c..0000000000000000000000000000000000000000 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Spreadsheet/numericEditor.js +++ /dev/null @@ -1,147 +0,0 @@ -import React, { Component } from 'react'; - -const KEY_BACKSPACE = 8; -const KEY_DELETE = 46; -const KEY_F2 = 113; -const KEY_ENTER = 13; -const KEY_TAB = 9; - -export default class NumericEditor extends Component { - constructor(props) { - super(props); - - this.cancelBeforeStart = - this.props.charPress && '1234567890'.indexOf(this.props.charPress) < 0; - this.state = this.createInitialState(props); - this.onKeyDown = this.onKeyDown.bind(this); - this.handleChange = this.handleChange.bind(this); - } - - createInitialState(props) { - let startValue; - let highlightAllOnFocus = true; - - if (props.keyPress === KEY_BACKSPACE || props.keyPress === KEY_DELETE) { - // if backspace or delete pressed, we clear the cell - startValue = ''; - } else if (props.charPress) { - // if a letter was pressed, we start with the letter - startValue = props.charPress; - highlightAllOnFocus = false; - } else { - // otherwise we start with the current value - startValue = props.value; - if (props.keyPress === KEY_F2) { - highlightAllOnFocus = false; - } - } - - return { - value: startValue, - highlightAllOnFocus, - }; - } - - componentDidMount() { - this.refs.input.addEventListener('keydown', this.onKeyDown); - } - - componentWillUnmount() { - this.refs.input.removeEventListener('keydown', this.onKeyDown); - } - - afterGuiAttached() { - // get ref from React component - const eInput = this.refs.input; - eInput.focus(); - if (this.state.highlightAllOnFocus) { - eInput.select(); - - this.setState({ - highlightAllOnFocus: false, - }); - } else { - // when we started editing, we want the carot at the end, not the start. - // this comes into play in two scenarios: a) when user hits F2 and b) - // when user hits a printable character, then on IE (and only IE) the carot - // was placed after the first character, thus 'apply' would end up as 'pplea' - const length = eInput.value ? eInput.value.length : 0; - if (length > 0) { - eInput.setSelectionRange(length, length); - } - } - } - - getValue() { - return this.state.value?parseFloat(this.state.value):""; - } - - isCancelBeforeStart() { - return this.cancelBeforeStart; - } - - // will reject the number if it greater than 1,000,000 - // not very practical, but demonstrates the method. - isCancelAfterEnd() { - return this.state.value > 1000000; - } - - onKeyDown(event) { - if (this.isLeftOrRight(event) || this.deleteOrBackspace(event)) { - event.stopPropagation(); - return; - } - - if ( - !this.finishedEditingPressed(event) && - !this.isKeyPressedNumeric(event) - ) { - if (event.preventDefault) event.preventDefault(); - } - } - - isLeftOrRight(event) { - return [37, 39].indexOf(event.keyCode) > -1; - } - - handleChange(event) { - this.setState({ value: event.target.value }); - } - - getCharCodeFromEvent(event) { - event = event || window.event; - return typeof event.which === 'undefined' ? event.keyCode : event.which; - } - - isCharNumeric(charStr) { - return !!/^\d*\.?\d*$/.test(charStr); - } - - isKeyPressedNumeric(event) { - const charCode = this.getCharCodeFromEvent(event); - const charStr = event.key ? event.key : String.fromCharCode(charCode); - return `${event.target.value + charStr}`.split('.').length <= 2 && this.isCharNumeric(charStr); - } - - - render() { - return ( - <input - ref="input" - value={this.state.value} - onChange={this.handleChange} - className="numericAgGridField" - style={{ width: '100%' }} - /> - ); - } - - deleteOrBackspace(event) { - return [KEY_DELETE, KEY_BACKSPACE].indexOf(event.keyCode) > -1; - } - - finishedEditingPressed(event) { - const charCode = this.getCharCodeFromEvent(event); - return charCode === KEY_ENTER || charCode === KEY_TAB; - } -} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js b/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js index ac6c780910f50b2dcdce442f0e36ee80f183b4dd..703908a59169ff8880691c4d04ee2e8b2a9a6125 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js @@ -1622,7 +1622,7 @@ function DurationRangeFilter({ tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter Minimum Range value in HH:mm:ss format and press ‘Enter’ key to search"} onChange={e => { let val = e.target.value; - if (val.includes(":") && !Validator.isValidHHmmss(val, true)) { + if (val.includes(":") && !Validator.isValidHHmmss(val)) { val = rangeValue[0]; } let max = rangeValue[1]; @@ -1650,7 +1650,7 @@ function DurationRangeFilter({ placeholder="HH:mm:ss" onChange={e => { let val = e.target.value; - if (val.includes(":") && !Validator.isValidHHmmss(val, true)) { + if (val.includes(":") && !Validator.isValidHHmmss(val)) { val = rangeValue[1]; } let min = rangeValue[0]; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ObservationStrategySelector.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ObservationStrategySelector.js new file mode 100644 index 0000000000000000000000000000000000000000..b897c684fc9d307cfad3e22eb79db2d84dc9f3bc --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ObservationStrategySelector.js @@ -0,0 +1,156 @@ +import {MultiSelect} from "primereact/multiselect"; +import {Dropdown} from "primereact/dropdown"; +import React from "react"; +import UIConstants from "../../../utils/ui.constants"; +import UtilService from "../../../services/util.service"; +import ScheduleService from "../../../services/schedule.service"; + +function purposeFilter(selectedPurpose, allPurposes, localStoreKey, setState) { + return <MultiSelect inputId="observStrategyPurpose" + optionLabel="name" + optionValue="value" + className="observ_strategy_purpose" + tooltip="Filter Observation Strategy by Purpose" + tooltipOptions={UIConstants.tooltipOptions} + value={selectedPurpose} + options={allPurposes} + maxSelectedLabels="1" + selectedItemsLabel="{0} Selected" + data-testid="strategy-purpose" + onChange={(e) => setObservationStrategyFilters(e.value, 'purpose', localStoreKey, setState)} + placeholder="Purpose"/>; +} + +function stateFilter(selectedState, allStates, localStoreKey, setState) { + return <MultiSelect inputId="observStrategyState" + optionLabel="name" + optionValue="value" + className="observ_strategy_state" + tooltip="Filter Observation Strategy by State" + tooltipOptions={UIConstants.tooltipOptions} + value={selectedState} + options={allStates} + onChange={(e) => setObservationStrategyFilters(e.value, 'state', localStoreKey, setState)} + maxSelectedLabels="1" + selectedItemsLabel="{0} Selected" + data-testid="strategy-state" + placeholder="State"/>; +} + +function strategySelector(selectedObservationStrategy, allObservationStrategies, observationIdSet, changeStrategy) { + const dropdown = <Dropdown inputId="observStrategy" + optionLabel="uniqueId" + optionValue="id" + style={{width: "100%"}} + data-testid="observStrategy" + tooltip="Observation Strategy Template to be used to create the Scheduling Unit and Tasks. Strategies already used are highlighted in bold" + tooltipOptions={UIConstants.tooltipOptions} + value={selectedObservationStrategy.id} + options={allObservationStrategies} filter + itemTemplate={option => optionTemplate(option, observationIdSet)} + onChange={(e) => changeStrategy(e.value)} + placeholder="Select Strategy"/>; + return <div> + {dropdown} + <label className="info"> + {selectedObservationStrategy ? selectedObservationStrategy.description : "Select Observation Strategy"} + </label> + </div>; +} + +/** + * Template for displaying the options in the dropdown + * @param option: strategy's description, name, purpose_value, state_value, version + * @param observationIdSet set of strategy Id's that have been used before (empty array if none) + * @return {JSX.Element} + */ +export const optionTemplate = (option, observationIdSet) => { + if (observationIdSet) { + return ( + <div + title={option.description} + style={{ + display: "flex", fontWeight: observationIdSet.includes(option.id) ? 'bold' : 'normal' + }}> + {`${option.name} (${option.purpose_value}, ${option.state_value}, v${option.version})`} + </div> + ); + } +}; + +export async function queryMatchingObservationStrategyTemplates(filters) { + let filterQueryParams = ''; + filters.states?.forEach(state => filterQueryParams += `state=${state}&`) + filters.purpose?.forEach(purpose => filterQueryParams += `purpose=${purpose}&`) + + return await ScheduleService.getObservationStrategies(filterQueryParams); +} + +/** + * Based upon the selected filters + the stored filters (if present), it retrieves the observation strategy templates + * via the API and sets these values in the parent component + * @param selectedFilter the values from the multiselect element + * @param filterId the id related to which multiselect element + * @param localStoreKey key that points to the correct local storage (since this function is used of multiple components) + * @param setState callback function to parent component + */ +export async function setObservationStrategyFilters(selectedFilter, filterId, localStoreKey, setState) { + let storedFilters = await UtilService.localStore({type: 'get', key: localStoreKey}); + + if (Object.keys(storedFilters).length === 0) { + storedFilters = {states: ['active'], purpose: []} + } + + if (filterId === 'purpose') { + storedFilters.purpose = selectedFilter; + } else if (filterId === 'state') { //TODO: 'state' and 'states' are mixed throughout the application (unwanted) + storedFilters.states = selectedFilter; + } else { + //reset: do nothing + } + + await UtilService.localStore({type: 'set', key: localStoreKey, value: storedFilters}); + + setState({ + observStrategyFilters: storedFilters, + observStrategies: await queryMatchingObservationStrategyTemplates(storedFilters), + paramsSchema: null, + observStrategy: {}, + rowData: [] + }) +} + +/** + * @prop selectedPurpose Object{name<string>, value<string>} + * @prop allPurposes list<Object{name<string>, value<string>}> + * @prop selectedState list<string> (multi-select) + * @prop allStates list<Object{name<string>, value<string>}> + * @prop selectedObservationStrategy list<string> (multi-select) + * @prop allObservationStrategies list<string> + * @prop observationIdSet list<integer> set of strategy Id's that have been used before (empty array if none) + * @prop changeStrategy function: callback + * @prop localStoreKey <string> + * @prop setState function: callback (for strategy filters) + * @return {JSX.Element} + */ +export default function ObservationStrategySelector(props) { + const { + selectedPurpose, + allPurposes, + selectedState, + allStates, + selectedObservationStrategy, + allObservationStrategies, + observationIdSet, + changeStrategy, + localStoreKey, + setState + } = props + return <div className="col-lg-3 col-md-3 col-sm-12"> + <div className='p-field p-grid'> + {purposeFilter(selectedPurpose, allPurposes, localStoreKey, setState)} + {stateFilter(selectedState, allStates, localStoreKey, setState)} + </div> + {strategySelector(selectedObservationStrategy, allObservationStrategies, observationIdSet, changeStrategy)} + </div> +} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ObservationStrategySelector.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ObservationStrategySelector.test.js new file mode 100644 index 0000000000000000000000000000000000000000..2fd2fa33e2c3be8cad72f3ddb3c9019bafb77425 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ObservationStrategySelector.test.js @@ -0,0 +1,246 @@ +import {render} from '@testing-library/react'; +import ObservationStrategySelector, { + optionTemplate, + setObservationStrategyFilters +} from './ObservationStrategySelector'; +import {clickItem, removeReact18ConsoleErrors, setMultiSelectValue} from "../../../utils/test.helper"; +import UtilService from "../../../services/util.service"; +import ScheduleService from "../../../services/schedule.service"; + +removeReact18ConsoleErrors(); + +const allObservationStrategies = [ + {id: 1, name: "Fist", value: "fist", purpose_value: "1", state_value: "1", version: 11, description: 'Hitting'}, + {id: 2, name: "Gun", value: "gun", purpose_value: "2", state_value: "3", version: 12, description: 'Shooting'}, + {id: 3, name: "Sword", value: "sword", purpose_value: "3", state_value: "2", version: 13, description: 'Slicing'}, +]; +const observationIdSet = [1, 2, 3]; + +describe('itemTemplate', () => { + test.each([allObservationStrategies])('Renders the dropdown options correctly', (observationStrategy) => { + const {getByTitle} = render( + <div>{optionTemplate(observationStrategy, observationIdSet)}</div> + ); + + const renderedElement = getByTitle(observationStrategy.description); + expect(renderedElement).toBeInTheDocument(); + const textContent = renderedElement.textContent; + expect(textContent).toContain(observationStrategy.name) + expect(textContent).toContain(observationStrategy.purpose_value) + expect(textContent).toContain(observationStrategy.state_value) + expect(textContent).toContain(`v${observationStrategy.version}`) + expect(renderedElement).toHaveStyle(`font-weight: bold`); + }); + + test('Does not render the dropdown options when observationIdSet is not provided', () => { + const {getByTitle} = render(<div>{optionTemplate(allObservationStrategies[0])}</div>); + expect(() => getByTitle(allObservationStrategies[0].description)).toThrow() + }); + + const observationIdSets = [[], [4], [5, 6]]; + observationIdSets.forEach(observationIdSet => { + test('Renders the dropdown option with normal fontWeight when option id is not in observationIdSet: ' + observationIdSet, () => { + const {getByTitle} = render(<div>{optionTemplate(allObservationStrategies[0], observationIdSet)}</div>); + const renderedElement = getByTitle(allObservationStrategies[0].description); + expect(renderedElement).toHaveStyle(`font-weight: normal`); + }); + }) +}); + + +describe('ObservationStrategySelector', () => { + let content; + const allPurposes = [{name: 'Head', value: '1'}, {name: 'Chest', value: '2'}, {name: 'Stomach', value: '3'}]; + const allStates = [{name: 'Low', value: '1'}, {name: 'Medium', value: '2'}, {name: 'High', value: '3'}]; + + const observationOptionTemplate = <div></div>; + const changeStrategy = jest.fn(); + const setState = jest.fn(); + let parentStateFields = { + "observStrategies": [], + "observStrategy": {}, + "observStrategyFilters": {states: ["active"]}, + "paramsSchema": null, + "rowData": [] + } + + beforeEach(() => { + content = render( + <ObservationStrategySelector + selectedPurpose={[allPurposes[0].value]} + allPurposes={allPurposes} + selectedState={[allStates[0].value]} + allStates={allStates} + selectedObservationStrategy={allObservationStrategies[0]} + allObservationStrategies={allObservationStrategies} + observationOptionTemplate={observationOptionTemplate} + observationIdSet={observationIdSet} + changeStrategy={changeStrategy} + setState={setState} + /> + ) + }) + + test('Renders all elements correctly', () => { + expect(content.getByTestId('strategy-purpose')).toBeInTheDocument(); + expect(content.getByTestId('strategy-state')).toBeInTheDocument(); + expect(content.getByTestId('observStrategy')).toBeInTheDocument(); + }); + + test('calls setObservationStrategyFilters. First when state is changed, second when purpose is changed', async () => { + const originalState = allStates[0]; + + const additionalState = allStates[1]; + await setMultiSelectValue(content, "strategy-state", additionalState.name) + + //initial filter value (=states) is changed and the purpose filter is initialized + parentStateFields.observStrategyFilters = { + "purpose": [], + "states": [originalState.value, additionalState.value] + } + expect(setState).toHaveBeenCalledWith(parentStateFields); + + const originalPurpose = allPurposes[0]; + + const additionalPurpose = allPurposes[1]; + await setMultiSelectValue(content, "strategy-purpose", additionalPurpose.name) + //initial filter value (=states) is not changed when only purpose filter is set + parentStateFields.observStrategyFilters = { + ...{"purpose": [originalPurpose.value, additionalPurpose.value]}, + ...parentStateFields.observStrategyFilters + } + expect(setState).toHaveBeenCalledWith(parentStateFields); + }); + + test('calls changeStrategy when observation strategy is changed', async () => { + const newStrategy = allObservationStrategies[1]; + + await clickItem(content.getByTestId('observStrategy')) + await clickItem(content.getByTitle(newStrategy.description)) + expect(changeStrategy).toHaveBeenCalledWith(newStrategy.id); + }); +}) +; + +describe('setObservationStrategyFilters', () => { + let setStateMock, observStrategiesSpy, utilSpy; + const localStoreKey = 'the-pride-lands' + + beforeEach(() => { + utilSpy = jest.spyOn(UtilService, 'localStore').mockReturnValueOnce({}); + + observStrategiesSpy = jest.spyOn(ScheduleService, 'getObservationStrategies').mockImplementation(() => { + return Promise.resolve('mockedObservationStrategies') + }); + + setStateMock = jest.fn() + }) + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should set observation strategy filters for purpose', async () => { + const selectedFilter = ['simba', 'nala']; + const filterId = 'purpose'; + + await setObservationStrategyFilters(selectedFilter, filterId, localStoreKey, setStateMock); + + expect(utilSpy).toHaveBeenCalledTimes(2); //get and set + expect(observStrategiesSpy).toHaveBeenCalledWith( + "state=active&purpose=simba&purpose=nala&" + ); + + expect(setStateMock).toHaveBeenCalledWith({ + observStrategyFilters: { + states: ['active'], + purpose: ['simba', 'nala'], + }, + observStrategies: 'mockedObservationStrategies', + paramsSchema: null, + observStrategy: {}, + rowData: [], + }); + }); + + it('should set observation strategy filters for state', async () => { + const selectedFilter = ['young', 'old']; + const filterId = 'state'; + + await setObservationStrategyFilters(selectedFilter, filterId, localStoreKey, setStateMock); + + expect(utilSpy).toHaveBeenCalledTimes(2); //get and set + expect(observStrategiesSpy).toHaveBeenCalledWith( + 'state=young&state=old&', + ); + + expect(setStateMock).toHaveBeenCalledWith({ + observStrategyFilters: { + states: ['young', 'old'], + purpose: [], + }, + observStrategies: 'mockedObservationStrategies', + paramsSchema: null, + observStrategy: {}, + rowData: [], + }); + }); + + it('Reset; called without filters', async () => { + await setObservationStrategyFilters([], '', localStoreKey, setStateMock); + + expect(utilSpy).toHaveBeenCalledTimes(2); //get and set + expect(observStrategiesSpy).toHaveBeenCalledWith( + 'state=active&', + ); + + expect(setStateMock).toHaveBeenCalledWith({ + observStrategyFilters: { + states: ['active'], + purpose: [], + }, + observStrategies: 'mockedObservationStrategies', + paramsSchema: null, + observStrategy: {}, + rowData: [], + }); + }); + + it('Updating twice to test with local store', async () => { + utilSpy + .mockReturnValueOnce({}) //first call + .mockReturnValueOnce({states: ['young', 'old'], purpose: []}); //second call + + let selectedFilter = ['young', 'old']; + let filterId = 'state'; + + await setObservationStrategyFilters(selectedFilter, filterId, localStoreKey, setStateMock); + + selectedFilter = ['simba', 'nala']; + filterId = 'purpose'; + + await setObservationStrategyFilters(selectedFilter, filterId, localStoreKey, setStateMock); + + expect(utilSpy).toHaveBeenNthCalledWith(1, {"key": localStoreKey, "type": "get"}); + expect(utilSpy).toHaveBeenNthCalledWith(2, { + "key": localStoreKey, "type": "set", "value": {"purpose": [], "states": ["young", "old"]} + }); + expect(utilSpy).toHaveBeenNthCalledWith(3, {"key": localStoreKey, "type": "get"}); + expect(utilSpy).toHaveBeenNthCalledWith(4, { + "key": localStoreKey, "type": "set", "value": {"purpose": ['simba', 'nala'], "states": ["young", "old"]} + }); + + expect(observStrategiesSpy).toHaveBeenNthCalledWith(1, "state=young&state=old&"); + expect(observStrategiesSpy).toHaveBeenNthCalledWith(2, "state=young&state=old&purpose=simba&purpose=nala&"); + expect(setStateMock).toHaveBeenCalledWith({ + observStrategyFilters: { + states: ['young', 'old'], + purpose: ['simba', 'nala'], + }, + observStrategies: 'mockedObservationStrategies', + paramsSchema: null, + observStrategy: {}, + rowData: [], + }); + }); +}); \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ProjectScheduleSetSelector.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ProjectScheduleSetSelector.js new file mode 100644 index 0000000000000000000000000000000000000000..36f7601cc7f97e06af649defabb35c04f56a41ce --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ProjectScheduleSetSelector.js @@ -0,0 +1,107 @@ +import {Dropdown} from "primereact/dropdown"; +import {Button} from "primereact/button"; +import React from "react"; +import UIConstants from "../../../utils/ui.constants"; + +function projectSelector(selectedProject, projectDisabled, allProjects, projectErrors, touchedProject, changeProject) { + const dropdown = <Dropdown inputId="project" + optionLabel="name" + optionValue="name" + data-testid="project" + testid="project" + tooltip="Project" + tooltipOptions={UIConstants.tooltipOptions} + className="projectDropdown" + value={selectedProject} + disabled={projectDisabled} + options={allProjects} + onChange={(e) => changeProject(e.value)} + placeholder="Select Project"/>; + return <div className="col-lg-3 col-md-3 col-sm-12" data-testid="projectdiv"> + {dropdown} + <label + className={(projectErrors && touchedProject) ? "error" : "info"}> + {(projectErrors && touchedProject) ? projectErrors : "Select Project to get Scheduling Sets"} + </label> + </div> +} + +function schedulingSetSelector(selectedSchedulingSet, allSchedulingSetS, schedulingSetErrors, touchedSchedulingSet, setSchedUnitParams) { + const dropdown = <Dropdown data-testid="schedSet" + id="schedSet" + optionLabel="name" + optionValue="id" + tooltip="Scheduling set of the project" + tooltipOptions={UIConstants.tooltipOptions} + value={selectedSchedulingSet} + options={allSchedulingSetS} + onChange={(e) => setSchedUnitParams('scheduling_set_id', e.value)} + placeholder="Select Scheduling Set"/>; + return <div className="col-lg-3 col-md-3 col-sm-10"> + {dropdown} + <label + className={(schedulingSetErrors && touchedSchedulingSet) ? "error" : "info"}> + {(schedulingSetErrors && touchedSchedulingSet) ? schedulingSetErrors : "Scheduling Set of the Project"} + </label> + </div> +} + +function addSchedulingSetButton(selectedProject, scheduleUnitDraft, setState) { + return <div className="col-lg-1 col-md-1 col-sm-2"> + <Button label="" + className="p-button-primary" + icon="pi pi-plus" + data-testid="add-schedulingset-button" + onClick={() => setState({showAddSet: true})} + tooltip="Add new Scheduling Set" + style={{marginLeft: '-10px'}} + disabled={!(selectedProject !== null && scheduleUnitDraft?.scheduling_set !== undefined)}/> + </div> +} + +/** + * @prop selectedProject Object{name<string>, value<string>} + * @prop projectDisabled boolean + * @prop allProjects list<Object{name<string>, value<string>}> + * @prop projectErrors <string> + * @prop touchedProject boolean + * @prop selectedSchedulingSet list<string> (multi-select) + * @prop allSchedulingSets list<Object{name<string>, value<string>, id<integer>}> + * @prop schedulingSetErrors <string> + * @prop touchedSchedulingSet boolean + * @prop scheduleUnitDraft ??? + * @prop changeProject function: callback + * @prop setSchedUnitParams function: callback + * @prop setState function: callback + * @return {JSX.Element} + */ +export default function ProjectScheduleSetSelector(props) { + const { + selectedProject, + projectDisabled, + allProjects, + projectErrors, + touchedProject, + selectedSchedulingSet, + allSchedulingSets, + schedulingSetErrors, + touchedSchedulingSet, + scheduleUnitDraft, + changeProject, + setSchedUnitParams, + setState, + } = props + + return <div className="p-field p-grid"> + <label htmlFor="project" className="col-lg-2 col-md-2 col-sm-12"> + Project <span style={{color: 'red'}}>*</span> + </label> + {projectSelector(selectedProject, projectDisabled, allProjects, projectErrors, touchedProject, changeProject)} + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="schedSet" className="col-lg-2 col-md-2 col-sm-12"> + Scheduling Set <span style={{color: 'red'}}>*</span> + </label> + {schedulingSetSelector(selectedSchedulingSet, allSchedulingSets, schedulingSetErrors, touchedSchedulingSet, setSchedUnitParams)} + {addSchedulingSetButton(selectedProject, scheduleUnitDraft, setState)} + </div> +} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ProjectScheduleSetSelector.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ProjectScheduleSetSelector.test.js new file mode 100644 index 0000000000000000000000000000000000000000..15b20a4759a4a31658c2a928b8325feb6da02e1b --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/components/ProjectScheduleSetSelector.test.js @@ -0,0 +1,126 @@ +import {render} from '@testing-library/react'; +import {clickItem, removeReact18ConsoleErrors} from "../../../utils/test.helper"; +import ProjectScheduleSetSelector from "./ProjectScheduleSetSelector"; + +removeReact18ConsoleErrors(); + + +describe('ProjectScheduleSetSelector', () => { + const allProjects = [ + {name: 'Batman', value: '1'}, + {name: 'Robin', value: '2'}, + ] + const allSchedulingSets = [ + {name: 'X-men', value: '3', id: 12}, + {name: 'Batman', value: '4', id: 13}]; + const projectErrorMsg = 'robin is not better than batman'; + const scheduleSetErrorMsg = 'batman is not part of x-men'; + const scheduleUnitDraft = {scheduling_set: allSchedulingSets[0]}; + + test('Renders all elements correctly with errors', () => { + const {getByTestId, getByText} = render( + <ProjectScheduleSetSelector + selectedProject={allProjects[0]} + projectDisabled={false} + allProjects={allProjects} + projectErrors={projectErrorMsg} + touchedProject={true} + changeProject={jest.fn()} + selectedSchedulingSet={allSchedulingSets[0]} + allSchedulingSets={allSchedulingSets} + schedulingSetErrors={scheduleSetErrorMsg} + touchedSchedulingSet={true} + setSchedUnitParams={jest.fn()} + scheduleUnitDraft={scheduleUnitDraft} + setState={jest.fn()} + /> + ); + + expect(getByTestId('schedSet')).toBeInTheDocument(); + expect(getByTestId('project')).toBeInTheDocument(); + expect(getByTestId('add-schedulingset-button')).toBeInTheDocument(); + expect(getByText(projectErrorMsg)).toBeInTheDocument(); + expect(getByText(scheduleSetErrorMsg)).toBeInTheDocument(); + }); + + test('Renders all elements correctly without errors but disabled button', () => { + const {getByTestId, getByText} = render( + <ProjectScheduleSetSelector + selectedProject={allProjects[0]} + projectDisabled={false} + allProjects={allProjects} + projectErrors={undefined} + touchedProject={false} + changeProject={jest.fn()} + selectedSchedulingSet={allSchedulingSets[0]} + allSchedulingSets={allSchedulingSets} + schedulingSetErrors={undefined} + touchedSchedulingSet={false} + setSchedUnitParams={jest.fn()} + scheduleUnitDraft={undefined} + setState={jest.fn()} + /> + ); + + expect(getByTestId('schedSet')).toBeInTheDocument(); + expect(getByTestId('project')).toBeInTheDocument(); + const buttonElement = getByTestId('add-schedulingset-button'); + expect(buttonElement).toBeInTheDocument(); + expect(buttonElement).toHaveAttribute('disabled') + expect(() => getByText(projectErrorMsg)).toThrow(); + expect(() => getByText(scheduleSetErrorMsg)).toThrow(); + }); + + test('Calls changeProject when project is changed', async () => { + const changeProject = jest.fn(); + const content = render( + <ProjectScheduleSetSelector + selectedProject={allProjects[0]} + projectDisabled={false} + allProjects={allProjects} + projectErrors={projectErrorMsg} + touchedProject={true} + changeProject={changeProject} + /> + ); + + const newProject = allProjects[1]; + + await clickItem(content.getByTestId('project')) + await clickItem(content.getByText(newProject.name)) + expect(changeProject).toHaveBeenCalledWith(newProject.name); + }); + + test('Calls setSchedUnitParams when scheduling set changed', async () => { + const setSchedUnitParams = jest.fn(); + const content = render( + <ProjectScheduleSetSelector + selectedSchedulingSet={allSchedulingSets[0]} + allSchedulingSets={allSchedulingSets} + schedulingSetErrors={scheduleSetErrorMsg} + touchedSchedulingSet={true} + setSchedUnitParams={setSchedUnitParams} + /> + ); + + const newSchedulingSet = allSchedulingSets[1]; + + await clickItem(content.getByTestId('schedSet')) + await clickItem(content.getByText(newSchedulingSet.name)) + expect(setSchedUnitParams).toHaveBeenCalledWith('scheduling_set_id', newSchedulingSet.id); + }); + test('Calls setState for modal when add scheduling set button is clicked', async () => { + const setState = jest.fn(); + const content = render( + <ProjectScheduleSetSelector + selectedProject={allProjects[0]} + scheduleUnitDraft={scheduleUnitDraft} + setState={setState} + /> + ); + + await clickItem(content.getByTestId('add-schedulingset-button')) + expect(setState).toHaveBeenCalledWith({showAddSet: true}); + }); + +}); \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js index 56ec2375aab083b72a12eaecc9f7440b14974ae1..b00039fc0bbdc922c1daaf8f056d0b5746e9d36c 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js @@ -9,7 +9,6 @@ import {Checkbox} from 'primereact/checkbox'; import {InputText} from 'primereact/inputtext'; import {InputTextarea} from 'primereact/inputtextarea'; import {Dropdown} from 'primereact/dropdown'; -import {Button} from 'primereact/button'; import {Toast} from 'primereact/toast'; import AppLoader from '../../layout/components/AppLoader'; import Jeditor from '../../components/JSONEditor/JEditor'; @@ -28,10 +27,13 @@ import SchedulingSet from './schedulingset.create'; import UtilService from '../../services/util.service'; import ReactTooltip from "react-tooltip"; import AuthUtil from '../../utils/auth.util'; -import {MultiSelect} from 'primereact/multiselect'; import {appGrowl, setAppGrowl} from '../../layout/components/AppGrowl'; import FormActionbar from '../../components/FormActionbar'; import {getConstraintTemplate, getTaskTemplateForTask} from "./create.helper"; +import ObservationStrategySelector, { + queryMatchingObservationStrategyTemplates +} from "./components/ObservationStrategySelector"; +import ProjectScheduleSetSelector from "./components/ProjectScheduleSetSelector"; /** * Component to create a new SchedulingUnit from Observation strategy template @@ -85,7 +87,6 @@ export class SchedulingUnitCreate extends Component { this.taskFilters = []; // To get Short_Description details this.priorityQueueTypes = []; this.projects = []; // All projects to load project dropdown - this.observStrategies = []; // All Observing strategy templates this.taskTemplates = []; // All task templates to be filtered based on tasks in selected strategy template this.tooltipOptions = UIConstants.tooltipOptions; this.constraintTemplates = []; @@ -116,9 +117,9 @@ export class SchedulingUnitCreate extends Component { this.checkIsDirty = this.checkIsDirty.bind(this); this.close = this.close.bind(this); this.setSUSet = this.setSUSet.bind(this); - this.observOptionTemplate = this.observOptionTemplate.bind(this); this.updateConstraintsParams = this.updateConstraintsParams.bind(this); this.setCreateAnother = this.setCreateAnother.bind(this); + this.setState = this.setState.bind(this); } async componentDidMount() { @@ -170,8 +171,20 @@ export class SchedulingUnitCreate extends Component { } else { this.setState({isLoading: false}); } - this.setObservStrateyFilters(); }); + let observStrategyFilter = UtilService.localStore({type: 'get', key: 'strategyFilterSUCreate'}); + if (Object.keys(observStrategyFilter).length === 0) { + observStrategyFilter = {states: ['active'], purpose: []} + } + + this.setState({ + observStrategies: await this.getMatchingObservationStrategyTemplates(observStrategyFilter), + observStrategyFilters: observStrategyFilter + }); + } + + async getMatchingObservationStrategyTemplates(filters) { + return await queryMatchingObservationStrategyTemplates(filters) } getStrategyFilterOptions(filterValues, filterId) { @@ -219,10 +232,9 @@ export class SchedulingUnitCreate extends Component { * @param {number} strategyId */ async changeStrategy(strategyId) { - const observStrategy = _.cloneDeep(_.find(this.observStrategies, {'id': strategyId})); + const observStrategy = _.cloneDeep(_.find(this.state.observStrategies, {'id': strategyId})); if (ParserUtility.addStationParameters(observStrategy)) { await this.setState({ - observStrategies: this.observStrategies, observStrategy: observStrategy, constraintSchema: null }); @@ -897,24 +909,6 @@ export class SchedulingUnitCreate extends Component { this.setState({saveDialogVisible: false, showAddSet: false, schedulingSets: schedulingSets}); } - /** - * Observation Strategy Template Dropdown option Component to display the description in tooltip - * @param {any} option - * @returns - */ - observOptionTemplate(option) { - if (this.state.observationIdSet) { - return ( - <div title={option.description} style={{ - display: "flex", - fontWeight: _.includes(this.state.observationIdSet, option.id) ? 'bold' : 'normal' - }}> - {`${option.name} (${option.purpose_value}, ${option.state_value}, v${option.version})`} - </div> - ); - } - } - /** * Set newly created as current SU Set * @param {SU Set Name} suSet @@ -923,38 +917,6 @@ export class SchedulingUnitCreate extends Component { this.setState({newSet: suSet}); } - async setObservStrateyFilters(selectedFilter, filterId) { - let observStrategyFilters = UtilService.localStore({type: 'get', key: 'strategyFilterSUCreate'}); - if (_.isEmpty(observStrategyFilters)) { - observStrategyFilters = { - states: ['active'], - purpose: [] - } - } - if (filterId === 'purpose') { - observStrategyFilters.purpose = selectedFilter; - } - if (filterId === 'state') { - observStrategyFilters.states = selectedFilter; - } - let filterQry = ''; - _.map(observStrategyFilters.states, state => { - filterQry = filterQry + `state=${state}&` - }) - _.map(observStrategyFilters.purpose, purpose => { - filterQry = filterQry + `purpose=${purpose}&` - }) - let observStrategies = await ScheduleService.getObservationStrategies(filterQry) - this.observStrategies = observStrategies; - UtilService.localStore({type: 'set', key: 'strategyFilterSUCreate', value: observStrategyFilters}); - this.setState({ - observStrategyFilters: observStrategyFilters, - observStrategies: observStrategies, - paramsSchema: null, - observStrategy: {} - }) - } - /** * Select/Unselect the create another check box * @param {*} e @@ -1033,57 +995,21 @@ export class SchedulingUnitCreate extends Component { </label> </div> </div> - <div className="p-field p-grid"> - <label htmlFor="project" className="col-lg-2 col-md-2 col-sm-12">Project <span - style={{color: 'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12" data-testid="projectdiv"> - <Dropdown inputId="project" optionLabel="name" optionValue="name" - data-testid="project" testid="project" - tooltip="Project" tooltipOptions={this.tooltipOptions} - className="projectDropdown" - value={this.state.schedulingUnit.project} - disabled={this.state.projectDisabled} - options={this.projects} - onChange={(e) => { - this.changeProject(e.value) - }} - placeholder="Select Project"/> - <label - className={(this.state.errors.project && this.state.touched.project) ? "error" : "info"}> - {(this.state.errors.project && this.state.touched.project) ? this.state.errors.project : "Select Project to get Scheduling Sets"} - </label> - </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="schedSet" className="col-lg-2 col-md-2 col-sm-12">Scheduling - Set <span style={{color: 'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-10"> - <Dropdown data-testid="schedSet" id="schedSet" optionLabel="name" - optionValue="id" - tooltip="Scheduling set of the project" - tooltipOptions={this.tooltipOptions} - value={this.state.schedulingUnit.scheduling_set_id} - options={this.state.schedulingSets} - onChange={(e) => { - this.setSchedUnitParams('scheduling_set_id', e.value) - }} - placeholder="Select Scheduling Set"/> - <label - className={(this.state.errors.scheduling_set_id && this.state.touched.scheduling_set_id) ? "error" : "info"}> - {(this.state.errors.scheduling_set_id && this.state.touched.scheduling_set_id) ? this.state.errors.scheduling_set_id : "Scheduling Set of the Project"} - </label> - </div> - <div className="col-lg-1 col-md-1 col-sm-2"> - <Button label="" className="p-button-primary" icon="pi pi-plus" - onClick={() => { - this.setState({showAddSet: true}) - }} - tooltip="Add new Scheduling Set" - style={{marginLeft: '-10px'}} - disabled={!(this.state.schedulingUnit.project !== null && scheduleunit_draft?.scheduling_set !== undefined)}/> - - - </div> - </div> + {<ProjectScheduleSetSelector + selectedProject={this.state.schedulingUnit.project} + projectDisabled={this.state.projectDisabled} + allProjects={this.projects} + projectErrors={this.state.errors.project} + touchedProject={this.state.touched.project} + changeProject={this.changeProject} + selectedSchedulingSet={this.state.schedulingUnit.scheduling_set_id} + allSchedulingSets={this.state.schedulingSets} + schedulingSetErrors={this.state.errors.scheduling_set_id} + touchedSchedulingSet={this.state.touched.scheduling_set_id} + setSchedUnitParams={this.setSchedUnitParams} + scheduleUnitDraft={scheduleunit_draft} + setState={this.setState} + />} <div className="p-field p-grid"> <label htmlFor="rank" className="col-lg-2 col-md-2 col-sm-12">Rank</label> <div className="col-lg-3 col-md-3 col-sm-12" data-testid="rank"> @@ -1115,55 +1041,21 @@ export class SchedulingUnitCreate extends Component { </div> </div> <div className="p-field p-grid"> - <label htmlFor="observStrategy" className="col-lg-2 col-md-2 col-sm-12">Observation - Strategy <span style={{color: 'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <div className='p-field p-grid'> - <MultiSelect inputId="observStrategyPurpose" optionLabel="name" - optionValue="value" className="observ_strategy_purpose" - tooltip="Filter Observation Strategy by Purpose" - tooltipOptions={this.tooltipOptions} - value={this.state.observStrategyFilters.purpose} - options={this.templatePurposes} - maxSelectedLabels="1" - selectedItemsLabel="{0} Selected" - data-testid="strategy-purpose" - onChange={(e) => { - this.setObservStrateyFilters(e.value, 'purpose') - }} - placeholder="Purpose"/> - {/* <div className="col-lg-1 col-md-1 col-sm-12"></div> */} - <MultiSelect inputId="observStrategyState" optionLabel="name" - optionValue="value" className="observ_strategy_state" - tooltip="Filter Observation Strategy by State" - tooltipOptions={this.tooltipOptions} - value={this.state.observStrategyFilters.states} - options={this.templateState} - onChange={(e) => { - this.setObservStrateyFilters(e.value, 'state') - }} - maxSelectedLabels="1" - selectedItemsLabel="{0} Selected" - data-testid="strategy-state" - placeholder="State"/> - </div> - <div> - <Dropdown inputId="observStrategy" optionLabel="uniqueId" optionValue="id" - style={{width: "100%"}} data-testid="observStrategy" - tooltip="Observation Strategy Template to be used to create the Scheduling Unit and Tasks. Strategies already used are highlighted in bold" - tooltipOptions={this.tooltipOptions} - value={this.state.observStrategy.id} - options={this.state.observStrategies} filter - itemTemplate={this.observOptionTemplate} - onChange={(e) => { - this.changeStrategy(e.value) - }} - placeholder="Select Strategy"/> - <label className="info"> - {this.state.observStrategy ? this.state.observStrategy.description : ""} - </label> - </div> - </div> + <label htmlFor="observStrategy" className="col-lg-2 col-md-2 col-sm-12"> + Observation Strategy <span style={{color: 'red'}}>*</span> + </label> + {<ObservationStrategySelector + selectedPurpose={this.state.observStrategyFilters.purpose} + allPurposes={this.templatePurposes} + selectedState={this.state.observStrategyFilters.states} + allStates={this.templateState} + selectedObservationStrategy={this.state.observStrategy} + allObservationStrategies={this.state.observStrategies} + observationIdSet={this.state.observationIdSet} + changeStrategy={this.changeStrategy} + localStoreKey={"strategyFilterSUCreate"} + setState={this.setState} + />} <div className="col-lg-1 col-md-1 col-sm-12"></div> <label htmlFor="autodeletion" className="col-lg-2 col-md-2 col-sm-12">Pin Data</label> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.test.js index adbe862f751a3def109505a91f0d0fb7bc638482..43250901b981b64da22fcaaad4256b2a8c7e40d1 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.test.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.test.js @@ -1,7 +1,5 @@ import React from 'react'; -import {BrowserRouter as Router} from 'react-router-dom'; -import {act} from "react-dom/test-utils"; -import {cleanup, fireEvent, render, screen} from '@testing-library/react'; +import {cleanup, fireEvent, screen} from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import '@testing-library/jest-dom'; @@ -15,11 +13,15 @@ import SUServiceMock from '../../__mocks__/scheduleunit.service.data'; import ProjectServiceMock from '../../__mocks__/project.service.data'; import TaskServiceMock from '../../__mocks__/task.service.data'; import UtilServiceMock from '../../__mocks__/util.service.data'; -import {clickItem, setMultiSelectValue, removeReact18ConsoleErrors} from "../../utils/test.helper"; +import {clickItem, removeReact18ConsoleErrors, renderPage, setMultiSelectValue} from "../../utils/test.helper"; +import {setSchedulingUnitStrategy} from "./schedulingunit.test.helper"; let projectListSpy, observStrategiesSpy, taskTemplatesSpy, utilSpy, taskFilterDefSpy, suConstraintTemplateSpy, stationGroupSpy, templatePurposesSpy, templateStatesSpy, rolesSpy, scheduleSetByIdSpy; +//Jest issue: https://github.com/jestjs/jest/issues/9709: Cannot be called in beforeXX test method +const OBSERVATION_STRATEGY_TEMPLATES = SUServiceMock.getObservStrategies() + jest.setTimeout(10000); removeReact18ConsoleErrors(); @@ -60,17 +62,6 @@ function createStandardPageMocks() { }); } -async function renderSchedulingUnitCreatePage() { - let content; - await act(async () => { - content = render(<Router><SchedulingUnitCreate location={{pathname: '/schedulingunit/create'}}/></Router>); - await new Promise((r) => setTimeout(r, 500)); - }); - console.error = () => { - }; - return content; -} - async function setSchedulingUnitBasics(pageContent) { fireEvent.change(pageContent.queryByTestId('name'), {target: {value: "Wolverine"}}); fireEvent.change(pageContent.queryByTestId('description'), {target: {value: "Big claws"}}); @@ -78,20 +69,12 @@ async function setSchedulingUnitBasics(pageContent) { await setMultiSelectValue(pageContent, "schedSet", "Test Scheduling Set") } -async function createSchedulingUnit(strategy, pageContent) { - await setSchedulingUnitBasics(pageContent); - await setMultiSelectValue(pageContent, "strategy-purpose", strategy.purpose) - await setMultiSelectValue(pageContent, "strategy-state", strategy.state) - const optionLabel = `${strategy.name} (${strategy.purpose}, ${strategy.state}, v${strategy.version})`; - await setMultiSelectValue(pageContent, "observStrategy", optionLabel) -} - describe('Scheduling Unit create page default', () => { let pageContent; beforeEach(async () => { createStandardPageMocks(); - pageContent = await renderSchedulingUnitCreatePage(); + pageContent = await renderPage(<SchedulingUnitCreate location={{pathname: '/schedulingunit/create'}}/>); }) afterEach(() => { @@ -133,13 +116,6 @@ describe('Scheduling Unit create page default', () => { }); }); -/** - * Jest issue: https://github.com/jestjs/jest/issues/9709 - * Cannot use the before methods upon building the test cases. - * Setting it as a global constant is needed for information within the test cases - */ -const OBSERVATION_STRATEGY_TEMPLATES = SUServiceMock.getObservStrategies() - describe('Scheduling Unit create page with an observation strategy template', () => { let pageContent, saveButton; let scheduleSetListSpy, stationSpy, utcSpy, validatorSpy, stationGroupSpy; @@ -167,7 +143,7 @@ describe('Scheduling Unit create page with an observation strategy template', () beforeEach(async () => { createStandardPageMocks(); createTemplatePageMocks(); - pageContent = await renderSchedulingUnitCreatePage(); + pageContent = await renderPage(<SchedulingUnitCreate location={{pathname: '/schedulingunit/create'}}/>); saveButton = pageContent.queryByTestId('save-btn'); }) @@ -197,10 +173,10 @@ describe('Scheduling Unit create page with an observation strategy template', () } } - test.each(OBSERVATION_STRATEGY_TEMPLATES)(`Correctly creates, saves and checks constraints for template '$name, $purpose, $state, v$version'`, async (observationStrategy) => { + test.each(OBSERVATION_STRATEGY_TEMPLATES)(`Correctly creates, saves and checks constraints for template '$name'`, async (observationStrategy) => { //TODO: increase test coverage to include previous tests https://support.astron.nl/jira/browse/TMSS-2537 await setSchedulingUnitBasics(pageContent); - await createSchedulingUnit(observationStrategy, pageContent) + await setSchedulingUnitStrategy(observationStrategy, pageContent); checkTemplateMocks(); checkCorrectSpecifications(); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.helper.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.helper.js new file mode 100644 index 0000000000000000000000000000000000000000..7d9c93e5c4c97c0b4de23f2b30a52064ac5c4616 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.helper.js @@ -0,0 +1,13 @@ +/** + * Get daily columns based on the constraint template daily settings + * @param {*} dailyConstraint + */ +export function getDailyColumns(dailyConstraint) { + if (!dailyConstraint) { + return [] + } + let dailyColumnDefinitions = []; + const dailyColumnKeys = ['require_day', 'require_night', 'avoid_twilight'] + dailyColumnKeys.filter(key => dailyConstraint[key] === true).map(key => dailyColumnDefinitions.push(key)) + return dailyColumnDefinitions; +} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.helper.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.helper.test.js new file mode 100644 index 0000000000000000000000000000000000000000..5f2681ca3c2b7f3078492bfe986e8f44bc564d84 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.helper.test.js @@ -0,0 +1,47 @@ +import {getDailyColumns} from "./excelview.schedulingset.helper"; + +describe('getDailyColumns', () => { + test.each([null, undefined])('Returns empty array when dailyConstraint is: %s', (dailyConstraint) => { + const result = getDailyColumns(dailyConstraint); + expect(result).toEqual([]); + }) + + test('should return an empty array when no properties in dailyConstraint are set to true', () => { + const dailyConstraint = { + require_day: false, + require_night: false, + avoid_twilight: false + }; + const result = getDailyColumns(dailyConstraint); + expect(result).toEqual([]); + }); + + test('should return ["require_day"] when dailyConstraint.require_day is true', () => { + const dailyConstraint = { + require_day: true, + require_night: false, + avoid_twilight: false + }; + const result = getDailyColumns(dailyConstraint); + expect(result).toEqual(['require_day']); + }); + + test('should return an array with all column names when all properties in dailyConstraint are set to true', () => { + const dailyConstraint = { + require_day: true, + require_night: true, + avoid_twilight: true + }; + const result = getDailyColumns(dailyConstraint); + expect(result).toEqual(['require_day', 'require_night', 'avoid_twilight']); + }); + + test('should return an empty array when the properties in dailyConstraint are unknown', () => { + const dailyConstraint = { + schaap: true, + requireDay: true, + }; + const result = getDailyColumns(dailyConstraint); + expect(result).toEqual([]); + }); +}); 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 191d5028cbea1eb0d7f4c5b95547a3b5b06e8075..e3a4334cdc910d8be46f6b83de77b1cdc76f8ab0 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 @@ -1,30 +1,30 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router-dom'; -import { Dropdown } from 'primereact/dropdown'; -import { Button } from 'primereact/button'; -import { Toast } from 'primereact/toast'; -import { Checkbox } from 'primereact/checkbox'; -import { Accordion, AccordionTab } from 'primereact/accordion'; -import { DataTable } from 'primereact/datatable'; -import { Column } from 'primereact/column'; +import React, {Component} from 'react'; +import {Redirect} from 'react-router-dom'; +import {Dropdown} from 'primereact/dropdown'; +import {Button} from 'primereact/button'; +import {Toast} from 'primereact/toast'; +import {Checkbox} from 'primereact/checkbox'; +import {Accordion, AccordionTab} from 'primereact/accordion'; +import {DataTable} from 'primereact/datatable'; +import {Column} from 'primereact/column'; import TimeInputmask from '../../components/Spreadsheet/TimeInputmask' import OffsetTimeInputmask from '../../components/Spreadsheet/OffsetTimeInputmask' import DegreeInputmask from '../../components/Spreadsheet/DegreeInputmask' -import NumericEditor from '../../components/Spreadsheet/numericEditor'; -import BetweenEditor from '../../components/Spreadsheet/BetweenEditor'; +import NumericEditor from '../../components/Spreadsheet/NumericEditor'; +import BetweenEditor from '../../components/Spreadsheet/BetweenEditor'; import BetweenRenderer from '../../components/Spreadsheet/BetweenRenderer'; import BeamformersRenderer from '../../components/Spreadsheet/BeamformerRenderer'; import MultiSelector from '../../components/Spreadsheet/MultiSelector'; import CustomDateComp from '../../components/Spreadsheet/CustomDateComp'; import StationEditor from '../../components/Spreadsheet/StationEditor'; import Beamformer from '../../components/Spreadsheet/Beamformer'; -import { CustomPageSpinner } from '../../components/CustomPageSpinner'; +import {CustomPageSpinner} from '../../components/CustomPageSpinner'; import AppLoader from '../../layout/components/AppLoader'; import PageHeader from '../../layout/components/PageHeader'; -import { publish } from '../../App'; -import { CustomDialog } from '../../layout/components/CustomDialog'; +import {publish} from '../../App'; +import {CustomDialog} from '../../layout/components/CustomDialog'; import SchedulingSet from './schedulingset.create'; import SchedulingConstraints from './Scheduling.Constraints'; @@ -33,7 +33,7 @@ import ScheduleService from '../../services/schedule.service'; import TaskService from '../../services/task.service'; import UtilService from '../../services/util.service'; -import Validator from '../../utils/validator'; +import Validator from '../../utils/validator'; import UnitConverter from '../../utils/unit.converter' import UIConstants from '../../utils/ui.constants'; import ParserUtility from '../../utils/parser.utility'; @@ -42,35 +42,41 @@ import moment from 'moment'; import _ from 'lodash'; import $RefParser from "@apidevtools/json-schema-ref-parser"; -import { AgGridReact } from 'ag-grid-react'; -import { AllCommunityModules } from '@ag-grid-community/all-modules'; +import {AgGridReact} from 'ag-grid-react'; +import {AllCommunityModules} from '@ag-grid-community/all-modules'; import 'ag-grid-community/dist/styles/ag-grid.css'; import 'ag-grid-community/dist/styles/ag-theme-alpine.css'; import AccessDenied from '../../layout/components/AccessDenied'; import AuthUtil from '../../utils/auth.util'; -import { MultiSelect } from 'primereact/multiselect'; import TopProgressBar from '../../layout/components/TopProgressBar'; +import ObservationStrategySelector, { + queryMatchingObservationStrategyTemplates +} from "./components/ObservationStrategySelector"; +import ProjectScheduleSetSelector from "./components/ProjectScheduleSetSelector"; +import {getConstraintTemplate, getTaskTemplateForTask} from "./create.helper"; +import {getDailyColumns} from "./excelview.schedulingset.helper"; /* eslint-disable no-loop-func */ const BG_COLOR = '#f878788f'; + /** * Component to create / update Scheduling Unit Drafts using Spreadsheet */ export class SchedulingSetCreate extends Component { - SU_DRAFT_EXPAND= 'task_drafts' - SU_DRAFT_FIELDS=['id','name', 'url','rank','priority_queue_value','description', - 'scheduling_constraints_doc','scheduling_constraints_template', - 'observation_strategy_template_id','task_drafts.url','scheduling_constraints_template_id', - 'scheduling_set','task_drafts.task_type','task_drafts.id', - 'task_drafts.name','task_drafts.description','task_drafts.short_description','task_drafts.specifications_doc', - 'task_drafts.specifications_template.id','task_drafts.specifications_template.type_value','task_drafts.specifications_template.schema', - 'scheduling_unit_blueprints', - 'observation_strategy_template', - 'specifications_template'] + SU_DRAFT_EXPAND = 'task_drafts' + SU_DRAFT_FIELDS = ['id', 'name', 'url', 'rank', 'priority_queue_value', 'description', + 'scheduling_constraints_doc', 'scheduling_constraints_template', + 'observation_strategy_template_id', 'task_drafts.url', 'scheduling_constraints_template_id', + 'scheduling_set', 'task_drafts.task_type', 'task_drafts.id', + 'task_drafts.name', 'task_drafts.description', 'task_drafts.short_description', 'task_drafts.specifications_doc', + 'task_drafts.specifications_template.id', 'task_drafts.specifications_template.type_value', 'task_drafts.specifications_template.schema', + 'scheduling_unit_blueprints', + 'observation_strategy_template', + 'specifications_template'] constructor(props) { super(props); - this.state= { + this.state = { observStrategyFilters: { states: ['active'], purpose: [] @@ -83,11 +89,12 @@ export class SchedulingSetCreate extends Component { selectedProject: {}, copyHeader: false, // Copy Table Header to clipboard applyEmptyValue: false, - projectDisabled: (props.match?(props.match.params.project? true:false):false), - isLoading: true, + showAddSet: false, + projectDisabled: (props.match ? (props.match.params.project ? true : false) : false), + isLoading: true, isAGLoading: false, // Flag for loading spinner - dialog: { header: '', detail: ''}, // Dialog properties - clipboard: [], + dialog: {header: '', detail: ''}, // Dialog properties + clipboard: [], totalCount: 0, validEditor: false, noOfSU: 10, @@ -98,10 +105,10 @@ export class SchedulingSetCreate extends Component { observationIdSet: [], multiSelectorOptions: {}, isObsoletStrategy: false, - schedulingUnit: { + schedulingUnit: { name: '', description: '', - project: (props.match?props.match.params.project:null) || null, + project: (props.match ? props.match.params.project : null) || null, }, columnMap: [], columnDefs: [], @@ -117,12 +124,12 @@ export class SchedulingSetCreate extends Component { editable: true, flex: 1, sortable: true, minWidth: 100, resizable: true, }, rowSelection: 'multiple', - context: { componentParent: this }, + context: {componentParent: this}, modules: AllCommunityModules, frameworkComponents: { numericEditor: NumericEditor, timeInputMask: TimeInputmask, - offsetTimeInputmask:OffsetTimeInputmask, + offsetTimeInputmask: OffsetTimeInputmask, degreeInputMask: DegreeInputmask, betweenRenderer: BetweenRenderer, betweenEditor: BetweenEditor, @@ -140,12 +147,12 @@ export class SchedulingSetCreate extends Component { inValidCount: 0, }, noOfSUOptions: [ - { label: '10', value: '10' }, - { label: '50', value: '50' }, - { label: '100', value: '100' }, - { label: '250', value: '250' }, - { label: '500', value: '500' } - ], + {label: '10', value: '10'}, + {label: '50', value: '50'}, + {label: '100', value: '100'}, + {label: '250', value: '250'}, + {label: '500', value: '500'} + ], customSelectedStations: [], selectedStations: [], defaultStationGroups: {}, @@ -156,13 +163,13 @@ export class SchedulingSetCreate extends Component { validSpecification: false, specificationValidationMessage: '', validConstraints: false, - constraintsValidationMessage: '' + constraintsValidationMessage: '', + custId: 1 }; this.constraintsValidationMessage = {}; this.specificationValidationMessage = {}; this.durationList = []; this.stationGroupsName = []; - this.custId = 1; this.columnDefinition = {}; this.taskFilters = []; this.isMacOS = false; @@ -184,7 +191,7 @@ export class SchedulingSetCreate extends Component { this.applyToAllRow = false; this.callBackFunction = ""; this.onClose = this.close; - this.onCancel =this.close; + this.onCancel = this.close; this.applyToEmptyRowOnly = false; this.isSpecificationValid = true; this.isConstraintValid = true; @@ -195,7 +202,6 @@ export class SchedulingSetCreate extends Component { this.dialogMsg = ""; this.dialogContent = ""; this.projects = []; // All projects to load project dropdown - this.observStrategies = []; // All Observing strategy templates this.taskTemplates = []; // All task templates to be filtered based on tasks in selected strategy template this.taskTemplateSchemas = []; this.constraintTemplates = []; @@ -207,11 +213,10 @@ export class SchedulingSetCreate extends Component { this.constraintVariables = []; this.strategyVariables = []; - this.onProjectChange = this.onProjectChange.bind(this); + this.onProjectChange = this.onProjectChange.bind(this); this.setSchedulingSetParams = this.setSchedulingSetParams.bind(this); this.onStrategyChange = this.onStrategyChange.bind(this); this.setNoOfSUint = this.setNoOfSUint.bind(this); - this.showAddSchedulingSet = this.showAddSchedulingSet.bind(this); this.isNotEmpty = this.isNotEmpty.bind(this); this.onGridReady = this.onGridReady.bind(this); this.onTopGridReady = this.onTopGridReady.bind(this); @@ -231,16 +236,15 @@ export class SchedulingSetCreate extends Component { this.clipboardEvent = this.clipboardEvent.bind(this); this.copyFromClipboard = this.copyFromClipboard.bind(this); this.pasteTextField = this.pasteTextField.bind(this); - this.applyToAll = this.applyToAll.bind(this); - this.applyToSelected = this.applyToSelected.bind(this); - this.applyToEmptyRows = this.applyToEmptyRows.bind(this); + this.applyToAll = this.applyToAll.bind(this); + this.applyToSelected = this.applyToSelected.bind(this); + this.applyToEmptyRows = this.applyToEmptyRows.bind(this); this.resetCommonData = this.resetCommonData.bind(this); this.reload = this.reload.bind(this); - this.applyChanges = this.applyChanges.bind(this); + this.applyChanges = this.applyChanges.bind(this); this.getSchedulingDialogContent = this.getSchedulingDialogContent.bind(this); this.setSUSet = this.setSUSet.bind(this); this.copyClipText = this.copyClipText.bind(this); - this.observOptionTemplate = this.observOptionTemplate.bind(this); this.copyWarningContent = this.copyWarningContent.bind(this); this.showConstraintError = this.showConstraintError.bind(this); this.showSpecificationError = this.showSpecificationError.bind(this); @@ -251,70 +255,82 @@ export class SchedulingSetCreate extends Component { scheduling_set_id: {required: true, message: "Select the Scheduling Set"}, }; this.setConstraintsEditorOutput = this.setConstraintsEditorOutput.bind(this); + this.setState = this.setState.bind(this); + this.prepareScheduleUnitListForGrid = this.prepareScheduleUnitListForGrid.bind(this); } - - async onTopGridReady (params) { + + async onTopGridReady(params) { await this.setState({ - topGridApi:params.api, - topGridColumnApi:params.columnApi, + topGridApi: params.api, + topGridColumnApi: params.columnApi, }) this.state.topGridApi.hideOverlay(); } - async onGridReady (params) { + async onGridReady(params) { await this.setState({ - gridApi:params.api, - gridColumnApi:params.columnApi, + gridApi: params.api, + gridColumnApi: params.columnApi, }) this.state.gridApi.hideOverlay(); } /** - * Check is empty string - * @param {*} value + * Check is empty string + * @param {*} value */ - isNotEmpty(value){ - if ( value === null || value === undefined || value.length === 0 ){ + isNotEmpty(value) { + if (value === null || value === undefined || value.length === 0) { return false; } else { return true; } } - + /** * Trigger when the project drop down get changed and check isDirty - * @param {*} projectName + * @param {*} projectName */ - onProjectChange(projectName) { + onProjectChange(projectName) { if (this.state.isDirty) { - this.showWarning(() =>{ + this.showWarning(() => { this.changeProject(projectName); }); - } else { + } else { this.changeProject(projectName); } } /** * Function to call on change of project and reload scheduling set dropdown - * @param {string} projectName + * @param {string} projectName */ - async changeProject(projectName) { + async changeProject(projectName) { const projectSchedluingSets = await ScheduleService.getSchedulingSets(`project=${encodeURIComponent(projectName)}`); let schedulingUnit = this.state.schedulingUnit; schedulingUnit.project = projectName; const selectedProject = _.filter(this.projects, {'name': projectName}); - this.setState({confirmDialogVisible: false, isDirty: false, selectedProject: selectedProject, schedulingUnit: schedulingUnit, - schedulingSets: projectSchedluingSets, validForm: this.validateForm('project'), rowData: [],observStrategy: {}, copyHeader: false, observationIdSet: []}); + this.setState({ + confirmDialogVisible: false, + isDirty: false, + selectedProject: selectedProject, + schedulingUnit: schedulingUnit, + schedulingSets: projectSchedluingSets, + validForm: this.validateForm('project'), + rowData: [], + observStrategy: {}, + copyHeader: false, + observationIdSet: [] + }); publish('edit-dirty', false); schedulingUnit.scheduling_set_id = null; } /** * Function to set form values to the SU object - * @param {string} key - * @param {object} value + * @param {string} key + * @param {object} value */ async setSchedulingSetParams(key, value) { @@ -323,48 +339,58 @@ export class SchedulingSetCreate extends Component { let schedulingUnit = this.state.schedulingUnit; schedulingUnit[key] = value; // Fetches only the observation_strategy_template_id of the SUDrafts of selected SUSet - let schedulingUnitList= await ScheduleService.getSchedulingBySet(value, 'observation_strategy_template_id'); - let observationIdSet = new Set(); + let schedulingUnitList = await ScheduleService.getSchedulingBySet(value, 'observation_strategy_template_id'); + let observationIdSet = new Set(); if (schedulingUnitList && schedulingUnitList.length > 0) { schedulingUnitList.forEach(schedulingUnit => { observationIdSet.add(schedulingUnit.observation_strategy_template_id); }); - this.setState({schedulingUnitList:schedulingUnitList}); + this.setState({schedulingUnitList: schedulingUnitList}); } observationIdSet = Array.from(observationIdSet); - this.setState({observStrategy:{}, schedulingUnit, selectedSchedulingSetId: value, copyHeader: false, confirmDialogVisible: false, isDirty: false, rowData: [], observationIdSet: observationIdSet}); + this.setState({ + observStrategy: {}, + schedulingUnit: schedulingUnit, + selectedSchedulingSetId: value, + copyHeader: false, + confirmDialogVisible: false, + isDirty: false, + rowData: [], + observationIdSet: observationIdSet + }); if (observationIdSet.length === 1) { this.onStrategyChange(observationIdSet[0]); - } else if(this.state.observStrategy && this.state.observStrategy.id) { - this.onStrategyChange(this.state.observStrategy.id); - } else { - this.setState({isAGLoading: false}); + } else if (this.state.observStrategy && this.state.observStrategy.id) { + this.onStrategyChange(this.state.observStrategy.id); + } else { + this.setState({isAGLoading: false}); } } /** * Set No. of Scheduling Unit load/show in the excel view table - * @param {*} value + * @param {*} value */ - async setNoOfSUint(value){ + async setNoOfSUint(value) { this.setState({isDirty: true, isAGLoading: true}); publish('edit-dirty', true); - if (value >= 0 && value < 501){ + if (value >= 0 && value < 501) { await this.setState({noOfSU: value}); - } else { + } else { await this.setState({noOfSU: 500}); } let noOfSU = this.state.noOfSU; this.tmpRowData = []; - if (this.state.rowData && this.state.rowData.length >0 && this.state.emptyRow) { + if (this.state.rowData && this.state.rowData.length > 0 && this.state.emptyRow) { if (this.state.totalCount <= noOfSU) { for (var count = 0; count < noOfSU; count++) { - if(this.state.rowData.length > count ) { + if (this.state.rowData.length > count) { this.tmpRowData.push(_.cloneDeep(this.state.rowData[count])); - } else { + } else { let tmpRow = _.cloneDeep(this.state.agSUWithDefaultValue); - tmpRow['custId'] = this.custId++; + this.setState({custId: this.state.custId + 1}) + tmpRow['custId'] = this.state.custId; this.tmpRowData.push(tmpRow); } @@ -380,43 +406,39 @@ export class SchedulingSetCreate extends Component { }) } } else { - for (let count = 0; count < noOfSU; count++) { - let tmpRow = _.cloneDeep(this.state.agSUWithDefaultValue); - tmpRow['custId'] = this.custId++; - this.tmpRowData.push(tmpRow); + if (Object.keys(this.state.observStrategy).length > 0) { + for (let count = 0; count < noOfSU; count++) { + let tmpRow = _.cloneDeep(this.state.agSUWithDefaultValue); + this.setState({custId: this.state.custId + 1}) + tmpRow['custId'] = this.state.custId; + this.tmpRowData.push(tmpRow); + } + this.setState({ + rowData: this.tmpRowData, + noOfSU: noOfSU, + isAGLoading: false + }); } - this.setState({ - rowData: this.tmpRowData, - noOfSU: noOfSU, - isAGLoading: false - }); } } /** * Set newly created as current SU Set - * @param {SU Set Name} suSet + * @param {SU Set Name} suSet */ setSUSet(suSet) { this.setState({newSet: suSet}); } - /** - * Show Dialog to add Scheduling Set - */ - showAddSchedulingSet() { - this.setState({addSchedulingSetVisible: true}); - } - /** * Update isDirty when cell value updated in AG grid - * @param {*} params + * @param {*} params */ - cellValueChageEvent(params) { - let rowData = this.state.rowData; - let paramRowData = rowData[params.rowIndex]; - if( params.value && !_.isEqual(params.value, params.oldValue)) { + cellValueChageEvent(params) { + let rowData = this.state.rowData; + let paramRowData = rowData[params.rowIndex]; + if (params.value && !_.isEqual(params.value, params.oldValue)) { paramRowData["isDirty"] = true; this.setState({isDirty: true, rowData: rowData}); publish('edit-dirty', true); @@ -425,17 +447,17 @@ export class SchedulingSetCreate extends Component { this.validateSchedulingConstraints(params.rowIndex, paramRowData); } if (_.includes(this.strategyVariables, params.column['colId'])) { - this.validateSpecificationsDoc(params.rowIndex,paramRowData); + this.validateSpecificationsDoc(params.rowIndex, paramRowData); } } /** - * Validate the updated cell value when the focus leaves - * @param {int} rowIndex - * @param {String} field - * @param {Object} value + * Validate the updated cell value when the focus leaves + * @param {int} rowIndex + * @param {String} field + * @param {Object} value */ - onFocusLost (rowIndex, field, value) { + onFocusLost(rowIndex, field, value) { let row = {}; if (!field.startsWith('gdef_')) { row = this.state.rowData[rowIndex]; @@ -448,32 +470,33 @@ export class SchedulingSetCreate extends Component { } } } - + /** - * If any changes detected warn before cancel the page + * If any changes detected warn before cancel the page */ - /* - Duplicate fnction - checkIsDirty() { - if( this.state.isDirty ){ - this.showIcon = true; - this.dialogType = "confirmation"; - this.dialogHeader = "Add Multiple Scheduling Unit(s)"; - this.dialogMsg = "Do you want to leave this page? Your changes may not be saved."; - this.dialogContent = ""; - this.dialogHeight = '5em'; - this.callBackFunction = this.cancelCreate; - this.onClose = this.close; - this.onCancel = this.close; - this.setState({confirmDialogVisible: true}); - } else { - this.cancelCreate(); - } - }*/ + /* + Duplicate fnction + checkIsDirty() { + if( this.state.isDirty ){ + this.showIcon = true; + this.dialogType = "confirmation"; + this.dialogHeader = "Add Multiple Scheduling Unit(s)"; + this.dialogMsg = "Do you want to leave this page? Your changes may not be saved."; + this.dialogContent = ""; + this.dialogHeight = '5em'; + this.callBackFunction = this.cancelCreate; + this.onClose = this.close; + this.onCancel = this.close; + this.setState({confirmDialogVisible: true}); + } else { + this.cancelCreate(); + } + }*/ /** * Set the new Set created in drop down */ + /*async setCurrentSUSet(id) { this.refreshSchedulingSet(); if(id) { @@ -481,11 +504,11 @@ export class SchedulingSetCreate extends Component { currentSU.scheduling_set_id = id; this.setState({schedulingUnit: currentSU}); } - + }*/ /** After adding new Scheduling Set, refresh the Scheduling Set list */ - async refreshSchedulingSet(){ + async refreshSchedulingSet() { let tmpSU = _.cloneDeep(this.state.schedulingUnit); const schedulingSets = await ScheduleService.getSchedulingSets(`project=${encodeURIComponent(this.state.schedulingUnit.project)}`); if (this.state.newSet) { @@ -495,11 +518,16 @@ export class SchedulingSetCreate extends Component { newSet: null }); } - this.setState({addSchedulingSetVisible: false, confirmDialogVisible: false, schedulingSets: schedulingSets, observStrategy: {}}); - this.setSchedulingSetParams('scheduling_set_id',tmpSU.scheduling_set_id); + this.setState({ + showAddSet: false, + confirmDialogVisible: false, + schedulingSets: schedulingSets, + observStrategy: {} + }); + this.setSchedulingSetParams('scheduling_set_id', tmpSU.scheduling_set_id); } - close(){ + close() { this.setState({confirmDialogVisible: false}); } @@ -515,13 +543,13 @@ export class SchedulingSetCreate extends Component { const fieldValue = this.state.schedulingUnit[fieldName]; if (rule.required) { if (!fieldValue) { - errors[fieldName] = rule.message?rule.message:`${fieldName} is required`; - } else { + errors[fieldName] = rule.message ? rule.message : `${fieldName} is required`; + } else { validFields[fieldName] = true; } } } - } else { + } else { errors = {}; validFields = {}; for (const fieldName in this.formRules) { @@ -529,8 +557,8 @@ export class SchedulingSetCreate extends Component { const fieldValue = this.state.schedulingUnit[fieldName]; if (rule.required) { if (!fieldValue) { - errors[fieldName] = rule.message?rule.message:`${fieldName} is required`; - } else { + errors[fieldName] = rule.message ? rule.message : `${fieldName} is required`; + } else { validFields[fieldName] = true; } } @@ -547,13 +575,12 @@ export class SchedulingSetCreate extends Component { * This function is mainly added for Unit Tests. If this function is removed Unit Tests will fail. */ validateEditor() { - return this.validEditor?true:false; + return this.validEditor ? true : false; } async componentDidMount() { - const promises = [ - ProjectService.getProjectList(false, 'name,url,auto_pin'), - ScheduleService.getObservationStrategies(), + const promises = [ + ProjectService.getProjectList(false, 'name,url,auto_pin'), TaskService.getTaskTemplates(), ScheduleService.getSchedulingConstraintTemplates(), UtilService.getPriorityQueueType(), @@ -564,27 +591,28 @@ export class SchedulingSetCreate extends Component { ]; const permission = await AuthUtil.getUserPermissionByModule('scheduleunit_draft'); this.setState({userrole: permission}); - await Promise.all(promises).then(async(responses) => { + await Promise.all(promises).then(async (responses) => { this.projects = responses[0]; - this.observStrategies = responses[1]; - this.taskTemplates = responses[2]; - this.constraintTemplates = responses[3]; - this.priorityQueuelist = responses[4]; - this.taskFilters = responses[5]; - this.templatePurposes = this.getStrategyFilterOptions(responses[6], 'purpose'); - this.templateState = this.getStrategyFilterOptions(responses[7], 'state'); - this.myRoles = responses[8]; - const systemRoles = this.myRoles.system_roles.map(role => { return role.toLowerCase()}); + this.taskTemplates = responses[1]; + this.constraintTemplates = responses[2]; + this.priorityQueuelist = responses[3]; + this.taskFilters = responses[4]; + this.templatePurposes = this.getStrategyFilterOptions(responses[5], 'purpose'); + this.templateState = this.getStrategyFilterOptions(responses[6], 'state'); + this.myRoles = responses[7]; + const systemRoles = this.myRoles.system_roles.map(role => { + return role.toLowerCase() + }); const projectRoles = Object.fromEntries( Object.entries(this.myRoles.project_roles).map(([project, roles]) => [project.toLowerCase(), roles]) ); // Users with system_roles other than superuser, operator & support can view the projects for which they have shared_support project_role - if(_.intersection(systemRoles, ['superuser', 'operator', 'support']).length < 1) { - this.projects = _.filter(this.projects, (project)=>{ - return _.includes(projectRoles[project.name.toLowerCase()], 'shared_support') + if (_.intersection(systemRoles, ['superuser', 'operator', 'support']).length < 1) { + this.projects = _.filter(this.projects, (project) => { + return _.includes(projectRoles[project.name.toLowerCase()], 'shared_support') }) } - + let queueList = [" "]; if (this.priorityQueuelist) { this.priorityQueuelist.forEach(queue => { @@ -592,72 +620,100 @@ export class SchedulingSetCreate extends Component { }); } if (this.state.schedulingUnit.project) { - const selectedProject = _.filter(this.projects, {'name': this.state.schedulingUnit.project}); + const selectedProject = _.filter(this.projects, {'name': this.state.schedulingUnit.project}); const projectSchedluingSets = await ScheduleService.getSchedulingSets(`project=${encodeURIComponent(this.state.schedulingUnit.project)}`); - this.setState({isLoading: false, schedulingSets: projectSchedluingSets, priorityQueuelist:queueList, selectedProject: selectedProject}); - } else { - this.setState({isLoading: false,priorityQueuelist:queueList}); - } - let observStrategyFilter = UtilService.localStore({ type: 'get', key: 'strategyFilterSUSetEditor' }); - if(_.isEmpty(observStrategyFilter)) { - observStrategyFilter = { - states: ['active'], - purpose: [] - } + this.setState({ + isLoading: false, + schedulingSets: projectSchedluingSets, + priorityQueuelist: queueList, + selectedProject: selectedProject + }); + } else { + this.setState({isLoading: false, priorityQueuelist: queueList}); } - this.setState({observStrategies: _.cloneDeep(this.observStrategies), observStrategyFilters: observStrategyFilter}); - this.setObservStrateyFilters(); }); + let observStrategyFilter = UtilService.localStore({type: 'get', key: 'strategyFilterSUSetEditor'}); + if (Object.keys(observStrategyFilter).length === 0) { + observStrategyFilter = {states: ['active'], purpose: []} + } + + this.setState({ + observStrategies: await this.getMatchingObservationStrategyTemplates(observStrategyFilter), + observStrategyFilters: observStrategyFilter + }); + } + + async getMatchingObservationStrategyTemplates(filters) { + return await queryMatchingObservationStrategyTemplates(filters) } /** * Trigger when the Strategy drop down get changed and check isDirty - * @param {*} strategyId + * @param {*} strategyId */ onStrategyChange(strategyId) { if (this.state.isDirty) { - this.showWarning(() =>{ + this.showWarning(() => { this.changeStrategy(strategyId); }); - } else { + } else { this.changeStrategy(strategyId); } - } + } /** - * Function called when observation strategy template is changed. + * Function called when observation strategy template is changed. * - * @param {number} strategyId + * @param {number} strategyId */ async changeStrategy(strategyId) { - await this.setState({noOfSU: 10, isAGLoading: true, copyHeader: false, rowData: [], confirmDialogVisible: false, isDirty: false}); + await this.setState({ + noOfSU: 10, + isAGLoading: true, + copyHeader: false, + rowData: [], + confirmDialogVisible: false, + isDirty: false + }); publish('edit-dirty', false); const observStrategy = _.find(this.state.observStrategies, {'id': strategyId}); ParserUtility.addStationParameters(observStrategy); - this.setState({observStrategy: observStrategy, noOfSU: 10, isAGLoading: true, copyHeader: false, rowData: [], agSUWithDefaultValue: {}, - confirmDialogVisible: false, isDirty: false, isObsoletStrategy: observStrategy.state_value === 'obsolete'? true: false}); + this.setState({ + observStrategy: observStrategy, + noOfSU: 10, + isAGLoading: true, + copyHeader: false, + rowData: [], + agSUWithDefaultValue: {}, + confirmDialogVisible: false, + isDirty: false, + isObsoletStrategy: observStrategy.state_value === 'obsolete' ? true : false + }); await this.getTaskSchema(observStrategy); - if(this.state.schedulingUnit.project && this.state.schedulingUnit.scheduling_set_id) { + if (this.state.schedulingUnit.project && this.state.schedulingUnit.scheduling_set_id) { this.prepareScheduleUnitListForGrid(); } } async getTaskSchema(observStrategy) { //let station_group = []; - let station_groups ={} + let station_groups = {} this.stationGroupsName = []; - if(observStrategy) { + if (observStrategy) { const tasks = observStrategy.template.tasks; const parameters = observStrategy.template.parameters; let paramsOutput = {}; - let schema = { type: 'object', additionalProperties: false, - properties: {}, definitions:{} - }; + let schema = { + type: 'object', additionalProperties: false, + properties: {}, definitions: {} + }; const $strategyRefs = await $RefParser.resolve(observStrategy.template); // TODo: This schema reference resolving code has to be moved to common file and needs to rework for (const param of parameters) { // TODO: make parameter handling more generic, instead of task specific. - if (!param.refs[0].startsWith("#/tasks/")) { continue; } + if (!param.refs[0].startsWith("#/tasks/")) { + continue; + } let taskPaths = param.refs[0].split("/"); const taskName = taskPaths[2]; @@ -665,16 +721,19 @@ export class SchedulingSetCreate extends Component { * For Short_Description, the task path length will be 4, so added below condition to get short_description details * #/tasks/Combined Observation/short_description */ - taskPaths = taskPaths.slice((taskPaths.length===4?3:4), taskPaths.length); - // taskPaths = taskPaths.slice(4, taskPaths.length); + taskPaths = taskPaths.slice((taskPaths.length === 4 ? 3 : 4), taskPaths.length); + // taskPaths = taskPaths.slice(4, taskPaths.length); const task = tasks[taskName]; if (task) { - const taskTemplate = _.find(this.taskTemplates, {'name': task.specifications_template.name, - 'version': task.specifications_template.version}); + const taskTemplate = getTaskTemplateForTask(this.taskTemplates, task); if (!taskTemplate) { - this.growl.show({severity: 'error', summary: 'Task template missing', detail: 'Unable to find task template defined in the strategy template'}); + this.growl.show({ + severity: 'error', + summary: 'Task template missing', + detail: 'Unable to find task template defined in the strategy template' + }); } - if (taskTemplate.type_value==='observation' && task.specifications_doc.station_configuration?.station_groups) { + if (taskTemplate?.type_value === 'observation' && task.specifications_doc.station_configuration?.station_groups) { if (taskPaths.includes("station_groups")) { station_groups[param.name] = task.specifications_doc.station_configuration.station_groups; this.stationGroupsName.push(param.name); @@ -686,23 +745,31 @@ export class SchedulingSetCreate extends Component { schema.properties[param.name] = _.cloneDeep(paramProp); if (schema.properties[param.name]) { schema.properties[param.name].title = param.name; - try { schema.properties[param.name].default = $strategyRefs.get(param.refs[0]); } catch(err) {} + try { + schema.properties[param.name].default = $strategyRefs.get(param.refs[0]); + } catch (err) { + } if (schema.properties[param.name].default) { paramsOutput[param.name] = schema.properties[param.name].default; } } } } - await this.setState({observStrategy: observStrategy, paramsSchema: schema, paramsOutput: paramsOutput,defaultStationGroups: station_groups}); + await this.setState({ + observStrategy: observStrategy, + paramsSchema: schema, + paramsOutput: paramsOutput, + defaultStationGroups: station_groups + }); } - } - + } + /** * Resolve JSON Schema */ - async resolveSchema(schema){ + async resolveSchema(schema) { let properties = schema.properties; - schema.definitions = schema.definitions?schema.definitions:{}; + schema.definitions = schema.definitions ? schema.definitions : {}; if (properties) { for (const propertyKey in properties) { let property = properties[propertyKey]; @@ -712,10 +779,10 @@ export class SchedulingSetCreate extends Component { if (refUrl.endsWith("/pointing")) { // For type pointing schema.definitions["pointing"] = (await $RefParser.resolve(refUrl)).get(newRef); property["$ref"] = newRef; - } else { // General object to resolve if any reference in child level + } else { // General object to resolve if any reference in child level property = await this.resolveSchema((await $RefParser.resolve(refUrl)).get(newRef)); } - } else if (property["type"] === "array") { // reference in array items definition + } else if (property["type"] === "array") { // reference in array items definition let resolvedItems = await this.resolveSchema(property["items"]); schema.definitions = {...schema.definitions, ...resolvedItems.definitions}; delete resolvedItems['definitions']; @@ -723,42 +790,43 @@ export class SchedulingSetCreate extends Component { } properties[propertyKey] = property; } - } else if (schema["oneOf"]) { // Reference in OneOf array + } else if (schema["oneOf"]) { // Reference in OneOf array let resolvedOneOfList = []; for (const oneOfProperty of schema["oneOf"]) { const resolvedOneOf = await this.resolveSchema(oneOfProperty); resolvedOneOfList.push(resolvedOneOf); } schema["oneOf"] = resolvedOneOfList; - } else if (schema["$ref"] && !schema["$ref"].startsWith("#")) { //reference in oneOf list item + } else if (schema["$ref"] && !schema["$ref"].startsWith("#")) { //reference in oneOf list item const refUrl = schema["$ref"]; let newRef = refUrl.substring(refUrl.indexOf("#")); if (refUrl.endsWith("/pointing")) { schema.definitions["pointing"] = (await $RefParser.resolve(refUrl)).get(newRef); schema["$ref"] = newRef; - } else { + } else { schema = await this.resolveSchema((await $RefParser.resolve(refUrl)).get(newRef)); } } return schema; } - + /** - * Function to prepare row data for ag-grid. + * Function to prepare row data for ag-grid. */ - async prepareScheduleUnitListForGrid(){ + async prepareScheduleUnitListForGrid() { this.custId = 1; - let defaultCommonRowData ={}; + let defaultCommonRowData = {}; let observStrategyContraints = this.state.observStrategy.template.scheduling_constraints_doc; - // Load selected fields of SUDrafts of the selected SU Set - let schedulingUnitList= await ScheduleService.getSchedulingBySet(this.state.selectedSchedulingSetId, this.SU_DRAFT_FIELDS, this.SU_DRAFT_EXPAND); + // Load selected fields of SUDrafts of the selected SU Set and filter the SUDrafts of selected observation strategy; + let schedulingUnitList = (await ScheduleService.getSchedulingBySet(this.state.selectedSchedulingSetId, this.SU_DRAFT_FIELDS, this.SU_DRAFT_EXPAND)) + .filter(su => su.observation_strategy_template_id === this.state.observStrategy.id) + .map(su => { + su.taskDrafts = su.task_drafts; + return su + }); + this.agSUWithDefaultValue = {'id': 0, 'suname': '', 'sudesc': '', isDirty: false}; - // Filter the SUDrafts of selected observation strategy - schedulingUnitList = _.filter(schedulingUnitList,{'observation_strategy_template_id': this.state.observStrategy.id}); - _.map(schedulingUnitList, (scheduleunit) => { - scheduleunit.taskDrafts = _.clone(scheduleunit.task_drafts); - return scheduleunit; - }); + /** Get Column details */ await this.createGridCellDetails(); let observationPropsList = []; @@ -766,91 +834,91 @@ export class SchedulingSetCreate extends Component { let totalSU = this.state.noOfSU; let lastRow = {}; //let hasSameValue = true; - this.agSUWithDefaultValue['scheduler'] = observStrategyContraints?.scheduler? observStrategyContraints.scheduler: this.resolvedConstraintSchema.properties.scheduler.default; - this.agSUWithDefaultValue['timeat'] = observStrategyContraints?.time?this.isNotEmpty(observStrategyContraints.time.at)?moment.utc(observStrategyContraints.time.at).format(UIConstants.CALENDAR_DATETIME_FORMAT): '':''; - this.agSUWithDefaultValue['timeafter'] = observStrategyContraints?.time?this.isNotEmpty(observStrategyContraints.time.after)?moment.utc(observStrategyContraints.time.after).format(UIConstants.CALENDAR_DATETIME_FORMAT):'': ''; - this.agSUWithDefaultValue['timebefore'] = observStrategyContraints?.time?this.isNotEmpty(observStrategyContraints.time.before)?moment.utc(observStrategyContraints.time.before).format(UIConstants.CALENDAR_DATETIME_FORMAT):'': ''; - if (observStrategyContraints?.time && observStrategyContraints?.time?.between){ + this.agSUWithDefaultValue['scheduler'] = observStrategyContraints?.scheduler ? observStrategyContraints.scheduler : this.resolvedConstraintSchema.properties.scheduler.default; + this.agSUWithDefaultValue['timeat'] = observStrategyContraints?.time ? this.isNotEmpty(observStrategyContraints.time.at) ? moment.utc(observStrategyContraints.time.at).format(UIConstants.CALENDAR_DATETIME_FORMAT) : '' : ''; + this.agSUWithDefaultValue['timeafter'] = observStrategyContraints?.time ? this.isNotEmpty(observStrategyContraints.time.after) ? moment.utc(observStrategyContraints.time.after).format(UIConstants.CALENDAR_DATETIME_FORMAT) : '' : ''; + this.agSUWithDefaultValue['timebefore'] = observStrategyContraints?.time ? this.isNotEmpty(observStrategyContraints.time.before) ? moment.utc(observStrategyContraints.time.before).format(UIConstants.CALENDAR_DATETIME_FORMAT) : '' : ''; + if (observStrategyContraints?.time && observStrategyContraints?.time?.between) { this.agSUWithDefaultValue['between'] = this.getBetweenStringValue(observStrategyContraints.time.between); } - if (observStrategyContraints?.time && observStrategyContraints?.time?.between){ + if (observStrategyContraints?.time && observStrategyContraints?.time?.between) { this.agSUWithDefaultValue['notbetween'] = this.getBetweenStringValue(observStrategyContraints.time.not_between); } - this.agSUWithDefaultValue['min_target_elevation'] = observStrategyContraints?.sky?.min_elevation && observStrategyContraints.sky.min_elevation.target?((observStrategyContraints.sky.min_elevation.target * 180) / Math.PI).toFixed(2):((this.resolvedConstraintSchema.properties.sky.properties.min_elevation.properties.target.default * 180) / Math.PI).toFixed(2); - this.agSUWithDefaultValue['min_calibrator_elevation'] = observStrategyContraints?.sky?.min_elevation && observStrategyContraints.sky.min_elevation.calibrator?((observStrategyContraints.sky.min_elevation.calibrator * 180) / Math.PI).toFixed(2):((this.resolvedConstraintSchema.properties.sky.properties.min_elevation.properties.calibrator.default * 180) / Math.PI).toFixed(2); - this.agSUWithDefaultValue['offset_from'] = observStrategyContraints?.sky?.transit_offset.from?(observStrategyContraints.sky.transit_offset.from<0?'-':'')+UnitConverter.getSecsToHHmmss(observStrategyContraints.sky.transit_offset.from):"00:00:00"; - this.agSUWithDefaultValue['offset_to'] = observStrategyContraints?.sky?.transit_offset.to?(observStrategyContraints.sky.transit_offset.to<0?'-':'')+UnitConverter.getSecsToHHmmss(observStrategyContraints.sky.transit_offset.to):"00:00:00"; + this.agSUWithDefaultValue['min_target_elevation'] = observStrategyContraints?.sky?.min_elevation && observStrategyContraints.sky.min_elevation.target ? ((observStrategyContraints.sky.min_elevation.target * 180) / Math.PI).toFixed(2) : ((this.resolvedConstraintSchema.properties.sky.properties.min_elevation.properties.target.default * 180) / Math.PI).toFixed(2); + this.agSUWithDefaultValue['min_calibrator_elevation'] = observStrategyContraints?.sky?.min_elevation && observStrategyContraints.sky.min_elevation.calibrator ? ((observStrategyContraints.sky.min_elevation.calibrator * 180) / Math.PI).toFixed(2) : ((this.resolvedConstraintSchema.properties.sky.properties.min_elevation.properties.calibrator.default * 180) / Math.PI).toFixed(2); + this.agSUWithDefaultValue['offset_from'] = observStrategyContraints?.sky?.transit_offset.from ? (observStrategyContraints.sky.transit_offset.from < 0 ? '-' : '') + UnitConverter.getSecsToHHmmss(observStrategyContraints.sky.transit_offset.from) : "00:00:00"; + this.agSUWithDefaultValue['offset_to'] = observStrategyContraints?.sky?.transit_offset.to ? (observStrategyContraints.sky.transit_offset.to < 0 ? '-' : '') + UnitConverter.getSecsToHHmmss(observStrategyContraints.sky.transit_offset.to) : "00:00:00"; if (this.resolvedConstraintSchema.properties.sky.properties.reference_pointing) { this.agSUWithDefaultValue['ref_pointing_enabled'] = observStrategyContraints?.sky?.reference_pointing?.enabled || this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.enabled.default; - this.agSUWithDefaultValue['ref_pointing_angle1'] = observStrategyContraints?.sky?.reference_pointing?.pointing?.angle1?UnitConverter.getAngleInput(observStrategyContraints.sky.reference_pointing.pointing.angle1,false):UnitConverter.getAngleInput(this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.pointing.default.angle1, false); - this.agSUWithDefaultValue['ref_pointing_angle2'] = observStrategyContraints?.sky?.reference_pointing?.pointing?.angle2?UnitConverter.getAngleInput(observStrategyContraints.sky.reference_pointing.pointing.angle2,true):UnitConverter.getAngleInput(this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.pointing.default.angle2, true); + this.agSUWithDefaultValue['ref_pointing_angle1'] = observStrategyContraints?.sky?.reference_pointing?.pointing?.angle1 ? UnitConverter.getAngleInput(observStrategyContraints.sky.reference_pointing.pointing.angle1, false) : UnitConverter.getAngleInput(this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.pointing.default.angle1, false); + this.agSUWithDefaultValue['ref_pointing_angle2'] = observStrategyContraints?.sky?.reference_pointing?.pointing?.angle2 ? UnitConverter.getAngleInput(observStrategyContraints.sky.reference_pointing.pointing.angle2, true) : UnitConverter.getAngleInput(this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.pointing.default.angle2, true); this.agSUWithDefaultValue['ref_pointing_direction_type'] = observStrategyContraints?.sky?.reference_pointing?.pointing?.direction_type || this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.pointing.default.direction_type; - this.agSUWithDefaultValue['ref_pointing_target'] = observStrategyContraints?.sky?.reference_pointing?.pointing?.target || this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.pointing.default.target|| this.resolvedConstraintSchema.definitions.pointing.properties.target.default; + this.agSUWithDefaultValue['ref_pointing_target'] = observStrategyContraints?.sky?.reference_pointing?.pointing?.target || this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.pointing.default.target || this.resolvedConstraintSchema.definitions.pointing.properties.target.default; } - this.agSUWithDefaultValue['md_sun'] = observStrategyContraints?.sky?.min_distance && !isNaN(observStrategyContraints.sky.min_distance.sun) ? ((observStrategyContraints.sky.min_distance.sun * 180)/Math.PI).toFixed(2): ((this.resolvedConstraintSchema.properties.sky.properties.min_distance.properties.sun.default * 180) / Math.PI).toFixed(2); - this.agSUWithDefaultValue['md_moon'] = observStrategyContraints?.sky?.min_distance && !isNaN(observStrategyContraints.sky.min_distance.moon) ? ((observStrategyContraints.sky.min_distance.moon * 180)/Math.PI).toFixed(2): ((this.resolvedConstraintSchema.properties.sky.properties.min_distance.properties.moon.default * 180) / Math.PI).toFixed(2); - this.agSUWithDefaultValue['md_jupiter'] = observStrategyContraints?.sky?.min_distance && !isNaN(observStrategyContraints.sky.min_distance.jupiter) ? ((observStrategyContraints.sky.min_distance.jupiter * 180)/Math.PI).toFixed(2): ((this.resolvedConstraintSchema.properties.sky.properties.min_distance.properties.jupiter.default * 180) / Math.PI).toFixed(2); - this.agSUWithDefaultValue['daily'] = this.fetchDailyFieldValue(observStrategyContraints?.daily? observStrategyContraints.daily: this.resolvedConstraintSchema.properties.daily); - - if(schedulingUnitList && schedulingUnitList.length > 0) { + this.agSUWithDefaultValue['md_sun'] = observStrategyContraints?.sky?.min_distance && !isNaN(observStrategyContraints.sky.min_distance.sun) ? ((observStrategyContraints.sky.min_distance.sun * 180) / Math.PI).toFixed(2) : ((this.resolvedConstraintSchema.properties.sky.properties.min_distance.properties.sun.default * 180) / Math.PI).toFixed(2); + this.agSUWithDefaultValue['md_moon'] = observStrategyContraints?.sky?.min_distance && !isNaN(observStrategyContraints.sky.min_distance.moon) ? ((observStrategyContraints.sky.min_distance.moon * 180) / Math.PI).toFixed(2) : ((this.resolvedConstraintSchema.properties.sky.properties.min_distance.properties.moon.default * 180) / Math.PI).toFixed(2); + this.agSUWithDefaultValue['md_jupiter'] = observStrategyContraints?.sky?.min_distance && !isNaN(observStrategyContraints.sky.min_distance.jupiter) ? ((observStrategyContraints.sky.min_distance.jupiter * 180) / Math.PI).toFixed(2) : ((this.resolvedConstraintSchema.properties.sky.properties.min_distance.properties.jupiter.default * 180) / Math.PI).toFixed(2); + this.agSUWithDefaultValue['daily'] = getDailyColumns(observStrategyContraints?.daily ? observStrategyContraints.daily : this.resolvedConstraintSchema.properties.daily); + + if (schedulingUnitList && schedulingUnitList.length > 0) { this.isNewSet = false; - for(const scheduleunit of schedulingUnitList){ + for (const scheduleunit of schedulingUnitList) { let observationProps = { id: scheduleunit.id, suname: scheduleunit.name, sudesc: scheduleunit.description, rank: scheduleunit.rank, priority_queue: scheduleunit.priority_queue_value, - //set default TRUE and it will reset this value while validating the row and will skip the invalid rows when save the row data + //set default TRUE and it will reset this value while validating the row and will skip the invalid rows when save the row data isValid: true, isDirty: false }; if (scheduleunit.observation_strategy_template_id) { let parameters = await this.getObservationValueFromTask(scheduleunit); let parametersName = Object.keys(parameters); - for(const parameter of parametersName){ + for (const parameter of parametersName) { let valueItem = parameters[parameter]; let excelColumns = this.state.columnMap[parameter]; if (excelColumns) { - let excelColumnsKeys = Object.keys(excelColumns); - for(const eColKey of excelColumnsKeys){ - if (eColKey === 'angle1') { + let excelColumnsKeys = Object.keys(excelColumns); + for (const eColKey of excelColumnsKeys) { + if (eColKey === 'angle1') { observationProps[excelColumns[eColKey]] = UnitConverter.getAngleInput(valueItem[eColKey], false); - } else if (eColKey === 'angle2') { + } else if (eColKey === 'angle2') { observationProps[excelColumns[eColKey]] = UnitConverter.getAngleInput(valueItem[eColKey], true); - } else if (_.includes(this.durationList, eColKey)) { + } else if (_.includes(this.durationList, eColKey)) { observationProps[excelColumns[eColKey]] = UnitConverter.getSecsToHHmmss(valueItem, true); - } else if (_.includes(this.stationGroupsName, eColKey)) { + } else if (_.includes(this.stationGroupsName, eColKey)) { observationProps[excelColumns[eColKey]] = await this.updateStationGroups(scheduleunit, eColKey); - } else { + } else { let keys = Object.keys(valueItem); - if(_.includes(keys, eColKey)) { + if (_.includes(keys, eColKey)) { observationProps[excelColumns[eColKey]] = valueItem[eColKey]; - } else { + } else { observationProps[excelColumns[eColKey]] = valueItem; } } } } } - } else { + } else { let parameters = this.state.observStrategy.template.parameters; - for(const parameter of parameters){ + for (const parameter of parameters) { let refUrl = parameter['refs']; - let valueItem = (await $RefParser.resolve( this.state.observStrategy.template)).get(refUrl[0]); + let valueItem = (await $RefParser.resolve(this.state.observStrategy.template)).get(refUrl[0]); let excelColumns = this.state.columnMap[parameter.name]; if (excelColumns) { - let excelColumnsKeys = Object.keys(excelColumns); - for(const eColKey of excelColumnsKeys){ - if (eColKey === 'angle1') { + let excelColumnsKeys = Object.keys(excelColumns); + for (const eColKey of excelColumnsKeys) { + if (eColKey === 'angle1') { observationProps[excelColumns[eColKey]] = UnitConverter.getAngleInput(valueItem[eColKey], false); - } else if (eColKey === 'angle2') { + } else if (eColKey === 'angle2') { observationProps[excelColumns[eColKey]] = UnitConverter.getAngleInput(valueItem[eColKey], true); - } else if (_.includes(this.durationList, eColKey)) { + } else if (_.includes(this.durationList, eColKey)) { observationProps[excelColumns[eColKey]] = UnitConverter.getSecsToHHmmss(valueItem[eColKey], true); - } else if (_.includes(this.stationGroupsName, eColKey)) { + } else if (_.includes(this.stationGroupsName, eColKey)) { observationProps[excelColumns[eColKey]] = await this.updateStationGroups(scheduleunit, eColKey); - } else { + } else { observationProps[excelColumns[eColKey]] = valueItem[eColKey]; } } @@ -858,40 +926,41 @@ export class SchedulingSetCreate extends Component { } } // Get Station details - // observationProps['stations'] = await this.getStationGrops(scheduleunit); - let constraint = scheduleunit.id?scheduleunit.scheduling_constraints_doc:null; - if (constraint){ - if (constraint.scheduler){ + // observationProps['stations'] = await this.getStationGrops(scheduleunit); + let constraint = scheduleunit.id ? scheduleunit.scheduling_constraints_doc : null; + if (constraint) { + if (constraint.scheduler) { observationProps['scheduler'] = constraint.scheduler; } - observationProps['timeat'] = this.isNotEmpty(constraint.time.at)?moment.utc(constraint.time.at).format(UIConstants.CALENDAR_DATETIME_FORMAT): ''; - observationProps['timeafter'] = this.isNotEmpty(constraint.time.after)?moment.utc(constraint.time.after).format(UIConstants.CALENDAR_DATETIME_FORMAT):''; - observationProps['timebefore'] = this.isNotEmpty(constraint.time.before)?moment.utc(constraint.time.before).format(UIConstants.CALENDAR_DATETIME_FORMAT):''; - if (constraint.time.between){ + + observationProps['timeat'] = this.isNotEmpty(constraint.time?.at) ? moment.utc(constraint.time.at).format(UIConstants.CALENDAR_DATETIME_FORMAT) : ''; + observationProps['timeafter'] = this.isNotEmpty(constraint.time?.after) ? moment.utc(constraint.time.after).format(UIConstants.CALENDAR_DATETIME_FORMAT) : ''; + observationProps['timebefore'] = this.isNotEmpty(constraint.time?.before) ? moment.utc(constraint.time.before).format(UIConstants.CALENDAR_DATETIME_FORMAT) : ''; + if (constraint.time?.between) { observationProps['between'] = this.getBetweenStringValue(constraint.time.between); } - if (constraint.time.not_between){ + if (constraint.time?.not_between) { observationProps['notbetween'] = this.getBetweenStringValue(constraint.time.not_between); } - - observationProps['daily'] = this.fetchDailyFieldValue(constraint.daily); + + observationProps['daily'] = getDailyColumns(constraint.daily); UnitConverter.radiansToDegree(constraint.sky); - if ( constraint.sky.min_elevation ) { + if (constraint.sky.min_elevation) { observationProps['min_target_elevation'] = constraint.sky.min_elevation.target; observationProps['min_calibrator_elevation'] = constraint.sky.min_elevation.calibrator; } - if ( constraint.sky.transit_offset ){ - observationProps['offset_from'] = constraint.sky.transit_offset.from?(constraint.sky.transit_offset.from<0?'-':'')+UnitConverter.getSecsToHHmmss(constraint.sky.transit_offset.from):"00:00:00"; - observationProps['offset_to'] = constraint.sky.transit_offset.to?(constraint.sky.transit_offset.to<0?'-':'')+UnitConverter.getSecsToHHmmss(constraint.sky.transit_offset.to):"00:00:00"; + if (constraint.sky.transit_offset) { + observationProps['offset_from'] = constraint.sky.transit_offset.from ? (constraint.sky.transit_offset.from < 0 ? '-' : '') + UnitConverter.getSecsToHHmmss(constraint.sky.transit_offset.from) : "00:00:00"; + observationProps['offset_to'] = constraint.sky.transit_offset.to ? (constraint.sky.transit_offset.to < 0 ? '-' : '') + UnitConverter.getSecsToHHmmss(constraint.sky.transit_offset.to) : "00:00:00"; observationProps['offset_from_max'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.maximum; - observationProps['offset_from_min'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.minimum; + observationProps['offset_from_min'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.minimum; observationProps['offset_to_max'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.maximum; observationProps['offset_to_min'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.minimum; } - if (constraint.sky.min_distance){ + if (constraint.sky.min_distance) { observationProps['md_sun'] = constraint.sky.min_distance.sun;//constraint.sky.min_distance.sun:0; - observationProps['md_moon'] = constraint.sky.min_distance.moon; //constraint.sky.min_distance.moon:0; - observationProps['md_jupiter'] = constraint.sky.min_distance.jupiter;//constraint.sky.min_distance.jupiter:0; + observationProps['md_moon'] = constraint.sky.min_distance.moon; //constraint.sky.min_distance.moon:0; + observationProps['md_jupiter'] = constraint.sky.min_distance.jupiter;//constraint.sky.min_distance.jupiter:0; } if (constraint.sky.reference_pointing) { observationProps['ref_pointing_enabled'] = constraint.sky.reference_pointing.enabled; @@ -899,7 +968,7 @@ export class SchedulingSetCreate extends Component { observationProps['ref_pointing_angle2'] = UnitConverter.getAngleInput(constraint.sky.reference_pointing.pointing.angle2, true); observationProps['ref_pointing_direction_type'] = constraint.sky.reference_pointing.pointing.direction_type; observationProps['ref_pointing_target'] = constraint.sky.reference_pointing.pointing.target; - } else { + } else { observationProps['ref_pointing_enabled'] = this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.enabled.default; observationProps['ref_pointing_angle1'] = UnitConverter.getAngleInput(this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.pointing.default.angle1, false); observationProps['ref_pointing_angle2'] = UnitConverter.getAngleInput(this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.pointing.default.angle2, true); @@ -913,106 +982,108 @@ export class SchedulingSetCreate extends Component { if (_.isEmpty(lastRow)) { lastRow = observationProps; defaultCommonRowData = _.cloneDeep(observationProps); - } else /* if (!_.isEqual( + } else /* if (!_.isEqual( _.omit(lastRow, ['id']), _.omit(observationProps, ['id']) - )) */ { + )) */ { const keys = Object.keys(lastRow); for (const key of keys) { if (key === 'daily') { // console.log("key =>",key,lastRow[key], observationProps[key]) } - if ( !_.isEqual(lastRow[key], observationProps[key])) { - defaultCommonRowData[key] = ''; + if (!_.isEqual(lastRow[key], observationProps[key])) { + defaultCommonRowData[key] = ''; } - } + } //hasSameValue = false; } } - } else { + } else { this.isNewSet = true; } - /* let defaultCommonRowData = {}; - if (hasSameValue) { - defaultCommonRowData = observationPropsList[observationPropsList.length-1]; - }*/ + /* let defaultCommonRowData = {}; + if (hasSameValue) { + defaultCommonRowData = observationPropsList[observationPropsList.length-1]; + }*/ this.tmpRowData = observationPropsList; // find No. of rows filled in array let totalCount = this.tmpRowData.length; - // Prepare No. Of SU for rows for UI - if (this.tmpRowData && this.tmpRowData.length > 0){ + // Prepare No. Of SU for rows for UI + if (this.tmpRowData && this.tmpRowData.length > 0) { const paramsOutputKey = Object.keys(this.tmpRowData[0]); let availableCount = this.tmpRowData.length; - if(this.isNewSet) { + if (this.isNewSet) { availableCount = 0; this.tmpRowData = []; - } - if (availableCount >= totalSU){ - totalSU = availableCount+1; } - for(var i = availableCount; i<totalSU; i++){ - let emptyRow = {}; - paramsOutputKey.forEach(key =>{ - if (key === 'id'){ + if (availableCount >= totalSU) { + totalSU = availableCount + 1; + } + for (var i = availableCount; i < totalSU; i++) { + let emptyRow = {}; + paramsOutputKey.forEach(key => { + if (key === 'id') { emptyRow[key] = 0; - } else { + } else { emptyRow[key] = ''; } }); let tmpRow = _.cloneDeep(this.state.agSUWithDefaultValue); tmpRow['custId'] = this.custId++; this.tmpRowData.push(tmpRow); - } - } else { + } + } else { let availableCount = this.tmpRowData.length; - for(let i = availableCount; i<totalSU; i++){ + for (let i = availableCount; i < totalSU; i++) { let tmpRow = _.cloneDeep(this.state.agSUWithDefaultValue); tmpRow['custId'] = this.custId++; this.tmpRowData.push(tmpRow); - } + } } - if(this.isNewSet) { - defaultCommonRowData = this.tmpRowData[this.tmpRowData.length-1]; + if (this.isNewSet) { + defaultCommonRowData = this.tmpRowData[this.tmpRowData.length - 1]; } this.setState({ schedulingUnitList: schedulingUnitList, rowData: this.tmpRowData, totalCount: totalCount, noOfSU: this.tmpRowData.length, - emptyRow: this.tmpRowData[this.tmpRowData.length-1], + emptyRow: this.tmpRowData[this.tmpRowData.length - 1], isAGLoading: false, commonRowData: [defaultCommonRowData], defaultCommonRowData: defaultCommonRowData, isFetchingData: true - // hasSameValue: hasSameValue + // hasSameValue: hasSameValue }); if (this.state.gridApi) { this.state.gridApi.setRowData(this.state.rowData); } // Lazy fetching of specifications_doc so that grid loads faster and data required while updating is loaded later ScheduleService.getSchedulingBySet(this.state.selectedSchedulingSetId, ["id", "specifications_doc"], null) - .then (specList => { - let rowData = this.state.schedulingUnitList; - rowData = _.map(rowData, data => { - let specData = _.find(specList, {id: data.id}); - data.specifications_doc = specData?specData.specifications_doc:{}; - return data; - }); - this.setState({schedulingUnitList: rowData, isFetchingData: false}); - }); + .then(specList => { + let rowData = this.state.schedulingUnitList; + rowData = _.map(rowData, data => { + let specData = _.find(specList, {id: data.id}); + data.specifications_doc = specData ? specData.specifications_doc : {}; + return data; + }); + this.setState({schedulingUnitList: rowData, isFetchingData: false}); + }); } /** * Get Station details from Scheduling Unit - * @param {*} schedulingUnit + * @param {*} schedulingUnit */ - async updateStationGroups(schedulingUnit, taskName){ + async updateStationGroups(schedulingUnit, taskName) { let stationValue = ''; - if (schedulingUnit && schedulingUnit.id>0) { + if (schedulingUnit && schedulingUnit.id > 0) { if (schedulingUnit && schedulingUnit.observation_strategy_template_id) { - let paramterRefs = this.state.observStrategy.template.parameters.find(parameter => {return parameter.name === taskName}); + let paramterRefs = this.state.observStrategy.template.parameters.find(parameter => { + return parameter.name === taskName + }); let taskPaths = paramterRefs.refs[0].split("/"); stationValue = this.getStationGroupForCell(taskPaths[2]); } @@ -1020,16 +1091,18 @@ export class SchedulingSetCreate extends Component { return stationValue; } - getStationGroupForCell (taskName) { + getStationGroupForCell(taskName) { let stationValue = ''; - let task = this.currentTaskDrafts.find(task => {return task.name === taskName}); + let task = this.currentTaskDrafts.find(task => { + return task.name === taskName + }); let stationGroups = task ? task.specifications_doc.station_configuration?.station_groups : []; if (stationGroups) { - stationGroups.map(stationGroup =>{ - stationValue += stationGroup.stations+':'+stationGroup.max_nr_missing+"|"; + stationGroups.map(stationGroup => { + stationValue += stationGroup.stations + ':' + stationGroup.max_nr_missing + "|"; return stationGroup; }); - return stationValue; + return stationValue; } return []; } @@ -1038,7 +1111,7 @@ export class SchedulingSetCreate extends Component { /** * Get Observation details from Scheduling->Task * @param {Object} scheduleunit - Scheduling Unit - * @returns + * @returns */ async getObservationValueFromTask(scheduleunit) { //let taskDrafts = []; @@ -1047,14 +1120,14 @@ export class SchedulingSetCreate extends Component { this.currentTaskDrafts = _.clone(scheduleunit.taskDrafts); scheduleunit.task_drafts = _.map(scheduleunit.taskDrafts, "url"); } - const observStrategy = _.cloneDeep(_.find(this.observStrategies, {'id': scheduleunit.observation_strategy_template_id})); + const observStrategy = _.cloneDeep(_.find(this.state.observStrategies, {'id': scheduleunit.observation_strategy_template_id})); ParserUtility.addStationParameters(observStrategy); - const tasks = observStrategy.template.tasks; + const tasks = observStrategy.template.tasks; let paramsOutput = []; - //let schema = { type: 'object', additionalProperties: false, - // properties: {}, definitions:{} - // }; - for (const taskName in tasks) { + //let schema = { type: 'object', additionalProperties: false, + // properties: {}, definitions:{} + // }; + for (const taskName in tasks) { const task = tasks[taskName]; const taskDraft = this.currentTaskDrafts.find(taskD => taskD.name === taskName); if (taskDraft) { @@ -1070,302 +1143,226 @@ export class SchedulingSetCreate extends Component { const $taskRefs = await $RefParser.resolve(task); // Identify the task specification template of every task in the strategy template - const taskTemplate = _.find(this.taskTemplates, {'name': task['specifications_template']['name'], - 'version': task.specifications_template.version}); + const taskTemplate = _.find(this.taskTemplates, { + 'name': task['specifications_template']['name'], + 'version': task.specifications_template.version + }); if (!taskTemplate) { - this.growl.show({severity: 'error', summary: 'Task template missing', detail: 'Unable to find task template defined in the strategy template'}); - } + this.growl.show({ + severity: 'error', + summary: 'Task template missing', + detail: 'Unable to find task template defined in the strategy template' + }); + } //schema['$id'] = taskTemplate.schema['$id']; //schema['$schema'] = taskTemplate.schema['$schema']; const taskParams = _.filter(observStrategy.template.parameters, param => param.refs[0].indexOf(`/tasks/${taskName}`) > 0); for (const param of taskParams) { // Resolve the identified template //const $templateRefs = await $RefParser.resolve(taskTemplate); - let property = { }; + let property = {}; let tempProperty = null; const taskPaths = param.refs[0].split("/"); - tempProperty = _.cloneDeep(taskTemplate.ref_resolved_schema.properties[taskPaths.length === 4 ?taskPaths[3]:taskPaths[4]]); + tempProperty = _.cloneDeep(taskTemplate.ref_resolved_schema.properties[taskPaths.length === 4 ? taskPaths[3] : taskPaths[4]]); if (tempProperty && tempProperty.type === 'array') { tempProperty = tempProperty.items.properties[taskPaths[6]]; } property = tempProperty; - if(property) { + if (property) { property.title = param.name; - } else { + } else { property = {}; property.title = param.name; } try { if (_.endsWith(param.refs[0], 'short_description')) { - property.default = taskDraft['short_description']? taskDraft['short_description']: ''; - } else { + property.default = taskDraft['short_description'] ? taskDraft['short_description'] : ''; + } else { property.default = $taskRefs.get(param.refs[0].replace(`#/tasks/${taskName}`, '#')); } - + //if ( param.name === 'Duration') { - // paramsOutput[param.name] = property.default; + // paramsOutput[param.name] = property.default; // } else { - paramsOutput[param.name] = property.default; + paramsOutput[param.name] = property.default; // } - } catch(err) {} + } catch (err) { + } } } - return paramsOutput; + return paramsOutput; } /** * Define AG Grid column properties - */ + */ getAGGridAngelColumnsDefinition(schema) { let cellProps = []; - cellProps['angle1'] = {/*isgroup: true,*/ type:'numberValueColumn', cellRenderer: 'timeInputMask',cellEditor: 'timeInputMask', valueSetter: 'valueSetter', cellStyle: function(params) { - if (params.value && !Validator.validateTimeAndAngle(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - },}; - cellProps['angle2'] = {/*isgroup: true,*/ type:'numberValueColumn', cellRenderer: 'degreeInputMask',cellEditor: 'degreeInputMask', valueSetter: 'valueSetter' , cellStyle: function(params) { - if (params.value && !Validator.validateAngle(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - }, }; - cellProps['angle3'] = {/*isgroup: true,*/ cellEditor: 'numericEditor',cellStyle: function(params) { - if (isNaN(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - }}; - //this.getColumnDefinition('direction_type', {type: 'string', enum: schema.definitions.pointing.properties.direction_type.enum, multiselect: false}); - cellProps['direction_type'] = {/*isgroup: true,*/ cellEditor: 'agSelectCellEditor',default: schema.definitions.pointing.properties.direction_type.default, - cellEditorParams: { - values: schema.definitions.pointing.properties.direction_type.enum, - }, - }; - //this.getColumnDefinition('filter', {type: 'string', enum: schema.definitions.filter.enum, multiselect: false}); - cellProps['filter'] = {/*isgroup: false,*/ cellEditor: 'agSelectCellEditor',default: schema.definitions.filter.default, - cellEditorParams: { - values: schema.definitions.filter.enum, - }, - }; - //this.getColumnDefinition('antenna_set', {type: 'string', enum: schema.definitions.antenna_set.enum, multiselect: false}); - cellProps['antenna_set'] = {/*isgroup: false,*/ cellEditor: 'agSelectCellEditor',default: schema.definitions.antenna_set.default, - cellEditorParams: { - values: schema.definitions.antenna_set.enum, + cellProps['angle1'] = {/*isgroup: true,*/ + type: 'numberValueColumn', + cellRenderer: 'timeInputMask', + cellEditor: 'timeInputMask', + valueSetter: 'valueSetter', + cellStyle: function (params) { + if (params.value && !Validator.validateTimeAndAngle(params.value)) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } }, }; - this.durationList = Object.keys(schema.properties).filter( durationKey => _.endsWith(schema.properties[durationKey]['$ref'],'timedelta' )); - if (this.durationList) { - for (const durationKey of this.durationList) { - this.getColumnDefinition(durationKey+'~'+durationKey, {type: 'string', format: 'hh:mm:ss', - schema: {minimum: schema.properties[durationKey]['minimum'] || 0, - maximum: schema.properties[durationKey]['maximum'] || 86400}}); - cellProps[durationKey+'~'+durationKey] = { cellEditor:'offsetTimeInputmask'}; - } - } - - this.getColumnDefinition('Beamformers~Beamformers', {type: 'object', format: 'json'}); - cellProps['beamformers'] = { cellRenderer: 'beamformersRenderer', cellEditor:'beamformer' }; - for (const paramKey of _.keys(schema.properties)) { - const param = schema.properties[paramKey]; - if (param.type) { - const paramType = param.type.toLowerCase(); - if (paramType === 'integer') { - cellProps[paramKey.toLowerCase()] = {/*isgroup: false,*/ cellEditor: 'numericEditor',cellStyle: function(params) { - if (isNaN(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - }}; - } else if (paramType === 'boolean') { - cellProps[paramKey.toLowerCase()] = {/*isgroup: false,*/ cellEditor: 'agSelectCellEditor',default: param.default?param.default:false, - cellEditorParams: { - values: [true, false], - }, - }; - } else if ( paramType === 'object' && param.properties) { - for (const subProp of Object.keys(param.properties)) { - if (param.properties[subProp].type && param.properties[subProp].type === 'boolean') { - cellProps[subProp.toLowerCase()] = { cellEditor: 'agSelectCellEditor', default: param.default && param.default[subProp] ? param.default[subProp] : false, - cellEditorParams: { values: [true, false]}, 'headerName': param.properties[subProp].title, 'headerTooltip': param.properties[subProp].title,} - } else if (param.properties[subProp].type && param.properties[subProp].type === 'integer' ) { - cellProps[subProp.toLowerCase()] = { cellEditor: 'numericEditor',cellStyle: function(params) { - if (isNaN(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - }, 'headerName': param.properties[subProp].title, 'headerTooltip': param.properties[subProp].title, type: 'integer', - maximum : param.properties[subProp].maximum?param.properties[subProp].maximum:undefined, minimum: param.properties[subProp].minimum?param.properties[subProp].minimum:undefined, - default: param.default && param.default[subProp] ? param.default[subProp] : undefined}; - } else if (param.properties[subProp].type && param.properties[subProp].type === 'number' ) { - cellProps[subProp.toLowerCase()] = { cellStyle: function(params) { - if (isNaN(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - }, 'headerName': param.properties[subProp].title, 'headerTooltip': param.properties[subProp].title, type: 'number', - maximum : param.properties[subProp].maximum?param.properties[subProp].maximum:undefined, minimum: param.properties[subProp].minimum?param.properties[subProp].minimum:undefined, - default: param.default && param.default[subProp] ? param.default[subProp] : undefined}; - } else { - cellProps[subProp.toLowerCase()] = { default: param.default && param.default[subProp] ? param.default[subProp] : undefined} - } - } + cellProps['angle2'] = {/*isgroup: true,*/ + type: 'numberValueColumn', + cellRenderer: 'degreeInputMask', + cellEditor: 'degreeInputMask', + valueSetter: 'valueSetter', + cellStyle: function (params) { + if (params.value && !Validator.validateAngle(params.value)) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; } - } - } - return cellProps; - } - - /* - getAGGridAngelColumnsDefinition_NeedToAlter(schema) { - let agCellDefs = []; - let cellProps = []; - const properties = Object.keys(schema.properties); - for (const property of properties) { - const schemaProp = schema.properties[property] - if (schemaProp.$ref) { - let refPath = schemaProp.$ref.split("/"); - refPath.reverse(); - const definition = schema.definition[refPath[0]]; - if (definition.$ref) { - - } else if (definition.properties) { - agCellDefs['headerName'] = schemaProp.title; - agCellDefs['headerTooltip'] = schemaProp.title; - for ( const defProp of Object.keys(definition.properties)) { - if (defProp === 'angle1') { - let childCellProps = { type:'numberValueColumn', cellRenderer: 'timeInputMask',cellEditor: 'timeInputMask', valueSetter: 'valueSetter', cellStyle: function(params) { - if (params.value && !Validator.validateTime(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - },}; - agCellDefs['children'] = childCellProps; - } else if (defProp === 'angle2') { - let childCellProps = {type:'numberValueColumn', cellRenderer: 'degreeInputMask',cellEditor: 'degreeInputMask', valueSetter: 'valueSetter' , cellStyle: function(params) { - if (params.value && !Validator.validateAngle(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - }, }; - agCellDefs['children'] = childCellProps; - } - } - } else { - + }, + }; + cellProps['angle3'] = {/*isgroup: true,*/ cellEditor: 'numericEditor', cellStyle: function (params) { + if (isNaN(params.value)) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; } - - } else if ( schemaProp.properties) { - - } else if (schemaProp.type) { - - } else { - } - } - cellProps['angle1'] = {type:'numberValueColumn', cellRenderer: 'timeInputMask',cellEditor: 'timeInputMask', valueSetter: 'valueSetter', cellStyle: function(params) { - if (params.value && !Validator.validateTime(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - },}; - cellProps['angle2'] = {type:'numberValueColumn', cellRenderer: 'degreeInputMask',cellEditor: 'degreeInputMask', valueSetter: 'valueSetter' , cellStyle: function(params) { - if (params.value && !Validator.validateAngle(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - }, }; - cellProps['angle3'] = {cellEditor: 'numericEditor',cellStyle: function(params) { - if (isNaN(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; - } - }}; + }; //this.getColumnDefinition('direction_type', {type: 'string', enum: schema.definitions.pointing.properties.direction_type.enum, multiselect: false}); - cellProps['direction_type'] = { cellEditor: 'agSelectCellEditor',default: schema.definitions.pointing.properties.direction_type.default, + cellProps['direction_type'] = {/*isgroup: true,*/ + cellEditor: 'agSelectCellEditor', + default: schema.definitions.pointing.properties.direction_type.default, cellEditorParams: { values: schema.definitions.pointing.properties.direction_type.enum, - }, + }, }; //this.getColumnDefinition('filter', {type: 'string', enum: schema.definitions.filter.enum, multiselect: false}); - cellProps['filter'] = { cellEditor: 'agSelectCellEditor',default: schema.definitions.filter.default, - cellEditorParams: { - values: schema.definitions.filter.enum, - }, + cellProps['filter'] = {/*isgroup: false,*/ + cellEditor: 'agSelectCellEditor', + default: schema.definitions.filter.default, + cellEditorParams: { + values: schema.definitions.filter.enum, + }, }; //this.getColumnDefinition('antenna_set', {type: 'string', enum: schema.definitions.antenna_set.enum, multiselect: false}); - cellProps['antenna_set'] = { cellEditor: 'agSelectCellEditor',default: schema.definitions.antenna_set.default, - cellEditorParams: { - values: schema.definitions.antenna_set.enum, + cellProps['antenna_set'] = {/*isgroup: false,*/ + cellEditor: 'agSelectCellEditor', + default: schema.definitions.antenna_set.default, + cellEditorParams: { + values: schema.definitions.antenna_set.enum, }, }; - this.durationList = Object.keys(schema.properties).filter( durationKey => _.endsWith(schema.properties[durationKey]['$ref'],'timedelta' )); + this.durationList = Object.keys(schema.properties).filter(durationKey => _.endsWith(schema.properties[durationKey]['$ref'], 'timedelta')); if (this.durationList) { - for (const durationKey of this.durationList) { - this.getColumnDefinition(durationKey+'~'+durationKey, {type: 'string', format: 'hh:mm:ss', - schema: {minimum: schema.properties[durationKey]['minimum'] || 0, - maximum: schema.properties[durationKey]['maximum'] || 86400}}); - cellProps[durationKey+'~'+durationKey] = { cellEditor:'offsetTimeInputmask'}; + for (const durationKey of this.durationList) { + this.getColumnDefinition(durationKey + '~' + durationKey, { + type: 'string', format: 'hh:mm:ss', + schema: { + minimum: schema.properties[durationKey]['minimum'] || 0, + maximum: schema.properties[durationKey]['maximum'] || 86400 + } + }); + cellProps[durationKey + '~' + durationKey] = {cellEditor: 'offsetTimeInputmask'}; } } - + this.getColumnDefinition('Beamformers~Beamformers', {type: 'object', format: 'json'}); - cellProps['beamformers'] = { cellRenderer: 'beamformersRenderer', cellEditor:'beamformer' }; + cellProps['beamformers'] = {cellRenderer: 'beamformersRenderer', cellEditor: 'beamformer'}; for (const paramKey of _.keys(schema.properties)) { const param = schema.properties[paramKey]; if (param.type) { const paramType = param.type.toLowerCase(); if (paramType === 'integer') { - cellProps[paramKey.toLowerCase()] = { cellEditor: 'numericEditor',cellStyle: function(params) { - if (isNaN(params.value)) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; + cellProps[paramKey.toLowerCase()] = {/*isgroup: false,*/ + cellEditor: 'numericEditor', + cellStyle: function (params) { + if (isNaN(params.value)) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } } - }}; - } else if (paramType === 'boolean') { - cellProps[paramKey.toLowerCase()] = { cellEditor: 'agSelectCellEditor',default: param.default?param.default:false, + }; + } else if (paramType === 'boolean') { + cellProps[paramKey.toLowerCase()] = {/*isgroup: false,*/ + cellEditor: 'agSelectCellEditor', + default: param.default ? param.default : false, cellEditorParams: { values: [true, false], - }, + }, }; + } else if (paramType === 'object' && param.properties) { + for (const subProp of Object.keys(param.properties)) { + if (param.properties[subProp].type && param.properties[subProp].type === 'boolean') { + cellProps[subProp.toLowerCase()] = { + cellEditor: 'agSelectCellEditor', + default: param.default && param.default[subProp] ? param.default[subProp] : false, + cellEditorParams: {values: [true, false]}, + 'headerName': param.properties[subProp].title, + 'headerTooltip': param.properties[subProp].title, + } + } else if (param.properties[subProp].type && param.properties[subProp].type === 'integer') { + cellProps[subProp.toLowerCase()] = { + cellEditor: 'numericEditor', + cellStyle: function (params) { + if (isNaN(params.value)) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } + }, + 'headerName': param.properties[subProp].title, + 'headerTooltip': param.properties[subProp].title, + type: 'integer', //TODO: this is not allowed + maximum: param.properties[subProp].maximum ? param.properties[subProp].maximum : undefined, + minimum: param.properties[subProp].minimum ? param.properties[subProp].minimum : undefined, + default: param.default && param.default[subProp] ? param.default[subProp] : undefined + }; + } else if (param.properties[subProp].type && param.properties[subProp].type === 'number') { + cellProps[subProp.toLowerCase()] = { + cellStyle: function (params) { + if (isNaN(params.value)) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } + }, + 'headerName': param.properties[subProp].title, + 'headerTooltip': param.properties[subProp].title, + type: 'number', //TODO: not allowed + maximum: param.properties[subProp].maximum ? param.properties[subProp].maximum : undefined, + minimum: param.properties[subProp].minimum ? param.properties[subProp].minimum : undefined, + default: param.default && param.default[subProp] ? param.default[subProp] : undefined + }; + } else { + cellProps[subProp.toLowerCase()] = {default: param.default && param.default[subProp] ? param.default[subProp] : undefined} + } + } } } } return cellProps; } - */ /** - * - * @param {*} predefineCellProps - * @param {*} childCellProps - * @param {*} cellName - * @returns + * + * @param {*} predefineCellProps + * @param {*} childCellProps + * @param {*} cellName + * @returns */ getAGGridAngelColumnsProperty(predefineCellProps, childCellProps, cellName) { - //cellName = _.lowerCase(cellName); let cellProperty = predefineCellProps[cellName]; - if(cellProperty) { - let cellKeys = Object.keys(cellProperty); - for(const cellKey of cellKeys){ + if (cellProperty) { + let cellKeys = Object.keys(cellProperty); + for (const cellKey of cellKeys) { childCellProps[cellKey] = predefineCellProps[cellName][cellKey]; - }; - } else { - // let defaultProp = {editable: true, cellRenderer: 'betweenRenderer',cellEditor: 'agDateInput', valueSetter: 'newValueSetter'}; - // childCellProps = Object.assign(childCellProps, defaultProp); + } } return childCellProps; } @@ -1375,49 +1372,75 @@ export class SchedulingSetCreate extends Component { let colProperty = {}; this.colKeyOrder = []; let columnDefs = [ - { // Row Index - headerName: '#', - editable: false, - maxWidth: 60, - cellRenderer: 'rowIdRenderer', - pinned: 'left', - lockPosition: true, - suppressSizeToFit: true, - tooltip: 'Testing' + { // Row Index + headerName: '#', + editable: false, + maxWidth: 60, + cellRenderer: 'rowIdRenderer', + pinned: 'left', + lockPosition: true, + suppressSizeToFit: true, + headerTooltip: 'Testing' + }, + { + headerName: 'Scheduling Unit', headerTooltip: 'Scheduling Unit', children: [ + {headerName: 'Name', field: 'suname', headerTooltip: "Name"}, + { + headerName: 'Description', + field: 'sudesc', + headerTooltip: "Description", + cellStyle: function (params) { + if (params.data && params.data.suname && (params.data.suname !== '' && (!params.value || params.value === ''))) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } + }, }, - {headerName: 'Scheduling Unit', headerTooltip: 'Scheduling Unit', children: [ - {headerName: 'Name', field: 'suname', headerTooltip: "Name"}, - {headerName: 'Description', field: 'sudesc', headerTooltip: "Description", cellStyle: function(params) { - if (params.data && params.data.suname && (params.data.suname !== '' && (!params.value || params.value === ''))) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''};} - },}, - {headerName: 'Rank', field: 'rank',cellEditor: 'numericEditor', headerTooltip: "Rank", cellStyle: function(params) { - let value = params.data.rank? params.data.rank: params.data.gdef_rank ? params.data.gdef_rank : ''; + { + headerName: 'Rank', + field: 'rank', + cellEditor: 'numericEditor', + headerTooltip: "Rank", + cellStyle: function (params) { + let value = params.data.rank ? params.data.rank : params.data.gdef_rank ? params.data.gdef_rank : ''; if (value && value !== '') { - const splitValue = _.split((value+''),"."); + const splitValue = _.split((value + ''), "."); if (value < 0 || value > 1 || (splitValue.length > 1 && splitValue[1].length > 4)) { return {backgroundColor: BG_COLOR}; - } else { - return {backgroundColor: ''};} - } else { - return {backgroundColor: ''};} + } else { + return {backgroundColor: ''}; + } + } else { + return {backgroundColor: ''}; + } }, - valueFormatter: function(params) { - if(params.value !== '') { + valueFormatter: function (params) { + if (params.value !== '') { const value = parseFloat(params.value); - return value >= 0?value.toFixed(4):params.value; - } else { + return value >= 0 ? value.toFixed(4) : params.value; + } else { return "0.0000"; } - } }, - {headerName: 'Priority Queue', headerTooltip: "Priority Queue", field: 'priority_queue',cellEditor: 'agSelectCellEditor', - cellEditorParams: {values: this.state.priorityQueuelist}},] - } - ]; + } + }, + { + headerName: 'Priority Queue', + headerTooltip: "Priority Queue", + field: 'priority_queue', + cellEditor: 'agSelectCellEditor', + cellEditorParams: {values: this.state.priorityQueuelist} + },] + } + ]; //this.columnDefinition = {'priority_queue': {'required': false, 'type': 'string', 'enum': ['A', 'B'], multiselect: false}} - colProperty = {'ID':'id', 'Name':'suname', 'Description':'sudesc', 'Rank':'rank', 'Priority Queue':'priority_queue'}; + colProperty = { + 'ID': 'id', + 'Name': 'suname', + 'Description': 'sudesc', + 'Rank': 'rank', + 'Priority Queue': 'priority_queue' + }; columnMap['Scheduling Unit'] = colProperty; this.colKeyOrder.push("suname"); this.colKeyOrder.push("sudesc"); @@ -1426,172 +1449,194 @@ export class SchedulingSetCreate extends Component { // Create Constraint Column for AG Grid columnDefs = await this.getConstraintColumns(columnDefs); let cellProps = {}; - this.strategyVariables= []; - //Observation Schema + this.strategyVariables = []; + //Observation Schema const schema = this.state.paramsSchema; - if(schema.properties && !_.isEmpty(schema.properties)) { - // let definitions = schema.definitions.pointing; + if (schema.properties && !_.isEmpty(schema.properties)) { + // let definitions = schema.definitions.pointing; let predefineCellProps = this.getAGGridAngelColumnsDefinition(schema); let propKeys = Object.keys(schema.properties); - for(const prop of propKeys) { + for (const prop of propKeys) { colProperty = {}; cellProps = {}; let property = schema.properties[prop]; - if(property && property.$ref) { + if (property && property.$ref) { cellProps['headerName'] = property.title; cellProps['headerTooltip'] = property.title; if (property.title === 'Filter' || property.title === 'Antenna Set') { - colProperty ={}; + colProperty = {}; cellProps['headerName'] = property.title; cellProps['headerTooltip'] = property.title; - this.strategyVariables.push(prop+"~"+property.title); - this.agSUWithDefaultValue[prop+"~"+property.title] = property.default; - cellProps['field'] = prop+"~"+property.title; - cellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, cellProps, property.title === 'Filter'?_.lowerCase(property.title):'antenna_set'); - colProperty[property.title] = prop+"~"+property.title; + this.strategyVariables.push(prop + "~" + property.title); + this.agSUWithDefaultValue[prop + "~" + property.title] = property.default; + cellProps['field'] = prop + "~" + property.title; + cellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, cellProps, property.title === 'Filter' ? _.lowerCase(property.title) : 'antenna_set'); + colProperty[property.title] = prop + "~" + property.title; columnMap[property.title] = colProperty; columnDefs.push(cellProps); - } else if (_.includes(this.stationGroupsName, property.title)) { + } else if (_.includes(this.stationGroupsName, property.title)) { //Configure the station at end of the row - } else if (_.includes(this.durationList, property.title)) { + } else if (_.includes(this.durationList, property.title)) { //colProperty ={}; cellProps['headerName'] = property.title; cellProps['headerTooltip'] = property.title; - this.strategyVariables.push(prop+"~"+property.title); - this.agSUWithDefaultValue[prop+"~"+property.title] = UnitConverter.getSecsToHHmmss(property.default); - cellProps['field'] = prop+"~"+property.title; + this.strategyVariables.push(prop + "~" + property.title); + this.agSUWithDefaultValue[prop + "~" + property.title] = UnitConverter.getSecsToHHmmss(property.default); + cellProps['field'] = prop + "~" + property.title; cellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, cellProps, _.lowerCase(property.title)); - colProperty[property.title] = prop+"~"+property.title; + colProperty[property.title] = prop + "~" + property.title; columnMap[property.title] = colProperty; columnDefs.push(cellProps); - } else { + } else { let defaultKeys = Object.keys(property.default); let children = []; if (defaultKeys.length > 0 && !property.title.endsWith("Beamformers")) { - for(const defaultKey of defaultKeys) { - this.strategyVariables.push(prop+"~"+defaultKey); - if(defaultKey === 'angle1') { - this.agSUWithDefaultValue[prop+"~"+defaultKey] = UnitConverter.getAngleInput( property.default[defaultKey], false); - } else if(defaultKey === 'angle2') { - this.agSUWithDefaultValue[prop+"~"+defaultKey] = UnitConverter.getAngleInput( property.default[defaultKey], true); - } else{ - this.agSUWithDefaultValue[prop+"~"+defaultKey] = property.default[defaultKey]; + for (const defaultKey of defaultKeys) { + this.strategyVariables.push(prop + "~" + defaultKey); + if (defaultKey === 'angle1') { + this.agSUWithDefaultValue[prop + "~" + defaultKey] = UnitConverter.getAngleInput(property.default[defaultKey], false); + } else if (defaultKey === 'angle2') { + this.agSUWithDefaultValue[prop + "~" + defaultKey] = UnitConverter.getAngleInput(property.default[defaultKey], true); + } else { + this.agSUWithDefaultValue[prop + "~" + defaultKey] = property.default[defaultKey]; } - let childCellProps = { headerName : _.startCase(defaultKey), headerTooltip: _.startCase(defaultKey), field : prop+"~"+defaultKey}; + let childCellProps = { + headerName: _.startCase(defaultKey), + headerTooltip: _.startCase(defaultKey), + field: prop + "~" + defaultKey + }; childCellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, childCellProps, defaultKey); - colProperty[defaultKey] = prop+"~"+defaultKey; + colProperty[defaultKey] = prop + "~" + defaultKey; children.push(childCellProps); } columnMap[property.title] = colProperty; cellProps['children'] = children; columnDefs.push(cellProps); - } else { + } else { cellProps['headerName'] = property.title; cellProps['headerTooltip'] = property.title; //cellProps.valueSetter= function(params) {}; - this.strategyVariables.push(prop+"~"+property.title); - this.agSUWithDefaultValue[prop+"~"+property.title] = property.default; - cellProps['field'] = prop+"~"+property.title; + this.strategyVariables.push(prop + "~" + property.title); + this.agSUWithDefaultValue[prop + "~" + property.title] = property.default; + cellProps['field'] = prop + "~" + property.title; cellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, cellProps, _.lowerCase(property.title)); - colProperty[property.title] = prop+"~"+property.title; + colProperty[property.title] = prop + "~" + property.title; columnMap[property.title] = colProperty; columnDefs.push(cellProps); } } - } else { - colProperty ={}; - if(property.type === 'array' && property.items.enum) { + } else { + colProperty = {}; + if (property.type === 'array' && property.items.enum) { await this.getDemixSourceColumnDef(property, prop, prop, colProperty, columnMap, columnDefs); - } else if (property.type === 'object' && property.properties) { + } else if (property.type === 'object' && property.properties) { cellProps['headerName'] = property.title; cellProps['headerTooltip'] = property.title; let children = []; for (const subProp of Object.keys(property.properties)) { - if(property.properties[subProp].type === 'array' && property.properties[subProp].items.enum) { + if (property.properties[subProp].type === 'array' && property.properties[subProp].items.enum) { await this.getDemixSourceColumnDef(property.properties[subProp], prop, subProp, colProperty, columnMap, children); - } else { - this.strategyVariables.push(prop+"~"+subProp); - this.agSUWithDefaultValue[prop+"~"+subProp] = property.default[subProp]; - let childCellProps = { headerName : _.startCase(subProp), headerTooltip: _.startCase(subProp), field : prop+"~"+subProp}; + } else { + this.strategyVariables.push(prop + "~" + subProp); + this.agSUWithDefaultValue[prop + "~" + subProp] = property.default[subProp]; + let childCellProps = { + headerName: _.startCase(subProp), + headerTooltip: _.startCase(subProp), + field: prop + "~" + subProp + }; childCellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, childCellProps, subProp.toLowerCase()); - colProperty[subProp] = prop+"~"+subProp; + colProperty[subProp] = prop + "~" + subProp; children.push(childCellProps); } } columnMap[property.title] = colProperty; cellProps['children'] = children; columnDefs.push(cellProps); - } else { + } else { cellProps['headerName'] = property.title; cellProps['headerTooltip'] = property.title; if (property.max_length) { cellProps['max_length'] = property.max_length; } - this.strategyVariables.push(prop+"~"+property.title); - this.agSUWithDefaultValue[prop+"~"+property.title] = property.default !== undefined?property.default:''; - cellProps['field'] = prop+"~"+property.title; + this.strategyVariables.push(prop + "~" + property.title); + this.agSUWithDefaultValue[prop + "~" + property.title] = property.default !== undefined ? property.default : ''; + cellProps['field'] = prop + "~" + property.title; cellProps = this.getAGGridAngelColumnsProperty(predefineCellProps, cellProps, property.title.toLowerCase()); - colProperty[property.title] = prop+"~"+property.title; + colProperty[property.title] = prop + "~" + property.title; columnMap[property.title] = colProperty; columnDefs.push(cellProps); } } } } - const stationKeys = Object.keys(schema.properties).filter(stationKey => _.includes(this.stationGroupsName,stationKey)); + const stationKeys = Object.keys(schema.properties).filter(stationKey => _.includes(this.stationGroupsName, stationKey)); if (stationKeys) { - for ( const stationKey of stationKeys) { + for (const stationKey of stationKeys) { let stationValue = ''; let cellProp = {}; - this.state.defaultStationGroups[stationKey].map(stationGroup =>{ - let missingStation = (stationGroup.max_nr_missing)?stationGroup.max_nr_missing:0; - stationValue += stationGroup.stations+':'+missingStation+"|"; + this.state.defaultStationGroups[stationKey].map(stationGroup => { + let missingStation = (stationGroup.max_nr_missing) ? stationGroup.max_nr_missing : 0; + stationValue += stationGroup.stations + ':' + missingStation + "|"; return stationGroup; }); let stationField = stationKey;//_.replace(stationKey, ' ', '_'); this.agSUWithDefaultValue[stationField] = stationValue; - columnDefs.push({headerName: stationKey, headerTooltip:stationKey, field: stationField, cellRenderer: 'betweenRenderer', cellEditor: 'station', valueSetter: 'newValueSetter'}); + columnDefs.push({ + headerName: stationKey, + headerTooltip: stationKey, + field: stationField, + cellRenderer: 'betweenRenderer', + cellEditor: 'station', + valueSetter: 'newValueSetter' + }); this.strategyVariables.push(stationField); - cellProp[stationField] =stationField; + cellProp[stationField] = stationField; columnMap[stationField] = cellProp; } } - - /* this.state.defaultStationGroups.map(stationGroup =>{ - let missingStation = (stationGroup.max_nr_missing)?stationGroup.max_nr_missing:0; - stationValue += stationGroup.stations+':'+missingStation+"|"; - }); - this.agSUWithDefaultValue['stations'] = stationValue; - columnDefs.push({headerName: 'Stations', field: 'stations', cellRenderer: 'betweenRenderer', cellEditor: 'station', valueSetter: 'newValueSetter'}); - */ + + /* this.state.defaultStationGroups.map(stationGroup =>{ + let missingStation = (stationGroup.max_nr_missing)?stationGroup.max_nr_missing:0; + stationValue += stationGroup.stations+':'+missingStation+"|"; + }); + this.agSUWithDefaultValue['stations'] = stationValue; + columnDefs.push({headerName: 'Stations', field: 'stations', cellRenderer: 'betweenRenderer', cellEditor: 'station', valueSetter: 'newValueSetter'}); + */ this.getEmptyRow(); this.colKeyOrder = [...this.colKeyOrder, ...this.strategyVariables]; - let globalColmunDef =_.cloneDeep(columnDefs); + let globalColmunDef = _.cloneDeep(columnDefs); globalColmunDef = await this.createGlobalColumnDefs(globalColmunDef, schema); - this.setState({colKeyOrder: this.colKeyOrder, globalColmunDef: globalColmunDef, columnDefs: columnDefs, columnMap: columnMap, agSUWithDefaultValue: this.agSUWithDefaultValue}); + this.setState({ + colKeyOrder: this.colKeyOrder, + globalColmunDef: globalColmunDef, + columnDefs: columnDefs, + columnMap: columnMap, + agSUWithDefaultValue: this.agSUWithDefaultValue + }); } - + /** * Prepare Demix Source column definition */ getDemixSourceColumnDef(property, prop, subProp, colProperty, columnMap, children) { let childCellProps = {}; - const title = property.title? property.title: subProp; - const tmpField = prop+"~"+ title; + const title = property.title ? property.title : subProp; + const tmpField = prop + "~" + title; this.getColumnDefinition(tmpField, {type: 'object', enum: property.items.enum, multiselect: true}); let multiSelectorOptions = _.cloneDeep(this.state.multiSelectorOptions); childCellProps['headerName'] = _.startCase(title); childCellProps['headerTooltip'] = _.startCase(title); childCellProps['cellEditor'] = 'multiselector'; - childCellProps['field'] = prop+"~"+title; - childCellProps.valueSetter = function(params) {} //This line required to set selected value in appropriate cell - this.strategyVariables.push(prop+"~"+title); - this.agSUWithDefaultValue[prop+"~"+title] = property.items.default; - colProperty[title] = prop+"~"+title; + childCellProps['field'] = prop + "~" + title; + childCellProps.valueSetter = function (params) { + } //This line required to set selected value in appropriate cell + this.strategyVariables.push(prop + "~" + title); + this.agSUWithDefaultValue[prop + "~" + title] = property.items.default; + colProperty[title] = prop + "~" + title; columnMap[title] = colProperty; let option = [] property.items.enum.forEach(prop => { - option.push({'name':prop, 'value':prop}); + option.push({'name': prop, 'value': prop}); }); multiSelectorOptions[childCellProps['field']] = option; children.push(childCellProps); @@ -1600,56 +1645,56 @@ export class SchedulingSetCreate extends Component { /** * Create AG Grid column definition for top table - * @param {*} globalColmunDef - * @param {*} schema - * @param {*} constraintSchema + * @param {*} globalColmunDef + * @param {*} schema + * @param {*} constraintSchema */ createGlobalColumnDefs(globalColmunDef, schema) { let schedulerValues = [...' ', ...this.resolvedConstraintSchema.properties.scheduler.enum]; - let direction_type_Values = [...' ', ...(schema.definitions.pointing?schema.definitions.pointing.properties.direction_type.enum:[])]; + let direction_type_Values = [...' ', ...(schema.definitions.pointing ? schema.definitions.pointing.properties.direction_type.enum : [])]; globalColmunDef.forEach(colDef => { if (colDef.children) { colDef.children.forEach(childColDef => { if (childColDef.field) { - if(childColDef.field.endsWith('direction_type')) { + if (childColDef.field.endsWith('direction_type')) { childColDef.cellEditorParams.values = direction_type_Values; - } else if(childColDef.field === 'filter') { - childColDef.cellEditorParams.values = [...' ', ...schema.definitions.filter.enum]; + } else if (childColDef.field === 'filter') { + childColDef.cellEditorParams.values = [...' ', ...schema.definitions.filter.enum]; } - childColDef.field = 'gdef_'+childColDef.field; + childColDef.field = 'gdef_' + childColDef.field; if (childColDef.default) { childColDef.default = ''; } } }); - } else { - if(colDef.headerName === '#') { - colDef['hide'] = true; + } else { + if (colDef.headerName === '#') { + colDef['hide'] = true; + } + if (colDef.field) { + if (colDef.field.endsWith('scheduler')) { + colDef.cellEditorParams.values = schedulerValues; } - if(colDef.field) { - if ( colDef.field.endsWith('scheduler')) { - colDef.cellEditorParams.values = schedulerValues; - } - colDef.field = 'gdef_'+colDef.field; - if (colDef.default) { - colDef.default = ''; - } + colDef.field = 'gdef_' + colDef.field; + if (colDef.default) { + colDef.default = ''; } } + } }); - return globalColmunDef; + return globalColmunDef; } /** - * + * */ getEmptyRow() { this.emptyAGSU = {}; let keys = Object.keys(this.agSUWithDefaultValue); - for(const key of keys) { - if (key === 'id'){ + for (const key of keys) { + if (key === 'id') { this.emptyAGSU[key] = 0; - } else { + } else { this.emptyAGSU[key] = ''; } } @@ -1657,8 +1702,8 @@ export class SchedulingSetCreate extends Component { /** * Column definition to validate the cell - * @param {*} key - * @param {*} props + * @param {*} key + * @param {*} props */ async getColumnDefinition(key, props) { this.columnDefinition[key] = {}; @@ -1680,8 +1725,8 @@ export class SchedulingSetCreate extends Component { if (props.multiselect) { this.columnDefinition[key]['multiselect'] = props.multiselect; } - - switch(key.toLowerCase()) { + + switch (key.toLowerCase()) { case 'offset_from': case 'offset_to': this.columnDefinition[key]['format'] = '-hh:mm:ss'; @@ -1696,42 +1741,45 @@ export class SchedulingSetCreate extends Component { this.columnDefinition[key]['format'] = 'date-time-array'; break; default: - if (_.includes(this.durationList, key)){ + if (_.includes(this.durationList, key)) { this.columnDefinition[key]['format'] = 'hh:mm:ss'; this.columnDefinition[key]['schema'] = props.schema; - } else if (props.format) { - this.columnDefinition[key]['format'] = props.format; + } else if (props.format) { + this.columnDefinition[key]['format'] = props.format; } break; - } + } } /** * Create Constraint columns for AG Grid - * @param {*} columnDefs - * @returns + * @param {*} columnDefs + * @returns */ async getConstraintColumns(columnDefs) { - this.constraintVariables= []; + this.constraintVariables = []; // currently only one constraint schema available and not propvided UI to choose constraints, so assign directly - this.constraintSchema = _.find(this.constraintTemplates, - {"name": this.state.observStrategy.template.scheduling_constraints_template.name, - "version": this.state.observStrategy.template.scheduling_constraints_template.version}); + const currentConstraintsTemplate = this.state.observStrategy.template.scheduling_constraints_template; + this.constraintSchema = getConstraintTemplate(this.constraintTemplates, currentConstraintsTemplate.name, currentConstraintsTemplate.version) if (!this.constraintSchema) { - this.growl.show({severity: 'error', summary: 'Constraint template missing', detail: 'Unable to find constraint template defined in the strategy template'}); + this.growl.show({ + severity: 'error', + summary: 'Constraint template missing', + detail: 'Unable to find constraint template defined in the strategy template' + }); } - this.resolvedConstraintSchema = this.constraintSchema.ref_resolved_schema; + this.resolvedConstraintSchema = this.constraintSchema?.ref_resolved_schema; //this.getColumnDefinition(this.constraintSchema.schema, this.constraintSchema.schema.definitions); - /** AG Grid Cell Specific Properties - In Excel View - expected column order is ['scheduler', 'time', 'daily', 'sky'] */ - let dailyProps = Object.keys( this.resolvedConstraintSchema.properties.daily.properties); + /** AG Grid Cell Specific Properties + In Excel View - expected column order is ['scheduler', 'time', 'daily', 'sky'] */ + let dailyProps = Object.keys(this.resolvedConstraintSchema.properties.daily.properties); let multiSelectorOptions = _.cloneDeep(this.state.multiSelectorOptions); - this.columnDefinition['daily'] = {required: false, type:'object', enum: dailyProps, multiselect: true} + this.columnDefinition['daily'] = {required: false, type: 'object', enum: dailyProps, multiselect: true} multiSelectorOptions['daily'] = dailyProps; this.daily = []; let dailyOption = []; dailyProps.forEach(prop => { - dailyOption.push({'name':prop, 'value':prop}); + dailyOption.push({'name': prop, 'value': prop}); this.daily.push(prop); }); multiSelectorOptions['daily'] = dailyOption; @@ -1751,18 +1799,18 @@ export class SchedulingSetCreate extends Component { this.getColumnDefinition('notbetween', {type: 'string', format: 'date-time-array'}); this.agSUWithDefaultValue['scheduler'] = this.resolvedConstraintSchema.properties.scheduler.default; - this.agSUWithDefaultValue['min_target_elevation'] = ((this.resolvedConstraintSchema.properties.sky.properties.min_elevation.properties.target.default * 180) / Math.PI).toFixed(2); + this.agSUWithDefaultValue['min_target_elevation'] = ((this.resolvedConstraintSchema.properties.sky.properties.min_elevation.properties.target.default * 180) / Math.PI).toFixed(2); const elevationMax = this.resolvedConstraintSchema.definitions.elevation.maximum; const elevationMin = this.resolvedConstraintSchema.definitions.elevation.minimum; this.agSUWithDefaultValue['elevation_max'] = elevationMax; - this.agSUWithDefaultValue['elevation_min'] = elevationMin; + this.agSUWithDefaultValue['elevation_min'] = elevationMin; this.agSUWithDefaultValue['min_calibrator_elevation'] = ((this.resolvedConstraintSchema.properties.sky.properties.min_elevation.properties.calibrator.default * 180) / Math.PI).toFixed(2); - this.agSUWithDefaultValue['offset_from'] = (this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.default<0?'-':'')+UnitConverter.getSecsToHHmmss(this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.default); - this.agSUWithDefaultValue['offset_to'] = (this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.default<0?'-':'')+UnitConverter.getSecsToHHmmss(this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.default); + this.agSUWithDefaultValue['offset_from'] = (this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.default < 0 ? '-' : '') + UnitConverter.getSecsToHHmmss(this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.default); + this.agSUWithDefaultValue['offset_to'] = (this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.default < 0 ? '-' : '') + UnitConverter.getSecsToHHmmss(this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.default); const maxOffsetFrom = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.maximum; const minOffsetFrom = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.minimum; this.agSUWithDefaultValue['offset_from_max'] = maxOffsetFrom; - this.agSUWithDefaultValue['offset_from_min'] = minOffsetFrom; + this.agSUWithDefaultValue['offset_from_min'] = minOffsetFrom; const maxOffsetTo = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.maximum; const minOffsetTo = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.minimum; this.agSUWithDefaultValue['offset_to_max'] = maxOffsetTo; @@ -1770,23 +1818,69 @@ export class SchedulingSetCreate extends Component { this.agSUWithDefaultValue['md_sun'] = ((this.resolvedConstraintSchema.properties.sky.properties.min_distance.properties.sun.default * 180) / Math.PI).toFixed(2); this.agSUWithDefaultValue['md_moon'] = ((this.resolvedConstraintSchema.properties.sky.properties.min_distance.properties.moon.default * 180) / Math.PI).toFixed(2); this.agSUWithDefaultValue['md_jupiter'] = ((this.resolvedConstraintSchema.properties.sky.properties.min_distance.properties.jupiter.default) / Math.PI).toFixed(2); - columnDefs.push({headerName: 'Scheduler',headerTooltip: 'Scheduler',field: 'scheduler',cellEditor: 'agSelectCellEditor',default: this.resolvedConstraintSchema.properties.scheduler.default, - cellEditorParams: {values: this.resolvedConstraintSchema.properties.scheduler.enum,}, }); - columnDefs.push({ headerName: 'Time',headerTooltip: 'Time', - children: [ - { headerName: 'At',headerTooltip: 'At', field:'timeat', editable: true, cellRenderer: 'betweenRenderer',cellEditor: 'agDateInput', valueSetter: 'newValueSetter'}, - { headerName: 'After', headerTooltip: 'After', field:'timeafter', editable: true, cellRenderer: 'betweenRenderer',cellEditor: 'agDateInput', valueSetter: 'newValueSetter'}, - { headerName: 'Before', headerTooltip: 'Before', field:'timebefore', editable: true, cellRenderer: 'betweenRenderer',cellEditor: 'agDateInput', valueSetter: 'newValueSetter'}, - ],}); + columnDefs.push({ + headerName: 'Scheduler', + headerTooltip: 'Scheduler', + field: 'scheduler', + cellEditor: 'agSelectCellEditor', + default: this.resolvedConstraintSchema.properties.scheduler.default, + cellEditorParams: {values: this.resolvedConstraintSchema.properties.scheduler.enum,}, + }); + columnDefs.push({ + headerName: 'Time', headerTooltip: 'Time', + children: [ + { + headerName: 'At', + headerTooltip: 'At', + field: 'timeat', + editable: true, + cellRenderer: 'betweenRenderer', + cellEditor: 'agDateInput', + valueSetter: 'newValueSetter' + }, + { + headerName: 'After', + headerTooltip: 'After', + field: 'timeafter', + editable: true, + cellRenderer: 'betweenRenderer', + cellEditor: 'agDateInput', + valueSetter: 'newValueSetter' + }, + { + headerName: 'Before', + headerTooltip: 'Before', + field: 'timebefore', + editable: true, + cellRenderer: 'betweenRenderer', + cellEditor: 'agDateInput', + valueSetter: 'newValueSetter' + }, + ], + }); this.constraintVariables.push('timeat'); this.constraintVariables.push('timeafter'); - this.constraintVariables.push('timebefore'); - this.constraintVariables.push('between'); + this.constraintVariables.push('timebefore'); + this.constraintVariables.push('between'); this.constraintVariables.push('notbetween'); - this.constraintVariables.push('daily'); - columnDefs.push({headerName: 'Between', headerTooltip: 'Between', field: 'between',cellRenderer: 'betweenRenderer',cellEditor: 'betweenEditor',valueSetter: 'newValueSetter'}); - columnDefs.push({headerName: 'Not Between', headerTooltip: 'Not Between', field: 'notbetween',cellRenderer: 'betweenRenderer',cellEditor: 'betweenEditor',valueSetter: 'newValueSetter'}); - this.constraintVariables.push('min_target_elevation'); + this.constraintVariables.push('daily'); + columnDefs.push({ + headerName: 'Between', + headerTooltip: 'Between', + field: 'between', + cellRenderer: 'betweenRenderer', + cellEditor: 'betweenEditor', + valueSetter: 'newValueSetter' + }); + columnDefs.push({ + headerName: 'Not Between', + headerTooltip: 'Not Between', + field: 'notbetween', + cellRenderer: 'betweenRenderer', + cellEditor: 'betweenEditor', + valueSetter: 'newValueSetter' + }); + this.constraintVariables.push('min_target_elevation'); this.constraintVariables.push('min_calibrator_elevation'); this.constraintVariables.push('offset_from'); this.constraintVariables.push('offset_to'); @@ -1795,191 +1889,264 @@ export class SchedulingSetCreate extends Component { this.constraintVariables.push('ref_pointing_angle2'); this.constraintVariables.push('ref_pointing_direction_type'); this.constraintVariables.push('ref_pointing_target'); - columnDefs.push({headerName: 'Daily', headerTooltip: 'Daily',field: 'daily',cellEditor: 'multiselector', valueSetter: function(params) {}}, - {headerName: 'Sky',headerTooltip: 'Sky', + columnDefs.push({ + headerName: 'Daily', + headerTooltip: 'Daily', + field: 'daily', + cellEditor: 'multiselector', + valueSetter: function (params) { + } + }, + { + headerName: 'Sky', headerTooltip: 'Sky', children: [ - {headerName: 'Min Target Elevation (Degrees)', headerTooltip: 'Min Target Elevation (Degrees)',field: 'min_target_elevation', cellEditor: 'numericEditor', cellStyle: function(params) { - if (params.value){ - if (params.value === undefined || params.value === null || isNaN(params.value)){ - return { backgroundColor: BG_COLOR}; + { + headerName: 'Min Target Elevation (Degrees)', + headerTooltip: 'Min Target Elevation (Degrees)', + field: 'min_target_elevation', + cellEditor: 'numericEditor', + cellStyle: function (params) { + if (params.value) { + if (params.value === undefined || params.value === null || isNaN(params.value)) { + return {backgroundColor: BG_COLOR}; + } else if (Number(params.value) < 0 || Number(params.value) > 90) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } } - else if ( Number(params.value) < 0|| Number(params.value) > 90) { - return { backgroundColor: BG_COLOR}; - } else{ - return { backgroundColor: ''}; + }, + valueFormatter: params => { + if (params.value !== '') { + const value = parseFloat(params.value); + return value >= 0 ? value.toFixed(2) : '0.00'; + } else { + return "0.00"; } } - }, - valueFormatter: params => { - if(params.value !== '') { - const value = parseFloat(params.value); - return value >= 0?value.toFixed(2):'0.00'; - } else { - return "0.00"; - } - } }, - {headerName: 'Min Calibrator Elevation (Degrees)', headerTooltip: 'Min Calibrator Elevation (Degrees)',field: 'min_calibrator_elevation', cellEditor: 'numericEditor', cellStyle: function(params) { - if (params.value){ - if (params.value === undefined || params.value === null || isNaN(params.value)){ - return { backgroundColor: BG_COLOR}; + }, + { + headerName: 'Min Calibrator Elevation (Degrees)', + headerTooltip: 'Min Calibrator Elevation (Degrees)', + field: 'min_calibrator_elevation', + cellEditor: 'numericEditor', + cellStyle: function (params) { + if (params.value) { + if (params.value === undefined || params.value === null || isNaN(params.value)) { + return {backgroundColor: BG_COLOR}; + } else if (Number(params.value) < 0 || Number(params.value) > 90) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } } - else if ( Number(params.value) < 0|| Number(params.value) > 90) { - return { backgroundColor: BG_COLOR}; - } else{ - return { backgroundColor: ''}; + }, + valueFormatter: params => { + if (params.value !== '') { + const value = parseFloat(params.value); + return value >= 0 ? value.toFixed(2) : '0.00'; + } else { + return "0.00"; } } }, - valueFormatter: params => { - if(params.value !== '') { - const value = parseFloat(params.value); - return value >= 0?value.toFixed(2):'0.00'; - } else { - return "0.00"; - } - } }, - {headerName: 'Offset Window From', headerTooltip: 'Offset Window From', field: 'offset_from',cellRenderer: 'betweenRenderer',cellEditor: 'offsetTimeInputmask',valueSetter: 'newValueSetter',cellStyle: function(params) { - const cellValue = _.split(params.value, ":"); - if ( typeof params.value === 'undefined' || - (cellValue.length !== 3 || isNaN(cellValue[1]) || cellValue[1]>59 || isNaN(cellValue[2]) || cellValue[2]>59)){ - return { backgroundColor: BG_COLOR}; - } else if ( UnitConverter.getHHmmssToSecs(params.value) < minOffsetFrom || - UnitConverter.getHHmmssToSecs(params.value) > maxOffsetFrom) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; + { + headerName: 'Offset Window From', + headerTooltip: 'Offset Window From', + field: 'offset_from', + cellRenderer: 'betweenRenderer', + cellEditor: 'offsetTimeInputmask', + valueSetter: 'newValueSetter', + cellStyle: function (params) { + const cellValue = _.split(params.value, ":"); + if (typeof params.value === 'undefined' || + (cellValue.length !== 3 || isNaN(cellValue[1]) || cellValue[1] > 59 || isNaN(cellValue[2]) || cellValue[2] > 59)) { + return {backgroundColor: BG_COLOR}; + } else if (UnitConverter.getHHmmssToSecs(params.value) < minOffsetFrom || + UnitConverter.getHHmmssToSecs(params.value) > maxOffsetFrom) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } } - }}, - {headerName: 'Offset Window To', headerTooltip: 'Offset Window To',field: 'offset_to', cellRenderer: 'betweenRenderer',cellEditor: 'offsetTimeInputmask',valueSetter: 'newValueSetter', cellStyle: function(params) { - const cellValue = _.split(params.value, ":"); - if ( typeof params.value === 'undefined' || - (cellValue.length !== 3 || isNaN(cellValue[1]) || cellValue[1]>59 || isNaN(cellValue[2]) || cellValue[2]>59)){ - return { backgroundColor: BG_COLOR}; - } else if ( UnitConverter.getHHmmssToSecs(params.value) < minOffsetTo || - UnitConverter.getHHmmssToSecs(params.value) > maxOffsetTo) { - return { backgroundColor: BG_COLOR}; - } else { - return { backgroundColor: ''}; + }, + { + headerName: 'Offset Window To', + headerTooltip: 'Offset Window To', + field: 'offset_to', + cellRenderer: 'betweenRenderer', + cellEditor: 'offsetTimeInputmask', + valueSetter: 'newValueSetter', + cellStyle: function (params) { + const cellValue = _.split(params.value, ":"); + if (typeof params.value === 'undefined' || + (cellValue.length !== 3 || isNaN(cellValue[1]) || cellValue[1] > 59 || isNaN(cellValue[2]) || cellValue[2] > 59)) { + return {backgroundColor: BG_COLOR}; + } else if (UnitConverter.getHHmmssToSecs(params.value) < minOffsetTo || + UnitConverter.getHHmmssToSecs(params.value) > maxOffsetTo) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } } - }} + } ], }); - // Add reference pointing constraint - if(this.resolvedConstraintSchema.properties.sky.properties.reference_pointing) { - columnDefs.push({headerName: `${this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.title}`, - headerTooltip: `${this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.description}`, children: [ - {headerName: 'Enabled', headerTooltip: `${this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.enabled.description}`,field: 'ref_pointing_enabled', + // Add reference pointing constraint + if (this.resolvedConstraintSchema.properties.sky.properties.reference_pointing) { + columnDefs.push({ + headerName: `${this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.title}`, + headerTooltip: `${this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.description}`, + children: [ + { + headerName: 'Enabled', + headerTooltip: `${this.resolvedConstraintSchema.properties.sky.properties.reference_pointing.properties.enabled.description}`, + field: 'ref_pointing_enabled', cellEditor: 'agSelectCellEditor', cellEditorParams: { values: [true, false], - }}, - {headerName: 'Angle1', headerTooltip: 'Reference Pointing - Angle1',field: 'ref_pointing_angle1', - type:'numberValueColumn', cellRenderer: 'timeInputMask',cellEditor: 'timeInputMask', valueSetter: 'valueSetter', - cellStyle: function(params) { - if (params.value && !Validator.validateTimeAndAngle(params.value)) { - return { backgroundColor: BG_COLOR}; + } + }, + { + headerName: 'Angle1', + headerTooltip: 'Reference Pointing - Angle1', + field: 'ref_pointing_angle1', + type: 'numberValueColumn', + cellRenderer: 'timeInputMask', + cellEditor: 'timeInputMask', + valueSetter: 'valueSetter', + cellStyle: function (params) { + if (params.value && !Validator.validateTimeAndAngle(params.value)) { + return {backgroundColor: BG_COLOR}; } else { - return { backgroundColor: ''}; + return {backgroundColor: ''}; } - }}, - {headerName: 'Angle2', headerTooltip: 'Reference Pointing - Angle2',field: 'ref_pointing_angle2', - type:'numberValueColumn', cellRenderer: 'degreeInputMask',cellEditor: 'degreeInputMask', valueSetter: 'valueSetter', - cellStyle: function(params) { - if (params.value && !Validator.validateAngle(params.value)) { - return { backgroundColor: BG_COLOR}; + } + }, + { + headerName: 'Angle2', + headerTooltip: 'Reference Pointing - Angle2', + field: 'ref_pointing_angle2', + type: 'numberValueColumn', + cellRenderer: 'degreeInputMask', + cellEditor: 'degreeInputMask', + valueSetter: 'valueSetter', + cellStyle: function (params) { + if (params.value && !Validator.validateAngle(params.value)) { + return {backgroundColor: BG_COLOR}; } else { - return { backgroundColor: ''}; + return {backgroundColor: ''}; } - }}, - {headerName: 'Reference Frame', headerTooltip: 'Reference Pointing - Reference Frame',field: 'ref_pointing_direction_type', - cellEditor: 'agSelectCellEditor', - cellEditorParams: { - values: this.resolvedConstraintSchema.definitions.pointing.properties.direction_type.enum, + } + }, + { + headerName: 'Reference Frame', + headerTooltip: 'Reference Pointing - Reference Frame', + field: 'ref_pointing_direction_type', + cellEditor: 'agSelectCellEditor', + cellEditorParams: { + values: this.resolvedConstraintSchema.definitions.pointing.properties.direction_type.enum, + } + }, + {headerName: 'Target', headerTooltip: 'Refernce Pointing - Target', field: 'ref_pointing_target'} + ] + }); + } + this.constraintVariables.push('md_sun'); + this.constraintVariables.push('md_moon'); + this.constraintVariables.push('md_jupiter'); + columnDefs.push({ + headerName: 'Min_distance', headerTooltip: 'Min_distance', children: [ + { + headerName: 'Sun (Degrees)', + headerTooltip: 'Sun (Degrees)', + field: 'md_sun', + cellEditor: 'numericEditor', + cellStyle: function (params) { + if (params.value) { + if (params.value === undefined || params.value === null || isNaN(params.value)) { + return {backgroundColor: BG_COLOR}; + } else if (Number(params.value) < 0 || Number(params.value) > 180) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; } - }, - {headerName: 'Target', headerTooltip: 'Refernce Pointing - Target',field: 'ref_pointing_target'} - ] - }); - } - this.constraintVariables.push('md_sun'); - this.constraintVariables.push('md_moon'); - this.constraintVariables.push('md_jupiter'); - columnDefs.push({headerName: 'Min_distance', headerTooltip: 'Min_distance',children: [ - {headerName: 'Sun (Degrees)', headerTooltip:'Sun (Degrees)', field: 'md_sun', cellEditor: 'numericEditor',cellStyle: function(params) { - if (params.value){ - if (params.value === undefined || params.value === null || isNaN(params.value)){ - return { backgroundColor: BG_COLOR}; } - else if ( Number(params.value) < 0 || Number(params.value) > 180) { - return { backgroundColor: BG_COLOR}; - } else{ - return { backgroundColor: ''}; + }, + valueFormatter: params => { + if (params.value !== '') { + const value = parseFloat(params.value); + return value >= 0 ? value.toFixed(2) : '0.00'; + } else { + return "0:00"; } } - }, valueFormatter: params => { - if(params.value !== '') { - const value = parseFloat(params.value); - return value >= 0?value.toFixed(2):'0.00'; - } else { - return "0:00"; - } - } }, - {headerName: 'Moon (Degrees)',headerTooltip:'Moon (Degrees)', field: 'md_moon', cellEditor: 'numericEditor', cellStyle: function(params) { - if (params.value){ - if (params.value === undefined || params.value === null || isNaN(params.value)){ - return { backgroundColor: BG_COLOR}; + { + headerName: 'Moon (Degrees)', + headerTooltip: 'Moon (Degrees)', + field: 'md_moon', + cellEditor: 'numericEditor', + cellStyle: function (params) { + if (params.value) { + if (params.value === undefined || params.value === null || isNaN(params.value)) { + return {backgroundColor: BG_COLOR}; + } else if (Number(params.value) < 0 || Number(params.value) > 180) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } } - else if ( Number(params.value) < 0 || Number(params.value) > 180) { - return { backgroundColor: BG_COLOR}; - } else{ - return { backgroundColor: ''}; + }, + valueFormatter: params => { + if (params.value !== '') { + const value = parseFloat(params.value); + return value >= 0 ? value.toFixed(2) : '0.00'; + } else { + return "0:00"; } } - }, valueFormatter: params => { - if(params.value !== '') { - const value = parseFloat(params.value); - return value >= 0?value.toFixed(2):'0.00'; - } else { - return "0:00"; - } - } - }, - {headerName: 'Jupiter (Degrees)',headerTooltip:'Jupiter (Degrees)', field: 'md_jupiter', cellEditor: 'numericEditor', cellStyle: function(params) { - if (params.value){ - if (params.value === undefined || params.value === null || isNaN(params.value)){ - return { backgroundColor: BG_COLOR}; + }, + { + headerName: 'Jupiter (Degrees)', + headerTooltip: 'Jupiter (Degrees)', + field: 'md_jupiter', + cellEditor: 'numericEditor', + cellStyle: function (params) { + if (params.value) { + if (params.value === undefined || params.value === null || isNaN(params.value)) { + return {backgroundColor: BG_COLOR}; + } else if (Number(params.value) < 0 || Number(params.value) > 180) { + return {backgroundColor: BG_COLOR}; + } else { + return {backgroundColor: ''}; + } } - else if ( Number(params.value) < 0 || Number(params.value) > 180) { - return { backgroundColor: BG_COLOR}; - } else{ - return { backgroundColor: ''}; + }, + valueFormatter: params => { + if (params.value !== '') { + const value = parseFloat(params.value); + return value >= 0 ? value.toFixed(2) : '0.00'; + } else { + return "0:00"; } } - }, valueFormatter: params => { - if(params.value !== '') { - const value = parseFloat(params.value); - return value >= 0?value.toFixed(2):'0.00'; - } else { - return "0:00"; - } - } - },], - }); + },], + }); this.colKeyOrder = [...this.colKeyOrder, ...this.constraintVariables]; return columnDefs; } - - /** - * Function called back from Degree/Time Input Mask to set value in row data. + + /** + * Function called back from Degree/Time Input Mask to set value in row data. * * @param {Stirng} cell -> contains Row ID, Column Name, Value, isDegree */ - async updateAngle(rowIndex, field, value, isDegree, isValid){ + async updateAngle(rowIndex, field, value, isDegree, isValid) { //TODO: refactor this to spreadsheet helper let row = {}; let tmpRowData = []; - if ( field.startsWith('gdef_')) { + if (field.startsWith('gdef_')) { row = this.state.commonRowData[0]; row[field] = value; row['isValid'] = isValid; @@ -1989,8 +2156,7 @@ export class SchedulingSetCreate extends Component { tmpRowData = this.state.commonRowData; tmpRowData[0] = row; await this.setState({commonRowData: tmpRowData}); - } - else { + } else { row = this.state.rowData[rowIndex]; row[field] = value; row['isValid'] = isValid; @@ -2001,44 +2167,43 @@ export class SchedulingSetCreate extends Component { */ tmpRowData = this.state.rowData; tmpRowData[rowIndex] = row; - await this.setState({rowData: tmpRowData,isDirty: true}); + await this.setState({rowData: tmpRowData, isDirty: true}); publish('edit-dirty', true); } } - /** + /** * CallBack Function : update time value in master grid */ async updateTime(rowIndex, field, value, isSetFocus, doBEValidation) { let row = {}; let tmpRowData = []; - if ( field.startsWith('gdef_')) { + if (field.startsWith('gdef_')) { row = this.state.commonRowData[0]; row[field] = value; - tmpRowData =this.state.commonRowData; + tmpRowData = this.state.commonRowData; tmpRowData[0] = row; await this.setState({commonRowData: tmpRowData}); //this.state.topGridApi.setRowData(this.state.commonRowData); - // this.state.topGridApi.redrawRows(); - } - else { + // this.state.topGridApi.redrawRows(); + } else { row = this.state.rowData[rowIndex]; row[field] = value; row["isDirty"] = true; tmpRowData = this.state.rowData; tmpRowData[rowIndex] = row; - await this.setState({rowData: tmpRowData,isDirty: true}); + await this.setState({rowData: tmpRowData, isDirty: true}); publish('edit-dirty', true); - // this.state.gridApi.setRowData(this.state.rowData); - // this.state.gridApi.redrawRows(); + // this.state.gridApi.setRowData(this.state.rowData); + // this.state.gridApi.redrawRows(); } - if(isSetFocus === true) { + if (isSetFocus === true) { if (field.startsWith('gdef_')) { this.state.topGridApi.stopEditing(); let focusedCell = this.state.topGridColumnApi.getColumn(field) this.state.topGridApi.ensureColumnVisible(focusedCell); this.state.topGridApi.setFocusedCell(rowIndex, focusedCell); - } else { + } else { this.state.gridApi.stopEditing(); let focusedCell = this.state.gridColumnApi.getColumn(field) this.state.gridApi.ensureColumnVisible(focusedCell); @@ -2046,8 +2211,8 @@ export class SchedulingSetCreate extends Component { } } - if(doBEValidation) { - if (_.includes(this.constraintVariables, field) ) { + if (doBEValidation) { + if (_.includes(this.constraintVariables, field)) { await this.validateSchedulingConstraints(rowIndex, row); } if (_.includes(this.strategyVariables, field)) { @@ -2058,39 +2223,38 @@ export class SchedulingSetCreate extends Component { /** * Update the Row columns value from external component - * @param {*} rowIndex - * @param {*} field - * @param {*} value + * @param {*} rowIndex + * @param {*} field + * @param {*} value * @param {Boolean} isMultiselect - It check if particular cell is Multiselect or not */ async updateCell(rowIndex, field, value, isMultiselect, doBEValidation) { let row = {}; let tmpRowData = []; - if ( field.startsWith('gdef_')) { + if (field.startsWith('gdef_')) { row = this.state.commonRowData[0]; row[field] = value; tmpRowData = this.state.commonRowData; tmpRowData[0] = row; await this.setState({commonRowData: tmpRowData}); - if(isMultiselect) { + if (isMultiselect) { //this.state.topGridApi.stopEditing(); var focusedCell = this.state.topGridColumnApi.getColumn(field) this.state.topGridApi.ensureColumnVisible(focusedCell); this.state.topGridApi.setFocusedCell(rowIndex, focusedCell); } - } - else { + } else { row = this.state.rowData[rowIndex]; - row[field] = value && value.param_0? value.param_0:value; + row[field] = value && value.param_0 ? value.param_0 : value; row["isDirty"] = true; // if (_.includes(field,'Station')) { // await this.validateSpecificationsDoc(rowIndex, row); // } tmpRowData = this.state.rowData; tmpRowData[rowIndex] = row; - await this.setState({rowData: tmpRowData,isDirty: true}); + await this.setState({rowData: tmpRowData, isDirty: true}); publish('edit-dirty', true); - if(isMultiselect) { + if (isMultiselect) { //this.state.gridApi.stopEditing(); let focusedCell = this.state.gridColumnApi.getColumn(field) this.state.gridApi.ensureColumnVisible(focusedCell); @@ -2098,8 +2262,8 @@ export class SchedulingSetCreate extends Component { } } - if(doBEValidation) { - if (_.includes(this.constraintVariables, field) ) { + if (doBEValidation) { + if (_.includes(this.constraintVariables, field)) { await this.validateSchedulingConstraints(rowIndex, row); } if (_.includes(this.strategyVariables, field)) { @@ -2114,63 +2278,65 @@ export class SchedulingSetCreate extends Component { async saveSchedulingUnit() { this.validateGridAndSave(); } - + /** * Validate Grid values on click Save button from UI */ - async validateGridAndSave(){ + async validateGridAndSave() { let validCount = 0; let inValidCount = 0; let isValidRow = true; let errorDisplay = []; let warningClipboardData = ''; //const colDefkeys = Object.keys(this.columnDefinition); - const mandatoryKeys = ['suname','sudesc','priority_queue','scheduler','min_target_elevation','min_calibrator_elevation','offset_from','offset_to','md_sun','md_moon','md_jupiter','param_0~angle1','param_0~angle2','param_0~direction_type','param_1~angle1','param_1~angle2','param_1~direction_type','param_2~angle1','param_2~angle2','param_2~direction_type']; + const mandatoryKeys = ['suname', 'sudesc', 'priority_queue', 'scheduler', 'min_target_elevation', 'min_calibrator_elevation', 'offset_from', 'offset_to', 'md_sun', 'md_moon', 'md_jupiter', 'param_0~angle1', 'param_0~angle2', 'param_0~direction_type', 'param_1~angle1', 'param_1~angle2', 'param_1~direction_type', 'param_2~angle1', 'param_2~angle2', 'param_2~direction_type']; let tmpMandatoryKeys = []; let tmpRowData = this.state.rowData; - const schema = this.state.paramsSchema; - + const schema = this.state.paramsSchema; + let columnDefinition = this.columnDefinition; let columnDefs = this.state.columnDefs; this.state.gridApi.forEachNode(function (node) { isValidRow = true; - let errorMsg = 'Row # ['+(Number(node.rowIndex)+1) +']'; + let errorMsg = 'Row # [' + (Number(node.rowIndex) + 1) + ']'; tmpMandatoryKeys = []; const rowData = node.data; let isFixedTimeScheduler = false; let hasData = true; - if (rowData) { + if (rowData) { errorMsg = errorMsg + ` (${rowData['suname']}) : ` - for(const key of mandatoryKeys) { - if (rowData[key] === '') { - if ( key === 'suname' ){ - if( rowData['sudesc'] !== ''){ + for (const key of mandatoryKeys) { + if (rowData[key] === '') { + if (key === 'suname') { + if (rowData['sudesc'] !== '') { tmpMandatoryKeys.push(key); - } else { + } else { hasData = false; } - } else if ( key === 'sudesc' ){ - if( rowData['suname'] !== ''){ + } else if (key === 'sudesc') { + if (rowData['suname'] !== '') { tmpMandatoryKeys.push(key); } } else { tmpMandatoryKeys.push(key); } - } else if (key === 'scheduler' && rowData[key] === 'fixed_time' ) { + } else if (key === 'scheduler' && rowData[key] === 'fixed_time') { isFixedTimeScheduler = true; } } const colDefkeys = Object.keys(columnDefinition); - for(const colDefkey of colDefkeys) { + for (const colDefkey of colDefkeys) { let column = _.find(columnDefs, {'field': colDefkey}); if (!column) { const children = _.find(columnDefs, _.flow( _.property('children'), - _.partialRight(_.some, { field: colDefkey }) + _.partialRight(_.some, {field: colDefkey}) )); if (children) { - column = _.find(children.children, function(o) { return o.field === colDefkey; }); + column = _.find(children.children, function (o) { + return o.field === colDefkey; + }); } } if (!column) { @@ -2180,115 +2346,115 @@ export class SchedulingSetCreate extends Component { if (rowData.hasOwnProperty(colDefkey)) { const defProperty = columnDefinition[colDefkey]; if (defProperty.hasOwnProperty('required') && defProperty.required) { - if (_.isEmpty(rowData[colDefkey]+'')) { - errorMsg += column.headerName+", "; + if (_.isEmpty(rowData[colDefkey] + '')) { + errorMsg += column.headerName + ", "; isValidRow = false; } } if (defProperty.hasOwnProperty('type')) { - if (rowData[colDefkey] && typeof rowData[colDefkey] === 'string' ) { + if (rowData[colDefkey] && typeof rowData[colDefkey] === 'string') { rowData[colDefkey] = rowData[colDefkey].trim() } if (rowData[colDefkey] && (typeof rowData[colDefkey] !== defProperty.type && typeof rowData[colDefkey] !== 'string')) { - errorMsg += column.headerName+", "; + errorMsg += column.headerName + ", "; isValidRow = false; - } else { - if (defProperty.hasOwnProperty('minimum')){ - if (rowData[colDefkey]<defProperty.minimum) { - errorMsg += column.headerName+", "; + } else { + if (defProperty.hasOwnProperty('minimum')) { + if (rowData[colDefkey] < defProperty.minimum) { + errorMsg += column.headerName + ", "; isValidRow = false; } } - if (defProperty.hasOwnProperty('maximum')){ - if (rowData[colDefkey]>defProperty.maximum) { - errorMsg += column.headerName+", "; + if (defProperty.hasOwnProperty('maximum')) { + if (rowData[colDefkey] > defProperty.maximum) { + errorMsg += column.headerName + ", "; isValidRow = false; } } - if (defProperty.hasOwnProperty('enum')){ + if (defProperty.hasOwnProperty('enum')) { let containsAll = true; - if (rowData[colDefkey] && rowData[colDefkey] !== '' && rowData[colDefkey].length>0 && defProperty.hasOwnProperty('multiselect') && defProperty.multiselect) { + if (rowData[colDefkey] && rowData[colDefkey] !== '' && rowData[colDefkey].length > 0 && defProperty.hasOwnProperty('multiselect') && defProperty.multiselect) { if (typeof rowData[colDefkey] !== 'object') { rowData[colDefkey] = rowData[colDefkey].split(','); } containsAll = rowData[colDefkey].length > 0 ? rowData[colDefkey].every(element => { return defProperty.enum.indexOf(element) !== -1; - }): true; - } else if (defProperty.hasOwnProperty('multiselect') && !defProperty.multiselect) { + }) : true; + } else if (defProperty.hasOwnProperty('multiselect') && !defProperty.multiselect) { if (rowData[colDefkey] && rowData[colDefkey].length > 0 && !_.includes(defProperty.enum, rowData[colDefkey])) { containsAll = false; } - } else { + } else { if (rowData[colDefkey] && rowData[colDefkey].length > 0 && !_.includes(defProperty.enum, rowData[colDefkey])) { containsAll = false; } } if (!containsAll) { - errorMsg += column.headerName+", "; + errorMsg += column.headerName + ", "; isValidRow = false; } } - if (defProperty.hasOwnProperty('format')){ + if (defProperty.hasOwnProperty('format')) { if (defProperty.format === '-hh:mm:ss') { - const result = Validator.validateTransitOffset(defProperty, rowData[colDefkey],{},null); + const result = Validator.validateTransitOffset(defProperty, rowData[colDefkey], {}, null); if (!_.isEmpty(result)) { - errorMsg += column.headerName+", "; + errorMsg += column.headerName + ", "; isValidRow = false; } - } else if (defProperty.format === 'hh:mm:ss') { + } else if (defProperty.format === 'hh:mm:ss') { if (defProperty.schema) { let errors = []; Validator.validateDurationHHmmss(defProperty.schema, rowData[colDefkey], errors, null); if (errors.length > 0) { - errorMsg += errors[0].message +", "; + errorMsg += errors[0].message + ", "; isValidRow = false; } - } else { - if (!Validator.isValidHHmmss(rowData[colDefkey],false)) { - errorMsg += column.headerName+", "; + } 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); + } else if (defProperty.format === 'dd:mm:ss') { // for angle2 , check the format and implement the validation + const result = Validator.isValidHHmmss(rowData[colDefkey], false); if (!_.isEmpty(result)) { - errorMsg += column.headerName+", "; + errorMsg += column.headerName + ", "; isValidRow = false; } - } else if (defProperty.format === 'date-time') { + } else if (defProperty.format === 'date-time') { if (rowData[colDefkey] !== '' && !moment(rowData[colDefkey], UIConstants.CALENDAR_DATETIME_FORMAT, true).isValid()) { - errorMsg += column.headerName+", "; - isValidRow = false; + errorMsg += column.headerName + ", "; + isValidRow = false; } - } else if (defProperty.format === 'json' && rowData[colDefkey].length>0) { + } else if (defProperty.format === 'json' && rowData[colDefkey].length > 0) { try { - const element = typeof rowData[colDefkey] !== "string"? JSON.stringify(rowData[colDefkey]): rowData[colDefkey]; + const element = typeof rowData[colDefkey] !== "string" ? JSON.stringify(rowData[colDefkey]) : rowData[colDefkey]; JSON.parse(element); } catch (e) { - errorMsg += column.headerName+", "; + errorMsg += column.headerName + ", "; isValidRow = false; } - - } else if (rowData[colDefkey] !== '' && defProperty.format === 'date-time-array') { + + } else if (rowData[colDefkey] !== '' && defProperty.format === 'date-time-array') { let valid = true; - const dateTimeArray = rowData[colDefkey]?rowData[colDefkey].split("|"):[]; + const dateTimeArray = rowData[colDefkey] ? rowData[colDefkey].split("|") : []; dateTimeArray.forEach(dateTimeValues => { if (dateTimeValues !== '') { const dateTimeValueArray = dateTimeValues.split(","); - if (dateTimeValueArray.length === 2){ + if (dateTimeValueArray.length === 2) { dateTimeValueArray.forEach(dateTime => { if (!moment(dateTime, UIConstants.CALENDAR_DATETIME_FORMAT, true).isValid()) { - valid = false; + valid = false; } }); - } else { - valid = false; + } else { + valid = false; } } }); if (!valid) { - errorMsg += column.headerName+", "; - isValidRow = false; + errorMsg += column.headerName + ", "; + isValidRow = false; } } } @@ -2296,147 +2462,147 @@ export class SchedulingSetCreate extends Component { } } } - - if (tmpMandatoryKeys.length !== mandatoryKeys.length) { + + if (tmpMandatoryKeys.length !== mandatoryKeys.length) { //isValidRow = true; - for (var i = 0; i< node.columnController.gridColumns.length; i++) { + for (var i = 0; i < node.columnController.gridColumns.length; i++) { let column = node.columnController.gridColumns[i]; - if (column.colId === '0'){ - } else { + if (column.colId === '0') { + } else { if (rowData.hasOwnProperty(column.colId) && typeof rowData[column.colId] === 'string') { rowData[column.colId] = rowData[column.colId].trim(); } - if (_.includes(tmpMandatoryKeys, column.colId)){ + if (_.includes(tmpMandatoryKeys, column.colId)) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; - } else { - if (column.colId === 'rank'){ - let value = rowData[column.colId]?rowData[column.colId]:'0'; + errorMsg += column.colDef.headerName + ", "; + } else { + if (column.colId === 'rank') { + let value = rowData[column.colId] ? rowData[column.colId] : '0'; rowData[column.colId] = value; - const splitValue = _.split((value+'') , "."); + const splitValue = _.split((value + ''), "."); if (rowData[column.colId] < 0 || rowData[column.colId] > 1 || (splitValue.length > 1 && splitValue[1].length > 4)) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } - } else if (isFixedTimeScheduler && (column.colId === 'timeat') && rowData[column.colId] === ''){ + } else if (isFixedTimeScheduler && (column.colId === 'timeat') && rowData[column.colId] === '') { // isValidRow = false; // errorMsg += column.colDef.headerName+", "; - } else if (column.colId === 'min_target_elevation' || column.colId === 'min_calibrator_elevation' ){ + } else if (column.colId === 'min_target_elevation' || column.colId === 'min_calibrator_elevation') { const skyElevationPattern = /^\d{1,2}(\.\d{1,2})?$/; - if (skyElevationPattern.test(rowData[column.colId])) { + if (skyElevationPattern.test(rowData[column.colId])) { const elevationValue = UnitConverter.convertToRadians(Number(rowData[column.colId])); - if (elevationValue < rowData["elevation_min"] || elevationValue > rowData["elevation_max"] ){ + if (elevationValue < rowData["elevation_min"] || elevationValue > rowData["elevation_max"]) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } - } else { + } else { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } - } else if (column.colId === 'offset_from'){ + } else if (column.colId === 'offset_from') { const tmpTime = _.split(rowData[column.colId], ":"); - if ( typeof rowData[column.colId] === 'undefined' || - (tmpTime.length !== 3 || isNaN(tmpTime[1]) || tmpTime[1]>59 || isNaN(tmpTime[2]) || tmpTime[2]>59)){ + if (typeof rowData[column.colId] === 'undefined' || + (tmpTime.length !== 3 || isNaN(tmpTime[1]) || tmpTime[1] > 59 || isNaN(tmpTime[2]) || tmpTime[2] > 59)) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; - // column.colDef.cellStyle = { backgroundColor: BG_COLOR}; - // rowNoColumn.colDef.cellStyle = { backgroundColor: BG_COLOR}; - } else if ( UnitConverter.getHHmmssToSecs(rowData[column.colId]) < rowData['offset_from_min'] || UnitConverter.getHHmmssToSecs(rowData[column.colId]) > rowData['offset_from_max']) { + errorMsg += column.colDef.headerName + ", "; + // column.colDef.cellStyle = { backgroundColor: BG_COLOR}; + // rowNoColumn.colDef.cellStyle = { backgroundColor: BG_COLOR}; + } else if (UnitConverter.getHHmmssToSecs(rowData[column.colId]) < rowData['offset_from_min'] || UnitConverter.getHHmmssToSecs(rowData[column.colId]) > rowData['offset_from_max']) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } - } else if (column.colId === 'offset_to'){ + } else if (column.colId === 'offset_to') { const tmpTime = _.split(rowData[column.colId], ":"); - if ( typeof rowData[column.colId] === 'undefined' || - (tmpTime.length !== 3 || tmpTime[1]>59 || tmpTime[2]>59)){ + if (typeof rowData[column.colId] === 'undefined' || + (tmpTime.length !== 3 || tmpTime[1] > 59 || tmpTime[2] > 59)) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; - } else if ( UnitConverter.getHHmmssToSecs(rowData[column.colId]) < rowData['offset_to_min'] || UnitConverter.getHHmmssToSecs(rowData[column.colId]) > rowData['offset_to_max']) { + errorMsg += column.colDef.headerName + ", "; + } else if (UnitConverter.getHHmmssToSecs(rowData[column.colId]) < rowData['offset_to_min'] || UnitConverter.getHHmmssToSecs(rowData[column.colId]) > rowData['offset_to_max']) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } - } else if (column.colId === 'md_sun' || column.colId === 'md_moon' || column.colId === 'md_jupiter'){ + } else if (column.colId === 'md_sun' || column.colId === 'md_moon' || column.colId === 'md_jupiter') { const skyMinPattern = /^\d{1,3}(\.\d{1,2})?$/; - if ((Number(rowData[column.colId]) < 0 || Number(rowData[column.colId]) > 180) || !skyMinPattern.test(rowData[column.colId])){ + if ((Number(rowData[column.colId]) < 0 || Number(rowData[column.colId]) > 180) || !skyMinPattern.test(rowData[column.colId])) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } - } else if (_.endsWith(column.colId, "angle1") && !Validator.validateTimeAndAngle(rowData[column.colId])){ + } else if (_.endsWith(column.colId, "angle1") && !Validator.validateTimeAndAngle(rowData[column.colId])) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; - } else if (_.endsWith(column.colId, "angle2") && !Validator.validateAngle(rowData[column.colId])){ + errorMsg += column.colDef.headerName + ", "; + } else if (_.endsWith(column.colId, "angle2") && !Validator.validateAngle(rowData[column.colId])) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; - } else if(_.endsWith(column.colId, "angle3")){ + errorMsg += column.colDef.headerName + ", "; + } else if (_.endsWith(column.colId, "angle3")) { if (isNaN(rowData[column.colId])) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } - } else if(_.endsWith(column.colId, "stations")){ + } else if (_.endsWith(column.colId, "stations")) { let sgCellValue = rowData[column.colId]; if (!_.includes(sgCellValue, '|')) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; - } else { - let stationGroups = _.split(sgCellValue, "|"); + errorMsg += column.colDef.headerName + ", "; + } else { + let stationGroups = _.split(sgCellValue, "|"); stationGroups.map(stationGroup => { let sgValue = _.split(stationGroup, ":"); - if (rowData['suname'] !== '' && rowData['sudesc'] !== '' && (sgValue[1] === 'undefined' || sgValue[1] === 'NaN' || Number(sgValue[1]) < 0 )){ + if (rowData['suname'] !== '' && rowData['sudesc'] !== '' && (sgValue[1] === 'undefined' || sgValue[1] === 'NaN' || Number(sgValue[1]) < 0)) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } return stationGroup; }); } - } else { - if (rowData.hasOwnProperty(column.colId) && rowData[column.colId] !== '' && column.colDef.cellEditorParams ) { + } else { + if (rowData.hasOwnProperty(column.colId) && rowData[column.colId] !== '' && column.colDef.cellEditorParams) { if (!_.includes(column.colDef.cellEditorParams.values, rowData[column.colId])) { - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; isValidRow = false; } - } else if (rowData.hasOwnProperty(column.colId) && rowData[column.colId] !== '') { + } else if (rowData.hasOwnProperty(column.colId) && rowData[column.colId] !== '') { if (column.colDef.minimum && Number(rowData[column.colId]) < column.colDef.minimum) { - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; isValidRow = false; } if (isValidRow && column.colDef.maximum && Number(rowData[column.colId]) >= column.colDef.maximum) { - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; isValidRow = false; - } + } - if (column.colDef.type && column.colDef.type === 'integer') { + if (column.colDef.type && column.colDef.type === 'integer') { //TODO: integer not allowed rowData[column.colId] = parseInt(rowData[column.colId]); - } else if (column.colDef.type && column.colDef.type === 'number') { + } else if (column.colDef.type && column.colDef.type === 'number') { rowData[column.colId] = Number(rowData[column.colId]); } } } - + if (column.colDef.max_length) { let sgCellValue = rowData[column.colId]; - if (sgCellValue && sgCellValue !== '' && sgCellValue.length>column.colDef.max_length) { + if (sgCellValue && sgCellValue !== '' && sgCellValue.length > column.colDef.max_length) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } } } } - let prefix = column.colId.split("~"); - if(schema.properties && prefix[0] && schema.properties[prefix[0]]) { + let prefix = column.colId.split("~"); + if (schema.properties && prefix[0] && schema.properties[prefix[0]]) { const type = schema.properties[prefix[0]].type; const minvalue = schema.properties[prefix[0]].minimum; if (type && type === 'integer') { if (isNaN(rowData[column.colId])) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; - } else if (minvalue && rowData[column.colId] < minvalue) { + errorMsg += column.colDef.headerName + ", "; + } else if (minvalue && rowData[column.colId] < minvalue) { isValidRow = false; - errorMsg += column.colDef.headerName+", "; + errorMsg += column.colDef.headerName + ", "; } } } } - // When no values entered or if the field doesn't have value for existing record, AG-grid API returns undefined. + // When no values entered or if the field doesn't have value for existing record, AG-grid API returns undefined. // So added validation message for fixed_time scheduler. if (isFixedTimeScheduler && rowData['timeat'] === undefined) { // isValidRow = false; @@ -2444,9 +2610,9 @@ export class SchedulingSetCreate extends Component { } } } - if(hasData) { - if (isValidRow) { - validCount++; + if (hasData) { + if (isValidRow) { + validCount++; tmpRowData[node.rowIndex]['isValid'] = true; } else { inValidCount++; @@ -2465,9 +2631,11 @@ export class SchedulingSetCreate extends Component { this.showIcon = true; this.dialogMsg = 'No valid Scheduling Unit found !'; this.dialogType = 'warning'; - this.onClose = () => {this.setState({confirmDialogVisible: false});}; + this.onClose = () => { + this.setState({confirmDialogVisible: false}); + }; this.setState({confirmDialogVisible: true}); - } else { + } else { this.setState({ validCount: validCount, inValidCount: inValidCount, @@ -2485,15 +2653,20 @@ export class SchedulingSetCreate extends Component { this.setState({confirmDialogVisible: false}); }; this.actions = [ - {id: "copy_warning_btn", style: {float: 'left', backgroundColor: '#ffbb08', borderColor: '#ffbb08'}, title: "Copy Warning", callback: this.copyWarningContent}, - {id: "yes_btn", title: "Yes", callback: this.saveSU}, - {id: "cancel_btn", title: "No", className:'act-btn-cancel', callback: this.onCancel},]; + { + id: "copy_warning_btn", + style: {float: 'left', backgroundColor: '#ffbb08', borderColor: '#ffbb08'}, + title: "Copy Warning", + callback: this.copyWarningContent + }, + {id: "yes_btn", title: "Yes", callback: this.saveSU}, + {id: "cancel_btn", title: "No", className: 'act-btn-cancel', callback: this.onCancel},]; this.dialogType = "confirmation"; this.dialogHeader = "Save Scheduling Unit(s)"; this.dialogMsg = "Some of the Scheduling Unit(s) has invalid data, Do you want to ignore and save valid Scheduling Unit(s) only?"; this.dialogContent = this.showDialogContent; } - } + } /** * Copy warning content to clipboard @@ -2507,109 +2680,126 @@ export class SchedulingSetCreate extends Component { /** * Show the content in custom dialog */ - showDialogContent(){ - if (typeof this.state.errorDisplay === 'undefined' || this.state.errorDisplay.length === 0 ){ + showDialogContent() { + if (typeof this.state.errorDisplay === 'undefined' || this.state.errorDisplay.length === 0) { return ""; - } else { - return <> <br/>Invalid Rows:- Row # , Name and Invalid columns<br/>{this.state.errorDisplay && this.state.errorDisplay.length>0 && - this.state.errorDisplay.map((msg, index) => ( - <React.Fragment key={index+10} > - <span key={'label1-'+ index}>{msg}</span> <br /> - </React.Fragment> - ))} </> + } else { + return <> <br/>Invalid Rows:- Row # , Name and Invalid + columns<br/>{this.state.errorDisplay && this.state.errorDisplay.length > 0 && + this.state.errorDisplay.map((msg, index) => ( + <React.Fragment key={index + 10}> + <span key={'label1-' + index}>{msg}</span> <br/> + </React.Fragment> + ))} </> } } /** * prepare Constraint Error dialog - * @returns + * @returns */ showConstraintErrorDialog() { const keys = Object.keys(this.constraintsValidationMessage); - return <> - <React.Fragment > - <br/><tr style={{backgroundColor: 'lightgray', textAlign: 'center'}}><th style={{width: '5em'}}> <span>Row Id </span></th> - <th style={{width: '20em'}}> <span>SchedulingUnit Name </span></th> - <th style={{width:'45em', textAlign: 'center'}}> <span > Error Details</span> </th></tr> + return <> + <React.Fragment> + <br/> + <tr style={{backgroundColor: 'lightgray', textAlign: 'center'}}> + <th style={{width: '5em'}}><span>Row Id </span></th> + <th style={{width: '20em'}}><span>SchedulingUnit Name </span></th> + <th style={{width: '45em', textAlign: 'center'}}><span> Error Details</span></th> + </tr> + </React.Fragment> + {keys.map((key, index) => ( + <React.Fragment key={index + 10}> + <tr style={{backgroundColor: index % 2 === 0 ? '' : '#e6e5e5'}}> + <td style={{width: '5em', paddingLeft: '7px'}}><span + key={'label1-' + index}>{(parseInt(key) + 1)} </span></td> + <td style={{width: '20em', paddingLeft: '7px'}}><span + key={'label1-' + index}>{this.constraintsValidationMessage[key]['suName']} </span></td> + <td style={{width: '45em'}}><span + key={'label1-' + index}>{this.constraintsValidationMessage[key]['message']}</span></td> + </tr> </React.Fragment> - {keys.map((key, index) => ( - <React.Fragment key={index+10} > - <tr style={{backgroundColor: index % 2 === 0 ? '':'#e6e5e5'}}> <td style={{width: '5em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{(parseInt(key)+1)} </span></td> - <td style={{width: '20em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{this.constraintsValidationMessage[key]['suName']} </span></td> - <td style={{width:'45em'}}><span key={'label1-'+ index}>{this.constraintsValidationMessage[key]['message']}</span> </td></tr> - </React.Fragment> - ))} - </> + ))} + </> } /** * prepare Specification Error dialog - * @returns + * @returns */ showSpecificationErrorDialog() { const keys = Object.keys(this.specificationValidationMessage); return <> - <React.Fragment > - <br/><tr style={{backgroundColor: 'lightgray', textAlign: 'center'}}><th style={{width: '5em'}}> <span>Row Id </span></th> - <th style={{width: '20em'}}> <span>SchedulingUnit Name </span></th> - <th style={{width:'45em', textAlign: 'center'}}> <span > Error Details</span> </th></tr> + <React.Fragment> + <br/> + <tr style={{backgroundColor: 'lightgray', textAlign: 'center'}}> + <th style={{width: '5em'}}><span>Row Id </span></th> + <th style={{width: '20em'}}><span>SchedulingUnit Name </span></th> + <th style={{width: '45em', textAlign: 'center'}}><span> Error Details</span></th> + </tr> + </React.Fragment> + {keys.map((key, index) => ( + <React.Fragment key={index + 10}> + <tr style={{backgroundColor: index % 2 === 0 ? '' : '#e6e5e5'}}> + <td style={{width: '5em', paddingLeft: '7px'}}><span + key={'label1-' + index}>{(parseInt(key) + 1)} </span></td> + <td style={{width: '20em', paddingLeft: '7px'}}><span + key={'label1-' + index}>{this.specificationValidationMessage[key]['suName']} </span></td> + <td style={{width: '45em'}}><span + key={'label1-' + index}>{this.specificationValidationMessage[key]['message']}</span></td> + </tr> </React.Fragment> - {keys.map((key, index) => ( - <React.Fragment key={index+10} > - <tr style={{backgroundColor: index % 2 === 0 ? '':'#e6e5e5'}}> <td style={{width: '5em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{(parseInt(key)+1)} </span></td> - <td style={{width: '20em', paddingLeft: '7px'}}> <span key={'label1-'+ index}>{this.specificationValidationMessage[key]['suName']} </span></td> - <td style={{width:'45em'}}><span key={'label1-'+ index}>{this.specificationValidationMessage[key]['message']}</span> </td></tr> - </React.Fragment> - ))} - </> + ))} + </> } /** * Prepare Scheduling Unit from Excel table - * @param {*} suRow - * @returns + * @param {*} suRow + * @returns */ async prepareObservStrategyFromExcelValue(suRow) { - let colKeys = Object.keys(suRow); + let colKeys = Object.keys(suRow); let paramsOutput = {}; let parameters = _.map(this.state.observStrategy.template.parameters, 'name'); const schema = this.state.paramsSchema; - for(const colKey of colKeys) { - if (_.includes(["true","false", "FALSE", "TRUE"], suRow[colKey])) { - suRow[colKey] = JSON.parse(_.lowerCase(suRow[colKey])); + for (const colKey of colKeys) { + if (_.includes(["true", "false", "FALSE", "TRUE"], suRow[colKey])) { + suRow[colKey] = JSON.parse(_.lowerCase(suRow[colKey])); } let prefix = colKey.split("~"); - const durationKey = prefix.length === 2 ? prefix[1]: colKey; + const durationKey = prefix.length === 2 ? prefix[1] : colKey; // if(colKey.startsWith('param_') && prefix.length > 1) { - if(prefix.length > 1 && parameters.indexOf(prefix[0])>=0 ) { - var res = Object.keys(suRow).filter(objKey => { + if (prefix.length > 1 && parameters.indexOf(prefix[0]) >= 0) { + var res = Object.keys(suRow).filter(objKey => { const tmp = objKey.split("~"); - return tmp[0] === prefix[0]; + return tmp[0] === prefix[0]; }); - if(res && res.length > 1) { + if (res && res.length > 1) { let res = paramsOutput[prefix[0]]; - if(prefix[1] === 'angle1' || prefix[1] === 'angle2') { + if (prefix[1] === 'angle1' || prefix[1] === 'angle2') { suRow[colKey] = UnitConverter.parseAngle(suRow[colKey]); } - if(res) { + if (res) { res[prefix[1]] = suRow[colKey]; - } else { + } else { res = {}; res[prefix[1]] = suRow[colKey]; paramsOutput[prefix[0]] = res; } - } else { - if(colKey.endsWith('Beamformers')){ + } else { + if (colKey.endsWith('Beamformers')) { let result = suRow[colKey]; - if(result['param_0']) { + if (result['param_0']) { paramsOutput[prefix[0]] = result['param_0']; - } else { + } else { paramsOutput[prefix[0]] = result; } - } else if(_.includes(this.durationList, durationKey)){ - paramsOutput[prefix[0]] = suRow[colKey] === undefined? 0:UnitConverter.getHHmmssToSecs(suRow[colKey]); - } else { - if(schema.properties && schema.properties[prefix[0]]) { + } else if (_.includes(this.durationList, durationKey)) { + paramsOutput[prefix[0]] = suRow[colKey] === undefined ? 0 : UnitConverter.getHHmmssToSecs(suRow[colKey]); + } else { + if (schema.properties && schema.properties[prefix[0]]) { const type = schema.properties[prefix[0]].type; if (type === 'array' && schema.properties[prefix[0]].items && schema.properties[prefix[0]]['items'].type && schema.properties[prefix[0]]['items'].type === 'integer') { @@ -2618,26 +2808,26 @@ export class SchedulingSetCreate extends Component { paramsOutput[prefix[0]] = suRow[colKey] === '' ? []:suRow[colKey].split(","); } else if(type === 'integer' && !isNaN(parseInt(suRow[colKey]))) { paramsOutput[prefix[0]] = parseInt(suRow[colKey]); - } else if(type === 'numeric' && !isNaN(parseInt(suRow[colKey]))) { + } else if (type === 'numeric' && !isNaN(parseInt(suRow[colKey]))) { paramsOutput[prefix[0]] = parseInt(suRow[colKey]); - } else { + } else { paramsOutput[prefix[0]] = suRow[colKey]; } - } else { + } else { paramsOutput[prefix[0]] = suRow[colKey]; } } } - } else { + } else { paramsOutput[prefix[0]] = suRow[colKey]; - } - } - this.setState({paramsOutput : paramsOutput}) + } + } + this.setState({paramsOutput: paramsOutput}) let observationIdSet = this.state.observationIdSet; let observStrategy = _.cloneDeep(this.state.observStrategy); observationIdSet.push(observStrategy.id); const $refs = await $RefParser.resolve(observStrategy.template); - observStrategy.template.parameters.forEach(async(param, index) => { + observStrategy.template.parameters.forEach(async (param, index) => { let refIndex = 0; // for (const ref of param.refs) { param.refs.forEach(() => { @@ -2651,21 +2841,21 @@ export class SchedulingSetCreate extends Component { /** * Prepare Constraint from Excel table - * @param {*} suRow - * @returns + * @param {*} suRow + * @returns */ async prepareConstraintFromExcelValue(suRow) { let between = this.getBetweenDateValue(suRow.between); - let notbetween = this.getBetweenDateValue(suRow.notbetween); + let notbetween = this.getBetweenDateValue(suRow.notbetween); let constraint = null; - if (suRow.id > 0){ - let schedulingUnit = _.find(this.state.schedulingUnitList, {'id': suRow.id}); + if (suRow.id > 0) { + let schedulingUnit = _.find(this.state.schedulingUnitList, {'id': suRow.id}); constraint = schedulingUnit.scheduling_constraints_doc; - } - if ( constraint === null || constraint === 'undefined' || constraint === {}){ + } + if (constraint === null || constraint === 'undefined' || constraint === {}) { // constraint = this.state.schedulingConstraintsDoc; } - if(!constraint) { + if (!constraint) { // let schedulingUnit = await ScheduleService.getSchedulingUnitDraftById(1); // constraint = (schedulingUnit)? schedulingUnit.scheduling_constraints_doc : {}; constraint = this.defaultConstraintDoc; @@ -2674,55 +2864,55 @@ export class SchedulingSetCreate extends Component { constraint['scheduler'] = suRow.scheduler; // Remove empty time constraint properties if (this.isNotEmpty(suRow.timeat)) { - constraint.time.at = `${moment(suRow.timeat).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, { trim: false })}Z`; - } else { + constraint.time.at = `${moment(suRow.timeat).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, {trim: false})}Z`; + } else { delete constraint.time.at; } if (this.isNotEmpty(suRow.timeafter)) { - constraint.time.after = `${moment(suRow.timeafter).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, { trim: false })}Z`; + constraint.time.after = `${moment(suRow.timeafter).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, {trim: false})}Z`; } else { delete constraint.time.after; } if (this.isNotEmpty(suRow.timebefore)) { - constraint.time.before = `${moment(suRow.timebefore).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, { trim: false })}Z`; + constraint.time.before = `${moment(suRow.timebefore).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, {trim: false})}Z`; } else { delete constraint.time.before; } - if (this.isNotEmpty(between)){ + if (this.isNotEmpty(between)) { constraint.time.between = between; - } else { + } else { delete constraint.time.between; } - if (this.isNotEmpty(notbetween)){ - constraint.time.not_between = notbetween; - } else { + if (this.isNotEmpty(notbetween)) { + constraint.time.not_between = notbetween; + } else { delete constraint.time.not_between; } this.state.daily.forEach(daily => { - if (_.includes(suRow.daily, daily)){ + if (_.includes(suRow.daily, daily)) { constraint.daily[daily] = true; - } else { + } else { constraint.daily[daily] = false; } - }) + }) let min_distance_res = {}; - min_distance_res['sun'] = isNaN(suRow.md_sun)?0:suRow.md_sun; - min_distance_res['moon'] = isNaN(suRow.md_moon)?0:suRow.md_moon; - min_distance_res['jupiter'] = isNaN(suRow.md_jupiter)?0:suRow.md_jupiter; + min_distance_res['sun'] = isNaN(suRow.md_sun) ? 0 : suRow.md_sun; + min_distance_res['moon'] = isNaN(suRow.md_moon) ? 0 : suRow.md_moon; + min_distance_res['jupiter'] = isNaN(suRow.md_jupiter) ? 0 : suRow.md_jupiter; constraint.sky.min_distance = min_distance_res; - + let transit_offset_res = {}; transit_offset_res['from'] = UnitConverter.getHHmmssToSecs(suRow.offset_from); transit_offset_res['to'] = UnitConverter.getHHmmssToSecs(suRow.offset_to); - if (transit_offset_res){ - constraint.sky.transit_offset= transit_offset_res; + if (transit_offset_res) { + constraint.sky.transit_offset = transit_offset_res; } - - constraint.sky.min_elevation.target = isNaN(suRow.min_target_elevation)?0:suRow.min_target_elevation; - constraint.sky.min_elevation.calibrator = isNaN(suRow.min_calibrator_elevation)?0:suRow.min_calibrator_elevation; - let ref_pointing = { enabled: false, pointing: {target:"-"}}; + constraint.sky.min_elevation.target = isNaN(suRow.min_target_elevation) ? 0 : suRow.min_target_elevation; + constraint.sky.min_elevation.calibrator = isNaN(suRow.min_calibrator_elevation) ? 0 : suRow.min_calibrator_elevation; + + let ref_pointing = {enabled: false, pointing: {target: "-"}}; ref_pointing.enabled = suRow.ref_pointing_enabled; ref_pointing.pointing.angle1 = UnitConverter.parseAngle(suRow.ref_pointing_angle1); ref_pointing.pointing.angle2 = UnitConverter.parseAngle(suRow.ref_pointing_angle2); @@ -2733,54 +2923,55 @@ export class SchedulingSetCreate extends Component { return constraint; } - getStationGroupFromRowData (rowData, stationField) { + getStationGroupFromRowData(rowData, stationField) { let sgCellValue = rowData[stationField]; let tmpStationGroups = []; let tmpStationGroup = {}; - let stationGroups = _.split(sgCellValue, "|"); - stationGroups.map(stationGroup =>{ + let stationGroups = _.split(sgCellValue, "|"); + stationGroups.map(stationGroup => { tmpStationGroup = {}; let sgValue = _.split(stationGroup, ":"); - if (sgValue && sgValue[0].length>0){ - let stationArray = _.split(sgValue[0], ","); - tmpStationGroup['stations'] = stationArray; - let missingStation = (sgValue[1])?sgValue[1]:0; - tmpStationGroup['max_nr_missing'] = Number(missingStation); - tmpStationGroups.push(tmpStationGroup); + if (sgValue && sgValue[0].length > 0) { + let stationArray = _.split(sgValue[0], ","); + tmpStationGroup['stations'] = stationArray; + let missingStation = (sgValue[1]) ? sgValue[1] : 0; + tmpStationGroup['max_nr_missing'] = Number(missingStation); + tmpStationGroups.push(tmpStationGroup); } return stationGroup; }); return tmpStationGroups; } - /** + + /** * Save/Update Scheduling Unit(s) */ async saveSU() { let newSUCount = 0; let existingSUCount = 0; let isUpdated = true; - try{ + try { this.setState({ confirmDialogVisible: false, showSpinner: true - }); + }); let newSU = this.state.schedulingUnit; let suStatus = []; let processCount = 0; const validRows = _.filter(this.state.rowData, {"isValid": true, "isDirty": true}); this.setState({progressPercent: 0}); - for(const validRow of validRows){ + for (const validRow of validRows) { let suRow = _.cloneDeep(validRow); delete suRow['custId']; - if (!suRow['isValid']){ + if (!suRow['isValid']) { continue; } - let observStrategy = await this.prepareObservStrategyFromExcelValue(suRow); - - const queue = _.find(this.priorityQueuelist, {value:suRow.priority_queue}); - if (queue) { - suRow.priority_queue = queue.value; - } else { + let observStrategy = await this.prepareObservStrategyFromExcelValue(suRow); + + const queue = _.find(this.priorityQueuelist, {value: suRow.priority_queue}); + if (queue) { + suRow.priority_queue = queue.value; + } else { delete suRow['priority_queue']; } //Stations @@ -2789,14 +2980,14 @@ export class SchedulingSetCreate extends Component { if (this.state.observStrategy) { tmpObservStrategy = _.cloneDeep(this.state.observStrategy); const $refs = await $RefParser.resolve(tmpObservStrategy.template); - tmpObservStrategy.template.parameters.forEach(async(param, index) => { + tmpObservStrategy.template.parameters.forEach(async (param, index) => { let refIndex = 0; for (const ref of param.refs) { let station_groups = []; if (ref.includes("station_groups")) { station_groups = this.getStationGroupFromRowData(suRow, param.name);//'Station Groups - '+taskName $refs.set(tmpObservStrategy.template.parameters[index]['refs'][refIndex], station_groups); - } else { + } else { $refs.set(tmpObservStrategy.template.parameters[index]['refs'][refIndex], this.state.paramsOutput[param.name]); } refIndex++; @@ -2804,31 +2995,31 @@ export class SchedulingSetCreate extends Component { }); } - let taskDrafts =[]; - if ( suRow.id === 0) { + let taskDrafts = []; + if (suRow.id === 0) { for (const taskName in observStrategy.template.tasks) { let task = observStrategy.template.tasks[taskName]; if (task.specifications_doc.station_configuration?.station_groups) { - let sgCellValue = suRow['Station Groups - '+taskName]; - let tmpStationGroups = []; - let tmpStationGroup = {}; - let stationGroups = _.split(sgCellValue, "|"); - stationGroups.map(stationGroup =>{ - tmpStationGroup = {}; - let sgValue = _.split(stationGroup, ":"); - if (sgValue && sgValue[0].length>0){ - let stationArray = _.split(sgValue[0], ","); - tmpStationGroup['stations'] = stationArray; - let missingStation = (sgValue[1])?sgValue[1]:0; - tmpStationGroup['max_nr_missing'] = Number(missingStation); - tmpStationGroups.push(tmpStationGroup); - } - return stationGroup; - }); - //task.specifications_doc.station_configuration.station_groups = tmpStationGroups; + let sgCellValue = suRow['Station Groups - ' + taskName]; + let tmpStationGroups = []; + let tmpStationGroup = {}; + let stationGroups = _.split(sgCellValue, "|"); + stationGroups.map(stationGroup => { + tmpStationGroup = {}; + let sgValue = _.split(stationGroup, ":"); + if (sgValue && sgValue[0].length > 0) { + let stationArray = _.split(sgValue[0], ","); + tmpStationGroup['stations'] = stationArray; + let missingStation = (sgValue[1]) ? sgValue[1] : 0; + tmpStationGroup['max_nr_missing'] = Number(missingStation); + tmpStationGroups.push(tmpStationGroup); + } + return stationGroup; + }); + //task.specifications_doc.station_configuration.station_groups = tmpStationGroups; } } - } else { + } else { let taskData = _.find(this.state.schedulingUnitList, {'id': suRow.id}); if (taskData) { taskDrafts = taskData.taskDrafts; @@ -2836,72 +3027,71 @@ export class SchedulingSetCreate extends Component { for (const task of taskDrafts) { //let task = observStrategy.template.tasks[taskName]; if (task.specifications_doc.station_configuration?.station_groups) { - let sgCellValue = suRow[('Station Groups - '+task.name)]; - let tmpStationGroups = []; - let tmpStationGroup = {}; - let stationGroups = _.split(sgCellValue, "|"); - stationGroups.map(stationGroup =>{ - tmpStationGroup = {}; - let sgValue = _.split(stationGroup, ":"); - if (sgValue && sgValue[0].length>0){ - let stationArray = _.split(sgValue[0], ","); - tmpStationGroup['stations'] = stationArray; - let missingStation = (sgValue[1])?sgValue[1]:0; - tmpStationGroup['max_nr_missing'] = Number(missingStation); - tmpStationGroups.push(tmpStationGroup); - } - return stationGroup; - }); - //task.specifications_doc.station_configuration.station_groups = tmpStationGroups; + let sgCellValue = suRow[('Station Groups - ' + task.name)]; + let tmpStationGroups = []; + let tmpStationGroup = {}; + let stationGroups = _.split(sgCellValue, "|"); + stationGroups.map(stationGroup => { + tmpStationGroup = {}; + let sgValue = _.split(stationGroup, ":"); + if (sgValue && sgValue[0].length > 0) { + let stationArray = _.split(sgValue[0], ","); + tmpStationGroup['stations'] = stationArray; + let missingStation = (sgValue[1]) ? sgValue[1] : 0; + tmpStationGroup['max_nr_missing'] = Number(missingStation); + tmpStationGroups.push(tmpStationGroup); + } + return stationGroup; + }); + //task.specifications_doc.station_configuration.station_groups = tmpStationGroups; } } } let isNewConstraint = false; let newConstraint = {}; let constraint = await this.prepareConstraintFromExcelValue(suRow); - if (suRow.id === 0){ + if (suRow.id === 0) { isNewConstraint = true; } - + UnitConverter.degreeToRadians(constraint.sky); - - if (isNewConstraint){ + + if (isNewConstraint) { newSU['scheduling_constraints_doc'] = constraint; } - - if (suRow.id === 0){ + + if (suRow.id === 0) { newConstraint['scheduling_constraints_doc'] = constraint; newConstraint['id'] = this.state.constraintId; - newConstraint['constraint'] = {'url':''}; + newConstraint['constraint'] = {'url': ''}; newConstraint.constraint.url = this.state.constraintUrl; } let suUpdateStatus = {}; - if (suRow.id > 0 && this.isNotEmpty(suRow.suname) && this.isNotEmpty(suRow.sudesc)){ + if (suRow.id > 0 && this.isNotEmpty(suRow.suname) && this.isNotEmpty(suRow.sudesc)) { newSU = _.find(this.state.schedulingUnitList, {'id': suRow.id}); newSU['name'] = suRow.suname; newSU['description'] = suRow.sudesc; newSU['output_pinned'] = this.state.selectedProject[0].auto_pin; - newSU['rank'] = suRow.rank && suRow.rank.length>0? _.round(parseFloat(suRow.rank), 4):suRow.rank; - if (suRow.priority_queue ) { + newSU['rank'] = suRow.rank && suRow.rank.length > 0 ? _.round(parseFloat(suRow.rank), 4) : suRow.rank; + if (suRow.priority_queue) { newSU['priority_queue'] = (_.find(this.priorityQueuelist, {"value": suRow.priority_queue})).url; } - + suUpdateStatus['suName'] = suRow.suname; suUpdateStatus['action'] = 'Update'; newSU.specifications_doc['parameters'] = tmpObservStrategy.template.parameters; newSU.specifications_doc['tasks'] = tmpObservStrategy.template.tasks; let updateSu = await ScheduleService.updateSchedulingUnitDraft(newSU); - suUpdateStatus['suStatus']= "Success"; + suUpdateStatus['suStatus'] = "Success"; suUpdateStatus['suId'] = updateSu.data.id; if (updateSu && updateSu['message'] === 'Success') { - suUpdateStatus['suStatus']= "Success"; + suUpdateStatus['suStatus'] = "Success"; existingSUCount++; - } else { - suUpdateStatus['suStatus']= "Failed"; + } else { + suUpdateStatus['suStatus'] = "Failed"; } - - } - else if (suRow.id === 0 && this.isNotEmpty(suRow.suname) && this.isNotEmpty(suRow.sudesc)){ + + } else if (suRow.id === 0 && this.isNotEmpty(suRow.suname) && this.isNotEmpty(suRow.sudesc)) { let newSchedulueUnit = { description: suRow.sudesc, name: suRow.suname, @@ -2915,64 +3105,79 @@ export class SchedulingSetCreate extends Component { suUpdateStatus['suName'] = suRow.suname; suUpdateStatus['action'] = 'Create'; let outputParam = {}; - for ( const parameter of this.state.observStrategy.template.parameters) { - for (const ref of parameter.refs) { + for (const parameter of this.state.observStrategy.template.parameters) { + for (const ref of parameter.refs) { let paramPath = ref.replaceAll('#/', ''); paramPath = paramPath.replaceAll('/', '.'); const value = _.get(tmpObservStrategy.template, paramPath); - if (value !== undefined) { - outputParam = _.set(outputParam, paramPath, value); + if (value !== undefined) { + outputParam = _.set(outputParam, paramPath, value); } } } outputParam['scheduling_constraints_doc'] = constraint; let savedSU = await ScheduleService.saveSchedulingUnit(newSchedulueUnit, outputParam); if (savedSU && savedSU['message'] === 'Success') { - suUpdateStatus['suStatus']= "Success"; + suUpdateStatus['suStatus'] = "Success"; suUpdateStatus['suId'] = savedSU.data.id; newSUCount++; - } else { - suUpdateStatus['suStatus']= "Failed"; + } else { + suUpdateStatus['suStatus'] = "Failed"; } } suStatus.push(suUpdateStatus); - this.setState({progressPercent: Math.round((++processCount/validRows.length)*100)}); + this.setState({progressPercent: Math.round((++processCount / validRows.length) * 100)}); } - - if ((newSUCount+existingSUCount) > 0){ - this.setState({suStatus:suStatus}); + + if ((newSUCount + existingSUCount) > 0) { + this.setState({suStatus: suStatus}); this.dialogType = "success"; this.dialogHeader = "Success"; this.showIcon = true; this.dialogWidth = "60vw"; if (isUpdated) { - this.dialogMsg = newSUCount+' Scheduling Units are created & '+existingSUCount+' Scheduling Units are updated successfully.'; - } else { + this.dialogMsg = newSUCount + ' Scheduling Units are created & ' + existingSUCount + ' Scheduling Units are updated successfully.'; + } else { this.dialogHeader = "Warning"; - this.dialogMsg = newSUCount+' Scheduling Units are created & '+existingSUCount+' Scheduling Units are updated successfully, and there are some Schedule Unit failed to create/update'; + this.dialogMsg = newSUCount + ' Scheduling Units are created & ' + existingSUCount + ' Scheduling Units are updated successfully, and there are some Schedule Unit failed to create/update'; } - this.actions= null; + this.actions = null; this.dialogContent = this.getSchedulingDialogContent; this.onCancel = null; this.onClose = this.reset; this.callBackFunction = null; - this.setState({isDirty : false, showSpinner: false, confirmDialogVisible: true, /*dialog: dialog,*/ isAGLoading: true, copyHeader: false, rowData: []}); + this.setState({ + isDirty: false, + showSpinner: false, + confirmDialogVisible: true, /*dialog: dialog,*/ + isAGLoading: true, + copyHeader: false, + rowData: [] + }); publish('edit-dirty', false); - } else { + } else { this.setState({isDirty: false, showSpinner: false,}); publish('edit-dirty', false); - this.growl.show({severity: 'warn', summary: 'Warning', detail: 'No new/changes in the Scheduling Units list to create/update'}); + this.growl.show({ + severity: 'warn', + summary: 'Warning', + detail: 'No new/changes in the Scheduling Units list to create/update' + }); } - } catch(err){ + } catch (err) { console.error(err); - this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to create/update Scheduling Units'}); + this.growl.show({ + severity: 'error', + summary: 'Error Occured', + detail: 'Unable to create/update Scheduling Units' + }); this.setState({showSpinner: false}); } } - + /** * Prepare constraint from SU row - * @param {Json Object} suRowData + * @param {Json Object} suRowData * @returns Json Object - constraint value */ async getConstraintValueFromGrid(suRowData) { @@ -2983,24 +3188,24 @@ export class SchedulingSetCreate extends Component { /** * Prepare Specification doc from SU row - * @param {Json Object} suRowData + * @param {Json Object} suRowData * @returns Json Object - specification value */ async getUpdatedSpecificationDoc(suRowData) { //Prepare output Param - await this.prepareObservStrategyFromExcelValue(suRowData); + await this.prepareObservStrategyFromExcelValue(suRowData); let tmpObservStrategy = null; if (this.state.observStrategy) { tmpObservStrategy = _.cloneDeep(this.state.observStrategy); const $refs = await $RefParser.resolve(tmpObservStrategy.template); - tmpObservStrategy.template.parameters.forEach(async(param, index) => { + tmpObservStrategy.template.parameters.forEach(async (param, index) => { let refIndex = 0; for (const ref of param.refs) { let station_groups = []; if (ref.includes("station_groups")) { station_groups = this.getStationGroupFromRowData(suRowData, param.name); $refs.set(tmpObservStrategy.template.parameters[index]['refs'][refIndex], station_groups); - } else { + } else { $refs.set(tmpObservStrategy.template.parameters[index]['refs'][refIndex], this.state.paramsOutput[param.name]); } refIndex++; @@ -3018,26 +3223,29 @@ export class SchedulingSetCreate extends Component { async validateSpecificationsDoc(rowIndex, suRowData, isValid) { let validationMessage = ''; let observStrategy = await this.getUpdatedSpecificationDoc(_.cloneDeep(suRowData)); - if (suRowData['suname']!=="" && suRowData['sudesc']!=="" && observStrategy) { + if (suRowData['suname'] !== "" && suRowData['sudesc'] !== "" && observStrategy) { const promises = []; promises.push(ScheduleService.validateSpecificationsDoc(observStrategy.scheduling_unit_template, observStrategy.template)) const validation_result = await Promise.all(promises); - if(validation_result.length > 0 && !validation_result[0]['valid']) { + if (validation_result.length > 0 && !validation_result[0]['valid']) { validationMessage = "Task Parameters specification is not valid"; this.isSpecificationValid = false; - this.setState({validSpecification: validation_result[0]['valid'], specificationValidationMessage: validationMessage}); + this.setState({ + validSpecification: validation_result[0]['valid'], + specificationValidationMessage: validationMessage + }); this.specificationValidationMessage[rowIndex] = { message: validation_result[0]['message'].replace('Could not validate the specifications_doc. Error:', ''), suName: suRowData.suname }; - } else { + } else { delete this.specificationValidationMessage[rowIndex]; - if(Object.keys(this.specificationValidationMessage).length>0) { + if (Object.keys(this.specificationValidationMessage).length > 0) { validationMessage = "Task Parameters specification is not valid"; } this.setState({specificationValidationMessage: validationMessage}); } - } + } } /** @@ -3045,23 +3253,26 @@ export class SchedulingSetCreate extends Component { */ async validateSchedulingConstraints(rowIndex, suRowData) { let constraint = await this.getConstraintValueFromGrid(_.cloneDeep(suRowData)); - if (suRowData['suname']!=="" && suRowData['sudesc']!=="" && constraint) { + if (suRowData['suname'] !== "" && suRowData['sudesc'] !== "" && constraint) { const promises = []; let validationMessage = ''; promises.push(ScheduleService.validateSpecificationsDoc(this.constraintSchema.url, constraint)) const validation_result = await Promise.all(promises); - if(validation_result.length > 0 && !validation_result[0]['valid']) { + if (validation_result.length > 0 && !validation_result[0]['valid']) { this.constraintsValidationMessage[rowIndex] = { message: validation_result[0]['message'].replaceAll('Could not validate the specifications_doc. Error:', ''), suName: suRowData.suname - }; + }; validationMessage = "Constraints specification is not valid"; this.isConstraintValid = false; - this.setState({validConstraints: validation_result[0]['valid'], constraintsValidationMessage: validationMessage}); - } else { + this.setState({ + validConstraints: validation_result[0]['valid'], + constraintsValidationMessage: validationMessage + }); + } else { //validationMessage = validation_result[0]['message']; delete this.constraintsValidationMessage[rowIndex]; - if(Object.keys(this.constraintsValidationMessage).length>0) { + if (Object.keys(this.constraintsValidationMessage).length > 0) { validationMessage = "Constraints specification is not valid"; } this.setState({constraintsValidationMessage: validationMessage}); @@ -3073,90 +3284,74 @@ export class SchedulingSetCreate extends Component { * Prepare Scheduling Unit(s) details to show on confirmation dialog */ getSchedulingDialogContent() { - let suStatus = this.state.suStatus; - return <> - {suStatus.length > 0 && - <div style={{marginTop: '1em'}}> - <b>Scheduling Unit(s) status</b> - <DataTable value={suStatus} resizableColumns columnResizeMode="expand" className="card" style={{paddingLeft: '0em'}}> - <Column field="suId" header="Scheduling Unit ID"></Column> - <Column field="suName" header="Scheduling Unit Name"></Column> - <Column field="action" header="Action"></Column> - <Column field="suStatus" header="Scheduling Unit Status"></Column> - {/* <Column field="taskStatus" header="Task(s) Status"></Column> */} - </DataTable> - </div> - } - </> + let suStatus = this.state.suStatus; + return <> + {suStatus.length > 0 && + <div style={{marginTop: '1em'}}> + <b>Scheduling Unit(s) status</b> + <DataTable value={suStatus} resizableColumns columnResizeMode="expand" className="card" + style={{paddingLeft: '0em'}}> + <Column field="suId" header="Scheduling Unit ID"></Column> + <Column field="suName" header="Scheduling Unit Name"></Column> + <Column field="action" header="Action"></Column> + <Column field="suStatus" header="Scheduling Unit Status"></Column> + {/* <Column field="taskStatus" header="Task(s) Status"></Column> */} + </DataTable> + </div> + } + </> } /** * Convert the date to string value for Between And Not-Between Columns - * @param {*} dates + * @param {*} dates */ - getBetweenStringValue(dates){ + getBetweenStringValue(dates) { let returnDate = ''; - if (dates){ + if (dates) { dates.forEach(utcDateArray => { - returnDate += moment.utc(utcDateArray.from).format(UIConstants.CALENDAR_DATETIME_FORMAT)+","; - returnDate += moment.utc(utcDateArray.to).format(UIConstants.CALENDAR_DATETIME_FORMAT)+"|"; + returnDate += moment.utc(utcDateArray.from).format(UIConstants.CALENDAR_DATETIME_FORMAT) + ","; + returnDate += moment.utc(utcDateArray.to).format(UIConstants.CALENDAR_DATETIME_FORMAT) + "|"; }); } - return returnDate; - } - - /** - * Get Daily column value - * @param {*} daily - */ - fetchDailyFieldValue(daily){ - let returnValue = []; - if (daily.require_day === true){ - returnValue.push('require_day'); - } - if (daily.require_night === true){ - returnValue.push('require_night'); - } - if (daily.avoid_twilight === true){ - returnValue.push('avoid_twilight'); - } - return returnValue; + return returnDate; } + /** * convert String to Date value for Between And Not-Between Columns */ - getBetweenDateValue(betweenValue){ + getBetweenDateValue(betweenValue) { let returnDate = []; - if (betweenValue){ + if (betweenValue) { let rowDateArray = _.split(betweenValue, "|"); - rowDateArray.forEach(betweenDates =>{ + rowDateArray.forEach(betweenDates => { let betweendate = _.split(betweenDates, ","); let dateres = {}; - if (betweendate && betweendate.length === 2){ - dateres['from'] = `${moment(betweendate[0]).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, { trim: false })}Z`; - dateres['to'] = `${moment(betweendate[1]).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, { trim: false })}Z`; + if (betweendate && betweendate.length === 2) { + dateres['from'] = `${moment(betweendate[0]).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, {trim: false})}Z`; + dateres['to'] = `${moment(betweendate[1]).format(UIConstants.UTC_DATE_TIME_MS_FORMAT, {trim: false})}Z`; returnDate.push(dateres); } }) } - return returnDate; + return returnDate; } /** - * warn before cancel the page if any changes detected + * warn before cancel the page if any changes detected */ checkIsDirty() { - if( this.state.isDirty ){ + if (this.state.isDirty) { this.showIcon = true; this.dialogType = "confirmation"; this.dialogHeader = "Add Multiple Scheduling Unit(s)"; this.dialogMsg = "Do you want to discard your changes? Your changes to the Scheduling Unit(s) will not be saved."; this.dialogContent = ""; this.dialogHeight = '5em'; - this.actions=[ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose' }, - {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]; - // this.callBackFunction = this.cancelCreate; + this.actions = [{id: "yes", title: 'Discard', callback: this.cancelCreate, className: 'act-btn-dispose'}, + {id: "no", title: 'Cancel', className: 'act-btn-cancel', callback: this.close}]; + // this.callBackFunction = this.cancelCreate; this.onClose = this.close; //this.onCancel = this.close; this.setState({ @@ -3167,16 +3362,16 @@ export class SchedulingSetCreate extends Component { } } - /** + /** * Refresh the grid with updated data */ async reset() { if (this.state.confirmDialogVisible) { // Fetches only the observation_strategy_template_id of the SUDrafts of selected SUSet let schedulingUnitList = await ScheduleService.getSchedulingBySet(this.state.selectedSchedulingSetId, "observation_strategy_template_id"); - schedulingUnitList = _.filter(schedulingUnitList,{'observation_strategy_template_id': this.state.observStrategy.id}) ; + schedulingUnitList = _.filter(schedulingUnitList, {'observation_strategy_template_id': this.state.observStrategy.id}); this.setState({ - schedulingUnitList: schedulingUnitList, + schedulingUnitList: schedulingUnitList, confirmDialogVisible: false, isDirty: false }); @@ -3187,8 +3382,8 @@ export class SchedulingSetCreate extends Component { this.state.gridApi.redrawRows(); } } - - /** + + /** * Cancel SU creation and redirect */ cancelCreate() { @@ -3200,45 +3395,45 @@ export class SchedulingSetCreate extends Component { /** * Set state to copy the table header to clipboard - * @param {*} value + * @param {*} value */ async copyHeader(value) { await this.setState({'copyHeader': value}); } - + /** * Copy the table header to clipboard */ async copyOnlyHeader() { - this.setState({ fade: true }); + this.setState({fade: true}); let clipboardData = ''; if (this.state.gridColumnApi) { var columnsName = this.state.gridColumnApi.getAllGridColumns(); var line = ''; - if( columnsName ) { - columnsName.map( column => { - if ( column.colId !== '0'){ + if (columnsName) { + columnsName.map(column => { + if (column.colId !== '0') { line += column.colDef.headerName + '\t'; } return column; }); } line = _.trim(line); - clipboardData += line + '\r\n'; + clipboardData += line + '\r\n'; clipboardData = _.trim(clipboardData); await navigator.clipboard.writeText(clipboardData); this.growl.show({severity: 'success', summary: '', detail: 'Header copied to clipboard '}); } } - + /** * Read Data from clipboard */ - async readClipBoard(){ + async readClipBoard() { let data = await navigator.clipboard.readText(); return data; - } + } /** * Function to copy the data to clipboard @@ -3250,63 +3445,63 @@ export class SchedulingSetCreate extends Component { let copySingleCell = false; var focusedCell = this.state.gridApi.getFocusedCell(); if (focusedCell && focusedCell.column['colId'] === '0') { - if ( this.state.copyHeader ) { + if (this.state.copyHeader) { let line = ''; - columnsName.map( column => { - if ( column.colId !== '0'){ + columnsName.map(column => { + if (column.colId !== '0') { line += column.colDef.headerName + '\t'; } return column; }) line = _.trim(line); - clipboardData += line + '\r\n'; + clipboardData += line + '\r\n'; } //for(const rowData of selectedRows){ - for(var rowCount=0; rowCount < selectedRows.length ; rowCount++) { + for (var rowCount = 0; rowCount < selectedRows.length; rowCount++) { const rowData = selectedRows[rowCount]; let line = ''; //for(const key of this.state.colKeyOrder){ - for(var count =0; count < this.state.colKeyOrder.length; count++) { + for (var count = 0; count < this.state.colKeyOrder.length; count++) { let value = ' '; const key = this.state.colKeyOrder[count]; - if(key.endsWith('Beamformers')) { + if (key.endsWith('Beamformers')) { let tmp = rowData[key]; - if(tmp['param_0']) { + if (tmp['param_0']) { value = JSON.stringify(tmp['param_0']); - } else { + } else { value = JSON.stringify(tmp); } - } else { + } else { value = rowData[key]; } - if(value === undefined) { + if (value === undefined) { value = ' '; } - line += value + ((count === this.state.colKeyOrder.length-1) ? '' :'\t'); + line += value + ((count === this.state.colKeyOrder.length - 1) ? '' : '\t'); } //line = line.slice(0, -2); - clipboardData += line + ((rowCount === selectedRows.length-1) ? '' : '\r\n'); + clipboardData += line + ((rowCount === selectedRows.length - 1) ? '' : '\r\n'); } - } else { + } else { copySingleCell = true; - if(focusedCell['column']['colId'].endsWith('Beamformers')) { + if (focusedCell['column']['colId'].endsWith('Beamformers')) { let tmp = this.state.rowData[focusedCell.rowIndex][focusedCell['column']['colId']]; - if(tmp['param_0']) { + if (tmp['param_0']) { clipboardData = JSON.stringify(tmp['param_0']); - } else { - clipboardData = focusedCell['column']['colId']+'\t'+JSON.stringify(tmp); + } else { + clipboardData = focusedCell['column']['colId'] + '\t' + JSON.stringify(tmp); } - } else { - clipboardData = focusedCell['column']['colId']+'\t'+this.state.rowData[focusedCell.rowIndex][focusedCell['column']['colId']]; + } else { + clipboardData = focusedCell['column']['colId'] + '\t' + this.state.rowData[focusedCell.rowIndex][focusedCell['column']['colId']]; } } - //clipboardData = clipboardData.slice(0, -2); + //clipboardData = clipboardData.slice(0, -2); try { - const queryOpts = { name: 'clipboard-write', allowWithoutGesture: true }; + const queryOpts = {name: 'clipboard-write', allowWithoutGesture: true}; await navigator.permissions.query(queryOpts); await navigator.clipboard.writeText(clipboardData); - } catch(error) { - try{ + } catch (error) { + try { const elem = document.createElement('textarea'); elem.value = clipboardData; document.body.appendChild(elem); @@ -3314,43 +3509,54 @@ export class SchedulingSetCreate extends Component { document.execCommand('copy'); document.body.removeChild(elem); } catch (error) { - this.growl.show({severity: 'error', summary: 'Warning', detail: 'Unable to copy the data to clipboard'}); + this.growl.show({ + severity: 'error', + summary: 'Warning', + detail: 'Unable to copy the data to clipboard' + }); } } if (copySingleCell) { - this.growl.show({severity: 'success', summary: '', detail: "Value '"+this.state.rowData[focusedCell.rowIndex][focusedCell['column']['colId']]+ "' is copied" }); - } else { - const headerText = (this.state.copyHeader) ?'with Header' : ''; - this.growl.show({severity: 'success', summary: '', detail: selectedRows.length+' row(s) copied to clipboard '+headerText }); + this.growl.show({ + severity: 'success', + summary: '', + detail: "Value '" + this.state.rowData[focusedCell.rowIndex][focusedCell['column']['colId']] + "' is copied" + }); + } else { + const headerText = (this.state.copyHeader) ? 'with Header' : ''; + this.growl.show({ + severity: 'success', + summary: '', + detail: selectedRows.length + ' row(s) copied to clipboard ' + headerText + }); } } - + /** * Copy data to/from clipboard - * @param {*} e + * @param {*} e */ - async clipboardEvent(e){ + async clipboardEvent(e) { let isError = false; if (navigator.appVersion.indexOf("Mac") >= 0) { - this.isMacOS = true; + this.isMacOS = true; } var key = e.which || e.keyCode; - var ctrl = (e.ctrlKey || e.metaKey )?true:false; - if ( ctrl && (key === 67 || key === 45) ) {//Ctrl+C(Windows & linux) or Cmd+C(Mac) or Ctrl+Insert(windows & linux) + var ctrl = (e.ctrlKey || e.metaKey) ? true : false; + if (ctrl && (key === 67 || key === 45)) {//Ctrl+C(Windows & linux) or Cmd+C(Mac) or Ctrl+Insert(windows & linux) this.copyToClipboard(); - } - else if ( (ctrl && key === 86) || (e.shiftKey && key === 45) ) { // Ctrl+V(windows & linux) or Cmd+V or Shift+Insert(windows & linux) - if (!this.isMacOS) { - try { + } else if ((ctrl && key === 86) || (e.shiftKey && key === 45)) { // Ctrl+V(windows & linux) or Cmd+V or Shift+Insert(windows & linux) + if (!this.isMacOS) { + try { var clipText = await this.readClipBoard(); await this.setState({clipText: clipText}); this.copyFromClipboard(); - } catch(error) { + } catch (error) { isError = true; } - } - - if (this.isMacOS || isError) { + } + + if (this.isMacOS || isError) { this.callBackFunction = this.copyFromClipboard; this.onCancel = () => { this.setState({confirmDialogVisible: false, clipText: ''}); @@ -3366,10 +3572,10 @@ export class SchedulingSetCreate extends Component { this.dialogMsg = "Paste copied content in the field and click Ok to paste to the selected row(s)"; this.dialogContent = this.pasteTextField; this.actions = [ - {id: "ok_btn", title: "Ok", callback: this.copyFromClipboard}, - {id: "cancel_btn", title: "Cancel", className:'act-btn-cancel', callback: this.onCancel}]; + {id: "ok_btn", title: "Ok", callback: this.copyFromClipboard}, + {id: "cancel_btn", title: "Cancel", className: 'act-btn-cancel', callback: this.onCancel}]; await this.setState({confirmDialogVisible: true}); - if(document.getElementById("clipTextField")) { + if (document.getElementById("clipTextField")) { document.getElementById("clipTextField").focus(); } if (!this.isMacOS) { @@ -3385,37 +3591,39 @@ export class SchedulingSetCreate extends Component { */ pasteTextField() { return <textarea id="clipTextField" value={this.state.clipText} style={{width: '100%'}} autoFocus - onChange={(e) => {this.copyClipText(e)}} placeholder="Paste your text here..." />; + onChange={(e) => { + this.copyClipText(e) + }} placeholder="Paste your text here..."/>; } - copyClipText(e){ + copyClipText(e) { this.setState({clipText: e.target.value}) } /** * Function to copy the data from clipboard */ - async copyFromClipboard(){ + async copyFromClipboard() { try { var selectedRows = this.state.gridApi.getSelectedNodes(); this.tmpRowData = this.state.rowData; let dataRowCount = this.state.totalCount; //Read Clipboard Data - let clipboardData = this.state.clipText?this.state.clipText:null;//await this.readClipBoard(); + let clipboardData = this.state.clipText ? this.state.clipText : null;//await this.readClipBoard(); let selectedRowIndex = 0; var focusedCell = this.state.gridApi.getFocusedCell(); this.isSpecificationValid = true; this.isConstraintValid = true; - if ((selectedRows && selectedRows.length>0)||focusedCell){ + if ((selectedRows && selectedRows.length > 0) || focusedCell) { const copiedValues = clipboardData.split('\t'); if (copiedValues.length === 2 && focusedCell && focusedCell.column['colId'] === '0') { - if (selectedRows && selectedRows.length>0) { - await selectedRows.map(async selectedRow =>{ + if (selectedRows && selectedRows.length > 0) { + await selectedRows.map(async selectedRow => { selectedRowIndex = selectedRow.rowIndex; this.tmpRowData[selectedRowIndex]["isDirty"] = true; - this.tmpRowData[selectedRowIndex][copiedValues[0]] = await this.getValidCellValue(copiedValues[0], copiedValues[0], - _.includes(["true","false", "FALSE", "TRUE"], copiedValues[1])? JSON.parse(_.lowerCase(copiedValues[1])): _.lowerCase(copiedValues[1])); - if (_.includes(this.constraintVariables, copiedValues[0]) ) { + this.tmpRowData[selectedRowIndex][copiedValues[0]] = await this.getValidCellValue(copiedValues[0], copiedValues[0], + _.includes(["true", "false", "FALSE", "TRUE"], copiedValues[1]) ? JSON.parse(_.lowerCase(copiedValues[1])) : _.lowerCase(copiedValues[1])); + if (_.includes(this.constraintVariables, copiedValues[0])) { await this.validateSchedulingConstraints(selectedRowIndex, this.tmpRowData[selectedRowIndex]); } if (_.includes(this.strategyVariables, copiedValues[0])) { @@ -3423,12 +3631,12 @@ export class SchedulingSetCreate extends Component { } }); } - } else if (copiedValues.length === 2 && focusedCell && focusedCell.column['colId'] !== '0') { + } else if (copiedValues.length === 2 && focusedCell && focusedCell.column['colId'] !== '0') { if (focusedCell.rowIndex >= 0) { this.tmpRowData[focusedCell.rowIndex][focusedCell.column['colId']] = this.getValidCellValue(focusedCell.column['colId'], copiedValues[0], copiedValues[1]); this.tmpRowData[focusedCell.rowIndex]['isDirty'] = true; - if (selectedRows && selectedRows.length === 0 ) { - if (_.includes(this.constraintVariables, focusedCell.column['colId']) ) { + if (selectedRows && selectedRows.length === 0) { + if (_.includes(this.constraintVariables, focusedCell.column['colId'])) { this.validateSchedulingConstraints(focusedCell.rowIndex, this.tmpRowData[focusedCell.rowIndex]); } if (_.includes(this.strategyVariables, focusedCell.column['colId'])) { @@ -3436,12 +3644,12 @@ export class SchedulingSetCreate extends Component { } } } - if (selectedRows && selectedRows.length>0) { - await selectedRows.map(selectedRow =>{ + if (selectedRows && selectedRows.length > 0) { + await selectedRows.map(selectedRow => { selectedRowIndex = selectedRow.rowIndex; this.tmpRowData[selectedRowIndex][focusedCell.column['colId']] = this.getValidCellValue(focusedCell.column['colId'], copiedValues[0], copiedValues[1]); this.tmpRowData[selectedRowIndex]['isDirty'] = true; - if (_.includes(this.constraintVariables, focusedCell.column['colId']) ) { + if (_.includes(this.constraintVariables, focusedCell.column['colId'])) { this.validateSchedulingConstraints(selectedRowIndex, this.tmpRowData[selectedRowIndex]); } if (_.includes(this.strategyVariables, focusedCell.column['colId'])) { @@ -3450,9 +3658,9 @@ export class SchedulingSetCreate extends Component { return selectedRow; }); } - } else if (selectedRows && selectedRows.length>0) { + } else if (selectedRows && selectedRows.length > 0) { let noOfRowsToPaste = 1; - if (clipboardData ){ + if (clipboardData) { // Get the row index from where the paste option starts selectedRowIndex = selectedRows[0].rowIndex - 1; clipboardData = _.trimEnd(clipboardData); @@ -3460,9 +3668,9 @@ export class SchedulingSetCreate extends Component { // Get the values of copied rows let suRows = clipboardData.split("\n"); // If multiple rows are copied, iteration occurs once and all copied rows will be pasted. - // If only one row is copied, iteration occurs as per the no of selected rows. - noOfRowsToPaste = suRows.length===1?selectedRows.length:1; - _.range(1, noOfRowsToPaste+1).forEach(() => { + // If only one row is copied, iteration occurs as per the no of selected rows. + noOfRowsToPaste = suRows.length === 1 ? selectedRows.length : 1; + _.range(1, noOfRowsToPaste + 1).forEach(() => { // Paste all copied rows suRows.map(async line => { selectedRowIndex++; @@ -3470,27 +3678,27 @@ export class SchedulingSetCreate extends Component { suGridRowData['id'] = 0; suGridRowData['isValid'] = true; suGridRowData["isDirty"] = true; - if ( this.tmpRowData.length <= selectedRowIndex ) { + if (this.tmpRowData.length <= selectedRowIndex) { this.tmpRowData.push(this.state.emptyRow); } let colCount = 0; let suRow = line.split("\t"); - for(const key of this.state.colKeyOrder){ - if(key.endsWith('Beamformers')) { + for (const key of this.state.colKeyOrder) { + if (key.endsWith('Beamformers')) { let cellValue = {}; - cellValue['param_0']=JSON.parse(suRow[colCount]); + cellValue['param_0'] = JSON.parse(suRow[colCount]); suGridRowData[key] = cellValue; - } else { - if (_.includes(["true","false", "FALSE", "TRUE"], suRow[colCount])) { + } else { + if (_.includes(["true", "false", "FALSE", "TRUE"], suRow[colCount])) { suGridRowData[key] = JSON.parse(_.lowerCase(suRow[colCount])); - } - else { + } else { suGridRowData[key] = suRow[colCount]; - }; + } + } colCount++; } - if (this.tmpRowData[selectedRowIndex].id > 0 ) { + if (this.tmpRowData[selectedRowIndex].id > 0) { suGridRowData['id'] = this.tmpRowData[selectedRowIndex].id; } this.tmpRowData[selectedRowIndex] = (suGridRowData); @@ -3500,14 +3708,14 @@ export class SchedulingSetCreate extends Component { }) } } - + dataRowCount = selectedRowIndex; let emptyRow = this.state.emptyRow; let tmpNoOfSU = this.state.noOfSU; - if (dataRowCount >= tmpNoOfSU){ + if (dataRowCount >= tmpNoOfSU) { tmpNoOfSU = dataRowCount; //Create additional empty row at the end - for(let i= this.tmpRowData.length; i<= tmpNoOfSU; i++){ + for (let i = this.tmpRowData.length; i <= tmpNoOfSU; i++) { this.tmpRowData.push(emptyRow); } } @@ -3520,48 +3728,47 @@ export class SchedulingSetCreate extends Component { isDirty: true }); this.actions = null; - publish('edit-dirty', true); + publish('edit-dirty', true); this.state.gridApi.setRowData(this.state.rowData); this.state.gridApi.redrawRows(); } - } - catch (err) { + } catch (err) { console.error('Error: ', err); } } - getValidCellValue (targetKey, key, value) { + getValidCellValue(targetKey, key, value) { value = _.replace(value, '\r\n', ''); if (key === 'stations') { return value; - } else if (key.endsWith('Beamformers')) { + } else if (key.endsWith('Beamformers')) { if (targetKey === 'stations') { return JSON.stringify(value); - } else { + } else { return JSON.parse(value); } - } else if (_.includes(["true","false", "FALSE", "TRUE"], value)) { + } else if (_.includes(["true", "false", "FALSE", "TRUE"], value)) { return JSON.parse(_.lowerCase(value)); - } else if (_.includes(value, ',') && (key !== 'between' && key !== 'notbetween')) { + } else if (_.includes(value, ',') && (key !== 'between' && key !== 'notbetween')) { return value.split(','); - } else { + } else { return value; } } - /** + /** * Show warning messgae if any changes not saved when the AG grid reload or cancel the page - * @param {*} functionName + * @param {*} functionName */ - showWarning (functionName) { + showWarning(functionName) { this.showIcon = true; this.dialogType = "confirmation"; this.dialogHeader = "Add Multiple Scheduling Unit(s)"; this.dialogMsg = "Do you want to discard your changes? Your changes to the Scheduling Unit(s) will not be saved."; this.dialogContent = ""; this.dialogHeight = '5em'; - this.actions=[ {id:"yes", title: 'Discard', callback: functionName, className:'act-btn-dispose' }, - {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]; + this.actions = [{id: "yes", title: 'Discard', callback: functionName, className: 'act-btn-dispose'}, + {id: "no", title: 'Cancel', className: 'act-btn-cancel', callback: this.close}]; this.callBackFunction = functionName; this.onClose = this.close; this.onCancel = this.close; @@ -3573,7 +3780,7 @@ export class SchedulingSetCreate extends Component { /** * Reset the top table values */ - async resetCommonData(){ + async resetCommonData() { await this.setState({commonRowData: []}); let tmpData = [this.state.defaultCommonRowData]; //[...[this.state.emptyRow]]; let gRowData = {}; @@ -3582,29 +3789,29 @@ export class SchedulingSetCreate extends Component { gRowData[key] = tmpData[0][key]; } //else if(this.state.hasSameValue) { - gRowData['gdef_'+key] = tmpData[0][key]; + gRowData['gdef_' + key] = tmpData[0][key]; /*} else { gRowData['gdef_'+key] = ''; }*/ } gRowData['gdef_offset_from_max'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.maximum; - gRowData['gdef_offset_from_min'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.minimum; + gRowData['gdef_offset_from_min'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.from.minimum; gRowData['gdef_offset_to_max'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.maximum; gRowData['gdef_offset_to_min'] = this.resolvedConstraintSchema.properties.sky.properties.transit_offset.properties.to.minimum; await this.setState({commonRowData: [gRowData]}); } - /** - * Reload the data from API + /** + * Reload the data from API */ - reload(){ + reload() { this.changeStrategy(this.state.observStrategy.id); } /** * Apply the changes to all rows */ - async applyToAll(){ + async applyToAll() { let isNotEmptyRow = true; if (!this.state.applyEmptyValue) { var row = this.state.commonRowData[0]; @@ -3613,10 +3820,14 @@ export class SchedulingSetCreate extends Component { isNotEmptyRow = false; } }); - } - if (!this.state.applyEmptyValue && isNotEmptyRow ) { - this.growl.show({severity: 'warn', summary: 'Warning', detail: 'Please enter value in the column(s) above to apply'}); - } else { + } + if (!this.state.applyEmptyValue && isNotEmptyRow) { + this.growl.show({ + severity: 'warn', + summary: 'Warning', + detail: 'Please enter value in the column(s) above to apply' + }); + } else { this.dialogType = "confirmation"; this.dialogHeader = "Warning"; this.showIcon = true; @@ -3627,30 +3838,38 @@ export class SchedulingSetCreate extends Component { this.applyToAllRow = true; this.applyToEmptyRowOnly = false; this.onClose = this.close; - this.onCancel =this.close; + this.onCancel = this.close; this.setState({confirmDialogVisible: true}); - } + } } /** * Apply the changes to selected rows */ - async applyToSelected(){ + async applyToSelected() { let isNotEmptyRow = true; let tmpRowData = this.state.gridApi.getSelectedRows(); if (!this.state.applyEmptyValue) { var row = this.state.commonRowData[0]; Object.keys(row).forEach(key => { if (key !== 'id' && row[key] !== '') { - isNotEmptyRow= false; + isNotEmptyRow = false; } }); - } - if (!this.state.applyEmptyValue && isNotEmptyRow ) { - this.growl.show({severity: 'warn', summary: 'Warning', detail: 'Please enter value in the column(s) above to apply'}); - } else if(tmpRowData && tmpRowData.length === 0){ - this.growl.show({severity: 'warn', summary: 'Warning', detail: 'Please select at least one row to apply the changes'}); - } else { + } + if (!this.state.applyEmptyValue && isNotEmptyRow) { + this.growl.show({ + severity: 'warn', + summary: 'Warning', + detail: 'Please enter value in the column(s) above to apply' + }); + } else if (tmpRowData && tmpRowData.length === 0) { + this.growl.show({ + severity: 'warn', + summary: 'Warning', + detail: 'Please select at least one row to apply the changes' + }); + } else { this.showIcon = true; this.dialogType = "confirmation"; this.dialogHeader = "Warning"; @@ -3663,25 +3882,29 @@ export class SchedulingSetCreate extends Component { this.onClose = this.close; this.onCancel = this.close; this.setState({confirmDialogVisible: true}); - } + } } - /** + /** * Apply the changes to Empty rows */ - async applyToEmptyRows(){ + async applyToEmptyRows() { let isNotEmptyRow = true; if (!this.state.applyEmptyValue) { var row = this.state.commonRowData[0]; Object.keys(row).forEach(key => { if (key !== 'id' && row[key] !== '') { - isNotEmptyRow= false; + isNotEmptyRow = false; } }); - } - if (!this.state.applyEmptyValue && isNotEmptyRow ) { - this.growl.show({severity: 'warn', summary: 'Warning', detail: 'Please enter value in the column(s) above to apply'}); - } else { + } + if (!this.state.applyEmptyValue && isNotEmptyRow) { + this.growl.show({ + severity: 'warn', + summary: 'Warning', + detail: 'Please enter value in the column(s) above to apply' + }); + } else { this.showIcon = true; this.dialogType = "confirmation"; this.dialogHeader = "Warning"; @@ -3694,19 +3917,19 @@ export class SchedulingSetCreate extends Component { this.onClose = this.close; this.onCancel = this.close; this.setState({confirmDialogVisible: true}); - } + } } /** * Make global changes in table data */ - async applyChanges() { + async applyChanges() { const currentSortingColumn = this.state.gridApi.getSortModel(); this.state.gridColumnApi.applyColumnState({ - state:[{colId: '0', sort: 'asc'}], - defaultState: { sort: true }, - }); - + state: [{colId: '0', sort: 'asc'}], + defaultState: {sort: true}, + }); + await this.setState({ confirmDialogVisible: false, isDirty: true @@ -3720,39 +3943,39 @@ export class SchedulingSetCreate extends Component { } var grow = _.cloneDeep(this.state.commonRowData[0]); delete grow['gdef_id']; - if(gridRows.length >0) { + if (gridRows.length > 0) { let tmpRows = (this.applyToAllRow) ? gridRows : selectedRows; - for( const tmpRow of tmpRows) { - if (this.applyToEmptyRowOnly && (tmpRow['id'] > 0 || (tmpRow['suname'] !== '' && tmpRow['sudesc'] !== '') ) ){ - continue; + for (const tmpRow of tmpRows) { + if (this.applyToEmptyRowOnly && (tmpRow['id'] > 0 || (tmpRow['suname'] !== '' && tmpRow['sudesc'] !== ''))) { + continue; } let row = _.find(gridRows, {'custId': tmpRow.custId}); if (row) { - for( const key of this.colKeyOrder) { - let value = grow['gdef_'+key]; - if ( (key === 'suname' || key === 'sudesc') && Validator.isEmpty(value)) { + for (const key of this.colKeyOrder) { + let value = grow['gdef_' + key]; + if ((key === 'suname' || key === 'sudesc') && Validator.isEmpty(value)) { continue; } - if (grow.hasOwnProperty(['gdef_'+key])) { - if( this.state.applyEmptyValue) { + if (grow.hasOwnProperty(['gdef_' + key])) { + if (this.state.applyEmptyValue) { row[key] = value; - } - else { + } else { //Lodash is not checking the number, so append string - row[key] = (_.isEmpty(value+''))? row.hasOwnProperty([key])? row[key]:grow['gdef_'+key] : value; + row[key] = (_.isEmpty(value + '')) ? row.hasOwnProperty([key]) ? row[key] : grow['gdef_' + key] : value; } row["isDirty"] = true; } - }; + } + } - this.validateSchedulingConstraints((tmpRow.custId-1), tmpRow); - this.validateSpecificationsDoc((tmpRow.custId-1), tmpRow); + this.validateSchedulingConstraints((tmpRow.custId - 1), tmpRow); + this.validateSpecificationsDoc((tmpRow.custId - 1), tmpRow); } this.state.gridApi.setRowData(gridRows); this.state.gridColumnApi.applyColumnState({ - state:currentSortingColumn, - defaultState: { sort: null }, - }); + state: currentSortingColumn, + defaultState: {sort: null}, + }); } } @@ -3769,30 +3992,15 @@ export class SchedulingSetCreate extends Component { this.defaultConstraintDoc = jsonOutput; } - /** - * Observation Strategy Template Dropdown option Component to display the description in tooltip - * @param {any} option - * @returns - */ - observOptionTemplate(option) { - if(this.state.observationIdSet) { - return ( - <div title={option.description} style={{display:"flex", fontWeight: _.includes(this.state.observationIdSet, option.id)? 'bold': 'normal'}}> - {`${option.name} (${option.purpose_value}, ${option.state_value}, v${option.version})` } - </div> - ); - } - } - - getStrategyFilterOptions(filterValues, filterId) { + getStrategyFilterOptions(filterValues, filterId) { if (filterValues) { let optionList = []; filterValues.forEach(choice => { const tmpVar = {name: choice.value, value: choice.value}; - if(filterId === 'purpose') { + if (filterId === 'purpose') { optionList.push(tmpVar); } - if(filterId === 'state') { + if (filterId === 'state') { optionList.push(tmpVar); } }); @@ -3800,34 +4008,6 @@ export class SchedulingSetCreate extends Component { } } - setObservStrateyFilters(selectedFilter, filterId) { - let purposeFiltered = true; - let stateFiltered = true; - let observStrategies = this.observStrategies; - let observStrategyFilters = this.state.observStrategyFilters; - if(filterId === 'purpose') { - observStrategyFilters.purpose = selectedFilter - - } - if(filterId === 'state') { - observStrategyFilters.states = selectedFilter - } - observStrategies = _.filter(observStrategies, function(strategy) { - if(observStrategyFilters.purpose.length === 0 && observStrategyFilters.states.length === 0) { - return true; - } - if(observStrategyFilters.purpose.length>0) { - purposeFiltered = _.includes(observStrategyFilters.purpose, strategy.purpose_value); - } - if(observStrategyFilters.states.length>0) { - stateFiltered = _.includes(observStrategyFilters.states, strategy.state_value); - } - return purposeFiltered && stateFiltered; - }); - UtilService.localStore({ type: 'set', key: 'strategyFilterSUSetEditor', value: observStrategyFilters}); - this.setState({observStrategyFilters: observStrategyFilters, observStrategies: observStrategies, observStrategy: {}, rowData: []}) - } - /** * Show Constraint Error dialog in UI */ @@ -3840,7 +4020,7 @@ export class SchedulingSetCreate extends Component { this.dialogContent = this.showConstraintErrorDialog; this.setState({confirmDialogVisible: true}); } - + /** * Show Specification Error dialog in UI */ @@ -3856,257 +4036,281 @@ export class SchedulingSetCreate extends Component { render() { if (this.state.redirect) { - return <Redirect to={ {pathname: this.state.redirect} }></Redirect> + return <Redirect to={{pathname: this.state.redirect}}></Redirect> } const {scheduleunit_draft} = this.state.userrole; return ( <React.Fragment> {scheduleunit_draft && scheduleunit_draft.list ? - <> - <Toast ref={(el) => this.growl = el} /> - <PageHeader location={this.props.location} title={'Scheduling Unit(s) Add Multiple'} - actions={[{icon: 'fa-window-close',title:'Close', type: 'button', actOn: 'click', props:{ callback: this.checkIsDirty }}]} - /> - {this.state.isFetchingData && - <TopProgressBar /> - } - { this.state.isLoading ? <AppLoader /> : - <> - {/* SchedulingConstraint editor to pass the scheduling_constraint schema and get the default + <> + <Toast ref={(el) => this.growl = el}/> + <PageHeader location={this.props.location} title={'Scheduling Unit(s) Add Multiple'} + actions={[{ + icon: 'fa-window-close', + title: 'Close', + type: 'button', + actOn: 'click', + props: {callback: this.checkIsDirty} + }]} + /> + {this.state.isFetchingData && + <TopProgressBar/> + } + {this.state.isLoading ? <AppLoader/> : + <> + {/* SchedulingConstraint editor to pass the scheduling_constraint schema and get the default constraint_doc for new SUs if not constraint fields are edited */} - <div style={{display: "none"}}> - <SchedulingConstraints constraintTemplate={this.constraintSchema} disable - formatOutput={false} callback={this.setConstraintsEditorOutput} /> - </div> - <div> - <div className="p-fluid"> - <div className="p-field p-grid"> - <label htmlFor="project" className="col-lg-2 col-md-2 col-sm-12">Project <span style={{color:'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12" data-testid="projectdiv" > - <Dropdown inputId="project" optionLabel="name" optionValue="name" data-testid="project" testid="project" - tooltip="Project" tooltipOptions={this.tooltipOptions} - value={this.state.schedulingUnit.project} disabled={this.state.projectDisabled} - options={this.projects} - onChange={(e) => {this.onProjectChange(e.value)}} - placeholder="Select Project" /> - <label className={this.state.errors.project ?"error":"info"}> - {this.state.errors.project ? this.state.errors.project : "Select Project to get Scheduling Sets"} - </label> - </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="schedSet" className="col-lg-2 col-md-2 col-sm-12">Scheduling Set <span style={{color:'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <Dropdown data-testid="schedSet" id="schedSet" optionLabel="name" optionValue="id" - tooltip="Scheduling set of the project" tooltipOptions={this.tooltipOptions} - value={this.state.schedulingUnit.scheduling_set_id} - options={this.state.schedulingSets} - onChange={(e) => {this.setSchedulingSetParams('scheduling_set_id',e.value)}} - placeholder="Select Scheduling Set" /> - <label className={this.state.errors.scheduling_set_id ?"error":"info"}> - {this.state.errors.scheduling_set_id ? this.state.errors.scheduling_set_id : "Scheduling Set of the Project"} - </label> - </div> - <div className="col-lg-1 col-md-1 col-sm-12"> - <Button label="" className="p-button-primary" icon="pi pi-plus" data-testid="addSet" id="addSet" - onClick={this.showAddSchedulingSet} - tooltip="Add new Scheduling Set" - style={{marginLeft: '-10px'}} - disabled={this.state.schedulingUnit.project !== null && scheduleunit_draft.scheduling_set? false : true }/> - </div> - </div> - <div className="p-field p-grid"> - <label htmlFor="observStrategy" className="col-lg-2 col-md-2 col-sm-12">Observation Strategy <span style={{color:'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <div className='p-field p-grid'> - <MultiSelect inputId="observStrategyPurpose" optionLabel="name" optionValue="value" className="observ_strategy_purpose" - tooltip="Filter Observation Strategy by Purpose" tooltipOptions={this.tooltipOptions} - value={this.state.observStrategyFilters.purpose} - options={this.templatePurposes} - onChange={(e) => {this.setObservStrateyFilters(e.value, 'purpose')}} - maxSelectedLabels="1" - selectedItemsLabel="{0} Selected" - placeholder="Purpose" /> - <MultiSelect inputId="observStrategyState" optionLabel="name" optionValue="value" className="observ_strategy_state" - tooltip="Filter Observation Strategy by State" tooltipOptions={this.tooltipOptions} - value={this.state.observStrategyFilters.states} - options={this.templateState} - maxSelectedLabels="1" - selectedItemsLabel="{0} Selected" - onChange={(e) => {this.setObservStrateyFilters(e.value, 'state')}} - placeholder="State" /> - </div> - <Dropdown inputId="observStrategy" optionLabel="uniqueId" optionValue="id" data-testid="observStrategy" - tooltip="Observation Strategy Template to be used to create the Scheduling Unit. Strategies already used in the Scheduling Set are in bold" tooltipOptions={this.tooltipOptions} - value={this.state.observStrategy.id} - options={this.state.observStrategies} filter - itemTemplate={this.observOptionTemplate} - onChange={(e) => {this.onStrategyChange(e.value)}} - placeholder="Select Strategy" /> - <label className="info"> - {this.state.observStrategy? this.state.observStrategy.description : "Select Observation Strategy"} - </label> - </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="noOfSU" className="col-lg-2 col-md-2 col-sm-12">No of Scheduling Unit <span style={{color:'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <Dropdown data-testid="noOfSU" id="noOfSU" - editable - options={this.state.noOfSUOptions} - value={this.state.noOfSU} - onChange={(e) => this.setNoOfSUint(e.value)} - tooltip="Enter No. of Scheduling Units, Range - 1 to 500" tooltipOptions={this.tooltipOptions} - placeholder='Enter No. of SU (1 to 500)' /> - <label className={this.state.errors.noOfSU ?"error":"info"}> - {this.state.errors.noOfSU ? this.state.errors.noOfSU : "Enter No. of Scheduling Units"} - </label> - </div> - </div> - { this.state.rowData && this.state.rowData.length > 0 && - <div className="p-field p-grid"> - <label htmlFor="observStrategy" className="col-lg-2 col-md-2 col-sm-12">Copy Data With Header</label> - <div className="col-lg-3 col-md-3 col-sm-12" > - <Checkbox inputId="csvheader" role="csvheader" - tooltip="Include column headers while copying the data to clipboard" - tooltipOptions={this.tooltipOptions} - checked={this.state.copyHeader} onChange={e => this.copyHeader(e.target.checked)}></Checkbox> - - <Button label="Copy Only Header" icon="fas fa-copy" onClick={this.copyOnlyHeader} style={{marginLeft: '3em', width: '12em'}} - onAnimationEnd={() => this.setState({ fade: false })} data-testid="copyHeaderOnly" id="copyHeaderOnly" - className={this.state.fade ? 'p-button-primary fade' : 'p-button-primary'} tooltip="Copy only header to clipboard" - /> - </div> + <div style={{display: "none"}}> + <SchedulingConstraints constraintTemplate={this.constraintSchema} disable + formatOutput={false} + callback={this.setConstraintsEditorOutput}/> </div> - } - - </div> - <> - { this.state.isAGLoading ? <AppLoader /> : - <> - {this.state.rowData && this.state.rowData.length > 0 && - <React.Fragment> - <Accordion onTabOpen={this.resetCommonData} style={{marginTop: '2em', marginBottom: '2em'}} > - <AccordionTab header={<React.Fragment><span style={{paddingLeft: '0.5em', paddingRight: '0.5em'}}>Input Values For Multiple Scheduling units</span> <i className="fas fa-clone"></i></React.Fragment>} > - <div className="ag-theme-alpine" style={ {overflowX: 'inherit !importent', height: '160px', marginBottom: '10px' } } onKeyDown={this.topAGGridEvent} > - <AgGridReact - suppressClipboardPaste={false} - columnDefs={this.state.globalColmunDef} - columnTypes={this.state.columnTypes} - defaultColDef={this.state.defaultColDef} - rowSelection={this.state.rowSelection} - onGridReady={this.onTopGridReady} - rowData={this.state.commonRowData} - frameworkComponents={this.state.frameworkComponents} - context={this.state.context} - components={this.state.components} - modules={this.state.modules} - enableRangeSelection={true} - tooltipShowDelay={50} - > - </AgGridReact> - + <div> + <div className="p-fluid"> + {<ProjectScheduleSetSelector + selectedProject={this.state.schedulingUnit.project} + projectDisabled={this.state.projectDisabled} + allProjects={this.projects} + projectErrors={this.state.errors.project} + touchedProject={true} + changeProject={this.onProjectChange} + selectedSchedulingSet={this.state.schedulingUnit.scheduling_set_id} + allSchedulingSets={this.state.schedulingSets} + schedulingSetErrors={this.state.errors.scheduling_set_id} + touchedSchedulingSet={true} + setSchedUnitParams={this.setSchedulingSetParams} + scheduleUnitDraft={scheduleunit_draft} + setState={this.setState} + />} + <div className="p-field p-grid"> + <label htmlFor="observStrategy" className="col-lg-2 col-md-2 col-sm-12"> + Observation Strategy <span style={{color: 'red'}}>*</span> + </label> + {<ObservationStrategySelector + selectedPurpose={this.state.observStrategyFilters.purpose} + allPurposes={this.templatePurposes} + selectedState={this.state.observStrategyFilters.states} + allStates={this.templateState} + selectedObservationStrategy={this.state.observStrategy} + allObservationStrategies={this.state.observStrategies} + observationIdSet={this.state.observationIdSet} + changeStrategy={this.onStrategyChange} + localStoreKey={"strategyFilterSUSetEditor"} + setState={this.setState} + />} + + + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="noOfSU" className="col-lg-2 col-md-2 col-sm-12">No of + Scheduling Unit <span style={{color: 'red'}}>*</span></label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <Dropdown data-testid="noOfSU" id="noOfSU" + editable + options={this.state.noOfSUOptions} + value={this.state.noOfSU} + onChange={(e) => this.setNoOfSUint(e.value)} + tooltip="Enter No. of Scheduling Units, Range - 1 to 500" + tooltipOptions={this.tooltipOptions} + placeholder='Enter No. of SU (1 to 500)'/> + <label className={this.state.errors.noOfSU ? "error" : "info"}> + {this.state.errors.noOfSU ? this.state.errors.noOfSU : "Enter No. of Scheduling Units"} + </label> + </div> + </div> + {this.state.rowData && this.state.rowData.length > 0 && + <div className="p-field p-grid"> + <label htmlFor="observStrategy" className="col-lg-2 col-md-2 col-sm-12">Copy + Data With Header</label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <Checkbox inputId="csvheader" role="csvheader" + tooltip="Include column headers while copying the data to clipboard" + tooltipOptions={this.tooltipOptions} + checked={this.state.copyHeader} + onChange={e => this.copyHeader(e.target.checked)}></Checkbox> + + <Button label="Copy Only Header" icon="fas fa-copy" + onClick={this.copyOnlyHeader} + style={{marginLeft: '3em', width: '12em'}} + onAnimationEnd={() => this.setState({fade: false})} + data-testid="copyHeaderOnly" id="copyHeaderOnly" + className={this.state.fade ? 'p-button-primary fade' : 'p-button-primary'} + tooltip="Copy only header to clipboard" + /> </div> - <div className="p-grid p-justify-start" > - <label htmlFor="observStrategy" className="p-col-1" style={{width: '14em'}}>Include empty value(s)</label> - <Checkbox - tooltip="Copy the input value ( empty values also ) as it is while apply the changes in table" - tooltipOptions={this.tooltipOptions} - checked={this.state.applyEmptyValue} - onChange={e => this.setState({'applyEmptyValue': e.target.checked})} - style={{marginTop: '10px'}} > - </Checkbox> - - <div className="p-col-1" style={{width: 'auto' , marginLeft: '2em'}}> - <Button label="Apply to All Rows" tooltip="Apply changes to all rows in below table" className="p-button-primary" icon="fas fa-check-double" onClick={this.applyToAll}/> - </div> - <div className="p-col-1" style={{width: 'auto',marginLeft: '2em'}}> - <Button label="Apply to Selected Rows" tooltip="Apply changes to selected row in below table" className="p-button-primary" icon="fas fa-check-square" onClick={this.applyToSelected} /> - </div> - <div className="p-col-1" style={{width: 'auto',marginLeft: '2em'}}> - <Button label="Apply to Empty Rows" tooltip="Apply changes to empty row in below table" className="p-button-primary" icon="pi pi-check" onClick={this.applyToEmptyRows} /> - </div> - <div className="p-col-1" style={{width: 'auto',marginLeft: '2em'}}> - <Button label="Reset" tooltip="Reset input values" className="p-button-primary" icon="pi pi-refresh" onClick={this.resetCommonData} /> - </div> - {/*} <div className="p-col-1" style={{width: 'auto',marginLeft: '2em'}}> + </div> + } + + </div> + <> + {this.state.isAGLoading ? <AppLoader/> : + <> + {this.state.rowData && this.state.rowData.length > 0 && + <React.Fragment> + <Accordion onTabOpen={this.resetCommonData} + style={{marginTop: '2em', marginBottom: '2em'}}> + <AccordionTab header={<React.Fragment><span + style={{paddingLeft: '0.5em', paddingRight: '0.5em'}} + data-testid='select-multiple-su-input'> + Input Values For Multiple Scheduling units</span> + <i className="fas fa-clone"></i></React.Fragment>}> + <div className="ag-theme-alpine" style={{ + overflowX: 'inherit !importent', + height: '160px', + marginBottom: '10px' + }} onKeyDown={this.topAGGridEvent}> + <AgGridReact + suppressClipboardPaste={false} + columnDefs={this.state.globalColmunDef} + columnTypes={this.state.columnTypes} + defaultColDef={this.state.defaultColDef} + rowSelection={this.state.rowSelection} + onGridReady={this.onTopGridReady} + rowData={this.state.commonRowData} + frameworkComponents={this.state.frameworkComponents} + context={this.state.context} + components={this.state.components} + modules={this.state.modules} + enableRangeSelection={true} + tooltipShowDelay={50} + > + </AgGridReact> + + </div> + <div className="p-grid p-justify-start"> + <label htmlFor="observStrategy" className="p-col-1" + style={{width: '14em'}}>Include empty + value(s)</label> + <Checkbox + tooltip="Copy the input value ( empty values also ) as it is while apply the changes in table" + tooltipOptions={this.tooltipOptions} + checked={this.state.applyEmptyValue} + onChange={e => this.setState({'applyEmptyValue': e.target.checked})} + style={{marginTop: '10px'}}> + </Checkbox> + + <div className="p-col-1" + style={{width: 'auto', marginLeft: '2em'}}> + <Button label="Apply to All Rows" + tooltip="Apply changes to all rows in below table" + className="p-button-primary" + icon="fas fa-check-double" + onClick={this.applyToAll}/> + </div> + <div className="p-col-1" + style={{width: 'auto', marginLeft: '2em'}}> + <Button label="Apply to Selected Rows" + tooltip="Apply changes to selected row in below table" + className="p-button-primary" + icon="fas fa-check-square" + onClick={this.applyToSelected}/> + </div> + <div className="p-col-1" + style={{width: 'auto', marginLeft: '2em'}}> + <Button label="Apply to Empty Rows" + tooltip="Apply changes to empty row in below table" + className="p-button-primary" + icon="pi pi-check" + onClick={this.applyToEmptyRows}/> + </div> + <div className="p-col-1" + style={{width: 'auto', marginLeft: '2em'}}> + <Button label="Reset" + tooltip="Reset input values" + className="p-button-primary" + icon="pi pi-refresh" + onClick={this.resetCommonData}/> + </div> + {/*} <div className="p-col-1" style={{width: 'auto',marginLeft: '2em'}}> <Button label="Refresh" tooltip="Refresh grid data" className="p-button-primary" icon="pi pi-refresh" onClick={this.reload} /> </div> */} - </div> - </AccordionTab> - </Accordion> - </React.Fragment> - } + </div> + </AccordionTab> + </Accordion> + </React.Fragment> + } - {this.state.observStrategy.id && - <div className="ag-theme-alpine" style={ {overflowX: 'inherit !importent', height: '500px', marginBottom: '3em', padding: '0.5em' } } onKeyDown={this.clipboardEvent}> - <label >Scheduling Unit(s) </label> - <AgGridReact - suppressClipboardPaste={false} - columnDefs={this.state.columnDefs} - columnTypes={this.state.columnTypes} - defaultColDef={this.state.defaultColDef} - rowSelection={this.state.rowSelection} - onGridReady={this.onGridReady} - rowData={this.state.rowData} - frameworkComponents={this.state.frameworkComponents} - context={this.state.context} - components={this.state.components} - modules={this.state.modules} - enableRangeSelection={true} - enableCellChangeFlash={true} - onCellValueChanged= {this.cellValueChageEvent} - suppressMultiRangeSelection={true} - data-testid="sulist" testid="sulist" - tooltipShowDelay={50} - suppressScrollOnNewData={true} - > - </AgGridReact> + {this.state.observStrategy.id && + <div className="ag-theme-alpine" style={{ + overflowX: 'inherit !importent', + height: '500px', + marginBottom: '3em', + padding: '0.5em' + }} onKeyDown={this.clipboardEvent}> + <label>Scheduling Unit(s) </label> + <AgGridReact + suppressClipboardPaste={false} + columnDefs={this.state.columnDefs} + columnTypes={this.state.columnTypes} + defaultColDef={this.state.defaultColDef} + rowSelection={this.state.rowSelection} + onGridReady={this.onGridReady} + rowData={this.state.rowData} + frameworkComponents={this.state.frameworkComponents} + context={this.state.context} + components={this.state.components} + modules={this.state.modules} + enableRangeSelection={true} + enableCellChangeFlash={true} + onCellValueChanged={this.cellValueChageEvent} + suppressMultiRangeSelection={true} + tooltipShowDelay={50} + suppressScrollOnNewData={true} + > + </AgGridReact> + </div> + } + </> + } + </> + {this.state.isObsoletStrategy && + <div> + <span style={{color: 'red'}}>* Cannot create new Scheduling Units from obsolete strategy. To edit the existing one, please edit from the details page.</span> </div> } - </> - } - </> - {this.state.isObsoletStrategy && - <div> - <span style={{color: 'red'}}>* Cannot create new Scheduling Units from obsolete strategy. To edit the existing one, please edit from the details page.</span> - </div> - } - {!this.state.validConstraints && this.state.constraintsValidationMessage.length>0 && - <div className="p-grid p-justify-start" style={{marginLeft: '10px'}}> - <label className= "error">{this.state.constraintsValidationMessage} </label> - <i class="fa fa-exclamation-circle validationError" aria-hidden="true" onClick={this.showConstraintError}></i> - </div> + {!this.state.validConstraints && this.state.constraintsValidationMessage.length > 0 && + <div className="p-grid p-justify-start" style={{marginLeft: '10px'}}> + <label className="error">{this.state.constraintsValidationMessage} </label> + <i class="fa fa-exclamation-circle validationError" aria-hidden="true" + onClick={this.showConstraintError}></i> + </div> + } + {!this.state.validSpecification && this.state.specificationValidationMessage.length > 0 && + <div className="p-grid p-justify-start" style={{marginLeft: '10px'}}> + <label className="error">{this.state.specificationValidationMessage}</label> + <i class="fa fa-exclamation-circle validationError" aria-hidden="true" + onClick={this.showSpecificationError}></i> + </div> + } + <div className="p-grid p-justify-start act-btn-grp"> + <div className="p-col-1"> + <Button label="Save" className="p-button-primary" icon="pi pi-check" + disabled={!(!this.state.isObsoletStrategy && scheduleunit_draft.create && scheduleunit_draft.edit) || this.state.isFetchingData} + onClick={this.saveSchedulingUnit} + data-testid="save-btn"/> + </div> + <div className="p-col-1"> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" + onClick={this.checkIsDirty}/> + </div> + </div> + </div> + </> } - {!this.state.validSpecification && this.state.specificationValidationMessage.length>0 && - <div className="p-grid p-justify-start" style={{marginLeft: '10px'}}> - <label className= "error">{this.state.specificationValidationMessage}</label> - <i class="fa fa-exclamation-circle validationError" aria-hidden="true" onClick={this.showSpecificationError}></i> - </div> + {this.state.showAddSet && + <SchedulingSet callbackFunction={this.setSUSet} project={this.state.selectedProject[0]} + onCancel={this.refreshSchedulingSet}/> } - <div className="p-grid p-justify-start act-btn-grp"> - <div className="p-col-1"> - <Button label="Save" className="p-button-primary" icon="pi pi-check" disabled={!(!this.state.isObsoletStrategy && scheduleunit_draft.create && scheduleunit_draft.edit) || this.state.isFetchingData} onClick={this.saveSchedulingUnit} - data-testid="save-btn" /> - </div> - <div className="p-col-1"> - <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> - </div> - </div> - </div> - </> - } - {this.state.addSchedulingSetVisible && - <SchedulingSet callbackFunction={this.setSUSet} project={this.state.selectedProject[0]} onCancel={this.refreshSchedulingSet} /> - } - <CustomDialog type={this.dialogType} visible={this.state.confirmDialogVisible} width={this.dialogWidth} height={this.dialogHeight} - header={this.dialogHeader} message={this.dialogMsg} - opacity={this.dialogHeader.startsWith("Paste")?(this.isMacOS?1:0):1} - content={this.dialogContent} onClose={this.onClose} - onCancel={this.onCancel} onSubmit={this.callBackFunction} - showIcon={this.showIcon} actions={this.actions}> - </CustomDialog> - <CustomPageSpinner visible={this.state.showSpinner} percentage={this.state.progressPercent} /> - </>:<AccessDenied/>} + <CustomDialog type={this.dialogType} visible={this.state.confirmDialogVisible} + width={this.dialogWidth} height={this.dialogHeight} + header={this.dialogHeader} message={this.dialogMsg} + opacity={this.dialogHeader.startsWith("Paste") ? (this.isMacOS ? 1 : 0) : 1} + content={this.dialogContent} onClose={this.onClose} + onCancel={this.onCancel} onSubmit={this.callBackFunction} + showIcon={this.showIcon} actions={this.actions}> + </CustomDialog> + <CustomPageSpinner visible={this.state.showSpinner} percentage={this.state.progressPercent}/> + </> : <AccessDenied/>} </React.Fragment> ); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.test.js index 52e6d27a9b30feff09312fc80a2ccc175b9a7659..839cc68b98a60d3f6e621fc737084fb9a2893048 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.test.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.test.js @@ -1,274 +1,197 @@ import React from 'react'; -import { BrowserRouter as Router } from 'react-router-dom'; -import { act } from "react-dom/test-utils"; -import { render, cleanup, fireEvent, screen } from '@testing-library/react'; +import {cleanup} from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import '@testing-library/jest-dom'; -import mockConsole from "jest-mock-console"; -import _ from "lodash"; -import {SchedulingSetCreate} from './excelview.schedulingset'; +import SchedulingSetCreate from './excelview.schedulingset'; import ScheduleService from '../../services/schedule.service'; -import UtilService from '../../services/util.service'; import ProjectService from '../../services/project.service'; import TaskService from '../../services/task.service'; -import AuthService from '../../services/auth.service'; - import AuthUtil from '../../utils/auth.util'; - import SUServiceMock from '../../__mocks__/scheduleunit.service.data'; import ProjectServiceMock from '../../__mocks__/project.service.data'; import TaskServiceMock from '../../__mocks__/task.service.data'; -import UtilServiceMock from '../../__mocks__/util.service.data'; import AuthServiceMock from '../../__mocks__/auth.service.data'; +import {clickItem, removeReact18ConsoleErrors, renderPage, setMultiSelectValue} from "../../utils/test.helper"; +import {setSchedulingUnitStrategy} from "./schedulingunit.test.helper"; -let projectListSpy, scheduleSetListSpy, observStrategiesSpy, taskTemplatesSpy, saveSUFromStrategySpy, updateSUSpy, createSUTasksSpy, -utilSpy, utcSpy, taskFilterDefSpy, suConstraintTemplateSpy, allTaskRelationSpy, stationGroupSpy, stationSpy, -userPermissionSpy, authPermissionSpy, scheduleSetByIdSpy, templatePurposesSpy, templateStatesSpy, rolesSpy; -jest.setTimeout(120000); -let restoreConsole; +let projectListSpy, observStrategiesSpy, taskTemplatesSpy, userPermissionSpy, rolesSpy, suConstraintTemplateSpy, + templatePurposesSpy, templateStatesSpy; -beforeEach(() => { - restoreConsole = mockConsole(); - setMockSpy(); -}); +//Jest issue: https://github.com/jestjs/jest/issues/9709: Cannot be called in beforeXX test method +const OBSERVATION_STRATEGY_TEMPLATES = SUServiceMock.getObservStrategies() -afterEach(() => { - restoreConsole(); - // cleanup on exiting - clearMockSpy(); - cleanup(); -}); +jest.setTimeout(120000); +removeReact18ConsoleErrors(); /** * To set mock spy for Services that have API calls to the back end to fetch data */ -const setMockSpy = () => { - projectListSpy = jest.spyOn(ProjectService, 'getProjectList'); - projectListSpy.mockImplementation(() => { return Promise.resolve(ProjectServiceMock.projectList)}); - scheduleSetListSpy = jest.spyOn(ScheduleService, 'getSchedulingSets'); - scheduleSetListSpy.mockImplementation((filter) => { - let allSUSets = SUServiceMock.scheduleSetList; - let project = filter.replace("project=",""); - return Promise.resolve(_.filter(allSUSets, {project_id: project})); - }); - observStrategiesSpy = jest.spyOn(ScheduleService, 'getObservationStrategies'); - observStrategiesSpy.mockImplementation(() => { return Promise.resolve(SUServiceMock.getObservStrategies())}); - taskTemplatesSpy = jest.spyOn(TaskService, 'getTaskTemplates'); - taskTemplatesSpy.mockImplementation(() => { return Promise.resolve(TaskServiceMock.getTaskTemplates())}); - saveSUFromStrategySpy = jest.spyOn(ScheduleService, 'saveSUDraftFromObservStrategy'); - saveSUFromStrategySpy.mockImplementation((observStrategy, schedulingUnit) => { - return Promise.resolve(SUServiceMock.getSchedulingUnitFromObservStrategy()); - }); - updateSUSpy = jest.spyOn(ScheduleService, 'updateSchedulingUnitDraft'); - updateSUSpy.mockImplementation((schedulingUnit) => { - return Promise.resolve(SUServiceMock.getSchedulingUnitFromObservStrategy()); +function createStandardPageMocks() { + projectListSpy = jest.spyOn(ProjectService, 'getProjectList').mockImplementation(() => { + return Promise.resolve(ProjectServiceMock.projectList) }); - createSUTasksSpy = jest.spyOn(ScheduleService, 'saveSUDraftFromObservStrategy'); - createSUTasksSpy.mockImplementation((schedulingUnit) => { - return Promise.resolve(SUServiceMock.getSchedulingUnitFromObservStrategy()); - }); - utilSpy = jest.spyOn(UtilService, 'getPriorityQueueType'); - utilSpy.mockImplementation(() => {return Promise.resolve(UtilServiceMock.getPriorityQueueType)}) - - utcSpy = jest.spyOn(UtilService, 'getUTC'); - utcSpy.mockImplementation(() => { return Promise.resolve(UtilServiceMock.getUTC)}); - - taskFilterDefSpy = jest.spyOn(TaskService, 'getTaskFilterDefinition'); - taskFilterDefSpy.mockImplementation(() => { return Promise.resolve(TaskServiceMock.getTaskFilterDefinition)}); - - templatePurposesSpy = jest.spyOn(ScheduleService, 'getStrategyPurpose'); - templatePurposesSpy.mockImplementation(() => { return Promise.resolve(SUServiceMock.templatePurposes)}); - - templateStatesSpy = jest.spyOn(ScheduleService, 'getStrategyState'); - templateStatesSpy.mockImplementation(() => { return Promise.resolve(SUServiceMock.templateStates)}); - suConstraintTemplateSpy = jest.spyOn(ScheduleService, 'getSchedulingConstraintTemplates'); - suConstraintTemplateSpy.mockImplementation(() => { return Promise.resolve(SUServiceMock.getSUCTemplates())}); - - allTaskRelationSpy = jest.spyOn(ScheduleService, 'getAllTaskRelation'); - allTaskRelationSpy.mockImplementation(() => { return Promise.resolve(SUServiceMock.getAllTaskRelation)}); + observStrategiesSpy = jest.spyOn(ScheduleService, 'getObservationStrategies').mockImplementation(() => { + return Promise.resolve(SUServiceMock.getObservStrategies()) + }); - stationGroupSpy = jest.spyOn(ScheduleService, 'getStationGroup'); - stationGroupSpy.mockImplementation(() => { return Promise.resolve(SUServiceMock.getStationGroup)}); + suConstraintTemplateSpy = jest.spyOn(ScheduleService, 'getSchedulingConstraintTemplates').mockImplementation(() => { + return Promise.resolve(SUServiceMock.getSUCTemplates()); + }); - stationSpy = jest.spyOn(ScheduleService, 'getStations'); - stationSpy.mockImplementation((group) => { return Promise.resolve(SUServiceMock.getStations)}); + taskTemplatesSpy = jest.spyOn(TaskService, 'getTaskTemplates').mockImplementation(() => { + return Promise.resolve(TaskServiceMock.getTaskTemplates()) + }); - userPermissionSpy = jest.spyOn(AuthUtil, 'getUserPermissionByModule'); - userPermissionSpy.mockImplementation((model) => { return Promise.resolve(AuthServiceMock.scheduleunit_draft)}); + templatePurposesSpy = jest.spyOn(ScheduleService, 'getStrategyPurpose').mockImplementation(() => { + return Promise.resolve(SUServiceMock.templatePurposes) + }); + templateStatesSpy = jest.spyOn(ScheduleService, 'getStrategyState').mockImplementation(() => { + return Promise.resolve(SUServiceMock.templateStates) + }); - authPermissionSpy = jest.spyOn(AuthService, 'getAccessControlMethod'); - authPermissionSpy.mockImplementation((module, id) => { + userPermissionSpy = jest.spyOn(AuthUtil, 'getUserPermissionByModule').mockImplementation(() => { return Promise.resolve(AuthServiceMock.scheduleunit_draft) }); - scheduleSetByIdSpy = jest.spyOn(ScheduleService, 'getSchedulingBySet'); - scheduleSetByIdSpy.mockImplementation((value) => { return Promise.resolve(SUServiceMock.getSchedulingBySet)}); - rolesSpy = jest.spyOn(ProjectService, 'getMyRoles'); - rolesSpy.mockImplementation((model) => { return Promise.resolve(SUServiceMock.getMyRoles)}); + rolesSpy = jest.spyOn(ProjectService, 'getMyRoles').mockImplementation(() => { + return Promise.resolve(SUServiceMock.getMyRoles) + }); } -const clearMockSpy = () => { - projectListSpy.mockRestore(); - scheduleSetListSpy.mockRestore(); - observStrategiesSpy.mockRestore(); - taskTemplatesSpy.mockRestore(); - saveSUFromStrategySpy.mockRestore(); - updateSUSpy.mockRestore(); - createSUTasksSpy.mockRestore(); - utilSpy.mockRestore(); - utcSpy.mockRestore(); - taskFilterDefSpy.mockRestore(); - suConstraintTemplateSpy.mockRestore(); - allTaskRelationSpy.mockRestore(); - stationGroupSpy.mockRestore(); - stationSpy.mockRestore(); - scheduleSetByIdSpy.mockRestore(); - rolesSpy.mockRestore(); -} +describe('Scheduling Units Excel View create page default', () => { + let pageContent; + + beforeEach(async () => { + createStandardPageMocks(); + pageContent = await renderPage(<SchedulingSetCreate location={{pathname: '/schedulingunit/'}}/>); + }) -it("renders SU Set create page with all fields and default values", async() => { - console.log("renders SU set create page with all fields and default values ------------------------"); - let content; - await act(async () => { - content = await render(<Router><SchedulingSetCreate location={{pathname: '/schedulingunit/'}}/></Router>); - await new Promise((r) => setTimeout(r, 500)); + afterEach(() => { + jest.clearAllMocks(); + cleanup(); }); - expect(content.queryByText('Scheduling Unit(s) Add Multiple')).not.toBe(null); // Page loaded successfully - expect(content.queryByText('Scheduling Unit(s)')).toBe(null); // AG Grid not visible - expect(projectListSpy).toHaveBeenCalled(); // Mock Spy called successfully - expect(observStrategiesSpy).toHaveBeenCalled(); // Mock Spy called successfully - expect(taskTemplatesSpy).toHaveBeenCalled(); // Mock Spy called successfully - - //It is disabled before select any project - expect(content.queryByTestId('addSet')).toHaveAttribute("disabled"); - fireEvent.click(screen.getAllByText("Select Project")[1]); - - const projInputEl = screen.getByText("high"); - await act( async() => { - fireEvent.click(projInputEl); - await new Promise((r) => setTimeout(r, 500)); + + test("Renders with standard service mocks", async () => { + expect(pageContent.queryByText('Scheduling Unit(s) Add Multiple')).not.toBe(null); + expect(projectListSpy).toHaveBeenCalled(); + expect(observStrategiesSpy).toHaveBeenCalled(); + expect(suConstraintTemplateSpy).toHaveBeenCalled(); + expect(taskTemplatesSpy).toHaveBeenCalled(); + expect(templatePurposesSpy).toHaveBeenCalled(); + expect(templateStatesSpy).toHaveBeenCalled(); + expect(userPermissionSpy).toHaveBeenCalled(); + expect(rolesSpy).toHaveBeenCalled(); }); - - expect(content.queryAllByText("high").length).toBe(2); - expect(scheduleSetListSpy).toHaveBeenCalled(); // Mock Spy called successfully - // It should be enabled after select project - expect(content.queryByTestId('addSet').hasAttribute("disabled")).toBeFalsy(); - fireEvent.click(screen.getAllByText("Select Scheduling Set")[1]); - expect(content.queryAllByText("Test Scheduling Set").length).toBe(1); - const suSetInputEl = screen.getByText("Test Scheduling Set"); - await act( async() => { - fireEvent.click(suSetInputEl); - await new Promise((r) => setTimeout(r, 500)); + + test("Selects a project, loads the project's sets, selects the test scheduling set and changes number of SUs", async () => { + const buttonElement = pageContent.queryByTestId('add-schedulingset-button'); + expect(buttonElement).toHaveAttribute('disabled'); + let scheduleSetListSpy = jest.spyOn(ScheduleService, 'getSchedulingSets').mockImplementation((filter) => { + let allSUSets = SUServiceMock.scheduleSetList; + let project = filter.replace("project=", ""); + return Promise.resolve(allSUSets.filter(set => set.project_id === project)); + }); + await setMultiSelectValue(pageContent, "project", "high") + await setMultiSelectValue(pageContent, "schedSet", "Test Scheduling Set") + expect(scheduleSetListSpy).toHaveBeenCalled() + expect(pageContent.queryByTestId("project").textContent).toContain("high"); + expect(pageContent.queryByTestId("schedSet").textContent).toContain("Test Scheduling Set"); + expect(buttonElement.hasAttribute("disabled")).toBeFalsy(); + + const noOfSUelement = pageContent.queryByTestId("noOfSU"); + expect(noOfSUelement.textContent).toBe("Enter No. of SU (1 to 500)") + expect(pageContent.queryByDisplayValue('10')).toBeDefined(); + await clickItem(noOfSUelement) + await clickItem(pageContent.getByText("50")) + expect(pageContent.queryByDisplayValue('10')).toBeNull() + expect(pageContent.queryByDisplayValue('50')).toBeDefined() }); - expect(content.queryAllByText("Test Scheduling Set").length).toBe(2); - expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); }); +function getColumnTypes(columnDefs) { + let columnTypes = columnDefs.map(col => { + if (col.children) { + return getColumnTypes(col.children) + } + return col.cellEditor + }); + columnTypes = columnTypes.flat().filter(colType => colType !== undefined) + return columnTypes; +} -// TODO: Need to check and update the test as all the gridcells are not loaded though the component related objects are derived properly. -it("renders AG grid loaded in SU Set create Page", async() => { - console.log("renders AG grid loaded in SU Set create Page ------------------------"); - let content; - await act(async () => { - content = await render(<Router><SchedulingSetCreate location={{pathname: '/schedulingunit/'}}/></Router>); - await new Promise((r) => setTimeout(r, 500)); +describe('Scheduling Units Excel View create page with an observation strategy template', () => { + let pageContent; + let scheduleSetListSpy, scheduleSetByIdSpy, saveSUFromStrategySpy, utcSpy, validatorSpy, stationGroupSpy; + + beforeEach(async () => { + createStandardPageMocks(); + pageContent = await renderPage(<SchedulingSetCreate location={{pathname: '/schedulingunit/'}}/>); + scheduleSetListSpy = jest.spyOn(ScheduleService, 'getSchedulingSets').mockImplementation((filter) => { + let allSUSets = SUServiceMock.scheduleSetList; + let project = filter.replace("project=", ""); + return Promise.resolve(allSUSets.filter(set => set.project_id === project)); + }); + scheduleSetByIdSpy = jest.spyOn(ScheduleService, 'getSchedulingBySet').mockImplementation(() => { + return Promise.resolve(SUServiceMock.getSchedulingBySet()) + }); + await setMultiSelectValue(pageContent, "project", "high") + await setMultiSelectValue(pageContent, "schedSet", "Test Scheduling Set") + }) + + afterEach(() => { + jest.clearAllMocks(); + cleanup() }); - expect(content.queryByText('Scheduling Unit(s) Add Multiple')).not.toBe(null); // Page loaded successfully - expect(content.queryByText('Scheduling Unit(s)')).toBe(null); // AG Grid not visible - expect(projectListSpy).toHaveBeenCalled(); // Mock Spy called successfully - expect(observStrategiesSpy).toHaveBeenCalled(); // Mock Spy called successfully - expect(taskTemplatesSpy).toHaveBeenCalled(); // Mock Spy called successfully - - //It is disabled before select any project - expect(content.queryByTestId('addSet')).toHaveAttribute("disabled"); - fireEvent.click(screen.getAllByText("Select Project")[1]); - - const projInputEl = screen.getByText("high"); - expect(content.queryAllByText("high").length).toBe(1); - await act( async() => { - fireEvent.click(projInputEl); - await new Promise((r) => setTimeout(r, 1000)); + test.each(OBSERVATION_STRATEGY_TEMPLATES)(`Correctly creates and checks headers for template '$name: $purpose, $state, v$version'`, async (observationStrategy) => { + expect(scheduleSetListSpy).toHaveBeenCalled(); + expect(scheduleSetByIdSpy).toHaveBeenCalled(); + await setSchedulingUnitStrategy(observationStrategy, pageContent); + + const gridElement = document.querySelector(".ag-root-wrapper").__agComponent.gridOptions; + //Min_distance section in the grid equals the scheduling constraints section in the template + let columnHeaderNames = gridElement.columnDefs.map(columnDef => + columnDef.headerName === "Min_distance" ? "Scheduling Constraints".toLowerCase() : columnDef.headerName.toLowerCase() + ); + const strategyParameterNames = observationStrategy.template.parameters.map(param => param.name.toLowerCase()) + expect(columnHeaderNames).toEqual(expect.arrayContaining(strategyParameterNames)) + + expect(gridElement.rowData.length).toBe(10) + + let saveSUFromStrategySpy = jest.spyOn(ScheduleService, 'saveSchedulingUnit').mockImplementation(() => { + let schedulingUnitDraft = SUServiceMock.getSchedulingUnitFromObservStrategy(observationStrategy); + schedulingUnitDraft["isSUUpdated"] = true; + return Promise.resolve(schedulingUnitDraft); + }); + await clickItem(pageContent.queryByTestId('save-btn')) + expect(saveSUFromStrategySpy).not.toHaveBeenCalled(); + expect(pageContent.getByRole('dialog')).toBeDefined(); //pop-up indicating errored SU }); - expect(content.queryAllByText("high").length).toBe(2); - expect(scheduleSetListSpy).toHaveBeenCalled(); // Mock Spy called successfully - // It should be enabled after select project - expect(content.queryByTestId('addSet').hasAttribute("disabled")).toBeFalsy(); - - fireEvent.click(screen.getAllByText("Select Scheduling Set")[1]); - expect(content.queryAllByText("Test Scheduling Set").length).toBe(1); - const suSetInputEl = screen.getByText("Test Scheduling Set"); - await act( async() => { - fireEvent.click(suSetInputEl); - await new Promise((r) => setTimeout(r, 500)); + test.each(OBSERVATION_STRATEGY_TEMPLATES)(`Checks column types for template '$name: $purpose, $state, v$version'`, async (observationStrategy) => { + await setSchedulingUnitStrategy(observationStrategy, pageContent); + await clickItem(pageContent.queryByTestId('select-multiple-su-input')) + const multipleInputValuesGridElement = document.querySelector(".ag-root-wrapper").__agComponent.gridOptions; + const columnDefs = multipleInputValuesGridElement.columnDefs; + + const uniqueColumnTypes = new Set(getColumnTypes(columnDefs)); + const knownColumnTypes = ['numericEditor', 'agSelectCellEditor', 'agDateInput', 'betweenEditor', 'multiselector', + 'offsetTimeInputmask', 'timeInputMask', 'degreeInputMask', 'station', 'beamformer'] + for (const type of uniqueColumnTypes) { + expect(knownColumnTypes, `A template in the back-end defined a column type that is not implemented in the front-end: ${type}`).toContain(type); + } }); - expect(content.queryAllByText("Test Scheduling Set").length).toBe(2); - - expect(content.queryByTestId('copyHeaderOnly')).toBe(null); - const simpleObsStrategies = _.filter(SUServiceMock.getObservStrategies(), strategy => { - return strategy.name==='Simple Observation' && strategy.state!=="obsolete" }); - const simpleObsStrategy = simpleObsStrategies.length>0?simpleObsStrategies[0]:null - // for (const simpleObsStrategy of simpleObsStrategies) { - if (simpleObsStrategy) { - const optionLabel = `${simpleObsStrategy.name} (${simpleObsStrategy.purpose}, ${simpleObsStrategy.state}, v${simpleObsStrategy.version})`; - console.log("Rendering ", optionLabel); - fireEvent.click(screen.getAllByText("Purpose")[0]); - const templatePurpose = screen.getAllByText(simpleObsStrategy.purpose)[0]; - await act( async() => { - fireEvent.click(templatePurpose); - await new Promise((r) => setTimeout(r, 500)); - }); - // fireEvent.click(screen.getAllByText("State")[0]); - fireEvent.click(screen.getAllByRole("listbox")[1].parentElement.parentElement.children[1]); - const templateState = screen.getAllByText(simpleObsStrategy.state)[0]; - await act( async() => { - fireEvent.click(templateState); - await new Promise((r) => setTimeout(r, 200)); - }); - // expect(content.queryAllByText(optionLabel).length).toBe(1); - fireEvent.click(screen.getAllByText("Select Strategy")[0]); - const observStrategyInput = screen.getAllByText(optionLabel); - await act( async() => { - // fireEvent.click(observStrategyInput[index]); - fireEvent.click(observStrategyInput[0].parentElement); - await new Promise((r) => setTimeout(r, 1000)); - }); - // expect(content.queryByTestId('copyHeaderOnly')).not.toBe(null); - expect(screen.getAllByLabelText('Press SPACE to select this row.').length).toBe(30); - expect(screen.getByPlaceholderText("Enter No. of SU (1 to 500)").value).toBe("10"); - fireEvent.click(screen.getByPlaceholderText("Enter No. of SU (1 to 500)").parentElement.children[3]); - const noOfSUEl = screen.getByLabelText("50"); - await act( async() => { - fireEvent.click(noOfSUEl); - await new Promise((r) => setTimeout(r, 1000)); - }); - expect(screen.getByPlaceholderText("Enter No. of SU (1 to 500)").value).toBe("50"); - expect(screen.getAllByLabelText('Press SPACE to select this row.').length).toBe(33); - // expect(content.queryAllByRole('row').length).toBe(48); + test.skip.each(OBSERVATION_STRATEGY_TEMPLATES)(`Correctly creates, alters via multi input grid and saves for template '$name: $purpose, $state, v$version'`, async (observationStrategy) => { + await setSchedulingUnitStrategy(observationStrategy, pageContent); - expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); + await clickItem(pageContent.queryByTestId('select-multiple-su-input')) + expect(pageContent.queryByRole('region')).not.toBeNull() - expect(content.queryByText('No valid Scheduling Unit found !')).not.toBeInTheDocument(); - // await act(async () => { - // fireEvent.click(content.queryByTestId('save-btn')); - // await new Promise((r) => setTimeout(r, 1000)); - // }); - // console.log(screen.getAllByRole("gridcell").length); - // for (const cell of screen.getAllByRole("gridcell")) { - // console.log(cell.getAttribute("col-id")); - // } - // expect(content.queryByText('No valid Scheduling Unit found !')).toBeInTheDocument(); //check popup appear after create SU successfully + const multipleInputValuesGridElement = document.querySelector(".ag-root-wrapper").__agComponent.gridOptions; - // Reset the purpose and state filter selection - await act( async() => { - fireEvent.click(templatePurpose); - fireEvent.click(templateState); - await new Promise((r) => setTimeout(r, 1000)); - }); - } - // } -}); + //TODO: finish test for actual input. Refactor Spreadsheet cell editor components + }); +}); \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/schedulingunit.test.helper.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/schedulingunit.test.helper.js new file mode 100644 index 0000000000000000000000000000000000000000..ed2a10ec38631bf10c8ff05ffc35727e3e3687f4 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/schedulingunit.test.helper.js @@ -0,0 +1,8 @@ +import {setMultiSelectValue} from "../../utils/test.helper"; + +export async function setSchedulingUnitStrategy(strategy, pageContent) { + await setMultiSelectValue(pageContent, "strategy-purpose", strategy.purpose) + await setMultiSelectValue(pageContent, "strategy-state", strategy.state) + const optionLabel = `${strategy.name} (${strategy.purpose}, ${strategy.state}, v${strategy.version})`; + await setMultiSelectValue(pageContent, "observStrategy", optionLabel) +} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/test.helper.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/test.helper.js index 6d771983caa2fd35b794b99764da8b6e5e220073..27f4ec21adfba0c4a653c2318cac082a4ad71975 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/test.helper.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/test.helper.js @@ -1,5 +1,6 @@ import {act} from "react-dom/test-utils"; -import {fireEvent, screen} from "@testing-library/react"; +import {fireEvent, render, screen} from "@testing-library/react"; +import {BrowserRouter as Router} from "react-router-dom"; const TIMEOUT = 300 @@ -12,15 +13,29 @@ export async function clickItem(item, timeout = TIMEOUT) { export async function setMultiSelectValue(pageContent, multiSelectTestId, multiSelectValue, timeout = TIMEOUT) { await clickItem(pageContent.queryByTestId(multiSelectTestId)); - const items = screen.getAllByText(multiSelectValue); - if (!items.length) { - throw new ReferenceError("No items found to select for multi selection element " + multiSelectValue) + + let items = [] + try { + items = screen.getAllByText(multiSelectValue); + } catch (error) { + throw new ReferenceError("No items found to select for multi selection element with test id '" + + multiSelectTestId + "' and value: " + multiSelectValue) } + for (let item of items) { await clickItem(item, timeout); } } +export async function renderPage(pageComponent) { + let content; + await act(async () => { + content = render(<Router>{pageComponent}</Router>); + await new Promise((r) => setTimeout(r, 500)); + }); + return content; +} + /** * Since the current version is React 17 but it has been patched to React 18, the behaviour is still the older version. * Therefore these specific warnings should be ignored until the app has been fully upgraded diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js index 06cb82e025b26c93fbace73a3b3a9851c9705567..eb4b77f680dd95a1223d4b8af306177630103043 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js @@ -1,5 +1,6 @@ import UnitConverter from "./unit.converter"; import _ from 'lodash'; +import moment from "moment"; const Validator = { validateTime(value) { @@ -99,36 +100,27 @@ const Validator = { }, /** * Validate time - * @param {string value with HH:mm:ss format} time - * @returns + * @param {string} time; HH:mm:ss format + * * @param {boolean} excludeHour the first hour number excluded (i.e. 01:23:45 is valid as 1:23:45) + * @returns {boolean} */ - isValidHHmmss(time, excludeHour) { - let isValid = true; - if (time && time.includes(':')) { - time = time.replaceAll("_", "0"); - if(excludeHour) { - let timeFormat = /^([0-9]|[0-9][0-9]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$/; - isValid = timeFormat.test(time); - } else { - let timeFormat = /^([0-9]|[0-1][0-9]|[0-2][0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$/; - isValid = timeFormat.test(time); - } - } else { - isValid = false; + isValidHHmmss(time, excludeHour = true) { + const momentFormats = ["HH:mm:ss"] + if (excludeHour) { + momentFormats.push("H:mm:ss") } - return isValid; + const momentTime = new moment(time, momentFormats, true) + return momentTime.isValid() }, + /** + * Validate duration in time + * @param duration in HH:mm:ss format + * @return {boolean} + */ isValidDuration(duration) { - let isValid = false; - if (duration && duration.includes(':')) { - //Here below condition required for unit test as its not recognized the replaceAll - if (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; + const timeFormat = /^([0-9]+):([0-5]?[0-9]):([0-5]?[0-9])$/; + + return timeFormat.test(duration); }, /** * Validate Day time diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.test.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.test.js new file mode 100644 index 0000000000000000000000000000000000000000..70b4315193259698132196315ec9695b3b794a8d --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.test.js @@ -0,0 +1,37 @@ +import Validator from "./validator"; + +describe('isValidHHmmss', () => { + test.each(['12:34:56', '23:00:59', '0:59:56', '1:01:59'])('Valid time: %s', (time) => { + expect(Validator.isValidHHmmss(time)).toBe(true); + }) + + test.each(['25:34:56', '12:60:30', '12:34:67', '25:34', 'invalid', '78:56', '12:67', '12:', '12'])('Invalid time: %s', (time) => { + expect(Validator.isValidHHmmss(time)).toBe(false); + }) + + test.each(['0:59:56', '1:01:59'])('Invalid time when hour must be included: %s', (time) => { + expect(Validator.isValidHHmmss(time, false)).toBe(false); + }) + + test.each(['', undefined])('Missing input: %s', (time) => { + expect(Validator.isValidHHmmss(time)).toBe(false); + }) +}); + +describe('isValidDuration', () => { + test.each(['00:00:00', '12:34:56', '01:02:03', '24:00:00', '50:00:00', '12345:00:00'])('Valid durations: %s', (time) => { + expect(Validator.isValidDuration(time)).toBe(true); + }) + + test.each(['00:61:00', '00:00:61', '12:34', 'invalid'])('Invalid durations: %s', (time) => { + expect(Validator.isValidDuration(time)).toBe(false); + }) + + test.each(['', undefined])('Missing input: %s', (time) => { + expect(Validator.isValidDuration(time)).toBe(false); + }) + + test.each(['12-34-56', '12.34.56', '12/34/56', '12_34_56'])('Input in wrong format: %s', (time) => { + expect(Validator.isValidDuration(time)).toBe(false); + }) +});