diff --git a/LCS/PyCommon/json_utils.py b/LCS/PyCommon/json_utils.py index f1aee10a64206f6c4897c69868991fd724b92f72..956fd3b0a29c34bc25bc3e204ff877943e266ca1 100644 --- a/LCS/PyCommon/json_utils.py +++ b/LCS/PyCommon/json_utils.py @@ -134,23 +134,13 @@ def resolved_refs(schema): '''return the given schema with all $ref fields replaced by the referred json (sub)schema that they point to.''' if isinstance(schema, dict): updated_schema = {} - for key, value in schema.items(): - if key in "$ref" and isinstance(value, str): - if value.startswith('#'): - # reference to local document, no need for http injection - updated_schema[key] = value - else: - try: - # by returning the referenced (sub)schema, the $ref-key and url-value are replaced from the caller's perspective. - # also, recursively resolve refs in referenced_subschema - referenced_subschema = get_referenced_subschema(value) - return resolved_refs(referenced_subschema) - except: - # can't get the referenced schema - # so, just accept the original value and assume that the user uploaded a proper schema - updated_schema[key] = value - else: - updated_schema[key] = resolved_refs(value) + keys = list(schema.keys()) + if "$ref" in keys and isinstance(schema['$ref'], str) and schema['$ref'].startswith('http'): + keys.remove("$ref") + updated_schema = resolved_refs(get_referenced_subschema(schema['$ref'])) + + for key in keys: + updated_schema[key] = resolved_refs(schema[key]) return updated_schema if isinstance(schema, list): diff --git a/LCS/PyCommon/test/t_json_utils.py b/LCS/PyCommon/test/t_json_utils.py index 2237f0f8d68717fe304b4babb301887b6bf89546..78609dbf21d6deb06cdc0cc663d6edd3a8f881fe 100755 --- a/LCS/PyCommon/test/t_json_utils.py +++ b/LCS/PyCommon/test/t_json_utils.py @@ -94,14 +94,15 @@ class TestJSONUtils(unittest.TestCase): "name": { "type": "string", "minLength": 2 }, - "email": { - "$ref": base_url + "/base_schema.json" + "#/definitions/email" - }, + "email": { + "$ref": base_url + "/base_schema.json" + "#/definitions/email", + "extra_prop": "very important" + }, "other_emails": { "type": "array", "items": { - "$ref": base_url + "/base_schema.json" + "#/definitions/email" - } + "$ref": base_url + "/base_schema.json" + "#/definitions/email" + } } } } class TestRequestHandler(http.server.BaseHTTPRequestHandler): @@ -134,7 +135,11 @@ class TestJSONUtils(unittest.TestCase): print('resolved_user_schema: ', json.dumps(resolved_user_schema, indent=2)) self.assertNotEqual(user_schema['properties']['email'], resolved_user_schema['properties']['email']) - self.assertEqual(base_schema['definitions']['email'], resolved_user_schema['properties']['email']) + for key,value in base_schema['definitions']['email'].items(): + self.assertEqual(value, resolved_user_schema['properties']['email'][key]) + self.assertTrue('extra_prop' in resolved_user_schema['properties']['email']) + self.assertEqual('very important', resolved_user_schema['properties']['email']['extra_prop']) + httpd.shutdown() thread.join(timeout=2) diff --git a/SAS/TMSS/src/tmss/tmssapp/schemas/UC1-scheduling-unit-observation-strategy.json b/SAS/TMSS/src/tmss/tmssapp/schemas/UC1-scheduling-unit-observation-strategy.json index 183f8933332dd016194b939315b4bc25a9c3d183..b7ba95dffbfe3d81a8d6830181b6b03796ac3190 100644 --- a/SAS/TMSS/src/tmss/tmssapp/schemas/UC1-scheduling-unit-observation-strategy.json +++ b/SAS/TMSS/src/tmss/tmssapp/schemas/UC1-scheduling-unit-observation-strategy.json @@ -62,7 +62,20 @@ }, "antenna_set": "HBA_DUAL_INNER", "filter": "HBA_110_190", - "stations":["CS001"], + "station_groups": [ + { + "stations": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"], + "max_nr_missing": 4 + }, + { + "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 + } + ], "tile_beam": { "direction_type": "J2000", "angle1": 0.42, diff --git a/SAS/TMSS/src/tmss/tmssapp/schemas/common_schema_template-stations-1.json b/SAS/TMSS/src/tmss/tmssapp/schemas/common_schema_template-stations-1.json index 877d9aa465075a271f0572df3a6aabd47f0c0ea0..1e6ef2fb974154228595d046c99c2b9a67934888 100644 --- a/SAS/TMSS/src/tmss/tmssapp/schemas/common_schema_template-stations-1.json +++ b/SAS/TMSS/src/tmss/tmssapp/schemas/common_schema_template-stations-1.json @@ -6,10 +6,10 @@ "version":"1", "type":"object", "definitions":{ - "station":{ + "station":{ "type":"string", "title":"Station", - "description":"", + "description":"These are the LOFAR stations", "enum":[ "CS001", "CS002", @@ -68,7 +68,6 @@ ] }, "station_list":{ - "title":"fixed station list", "default":[ "CS001" ], @@ -81,53 +80,188 @@ "minItems":1, "uniqueItems":true }, - "station_set":{ - "title":"dynamic station set", + "max_number_of_missing_stations": { + "type":"integer", + "title":"Maximum number of stations to omit", + "description":"Maximum number of stations that can be omitted from a group (due to maintenance for example)", + "minimum":0 + }, + "station_group":{ "type":"object", + "title": "Station group", + "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)", "default":{}, - "additionalProperties":false, - "properties":{ - "group":{ - "type":"string", - "title":"StationGroup", - "description":"Which (group of) station(s) to select from", - "default":"ALL", - "enum":[ - "ALL", - "SUPERTERP", - "CORE", - "REMOTE", - "DUTCH", - "INTERNATIONAL" - ] + "anyOf": [ + { + "title":"Superterp", + "description": "The group of all stations on the Superterp", + "type": "object", + "properties":{ + "stations":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list", + "enum": [["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"]], + "default": ["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"], + "uniqueItems": false + }, + "max_nr_missing":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations", + "default": 0 + } + }, + "required": ["stations", "max_nr_missing"], + "additionalProperties": false }, - "min_stations":{ - "type":"integer", - "title":"Minimum nr of stations", - "description":"Number of stations to use within group/station", - "default":1, - "minimum":0 - } - }, - "required":[ - "group", - "min_stations" - ] - }, - "stations": { - "title":"stations", - "description":"Use either the fixed station list, or one of the dynamic station sets.", - "oneOf": [ { - "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list" + { + "title":"Core", + "description": "The group of all Core stations", + "type": "object", + "properties":{ + "stations":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list", + "enum": [["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS301", "CS302", "CS401", "CS501"]], + "default": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS301", "CS302", "CS401", "CS501"], + "uniqueItems": false + }, + "max_nr_missing":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations", + "default": 4 + } + }, + "required": ["stations", "max_nr_missing"], + "additionalProperties": false }, { - "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_set" + "title":"Remote", + "description": "The group of all Dutch remote stations", + "type": "object", + "properties":{ + "stations":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list", + "enum": [["RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"]], + "default": ["RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"], + "uniqueItems": false + }, + "max_nr_missing":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations", + "default": 4 + } + }, + "required": ["stations", "max_nr_missing"], + "additionalProperties": false + }, + { + "title":"Dutch", + "description": "The group of all Dutch (Core + Remote) stations", + "type": "object", + "properties":{ + "stations":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list", + "enum": [["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"]], + "default": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "CS301", "CS302", "CS401", "CS501", "RS106", "RS205", "RS208", "RS210", "RS305", "RS306", "RS307", "RS310", "RS406", "RS407", "RS409", "RS503", "RS508", "RS509"], + "uniqueItems": false + }, + "max_nr_missing":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations", + "default": 4 + } + }, + "required": ["stations", "max_nr_missing"], + "additionalProperties": false + }, + { + "title":"International", + "description": "The group of all international stations", + "type": "object", + "properties":{ + "stations":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list", + "enum": [["DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"]], + "default": ["DE601", "DE602", "DE603", "DE604", "DE605", "DE609", "FR606", "SE607", "UK608", "PL610", "PL611", "PL612", "IE613", "LV614"], + "uniqueItems": false + }, + "max_nr_missing":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations", + "default": 2 + } + }, + "required": ["stations", "max_nr_missing"], + "additionalProperties": false + }, + { + "title":"International required", + "description": "A subgroup of the international stations which are required when doing observation with international stations", + "type": "object", + "properties":{ + "stations":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list", + "enum": [["DE601", "DE605"]], + "default": ["DE601", "DE605"], + "uniqueItems": false + }, + "max_nr_missing":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations", + "default": 1 + } + }, + "required": ["stations", "max_nr_missing"], + "additionalProperties": false + }, + { + "title":"All", + "description": "The group of all (Core + Remote + International) stations", + "type": "object", + "properties":{ + "stations":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list", + "enum": [["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "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"]], + "default": ["CS001", "CS002", "CS003", "CS004", "CS005", "CS006", "CS007", "CS011", "CS013", "CS017", "CS021", "CS024", "CS026", "CS028", "CS030", "CS031", "CS032", "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"], + "uniqueItems": false + }, + "max_nr_missing":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations", + "default": 6 + } + }, + "required": ["stations", "max_nr_missing"], + "additionalProperties": false + }, + { + "title":"Custom", + "description": "A custom group of stations which can be defined by the user", + "type": "object", + "properties":{ + "stations":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_list", + "default": ["CS001"], + "minItems": 1, + "additionalItems": false, + "additionalProperties": false, + "uniqueItems": true + }, + "max_nr_missing":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/max_number_of_missing_stations", + "default": 0 + } + }, + "required": ["stations", "max_nr_missing"], + "additionalProperties": false } - ], - "default": { - "group": "ALL", - "min_stations": 1 - } + ] + }, + "station_groups": { + "title":"Station groups", + "description": "One or more predefined or custom groups of stations", + "type":"array", + "additionalItems":false, + "additionalProperties":false, + "items":{ + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_group" + }, + "minItems":1, + "default": [ { + "stations": ["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"], + "max_nr_missing": 1 + } ] }, "antenna_set":{ "type":"string", @@ -210,4 +344,6 @@ "required": [ "fields" ] } } -} \ No newline at end of file +} + + diff --git a/SAS/TMSS/src/tmss/tmssapp/schemas/task_template-target_observation-1.json b/SAS/TMSS/src/tmss/tmssapp/schemas/task_template-target_observation-1.json index f6e92bc010d32d7e20cc879dfe7576fd18dfdb71..5777e4b1b98f9a13f63eacc8c8545438a62deff7 100644 --- a/SAS/TMSS/src/tmss/tmssapp/schemas/task_template-target_observation-1.json +++ b/SAS/TMSS/src/tmss/tmssapp/schemas/task_template-target_observation-1.json @@ -6,9 +6,12 @@ "version": 1, "type": "object", "properties": { - "stations": { - "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/stations", - "default": ["CS001"] + "station_groups": { + "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1#/definitions/station_groups", + "default": [ { + "stations": ["CS002", "CS003", "CS004", "CS005", "CS006", "CS007"], + "max_nr_missing": 1 + } ] }, "antenna_set": { "$ref": "http://tmss.lofar.org/api/schemas/commonschematemplate/stations/1/#/definitions/antenna_set", @@ -131,7 +134,7 @@ } }, "required": [ - "stations", + "station_groups", "antenna_set", "filter", "SAPs", diff --git a/SAS/TMSS/src/tmss/tmssapp/serializers/widgets.py b/SAS/TMSS/src/tmss/tmssapp/serializers/widgets.py index 93d00448ace439827d83caf4360a73e04350ffb1..cedabc794bf1c6b104c737b23a0d2f4344bf5eb5 100644 --- a/SAS/TMSS/src/tmss/tmssapp/serializers/widgets.py +++ b/SAS/TMSS/src/tmss/tmssapp/serializers/widgets.py @@ -59,6 +59,12 @@ class JSONEditorField(serializers.JSONField): # so, let's do the resolving here and feed the resolved schema to the josdejong_jsoneditor_widget schema = json_utils.resolved_refs(schema) + # the editor already fetched and cached common meta schema's from json-schema.org + # and raises an error if we supply it as well + # so, set schema to None for those + if 'json-schema.org' in value.get('$schema', ''): + schema = None + self.style = {'template': 'josdejong_jsoneditor_widget.html', 'schema': json.dumps(schema)} diff --git a/SAS/TMSS/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/src/tmss/tmssapp/subtasks.py index 94e2ce2064ba868c847e6d98c11f96150072d3f0..8dc5a528a9c0be66010cd8e80dc146a8c0f85d73 100644 --- a/SAS/TMSS/src/tmss/tmssapp/subtasks.py +++ b/SAS/TMSS/src/tmss/tmssapp/subtasks.py @@ -130,17 +130,20 @@ def create_observation_subtask_specifications_from_observation_task_blueprint(ta subtask_spec['stations']["antenna_set"] = task_spec["antenna_set"] subtask_spec['stations']["filter"] = task_spec["filter"] - if "stations" in task_spec: - if "group" in task_spec["stations"]: - try: - # retrieve stations in group from RADB virtual instrument - station_group_name = task_spec["stations"]["group"] - subtask_spec['stations']['station_list'] = get_stations_in_group(station_group_name) - except Exception as e: - raise SubtaskCreationException("Could not determine stations in group '%s' for task_blueprint id=%s. Error: %s" % ( - station_group_name, task_blueprint.id, e)) - else: - subtask_spec['stations']['station_list'] = task_spec["stations"] + # At this moment of subtask creation we known which stations we *want* from the task_spec + # But we do not know yet which stations are available at the moment of observing. + # So, we decided that we set the subtask station_list as the union of all stations in all specified groups. + # This way, the user can see which stations are (likely) to be used. + # At the moment of scheduling of this subtask, then station_list is re-evaluated, and the max_nr_missing per group is validated. + subtask_spec['stations']['station_list'] = [] + if "station_groups" in task_spec: + for station_group in task_spec["station_groups"]: + subtask_spec['stations']['station_list'].extend(station_group["stations"]) + # make list have unique items + subtask_spec['stations']['station_list'] = sorted(list(set(subtask_spec['stations']['station_list']))) + + if not subtask_spec['stations']['station_list']: + raise SubtaskCreationException("Cannot create observation subtask specifications for task_blueprint id=%s. No stations are defined." % (task_blueprint.id,)) if 'calibrator' not in task_blueprint.specifications_template.name.lower(): # copy/convert the analoge/digital_pointings only for non-calibrator observations (the calibrator has its own pointing) @@ -175,6 +178,16 @@ def create_observation_subtask_specifications_from_observation_task_blueprint(ta def get_stations_in_group(station_group_name: str) -> []: '''Get a list of station names in the given station_group. A lookup is performed in the RADB, in the virtual instrument table''' + + # TODO Make names RA and TMSS spec equal: 'NL' or 'DUTCH'? + if station_group_name == "DUTCH": + station_group_name = "NL" + + #International required is by defintion DE601 or DE605, take 601 for now + # TODO check with RA the availability of both stations + if station_group_name == "INTERNATIONAL_REQUIRED": + return ["DE601"] + with RADBRPC.create() as radbrpc: resource_group_memberships = radbrpc.getResourceGroupMemberships()['groups'] station_resource_group = next(rg for rg in resource_group_memberships.values() @@ -186,6 +199,10 @@ def get_stations_in_group(station_group_name: str) -> []: if 'RS408' in station_names: station_names.remove('RS408') + # HACK remove TEST1 from station list otherwise validate will fail + if 'TEST1' in station_names: + station_names.remove('TEST1') + return sorted(list(station_names)) @@ -753,6 +770,8 @@ def schedule_observation_subtask(observation_subtask: Subtask): # step 4: resource assigner (if possible) _assign_resources(observation_subtask) + # TODO: TMSS-382: evaluate the scheduled stations and see if the requiments given in the subtask.task_bluepring.specifications_doc are met for the station_groups and max_nr_missing. + # step 5: set state to SCHEDULED (resulting in the qaservice to pick this subtask up and run it) observation_subtask.state = SubtaskState.objects.get(value=SubtaskState.Choices.SCHEDULED.value) observation_subtask.save() diff --git a/SAS/TMSS/src/tmss/tmssapp/views.py b/SAS/TMSS/src/tmss/tmssapp/views.py index 3b163882b38e08b032d7700efe3145b8d70c02f2..58a389fd6e332c7fea88e113fda8fe8e0d734217 100644 --- a/SAS/TMSS/src/tmss/tmssapp/views.py +++ b/SAS/TMSS/src/tmss/tmssapp/views.py @@ -1,6 +1,6 @@ import os -from django.http import HttpResponse, JsonResponse +from django.http import HttpResponse, JsonResponse, Http404 from django.shortcuts import get_object_or_404, render from lofar.sas.tmss.tmss.tmssapp import models from lofar.common.json_utils import get_default_json_object_for_schema @@ -60,6 +60,30 @@ def get_template_json_schema(request, template:str, name:str, version:str): return response +# Allow everybody to GET our publicly available station group lookups +@permission_classes([AllowAny]) +@authentication_classes([AllowAny]) +@swagger_auto_schema(responses={200: 'A JSON object with two properties: group:<the_group_name>, stations:<the_list_of_stations>', + 404: 'No such group or template available'}, + operation_description="Get a JSON list of stations for the given <station_group> name the the group definitions in the common_schema_template given by <template_name> and <template_version>") +def get_stations_in_group(request, template_name:str, template_version:str, station_group:str): + station_schema_template = get_object_or_404(models.CommonSchemaTemplate, name=template_name, version=template_version) + station_schema = station_schema_template.schema + + if 'station_group' not in station_schema.get('definitions', {}): + raise Http404('The JSON schema in template %s version %s has no station_group definitions' % (template_name, template_version)) + + groups = station_schema['definitions']['station_group']['anyOf'] + try: + selected_group = next(g for g in groups if g['title'].lower() == station_group.lower()) + except StopIteration: + raise Http404('No station_group with name "%s" found in the JSON schema. template=%s version=%s' % (station_group, template_name, template_version)) + + stations = selected_group['properties']['stations']['enum'][0] + return JsonResponse({'group': station_group, + 'stations': stations}) + + def utc(request): return HttpResponse(datetime.utcnow().isoformat(), content_type='text/plain') diff --git a/SAS/TMSS/src/tmss/urls.py b/SAS/TMSS/src/tmss/urls.py index 85712c60e16cc15caf4b83962daed7c71ccb654f..c6c812bc3c5ee97115ef0c2664bcf56f524bfbc3 100644 --- a/SAS/TMSS/src/tmss/urls.py +++ b/SAS/TMSS/src/tmss/urls.py @@ -64,6 +64,8 @@ urlpatterns = [ path('redoc/', swagger_schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), path('schemas/<str:template>/<str:name>/<str:version>', views.get_template_json_schema, name='get_template_json_schema'), #TODO: how to make trailing slash optional? path('schemas/<str:template>/<str:name>/<str:version>/', views.get_template_json_schema, name='get_template_json_schema'), + path('station_groups/<str:template_name>/<str:template_version>/<str:station_group>', views.get_stations_in_group, name='get_stations_in_group'), #TODO: how to make trailing slash optional? + path('station_groups/<str:template_name>/<str:template_version>/<str:station_group>/', views.get_stations_in_group, name='get_stations_in_group'), path(r'util/utc', views.utc, name="system-utc"), path(r'util/lst', views.lst, name="conversion-lst"), path('__debug__/', include(debug_toolbar.urls)), diff --git a/SAS/TMSS/test/t_scheduling.py b/SAS/TMSS/test/t_scheduling.py index 8bdd2df6476ddb86c6b59629653875506c42f4cf..1f3d24c819fcb2099eebac85931e7348bf2799ba 100755 --- a/SAS/TMSS/test/t_scheduling.py +++ b/SAS/TMSS/test/t_scheduling.py @@ -64,6 +64,7 @@ from datetime import datetime, timedelta from lofar.sas.resourceassignment.resourceassigner.rarpc import RARPC from lofar.sas.tmss.tmss.tmssapp import models from lofar.sas.tmss.tmss.tmssapp.subtasks import * +from lofar.sas.tmss.tmss.tmssapp.tasks import * def create_subtask_object_for_testing(subtask_type_value, subtask_state_value): @@ -357,6 +358,56 @@ class SAPTest(unittest.TestCase): +class CreationFromSchedulingUnitDraft(unittest.TestCase): + """ + From scheduling_unit_draft test: + create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft: models.SchedulingUnitDraft) -> models.SchedulingUnitBlueprint: + This requires Resource Assigner testenvironment being alive + """ + + def test_create_task_blueprints_and_subtasks_from_scheduling_unit_draft_with_UC1_requirements(self): + """ + Create Scheduling Unit Draft with requirements_doc (read from file) + Create Task Blueprints and Subtasks + Check if tasks (7) are created: + Calibration 1 : 1 Observation and 1 Pipeline task + Target Observation: 1 Observation and 2 Pipeline tasks + Calibration 2 : 1 Observation and 1 Pipeline task + Check if subtasks (13) are created: + Every Observation Task: 3 subtasks (1 control, 2 QA) + Every Pipeline Task: 1 subtasks (1 control) + makes 3x3 + 4x1 = 13 + """ + strategy_template = models.SchedulingUnitObservingStrategyTemplate.objects.get(name="UC1 CTC+pipelines") + + scheduling_unit_draft = models.SchedulingUnitDraft.objects.create( + name="Test Scheduling Unit UC1", + requirements_doc=strategy_template.template, + requirements_template=strategy_template.scheduling_unit_template, + observation_strategy_template=strategy_template, + copy_reason=models.CopyReason.objects.get(value='template'), + generator_instance_doc="para", + copies=None, + scheduling_set=models.SchedulingSet.objects.create(**SchedulingSet_test_data())) + + create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft) + + scheduling_unit_draft.refresh_from_db() + task_drafts = scheduling_unit_draft.task_drafts.all() + self.assertEqual(7, len(task_drafts)) + + scheduling_unit_blueprints = scheduling_unit_draft.scheduling_unit_blueprints.all() + self.assertEqual(1, len(scheduling_unit_blueprints)) + + scheduling_unit_blueprint = scheduling_unit_blueprints[0] + task_blueprints = scheduling_unit_blueprint.task_blueprints.all() + self.assertEqual(7, len(task_blueprints)) + total_subtasks = 0 + for task_blueprint in task_blueprints: + total_subtasks += task_blueprint.subtasks.count() + self.assertEqual(13, total_subtasks) + + if __name__ == "__main__": os.environ['TZ'] = 'UTC' unittest.main() diff --git a/SAS/TMSS/test/t_tasks.py b/SAS/TMSS/test/t_tasks.py index a5a099bbdb7e13c8efd15d40df6941e166c243ee..1b4aefc9e56a1dccccb7fdbf92abac85068ace38 100755 --- a/SAS/TMSS/test/t_tasks.py +++ b/SAS/TMSS/test/t_tasks.py @@ -123,51 +123,9 @@ class CreationFromSchedulingUnitDraft(unittest.TestCase): task_drafts = scheduling_unit_draft.task_drafts.all() self.assertEqual(7, len(task_drafts)) - def test_create_task_blueprints_and_subtasks_from_scheduling_unit_draft_with_UC1_requirements(self): - """ - Create Scheduling Unit Draft with requirements_doc (read from file) - Create Task Blueprints and Subtasks - Check if tasks (7) are created: - Calibration 1 : 1 Observation and 1 Pipeline task - Target Observation: 1 Observation and 2 Pipeline tasks - Calibration 2 : 1 Observation and 1 Pipeline task - Check if subtasks (13) are created: - Every Observation Task: 3 subtasks (1 control, 2 QA) - Every Pipeline Task: 1 subtasks (1 control) - makes 3x3 + 4x1 = 13 - """ - strategy_template = models.SchedulingUnitObservingStrategyTemplate.objects.get(name="UC1 CTC+pipelines") - - scheduling_unit_draft = models.SchedulingUnitDraft.objects.create( - name="Test Scheduling Unit UC1", - requirements_doc=strategy_template.template, - requirements_template=strategy_template.scheduling_unit_template, - observation_strategy_template=strategy_template, - copy_reason=models.CopyReason.objects.get(value='template'), - generator_instance_doc="para", - copies=None, - scheduling_set=models.SchedulingSet.objects.create(**SchedulingSet_test_data())) - - create_task_blueprints_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft) - - scheduling_unit_draft.refresh_from_db() - task_drafts = scheduling_unit_draft.task_drafts.all() - self.assertEqual(7, len(task_drafts)) - - scheduling_unit_blueprints = scheduling_unit_draft.scheduling_unit_blueprints.all() - self.assertEqual(1, len(scheduling_unit_blueprints)) - - scheduling_unit_blueprint = scheduling_unit_blueprints[0] - task_blueprints = scheduling_unit_blueprint.task_blueprints.all() - self.assertEqual(7, len(task_blueprints)) - total_subtasks = 0 - for task_blueprint in task_blueprints: - total_subtasks += task_blueprint.subtasks.count() - self.assertEqual(13, total_subtasks) - def test_create_task_blueprints_and_subtasks_from_scheduling_unit_draft(self): """ - Create Scheduling Unit Draft + Create Scheduling Unit Draft with empty task specification Check if the name draft (specified) is equal to name blueprint (created) Check with REST-call if NO tasks are created """