diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e8e336c5fc789a9bc47574134599f41dea94eef5..7f96632c36e9b1caa7887e0fb323adc3fcf69678 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -265,6 +265,7 @@ integration_test_TMSS: RABBITMQ_DEFAULT_PASS: guest LOFAR_DEFAULT_BROKER: 'rabbitmq' # override default 'localhost' which does not work for CI service rabbitmq. needs: + - build_TMSS - unit_test_TMSS artifacts: name: integration-test-report @@ -289,6 +290,7 @@ integration_test_RAServices: - cd build/gnucxx11_opt - SKIP_INTEGRATION_TESTS=false SKIP_UNIT_TESTS=true ctest needs: + - build_RAServices - unit_test_RAServices artifacts: name: integration-test-report @@ -313,6 +315,7 @@ integration_test_LTAIngest: RABBITMQ_DEFAULT_PASS: guest LOFAR_DEFAULT_BROKER: 'rabbitmq' # override default 'localhost' which does not work for CI service rabbitmq. needs: + - build_LTAIngest - unit_test_LTAIngest artifacts: name: integration-test-report diff --git a/Docker/lofar-ci/Dockerfile_ci_sas b/Docker/lofar-ci/Dockerfile_ci_sas index 527639e256c50c98b1ef0550b41a7cbf69b3e1c3..1aa8f6689b56f7529d3a0a17e0022128a9ab2bbc 100644 --- a/Docker/lofar-ci/Dockerfile_ci_sas +++ b/Docker/lofar-ci/Dockerfile_ci_sas @@ -16,7 +16,7 @@ RUN yum erase -y postgresql postgresql-server postgresql-devel && \ cd /bin && ln -s /usr/pgsql-9.6/bin/initdb && ln -s /usr/pgsql-9.6/bin/postgres ENV PATH /usr/pgsql-9.6/bin:$PATH -RUN pip3 install cython kombu lxml requests pygcn xmljson mysql-connector-python python-dateutil Django==3.0.9 djangorestframework==3.11.1 djangorestframework-xml ldap==1.0.2 flask fabric coverage python-qpid-proton PyGreSQL numpy h5py psycopg2 testing.postgresql Flask-Testing scipy Markdown django-filter python-ldap python-ldap-test ldap3 django-jsonforms django-json-widget django-jsoneditor drf-yasg flex swagger-spec-validator django-auth-ldap mozilla-django-oidc jsonschema comet pyxb==1.2.5 graphviz isodate astropy packaging +RUN pip3 install cython kombu lxml requests pygcn xmljson mysql-connector-python python-dateutil Django==3.0.9 djangorestframework==3.11.1 djangorestframework-xml ldap==1.0.2 flask fabric coverage python-qpid-proton PyGreSQL numpy h5py psycopg2 testing.postgresql Flask-Testing scipy Markdown django-filter python-ldap python-ldap-test ldap3 django-jsonforms django-json-widget django-jsoneditor drf-yasg flex swagger-spec-validator django-auth-ldap mozilla-django-oidc jsonschema comet pyxb==1.2.5 graphviz isodate astropy packaging django-debug-toolbar #Viewflow package RUN pip3 install django-material django-viewflow diff --git a/SAS/TMSS/docker-compose-ua.yml b/SAS/TMSS/docker-compose-ua.yml index 74752f8596f9daa35763a85b7f5e355288b38cbd..73b699d14c94f2d0606c3242d4b964f593d05b68 100644 --- a/SAS/TMSS/docker-compose-ua.yml +++ b/SAS/TMSS/docker-compose-ua.yml @@ -17,9 +17,10 @@ services: - "8088:8088" web: image: nexus.cep4.control.lofar:18080/tmss_django:latest + hostname: tmss-ua restart: on-failure env_file: - ./.env - command: bash -c 'source /opt/lofar/lofarinit.sh && python3 lib64/python3.6/site-packages/lofar/sas/tmss/manage.py runserver 0.0.0.0:8008' + command: bash -c 'source /opt/lofar/lofarinit.sh && ALLOWED_HOSTS=* tmss_test_environment -H 0.0.0.0 -P tmss-ua -p 8008 --data' ports: - "8008:8008" diff --git a/SAS/TMSS/src/tmss/CMakeLists.txt b/SAS/TMSS/src/tmss/CMakeLists.txt index a38c2b149ed20a69a4ae3376365d869db9c1990e..3e7754777f2f6d34a58352c9d78765303dd9cfa4 100644 --- a/SAS/TMSS/src/tmss/CMakeLists.txt +++ b/SAS/TMSS/src/tmss/CMakeLists.txt @@ -13,3 +13,4 @@ python_install(${_py_files} DESTINATION lofar/sas/tmss/tmss) add_subdirectory(tmssapp) +add_subdirectory(workflowapp) diff --git a/SAS/TMSS/src/tmss/settings.py b/SAS/TMSS/src/tmss/settings.py index 3fcb6ea5e997dfabaa0357e8d62c9da6b4a54cac..97b14e0609ec957a3553493dec1a668033b7a841 100644 --- a/SAS/TMSS/src/tmss/settings.py +++ b/SAS/TMSS/src/tmss/settings.py @@ -120,8 +120,17 @@ INSTALLED_APPS = [ 'material.frontend', 'viewflow', 'viewflow.frontend', + 'lofar.sas.tmss.tmss.workflowapp', + 'debug_toolbar', ] +def show_debug_toolbar(*args, **kwargs): + return os.environ.get('SHOW_DJANGO_DEBUG_TOOLBAR', False) + +DEBUG_TOOLBAR_CONFIG = { + 'SHOW_TOOLBAR_CALLBACK': show_debug_toolbar +} + MIDDLEWARE = [ 'django.middleware.gzip.GZipMiddleware', 'django.middleware.security.SecurityMiddleware', @@ -133,6 +142,7 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware' ] + ROOT_URLCONF = 'lofar.sas.tmss.tmss.urls' TEMPLATES = [ diff --git a/SAS/TMSS/src/tmss/tmssapp/CMakeLists.txt b/SAS/TMSS/src/tmss/tmssapp/CMakeLists.txt index e24af6998d0ad9240a454cd41fdb389a38cb4208..58c545f7ed434d8c05064e1fad48ebf0c93d821a 100644 --- a/SAS/TMSS/src/tmss/tmssapp/CMakeLists.txt +++ b/SAS/TMSS/src/tmss/tmssapp/CMakeLists.txt @@ -23,5 +23,3 @@ add_subdirectory(serializers) add_subdirectory(viewsets) add_subdirectory(adapters) add_subdirectory(schemas) -add_subdirectory(workflows) - diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py index daa63f9369488f5e160485fbfec01af9cdb5121b..45b53b6b1f17acc72ba81dedbfe5036d1b420889 100644 --- a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py +++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.9 on 2020-09-24 15:47 +# Generated by Django 3.0.9 on 2020-09-30 09:15 from django.conf import settings import django.contrib.postgres.fields @@ -14,7 +14,6 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('viewflow', '0008_jsonfield_and_artifact'), ] operations = [ @@ -492,26 +491,6 @@ class Migration(migrations.Migration): 'abstract': False, }, ), - migrations.CreateModel( - name='SchedulingUnitDemo', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=50)), - ('state', models.IntegerField()), - ], - ), - migrations.CreateModel( - name='SchedulingUnitDemoProcess', - fields=[ - ('process_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='viewflow.Process')), - ('text', models.CharField(max_length=150)), - ('approved', models.BooleanField(default=False)), - ], - options={ - 'abstract': False, - }, - bases=('viewflow.process',), - ), migrations.CreateModel( name='SchedulingUnitDraft', fields=[ @@ -764,19 +743,6 @@ class Migration(migrations.Migration): 'abstract': False, }, ), - migrations.CreateModel( - name='HelloWorldProcess', - fields=[ - ], - options={ - 'verbose_name': 'World Request', - 'verbose_name_plural': 'World Requests', - 'proxy': True, - 'indexes': [], - 'constraints': [], - }, - bases=('viewflow.process',), - ), migrations.CreateModel( name='Setting', fields=[ @@ -1093,11 +1059,6 @@ class Migration(migrations.Migration): name='scheduling_set', field=models.ForeignKey(help_text='Set to which this scheduling unit draft belongs.', on_delete=django.db.models.deletion.CASCADE, related_name='scheduling_unit_drafts', to='tmssapp.SchedulingSet'), ), - migrations.AddField( - model_name='schedulingunitdemoprocess', - name='su', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='tmssapp.SchedulingUnitDemo'), - ), migrations.AddField( model_name='schedulingunitblueprint', name='draft', diff --git a/SAS/TMSS/src/tmss/tmssapp/models/CMakeLists.txt b/SAS/TMSS/src/tmss/tmssapp/models/CMakeLists.txt index 2ac64b115ecf2f4bc700c614a3ba9572f3af6aa6..7598bc12c79161c19b95275e001a28adb92d3b56 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/CMakeLists.txt +++ b/SAS/TMSS/src/tmss/tmssapp/models/CMakeLists.txt @@ -5,8 +5,6 @@ set(_py_files __init__.py specification.py scheduling.py - helloworldflow.py - schedulingunitdemoflow.py ) python_install(${_py_files} diff --git a/SAS/TMSS/src/tmss/tmssapp/models/__init__.py b/SAS/TMSS/src/tmss/tmssapp/models/__init__.py index be7a174d740d60b255c47117cb8abfc657cc9bde..93f3c7e6d54f95c40d6d9484aad802b13f9991ba 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/__init__.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/__init__.py @@ -1,4 +1,2 @@ from .specification import * -from .scheduling import * -from .helloworldflow import * -from .schedulingunitdemoflow import * \ No newline at end of file +from .scheduling import * \ No newline at end of file diff --git a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py index a3ebd865de710e9df320248e1614f9ba4f5344da..0ca0fd3810e9afb9ceeb5ad0249f0673e9557f40 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py @@ -149,7 +149,7 @@ class Subtask(BasicCommon): super().__init__(*args, **kwargs) # keep original state for logging - self.__original_state = self.state + self.__original_state_id = self.state_id @staticmethod def _send_state_change_event_message(subtask_id:int, old_state: str, new_state: str): @@ -189,7 +189,7 @@ class Subtask(BasicCommon): annotate_validate_add_defaults_to_doc_using_template(self, 'specifications_doc', 'specifications_template') - if self.state.value == SubtaskState.Choices.SCHEDULED.value and self.__original_state.value == SubtaskState.Choices.SCHEDULING.value: + if self.state.value == SubtaskState.Choices.SCHEDULED.value and self.__original_state_id == SubtaskState.Choices.SCHEDULING.value: if self.start_time is None: if self.predecessors.all().count() == 0: raise SubtaskSchedulingException("Cannot schedule subtask id=%s when start time is 'None'." % (self.pk, )) @@ -202,12 +202,12 @@ class Subtask(BasicCommon): super().save(force_insert, force_update, using, update_fields) # log if either state update or new entry: - if self.state != self.__original_state or creating == True: + if self.state_id != self.__original_state_id or creating == True: if self.created_or_updated_by_user is None: identifier = None else: identifier = self.created_or_updated_by_user.email - log_entry = SubtaskStateLog(subtask=self, old_state=self.__original_state, new_state=self.state, + log_entry = SubtaskStateLog(subtask=self, old_state=SubtaskState.objects.get(value=self.__original_state_id), new_state=self.state, user=self.created_or_updated_by_user, user_identifier=identifier) log_entry.save() @@ -217,7 +217,7 @@ class Subtask(BasicCommon): logger.error("Could not send state change to messagebus: %s", e) # update the previous state value - self.__original_state = self.state + self.__original_state_id = self.state_id class SubtaskStateLog(BasicCommon): """ diff --git a/SAS/TMSS/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/src/tmss/tmssapp/models/specification.py index f6665e3da24db201901f135fc5708efe6e8f0caa..3caf5c6a31395acee91640096b4c8438ca496eaf 100644 --- a/SAS/TMSS/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/models/specification.py @@ -19,7 +19,7 @@ import json import jsonschema from django.urls import reverse as revese_url from collections import Counter - +from django.utils.functional import cached_property # # Common @@ -41,7 +41,7 @@ class BasicCommon(Model): class NamedCommon(BasicCommon): name = CharField(max_length=128, help_text='Human-readable name of this object.', null=False) # todo: check if we want to have this primary_key=True - description = CharField(max_length=255, help_text='A longer description of this object.') + description = CharField(max_length=255, help_text='A longer description of this object.', blank=True) def __str__(self): return self.name @@ -412,7 +412,7 @@ class Cycle(NamedCommonPK): start = DateTimeField(help_text='Moment at which the cycle starts, that is, when its projects can run.') stop = DateTimeField(help_text='Moment at which the cycle officially ends.') - @property + @cached_property def duration(self) -> datetime.timedelta: '''the duration of the cycle (stop-start date)''' return self.stop - self.start @@ -553,19 +553,19 @@ class SchedulingUnitDraft(NamedCommon): validate_json_against_schema(self.observation_strategy_template.template, self.requirements_template.schema) if self.scheduling_constraints_doc is not None and self.scheduling_constraints_template_id and self.scheduling_constraints_template.schema is not None: - validate_json_against_schema(self.scheduling_constraints_doc, self.scheduling_constraints_template.schema) + validate_json_against_schema(self.scheduling_constraints_doc, self.scheduling_constraints_template.schema) annotate_validate_add_defaults_to_doc_using_template(self, 'requirements_doc', 'requirements_template') annotate_validate_add_defaults_to_doc_using_template(self, 'scheduling_constraints_doc', 'scheduling_constraints_template') super().save(force_insert, force_update, using, update_fields) - @property + @cached_property def duration(self) -> datetime.timedelta: '''return the overall duration of all tasks of this scheduling unit ''' return self.relative_stop_time - self.relative_start_time - @property + @cached_property def relative_start_time(self) -> datetime.timedelta: '''return the earliest relative start time of all tasks of this scheduling unit ''' @@ -575,7 +575,7 @@ class SchedulingUnitDraft(NamedCommon): else: return datetime.timedelta(seconds=0) - @property + @cached_property def relative_stop_time(self) -> datetime.timedelta: '''return the latest relative stop time of all tasks of this scheduling unit ''' @@ -597,7 +597,7 @@ class SchedulingUnitBlueprint(NamedCommon): super().save(force_insert, force_update, using, update_fields) - @property + @cached_property def duration(self) -> datetime.timedelta: '''return the overall duration of all tasks of this scheduling unit ''' @@ -606,7 +606,7 @@ class SchedulingUnitBlueprint(NamedCommon): else: return self.stop_time - self.start_time # <- todo: do we ever want this? - @property + @cached_property def relative_start_time(self) -> datetime.timedelta: '''return the earliest relative start time of all tasks of this scheduling unit ''' @@ -616,7 +616,7 @@ class SchedulingUnitBlueprint(NamedCommon): else: return datetime.timedelta(seconds=0) - @property + @cached_property def relative_stop_time(self) -> datetime.timedelta: '''return the latest relative stop time of all tasks of this scheduling unit ''' @@ -626,7 +626,7 @@ class SchedulingUnitBlueprint(NamedCommon): else: return datetime.timedelta(seconds=0) - @property + @cached_property def start_time(self) -> datetime or None: '''return the earliest start time of all tasks of this scheduling unit ''' @@ -636,7 +636,7 @@ class SchedulingUnitBlueprint(NamedCommon): else: return None - @property + @cached_property def stop_time(self) -> datetime or None: '''return the latest stop time of all tasks of this scheduling unit ''' @@ -758,7 +758,7 @@ class TaskDraft(NamedCommon): annotate_validate_add_defaults_to_doc_using_template(self, 'specifications_doc', 'specifications_template') super().save(force_insert, force_update, using, update_fields) - @property + @cached_property def successors(self) -> QuerySet: '''return the connect successor taskdraft(s) as queryset (over which you can perform extended queries, or return via the serializers/viewsets) If you want the result, add .all() like so: my_taskdraft.successors.all() @@ -768,7 +768,7 @@ class TaskDraft(NamedCommon): "INNER JOIN tmssapp_taskrelationdraft as task_rel on task_rel.consumer_id = successor_task.id\n" "WHERE task_rel.producer_id = %s", params=[self.id])) - @property + @cached_property def predecessors(self) -> QuerySet: '''return the connect predecessor taskdraft(s) as queryset (over which you can perform extended queries, or return via the serializers/viewsets) If you want the result, add .all() like so: my_taskdraft.predecessors.all() @@ -778,26 +778,26 @@ class TaskDraft(NamedCommon): "INNER JOIN tmssapp_taskrelationdraft as task_rel on task_rel.producer_id = successor_task.id\n" "WHERE task_rel.consumer_id = %s", params=[self.id])) - @property + @cached_property def duration(self) -> datetime.timedelta: '''returns the overall duration of this task ''' return self.relative_stop_time - self.relative_start_time - @property + @cached_property def relative_start_time(self) -> datetime.timedelta: '''return the earliest relative start time of all subtasks of this task ''' scheduling_relations = list(self.first_to_connect.all()) + list(self.second_to_connect.all()) for scheduling_relation in scheduling_relations: - if scheduling_relation.first.id == self.id and scheduling_relation.placement.value == "after": + if scheduling_relation.first.id == self._id and scheduling_relation.placement_id == "after": previous_related_task_draft = TaskDraft.objects.get(id=scheduling_relation.second.id) time_offset = scheduling_relation.time_offset # todo: max of several relations if previous_related_task_draft.relative_stop_time: return previous_related_task_draft.relative_stop_time + datetime.timedelta(seconds=time_offset) - if scheduling_relation.second.id == self.id and scheduling_relation.placement.value == "before": + if scheduling_relation.second.id == self._id and scheduling_relation.placement_id == "before": previous_related_task_draft = TaskDraft.objects.get(id=scheduling_relation.first.id) time_offset = scheduling_relation.time_offset # todo: max of several relations @@ -805,7 +805,7 @@ class TaskDraft(NamedCommon): return previous_related_task_draft.relative_stop_time + datetime.timedelta(seconds=time_offset) return datetime.timedelta(seconds=0) - @property + @cached_property def relative_stop_time(self) -> datetime.timedelta: '''return the latest relative stop time of all subtasks of this task ''' @@ -825,7 +825,7 @@ class TaskDraft(NamedCommon): # Only on the blueprints, we also aggregate start_stop times as they are in the system # I'll leave these code bits here for now, until we made up our minds about this, but this can probably be removed # - # @property + # @cached_property # def duration(self) -> datetime.timedelta: # '''returns the overall duration in seconds of all blueprints of this task # # todo: is this the wanted behavior? Do you want to consider all the blueprints created from your draft or do you want to preview a new blueprint? @@ -836,7 +836,7 @@ class TaskDraft(NamedCommon): # else: # return self.stop_time - self.start_time # - # @property + # @cached_property # def start_time(self) -> datetime or None: # '''return the earliest start time of all blueprints of this task # # todo: is this the wanted behavior? Do you want to consider all the blueprints created from your draft or do you want to preview a new blueprint? @@ -848,7 +848,7 @@ class TaskDraft(NamedCommon): # # todo: calculate? # return None # - # @property + # @cached_property # def stop_time(self) -> datetime or None: # '''return the latest stop time of all blueprints of this task # # todo: is this the wanted behavior? Do you want to consider all the blueprints created from your draft or do you want to preview a new blueprint? @@ -872,7 +872,7 @@ class TaskBlueprint(NamedCommon): annotate_validate_add_defaults_to_doc_using_template(self, 'specifications_doc', 'specifications_template') super().save(force_insert, force_update, using, update_fields) - @property + @cached_property def successors(self) -> QuerySet: '''return the connect successor taskblueprint(s) as queryset (over which you can perform extended queries, or return via the serializers/viewsets) If you want the result, add .all() like so: my_taskblueprint.successors.all() @@ -882,7 +882,7 @@ class TaskBlueprint(NamedCommon): "INNER JOIN tmssapp_taskrelationblueprint as task_rel on task_rel.consumer_id = successor_task.id\n" "WHERE task_rel.producer_id = %s", params=[self.id])) - @property + @cached_property def predecessors(self) -> QuerySet: '''return the connect predecessor taskblueprint(s) as queryset (over which you can perform extended queries, or return via the serializers/viewsets) If you want the result, add .all() like so: my_taskblueprint.predecessors.all() @@ -892,7 +892,7 @@ class TaskBlueprint(NamedCommon): "INNER JOIN tmssapp_taskrelationblueprint as task_rel on task_rel.producer_id = predecessor_task.id\n" "WHERE task_rel.consumer_id = %s", params=[self.id])) - @property + @cached_property def duration(self) -> datetime.timedelta: '''return the overall duration of this task ''' @@ -901,20 +901,20 @@ class TaskBlueprint(NamedCommon): else: return self.stop_time - self.start_time - @property + @cached_property def relative_start_time(self) -> datetime.timedelta: '''return the earliest relative start time of all subtasks of this task ''' scheduling_relations = list(self.first_to_connect.all()) + list(self.second_to_connect.all()) for scheduling_relation in scheduling_relations: - if scheduling_relation.first.id == self.id and scheduling_relation.placement.value == "after": + if scheduling_relation.first.id == self._id and scheduling_relation.placement_id == "after": # self.id and placement.value will hit the db, this does not previous_related_task_blueprint = TaskBlueprint.objects.get(id=scheduling_relation.second.id) time_offset = scheduling_relation.time_offset # todo: max of several relations if previous_related_task_blueprint.relative_stop_time: return previous_related_task_blueprint.relative_stop_time + datetime.timedelta(seconds=time_offset) - if scheduling_relation.second.id == self.id and scheduling_relation.placement.value == "before": + if scheduling_relation.second.id == self._id and scheduling_relation.placement_id == "before": # self.id and placement.value will hit the db, this does not previous_related_task_blueprint = TaskBlueprint.objects.get(id=scheduling_relation.first.id) time_offset = scheduling_relation.time_offset # todo: max of several relations @@ -922,7 +922,7 @@ class TaskBlueprint(NamedCommon): return previous_related_task_blueprint.relative_stop_time + datetime.timedelta(seconds=time_offset) return datetime.timedelta(seconds=666660) - @property + @cached_property def relative_stop_time(self) -> datetime.timedelta: '''return the latest relative stop time of all subtasks of this task ''' @@ -934,7 +934,7 @@ class TaskBlueprint(NamedCommon): pass return self.relative_start_time - @property + @cached_property def start_time(self) -> datetime or None: '''return the earliest start time of all subtasks of this task ''' @@ -944,7 +944,7 @@ class TaskBlueprint(NamedCommon): else: return None - @property + @cached_property def stop_time(self) -> datetime or None: '''return the latest stop time of all subtasks of this task ''' diff --git a/SAS/TMSS/src/tmss/tmssapp/populate.py b/SAS/TMSS/src/tmss/tmssapp/populate.py index d9f964aae6c3d0b9ac70e02c653edcce27eb4c2a..b786248f34773046434364d3ddc887ecd6d59e3a 100644 --- a/SAS/TMSS/src/tmss/tmssapp/populate.py +++ b/SAS/TMSS/src/tmss/tmssapp/populate.py @@ -55,6 +55,12 @@ def populate_test_data(): from lofar.sas.tmss.test.tmss_test_data_django_models import SchedulingSet_test_data, SchedulingUnitDraft_test_data from lofar.sas.tmss.tmss.tmssapp.tasks import create_task_blueprints_and_subtasks_from_scheduling_unit_draft, create_task_blueprints_and_subtasks_and_schedule_subtasks_from_scheduling_unit_draft from lofar.sas.tmss.tmss.tmssapp.subtasks import schedule_subtask + from lofar.common.json_utils import get_default_json_object_for_schema + + constraints_template = models.SchedulingConstraintsTemplate.objects.get(name="constraints") + constraints_spec = get_default_json_object_for_schema(constraints_template.schema) + + strategy_template = models.SchedulingUnitObservingStrategyTemplate.objects.get(name="UC1 CTC+pipelines") # create a Test Scheduling Set UC1 under project TMSS-Commissioning tmss_project = models.Project.objects.get(name="TMSS-Commissioning") @@ -67,8 +73,6 @@ def populate_test_data(): logger.info('created test scheduling_set: %s', scheduling_set.name) for unit_nr in range(5): - strategy_template = models.SchedulingUnitObservingStrategyTemplate.objects.get(name="UC1 CTC+pipelines") - # the 'template' in the strategy_template is a predefined json-data blob which validates against the given scheduling_unit_template # a user might 'upload' a partial json-data blob, so add all the known defaults @@ -79,7 +83,9 @@ def populate_test_data(): scheduling_set=scheduling_set, requirements_template=strategy_template.scheduling_unit_template, requirements_doc=scheduling_unit_spec, - observation_strategy_template=strategy_template) + observation_strategy_template=strategy_template, + scheduling_constraints_doc=constraints_spec, + scheduling_constraints_template=constraints_template) scheduling_unit_draft.tags = ["TEST", "UC1"] scheduling_unit_draft.save() 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 05b2946b839c5e1e0929d1f3622d849de8e3cb10..183f8933332dd016194b939315b4bc25a9c3d183 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 @@ -65,18 +65,18 @@ "stations":["CS001"], "tile_beam": { "direction_type": "J2000", - "angle1": 42, - "angle2": 42, - "angle3": 42 + "angle1": 0.42, + "angle2": 0.43, + "angle3": 0.44 }, "SAPs": [ { "name": "target0", "digital_pointing": { "direction_type": "J2000", - "angle1": 24, - "angle2": 24, - "angle3": 24 + "angle1": 0.24, + "angle2": 0.25, + "angle3": 0.26 }, "subbands": [ 349, @@ -87,9 +87,9 @@ "name": "target1", "digital_pointing": { "direction_type": "J2000", - "angle1": 24, - "angle2": 24, - "angle3": 24 + "angle1": 0.27, + "angle2": 0.28, + "angle3": 0.29 }, "subbands": [ 349, diff --git a/SAS/TMSS/src/tmss/tmssapp/schemas/scheduling_constraints_template-constraints-1.json b/SAS/TMSS/src/tmss/tmssapp/schemas/scheduling_constraints_template-constraints-1.json index 04bb208f0b4deff2d4a7d0491ef4108afe335922..77a916705c8df50c069f5929e11fc03d5586acf7 100644 --- a/SAS/TMSS/src/tmss/tmssapp/schemas/scheduling_constraints_template-constraints-1.json +++ b/SAS/TMSS/src/tmss/tmssapp/schemas/scheduling_constraints_template-constraints-1.json @@ -1,13 +1,32 @@ { + "$id":"http://tmss.lofar.org/api/schemas/schedulingconstraintstemplate/constraints/1#", "$schema": "http://json-schema.org/draft-06/schema#", - "title": "Constraints", - "description": "This schema defines the constraints for a scheduling unit", + "title": "constraints", + "description": "This schema defines the scheduling constraints for a scheduling unit", "version": 1, "definitions": { "timestamp": { + "description": "A timestamp defined in UTC", "type": "string", - "pattern": "\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+Z", - "format": "datetime" + "pattern": "\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d(\\.\\d+)?Z", + "format": "date-time" + }, + "timewindow": { + "type": "object", + "description": "A timewindow interval: [from, to)", + "properties": { + "from": { + "$ref": "#/definitions/timestamp" + }, + "to": { + "$ref": "#/definitions/timestamp" + } + }, + "additionalProperties": false, + "required": [ + "from", + "to" + ] }, "distance_on_sky": { "type": "number", @@ -24,7 +43,7 @@ "properties": { "scheduler": { "name": "Scheduler", - "description": "Which scheduling system wiil schedule this", + "description": "Which scheduling system will schedule this", "type": "string", "enum": [ "manual", @@ -34,6 +53,7 @@ }, "time": { "type": "object", + "default": {}, "properties": { "at": { "description": "Start at this moment", @@ -51,41 +71,28 @@ "description": "Run within one of these time windows", "type": "array", "items": { - "from": { - "$ref": "#/definitions/timestamp" - }, - "to": { - "$ref": "#/definitions/timestamp" - }, - "required": [ - "from", - "to" - ] + "$ref": "#/definitions/timewindow" }, - "additionalItems": false + "minItems":0, + "uniqueItems":true, + "default": [] }, "not_between": { - "description": "NOT run within one of these time windows", + "description": "Do NOT run within any of these time windows", "type": "array", "items": { - "from": { - "$ref": "#/definitions/timestamp" - }, - "to": { - "$ref": "#/definitions/timestamp" - }, - "required": [ - "from", - "to" - ] + "$ref": "#/definitions/timewindow" }, - "additionalItems": false + "minItems":0, + "uniqueItems":true, + "default": [] } }, "additionalProperties": false }, "daily": { "type": "object", + "default": {}, "properties": { "require_night": { "description": "Must run at night", @@ -107,6 +114,7 @@ }, "sky": { "type": "object", + "default": {}, "properties": { "min_calibrator_elevation": { "description": "Minimum elevation for all calibrator sources", @@ -123,14 +131,14 @@ "type": "object", "properties": { "from": { - "type": "integer", - "minimum": -43200, - "maximum": 43200 + "type": "number", + "minimum": -0.20943951, + "maximum": 0.20943951 }, "to": { - "type": "integer", - "minimum": -43200, - "maximum": 43200 + "type": "number", + "minimum": -0.20943951, + "maximum": 0.20943951 } }, "additionalProperties": false @@ -157,7 +165,6 @@ "additionalProperties": false } }, - "additionalProperties": false, "required": [ "scheduler" ] diff --git a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py index dc0f28d734c988f0343ccd657564d789ca62e9ee..bf250c5a51a2781970924e9ec30eb415d147b9fe 100644 --- a/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/serializers/specification.py @@ -288,6 +288,7 @@ class SchedulingSetSerializer(RelationalHyperlinkedModelSerializer): class SchedulingUnitDraftSerializer(RelationalHyperlinkedModelSerializer): requirements_doc = JSONEditorField(schema_source="requirements_template.schema") + scheduling_constraints_doc = JSONEditorField(schema_source="scheduling_constraints_template.schema") duration = FloatDurationField(read_only=True) class Meta: diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/CMakeLists.txt b/SAS/TMSS/src/tmss/tmssapp/viewsets/CMakeLists.txt index 445e0bbe4672e5cdad3a5a41be8575dbf2169ff0..fc0325a523508e371b2456d96b3467274dae748d 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/CMakeLists.txt +++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/CMakeLists.txt @@ -6,8 +6,6 @@ set(_py_files lofar_viewset.py specification.py scheduling.py - helloworldflow.py - schedulingunitdemoflow.py ) python_install(${_py_files} diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/__init__.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/__init__.py index 882458975ee4be50507620471ed1026433ddf589..93f3c7e6d54f95c40d6d9484aad802b13f9991ba 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/__init__.py +++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/__init__.py @@ -1,3 +1,2 @@ from .specification import * -from .scheduling import * -from .schedulingunitdemoflow import * \ No newline at end of file +from .scheduling import * \ No newline at end of file diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/scheduling.py index af49948d1615bac654f06ea03f55f8b09f679d6a..731dea891e03bf199867741111d93e58a53910ed 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/scheduling.py +++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/scheduling.py @@ -144,6 +144,8 @@ class SubtaskViewSet(LOFARViewSet): filter_class = SubTaskFilter ordering = ('start_time',) + queryset = queryset.prefetch_related('state') + @swagger_auto_schema(auto_schema=TextPlainAutoSchema, responses={200: 'A LOFAR parset for this subtask (as plain text)', 403: 'forbidden', diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py index ce3fa163142398bbaf6ae6bf7e197b33b6311cd2..242f06d05a6084ad6f0aa64a46eec8ce75ac164d 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py +++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py @@ -307,6 +307,16 @@ class SchedulingUnitDraftViewSet(LOFARViewSet): queryset = models.SchedulingUnitDraft.objects.all() serializer_class = serializers.SchedulingUnitDraftSerializer + # prefetch all reverse related references from other models on their related_name to avoid a ton of duplicate queries + queryset = queryset.prefetch_related('copied_from') \ + .prefetch_related('scheduling_unit_blueprints')\ + .prefetch_related('task_drafts') + + # preselect all references to other models to avoid even more duplicate queries + queryset = queryset.select_related('copies') \ + .select_related('copy_reason') \ + .select_related('scheduling_set') + @swagger_auto_schema(responses={201: 'The Created SchedulingUnitBlueprint, see Location in Response header', 403: 'forbidden'}, operation_description="Carve SchedulingUnitDraft in stone, and make an (uneditable) blueprint out of it.") @@ -580,6 +590,9 @@ class SchedulingUnitBlueprintViewSet(LOFARViewSet): queryset = models.SchedulingUnitBlueprint.objects.all() serializer_class = serializers.SchedulingUnitBlueprintSerializer + # prefetch all reverse related references from other models on their related_name to avoid a ton of duplicate queries + queryset = queryset.prefetch_related('task_blueprints') + @swagger_auto_schema(responses={201: "This SchedulingUnitBlueprint, with references to its created TaskBlueprints and (scheduled) Subtasks.", 403: 'forbidden'}, operation_description="Create TaskBlueprint(s) for this scheduling unit, create subtasks, and schedule the ones that are not dependend on predecessors.") @@ -633,6 +646,22 @@ class TaskDraftViewSet(LOFARViewSet): queryset = models.TaskDraft.objects.all() serializer_class = serializers.TaskDraftSerializer + # prefetch all reverse related references from other models on their related_name to avoid a ton of duplicate queries + queryset = queryset.prefetch_related('first_to_connect') \ + .prefetch_related('second_to_connect')\ + .prefetch_related('produced_by')\ + .prefetch_related('consumed_by')\ + .prefetch_related('task_blueprints')\ + .prefetch_related('copied_from') + + # prefetch nested references in reverse models to avoid duplicate lookup queries + queryset = queryset.prefetch_related('first_to_connect__placement') \ + .prefetch_related('second_to_connect__placement') + + # select all references to other models to avoid even more duplicate queries + queryset = queryset.select_related('copies') \ + .select_related('copy_reason') + @swagger_auto_schema(responses={201: 'The created task blueprint, see Location in Response header', 403: 'forbidden'}, operation_description="Carve this draft task specification in stone, and make an (uneditable) blueprint out of it.") @@ -726,6 +755,17 @@ class TaskBlueprintViewSet(LOFARViewSet): queryset = models.TaskBlueprint.objects.all() serializer_class = serializers.TaskBlueprintSerializer + # prefetch all reverse related references from other models on their related_name to avoid a ton of duplicate queries + queryset = queryset.prefetch_related('first_to_connect')\ + .prefetch_related('second_to_connect')\ + .prefetch_related('produced_by')\ + .prefetch_related('consumed_by')\ + .prefetch_related('subtasks') + + # prefetch nested references in reverse models to avoid duplicate lookup queries + queryset = queryset.prefetch_related('first_to_connect__placement') \ + .prefetch_related('second_to_connect__placement') + @swagger_auto_schema(responses={201: "This TaskBlueprint, with it is created subtasks", 403: 'forbidden'}, operation_description="Create subtasks.") diff --git a/SAS/TMSS/src/tmss/urls.py b/SAS/TMSS/src/tmss/urls.py index 37d9d081a3a3ecbee61e876ea3ee365b7c111ace..0776702860d8c041eb193e83f2ba72b78d1d8031 100644 --- a/SAS/TMSS/src/tmss/urls.py +++ b/SAS/TMSS/src/tmss/urls.py @@ -23,7 +23,7 @@ from django.views.generic.base import TemplateView, RedirectView from collections import OrderedDict from rest_framework import routers, permissions -from .tmssapp import viewsets, models, serializers, views, workflows +from .tmssapp import viewsets, models, serializers, views from rest_framework.documentation import include_docs_urls from drf_yasg.views import get_schema_view from drf_yasg import openapi @@ -33,6 +33,8 @@ from datetime import datetime from material.frontend import urls as frontend_urls from viewflow.flow.viewset import FlowViewSet +import debug_toolbar + # # Django style patterns # @@ -63,7 +65,8 @@ urlpatterns = [ 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(r'util/utc', views.utc, name="system-utc"), - path(r'util/lst', views.lst, name="conversion-lst") + path(r'util/lst', views.lst, name="conversion-lst"), + path('__debug__/', include(debug_toolbar.urls)), ] diff --git a/SAS/TMSS/src/tmss/workflowapp/CMakeLists.txt b/SAS/TMSS/src/tmss/workflowapp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..94b72e83e35a77ab9b16f84b7647f8ab0c8af94a --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/CMakeLists.txt @@ -0,0 +1,17 @@ + +include(PythonInstall) + +set(_py_files + __init__.py + admin.py + apps.py + tests.py + ) + +python_install(${_py_files} + DESTINATION lofar/sas/tmss/tmss/workflowapp) + +add_subdirectory(migrations) +add_subdirectory(models) +add_subdirectory(flows) +add_subdirectory(viewsets) diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/helloworldflow.py b/SAS/TMSS/src/tmss/workflowapp/__init__.py similarity index 100% rename from SAS/TMSS/src/tmss/tmssapp/viewsets/helloworldflow.py rename to SAS/TMSS/src/tmss/workflowapp/__init__.py diff --git a/SAS/TMSS/src/tmss/workflowapp/admin.py b/SAS/TMSS/src/tmss/workflowapp/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/SAS/TMSS/src/tmss/workflowapp/apps.py b/SAS/TMSS/src/tmss/workflowapp/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..d70dc7921a32145aa2a76285c3362041e091a358 --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class WorkflowappConfig(AppConfig): + name = 'workflowapp' diff --git a/SAS/TMSS/src/tmss/tmssapp/workflows/CMakeLists.txt b/SAS/TMSS/src/tmss/workflowapp/flows/CMakeLists.txt similarity index 72% rename from SAS/TMSS/src/tmss/tmssapp/workflows/CMakeLists.txt rename to SAS/TMSS/src/tmss/workflowapp/flows/CMakeLists.txt index 474aada33041160e598ac2b1a126d68971d75afd..769f922e4781a912f1c0488c3655f6ab61363d3a 100644 --- a/SAS/TMSS/src/tmss/tmssapp/workflows/CMakeLists.txt +++ b/SAS/TMSS/src/tmss/workflowapp/flows/CMakeLists.txt @@ -8,4 +8,4 @@ set(_py_files ) python_install(${_py_files} - DESTINATION lofar/sas/tmss/tmss/tmssapp/workflows) + DESTINATION lofar/sas/tmss/tmss/workflowapp/flows) diff --git a/SAS/TMSS/src/tmss/tmssapp/workflows/__init__.py b/SAS/TMSS/src/tmss/workflowapp/flows/__init__.py similarity index 100% rename from SAS/TMSS/src/tmss/tmssapp/workflows/__init__.py rename to SAS/TMSS/src/tmss/workflowapp/flows/__init__.py diff --git a/SAS/TMSS/src/tmss/tmssapp/workflows/helloworldflow.py b/SAS/TMSS/src/tmss/workflowapp/flows/helloworldflow.py similarity index 97% rename from SAS/TMSS/src/tmss/tmssapp/workflows/helloworldflow.py rename to SAS/TMSS/src/tmss/workflowapp/flows/helloworldflow.py index d3307efe5f773359de58c89bea4a8728fa809c05..cd7ee660823074d4a00e5dca9e87e240098442c9 100644 --- a/SAS/TMSS/src/tmss/tmssapp/workflows/helloworldflow.py +++ b/SAS/TMSS/src/tmss/workflowapp/flows/helloworldflow.py @@ -5,9 +5,7 @@ from viewflow.base import this, Flow from viewflow.compat import _ from viewflow.flow import views as flow_views - -from lofar.sas.tmss.tmss.tmssapp import models - +from .. import models @frontend.register diff --git a/SAS/TMSS/src/tmss/tmssapp/workflows/schedulingunitdemoflow.py b/SAS/TMSS/src/tmss/workflowapp/flows/schedulingunitdemoflow.py similarity index 99% rename from SAS/TMSS/src/tmss/tmssapp/workflows/schedulingunitdemoflow.py rename to SAS/TMSS/src/tmss/workflowapp/flows/schedulingunitdemoflow.py index a35c72db8e9430b929e9ada4f424bbf6a58527c9..0a2882d7a4550ef3ff8e60b190c4074f60356795 100644 --- a/SAS/TMSS/src/tmss/tmssapp/workflows/schedulingunitdemoflow.py +++ b/SAS/TMSS/src/tmss/workflowapp/flows/schedulingunitdemoflow.py @@ -8,7 +8,7 @@ from viewflow.activation import FuncActivation, ViewActivation from viewflow.flow.nodes import Signal from viewflow import mixins -from lofar.sas.tmss.tmss.tmssapp import models +from .. import models from viewflow import frontend, ThisObject from viewflow.activation import STATUS diff --git a/SAS/TMSS/src/tmss/workflowapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/workflowapp/migrations/0001_initial.py new file mode 100644 index 0000000000000000000000000000000000000000..2e95b97379265e5eb14cfd44e85357218eb63948 --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/migrations/0001_initial.py @@ -0,0 +1,50 @@ +# Generated by Django 3.0.9 on 2020-10-01 12:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('viewflow', '0008_jsonfield_and_artifact'), + ] + + operations = [ + migrations.CreateModel( + name='SchedulingUnitDemo', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ('state', models.IntegerField()), + ], + ), + migrations.CreateModel( + name='HelloWorldProcess', + fields=[ + ], + options={ + 'verbose_name': 'World Request', + 'verbose_name_plural': 'World Requests', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('viewflow.process',), + ), + migrations.CreateModel( + name='SchedulingUnitDemoProcess', + fields=[ + ('process_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='viewflow.Process')), + ('text', models.CharField(max_length=150)), + ('approved', models.BooleanField(default=False)), + ('su', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='workflowapp.SchedulingUnitDemo')), + ], + options={ + 'abstract': False, + }, + bases=('viewflow.process',), + ), + ] diff --git a/SAS/TMSS/src/tmss/workflowapp/migrations/CMakeLists.txt b/SAS/TMSS/src/tmss/workflowapp/migrations/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..158ea7946445fcee8e52b00447df80a873e98ec2 --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/migrations/CMakeLists.txt @@ -0,0 +1,8 @@ + +include(PythonInstall) + + +FILE(GLOB _py_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.py) + +python_install(${_py_files} + DESTINATION lofar/sas/tmss/tmss/workflowapp/migrations) \ No newline at end of file diff --git a/SAS/TMSS/src/tmss/workflowapp/migrations/__init__.py b/SAS/TMSS/src/tmss/workflowapp/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/SAS/TMSS/src/tmss/workflowapp/models/CMakeLists.txt b/SAS/TMSS/src/tmss/workflowapp/models/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1c94f0a15d5ade684111945ce5bb79dfe25f7a91 --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/models/CMakeLists.txt @@ -0,0 +1,11 @@ + +include(PythonInstall) + +set(_py_files + __init__.py + helloworldflow.py + schedulingunitdemoflow.py + ) + +python_install(${_py_files} + DESTINATION lofar/sas/tmss/tmss/workflowapp/models) diff --git a/SAS/TMSS/src/tmss/workflowapp/models/__init__.py b/SAS/TMSS/src/tmss/workflowapp/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..45516795a25730483ebfa40c1fbdb5f533df8ebe --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/models/__init__.py @@ -0,0 +1,2 @@ +from .helloworldflow import * +from .schedulingunitdemoflow import * \ No newline at end of file diff --git a/SAS/TMSS/src/tmss/tmssapp/models/helloworldflow.py b/SAS/TMSS/src/tmss/workflowapp/models/helloworldflow.py similarity index 100% rename from SAS/TMSS/src/tmss/tmssapp/models/helloworldflow.py rename to SAS/TMSS/src/tmss/workflowapp/models/helloworldflow.py diff --git a/SAS/TMSS/src/tmss/tmssapp/models/schedulingunitdemoflow.py b/SAS/TMSS/src/tmss/workflowapp/models/schedulingunitdemoflow.py similarity index 100% rename from SAS/TMSS/src/tmss/tmssapp/models/schedulingunitdemoflow.py rename to SAS/TMSS/src/tmss/workflowapp/models/schedulingunitdemoflow.py diff --git a/SAS/TMSS/src/tmss/workflowapp/tests.py b/SAS/TMSS/src/tmss/workflowapp/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/SAS/TMSS/src/tmss/workflowapp/viewsets/CMakeLists.txt b/SAS/TMSS/src/tmss/workflowapp/viewsets/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7adc12fcf7a85912784409d17f37177986c94298 --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/viewsets/CMakeLists.txt @@ -0,0 +1,10 @@ + +include(PythonInstall) + +set(_py_files + __init__.py + schedulingunitdemoflow.py + ) + +python_install(${_py_files} + DESTINATION lofar/sas/tmss/tmss/workflowapp/viewsets) diff --git a/SAS/TMSS/src/tmss/workflowapp/viewsets/__init__.py b/SAS/TMSS/src/tmss/workflowapp/viewsets/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b77c70aeb959e9d4f63c395fd1079cfbbe3bc078 --- /dev/null +++ b/SAS/TMSS/src/tmss/workflowapp/viewsets/__init__.py @@ -0,0 +1 @@ +from .schedulingunitdemoflow import * \ No newline at end of file diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/schedulingunitdemoflow.py b/SAS/TMSS/src/tmss/workflowapp/viewsets/schedulingunitdemoflow.py similarity index 92% rename from SAS/TMSS/src/tmss/tmssapp/viewsets/schedulingunitdemoflow.py rename to SAS/TMSS/src/tmss/workflowapp/viewsets/schedulingunitdemoflow.py index ea117c0f9c27a4324fe76c77fe1256e1b1eca446..da3dc24e15ff6f3bd93da9037101a718f4ebed66 100644 --- a/SAS/TMSS/src/tmss/tmssapp/viewsets/schedulingunitdemoflow.py +++ b/SAS/TMSS/src/tmss/workflowapp/viewsets/schedulingunitdemoflow.py @@ -3,7 +3,7 @@ from rest_framework import viewsets from rest_framework.response import Response from rest_framework.decorators import action from rest_framework.serializers import ModelSerializer -from lofar.sas.tmss.tmss.tmssapp import models +from lofar.sas.tmss.tmss.workflowapp import models # Create your views here. diff --git a/SAS/TMSS/test/t_subtasks.py b/SAS/TMSS/test/t_subtasks.py index ca94d9eb33be0377cbbd5aa1d3a3cb3623be411f..8d80cb4b2cd6a652d54506ca3fcc1c5a1174e153 100755 --- a/SAS/TMSS/test/t_subtasks.py +++ b/SAS/TMSS/test/t_subtasks.py @@ -257,15 +257,15 @@ class SubTasksCreationFromTaskBluePrintCalibrator(unittest.TestCase): create_observation_control_subtask_from_task_blueprint(cal_task_blueprint) cal_task_blueprint.specifications_doc['autoselect'] = False - cal_task_blueprint.specifications_doc['pointing']['angle1'] = 11.11 - cal_task_blueprint.specifications_doc['pointing']['angle2'] = 22.22 + cal_task_blueprint.specifications_doc['pointing']['angle1'] = 1.111 + cal_task_blueprint.specifications_doc['pointing']['angle2'] = 2.222 subtask = create_observation_control_subtask_from_task_blueprint(cal_task_blueprint) self.assertEqual("defined", str(subtask.state)) self.assertEqual("observation control", str(subtask.specifications_template.name)) self.assertEqual("observation", str(subtask.specifications_template.type)) self.assertEqual('J2000', subtask.specifications_doc['stations']['analog_pointing']['direction_type']) - self.assertEqual(11.11, subtask.specifications_doc['stations']['analog_pointing']['angle1']) - self.assertEqual(22.22, subtask.specifications_doc['stations']['analog_pointing']['angle2']) + self.assertEqual(1.111, subtask.specifications_doc['stations']['analog_pointing']['angle1']) + self.assertEqual(2.222, subtask.specifications_doc['stations']['analog_pointing']['angle2']) class SubtaskInputSelectionFilteringTest(unittest.TestCase): diff --git a/SAS/TMSS/test/t_tmssapp_specification_REST_API.py b/SAS/TMSS/test/t_tmssapp_specification_REST_API.py index 5bb9e175d4324f9ecdaa40176243dde8fa0da040..018c985f4b69f7b626564bda91f076dcc49591b9 100755 --- a/SAS/TMSS/test/t_tmssapp_specification_REST_API.py +++ b/SAS/TMSS/test/t_tmssapp_specification_REST_API.py @@ -1335,11 +1335,10 @@ class SchedulingUnitDraftTestCase(unittest.TestCase): GET_OK_and_assert_equal_expected_response(self, url, test_data) def test_GET_SchedulingUnitDraft_list_view_shows_entry(self): - test_data_1 = SchedulingUnitDraft_test_data("scheduler unit draft one") - models.SchedulingUnitDraft.objects.create(**test_data_1) + obj = models.SchedulingUnitDraft.objects.create(**test_data_1) nbr_results = models.SchedulingUnitDraft.objects.count() - GET_and_assert_in_expected_response_result_list(self, BASE_URL + '/scheduling_unit_draft/', test_data_1, nbr_results) + GET_and_assert_in_expected_response_result_list(self, BASE_URL + '/scheduling_unit_draft/', test_data_1, nbr_results, obj.id) def test_GET_SchedulingUnitDraft_view_returns_correct_entry(self): @@ -1385,8 +1384,8 @@ class SchedulingUnitDraftTestCase(unittest.TestCase): # setup test_data_1 = SchedulingUnitDraft_test_data("scheduler unit draft one") - tdt_test_data_1 = TaskDraft_test_data("task draft one") - tdt_test_data_2 = TaskDraft_test_data("task draft two") + tdt_test_data_1 = TaskDraft_test_data("task draft one of su1") + tdt_test_data_2 = TaskDraft_test_data("task draft two of su2 ") scheduling_unit_draft = models.SchedulingUnitDraft.objects.create(**test_data_1) task_draft_1 = models.TaskDraft.objects.create(**tdt_test_data_1) task_draft_1.scheduling_unit_draft = scheduling_unit_draft @@ -1522,9 +1521,9 @@ class TaskDraftTestCase(unittest.TestCase): def test_GET_TaskDraft_list_view_shows_entry(self): test_data_1 = TaskDraft_test_data("task draft") - models.TaskDraft.objects.create(**test_data_1) + obj = models.TaskDraft.objects.create(**test_data_1) nbr_results = models.TaskDraft.objects.count() - GET_and_assert_in_expected_response_result_list(self, BASE_URL + '/task_draft/', test_data_1, nbr_results) + GET_and_assert_in_expected_response_result_list(self, BASE_URL + '/task_draft/', test_data_1, nbr_results, obj.id) def test_GET_TaskDraft_view_returns_correct_entry(self): @@ -1540,7 +1539,7 @@ class TaskDraftTestCase(unittest.TestCase): def test_nested_TaskDraft_are_filtered_according_to_SchedulingUnitDraft(self): # setup - test_data_1 = TaskDraft_test_data("task draft one") + test_data_1 = TaskDraft_test_data("task draft three") sudt_test_data_1 = SchedulingUnitDraft_test_data("scheduling unit draft one") scheduling_unit_draft_1 = models.SchedulingUnitDraft.objects.create(**sudt_test_data_1) test_data_1 = dict(test_data_1) @@ -1552,7 +1551,7 @@ class TaskDraftTestCase(unittest.TestCase): def test_TaskDraft_contains_list_of_related_TaskBlueprint(self): # setup - test_data_1 = TaskDraft_test_data("task draft one") + test_data_1 = TaskDraft_test_data("task draft four") tbt_test_data_1 = TaskBlueprint_test_data() tbt_test_data_2 = TaskBlueprint_test_data() task_draft = models.TaskDraft.objects.create(**test_data_1) @@ -1569,7 +1568,7 @@ class TaskDraftTestCase(unittest.TestCase): def test_TaskDraft_contains_lists_of_related_TaskRelationDraft(self): # setup - test_data_1 = TaskDraft_test_data("task draft one") + test_data_1 = TaskDraft_test_data("task draft five") task_draft = models.TaskDraft.objects.create(**test_data_1) trdt_test_data_1 = TaskRelationDraft_test_data() diff --git a/SAS/TMSS/test/test_utils.py b/SAS/TMSS/test/test_utils.py index 52e18d0a8a10191285b7daaf6266fdd00768a4bc..2edeaae66b24887a9491527b23bbe6518f4456ae 100644 --- a/SAS/TMSS/test/test_utils.py +++ b/SAS/TMSS/test/test_utils.py @@ -214,7 +214,7 @@ class TMSSDjangoServerInstance(): # wait for server to be up and running.... # or exit via TimeoutError - self.check_running_server(timeout=30) + self.check_running_server(timeout=60) def stop(self): ''' diff --git a/SAS/TMSS/test/testdata/example_UC1_scheduling_unit.json b/SAS/TMSS/test/testdata/example_UC1_scheduling_unit.json index 639ad9535ae620604a82c8bdb9752c3a253d5618..38dc23b9cc5e09253801bbce32c50273cc05b8af 100644 --- a/SAS/TMSS/test/testdata/example_UC1_scheduling_unit.json +++ b/SAS/TMSS/test/testdata/example_UC1_scheduling_unit.json @@ -69,16 +69,16 @@ "stations": ["CS001","CS002","CS003"], "tile_beam": { "direction_type": "J2000", - "angle1": 42, - "angle2": 42 + "angle1": 0.42, + "angle2": 0.43 }, "SAPs": [ { "name": "target0", "digital_pointing": { "direction_type": "J2000", - "angle1": 24, - "angle2": 24 + "angle1": 0.24, + "angle2": 0.25 }, "subbands": [ 349, diff --git a/SAS/TMSS/test/tmss_test_environment_unittest_setup.py b/SAS/TMSS/test/tmss_test_environment_unittest_setup.py index dc4f72644bf2b40058a6eb6571218f7cf6fd3d89..04b9882454838c474e06523d8017ebc9320aca07 100644 --- a/SAS/TMSS/test/tmss_test_environment_unittest_setup.py +++ b/SAS/TMSS/test/tmss_test_environment_unittest_setup.py @@ -134,7 +134,7 @@ def GET_and_assert_equal_expected_code(test_instance, url, expected_code): return r_dict -def GET_and_assert_in_expected_response_result_list(test_instance, url, expected_content, expected_nbr_results): +def GET_and_assert_in_expected_response_result_list(test_instance, url, expected_content, expected_nbr_results, expected_id=None): """ GET from url and assert the expected code is returned and the expected content is in the response content Use this check when multiple results (list) are returned @@ -159,7 +159,17 @@ def GET_and_assert_in_expected_response_result_list(test_instance, url, expected test_instance.assertIn(key, item.keys()) if url_check: - assertDataWithUrls(test_instance, r_dict['results'][expected_nbr_results-1], expected_content) + # Find the expected id in result list if parameter is given (of curse for just one it does not make sense) + # There was an 'old' assumption that the last one should taken, but that is not reliable + if expected_id is not None: + for idx in range(0, expected_nbr_results): + if r_dict['results'][idx]['id'] == expected_id: + expected_idx = idx + break + else: + # this is the 'old' assumption that last object added will also be the last one in the result dict + expected_idx = expected_nbr_results-1 + assertDataWithUrls(test_instance, r_dict['results'][expected_idx], expected_content) return r_dict