From 066d9f590f3bee4a65202c181d8e1513e7ba605b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rn=20K=C3=BCnsem=C3=B6ller?=
 <jkuensem@physik.uni-bielefeld.de>
Date: Tue, 28 Sep 2021 18:56:08 +0200
Subject: [PATCH] TMSS-159: further adaptations to new start/stop times, add
 aggregate time properties, set process times when scheduling

---
 .../scheduling/lib/dynamic_scheduling.py        |  6 +++---
 .../src/tmss/tmssapp/models/scheduling.py       | 17 +++++++++++++++++
 .../src/tmss/tmssapp/serializers/scheduling.py  |  2 +-
 SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py   |  4 ++++
 .../src/tmss/tmssapp/viewsets/scheduling.py     |  8 ++++++--
 5 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
index 428bc37d3f0..f107a47b8eb 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
@@ -303,9 +303,9 @@ def get_scheduled_scheduling_units(lower:datetime=None, upper:datetime=None) ->
     '''get a list of all scheduling_units for which at least one 'independent' (with no predecessor like an observation) subtask is scheduled'''
     scheduled_subtasks = models.Subtask.independent_subtasks().filter(state__value='scheduled')
     if lower is not None:
-        scheduled_subtasks = scheduled_subtasks.filter(stop_time__gte=lower)
+        scheduled_subtasks = scheduled_subtasks.filter(scheduled_on_sky_stop_time__gte=lower)
     if upper is not None:
-        scheduled_subtasks = scheduled_subtasks.filter(start_time__lte=upper)
+        scheduled_subtasks = scheduled_subtasks.filter(scheduled_on_sky_start_time__lte=upper)
     return list(models.SchedulingUnitBlueprint.objects.filter(id__in=scheduled_subtasks.values('task_blueprint__scheduling_unit_blueprint_id').distinct()).all())
 
 
@@ -314,7 +314,7 @@ def get_running_observation_subtasks(stopping_after:datetime=None) -> [models.Su
     running_obs_subtasks = models.Subtask.objects.filter(state__value__in=[models.SubtaskState.Choices.STARTING.value, models.SubtaskState.Choices.STARTED.value],
                                                          specifications_template__type__value=models.SubtaskType.Choices.OBSERVATION.value)
     if stopping_after is not None:
-        running_obs_subtasks = running_obs_subtasks.filter(stop_time__gte=stopping_after)
+        running_obs_subtasks = running_obs_subtasks.filter(scheduled_on_sky_stop_time__gte=stopping_after)
     return list(running_obs_subtasks.all())
 
 
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py
index f474f4d9b20..cf5ef948746 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/scheduling.py
@@ -21,6 +21,7 @@ from lofar.sas.tmss.tmss.exceptions import SubtaskSchedulingException, SubtaskIl
 from django.conf import settings
 from lofar.sas.resourceassignment.resourceassignmentservice.rpc import RADBRPC
 import uuid
+from django.db.models.functions import Coalesce
 
 #
 # I/O
@@ -289,6 +290,22 @@ class Subtask(BasicCommon, ProjectPropertyMixin, TemplateSchemaMixin):
                                                                                                   self.specifications_template.type.value,
                                                                                                   self.state))
 
+    @property
+    def on_sky_start_time(self) -> datetime:
+        return self.scheduled_on_sky_start_time if self.actual_on_sky_start_time is None else self.actual_on_sky_start_time
+
+    @property
+    def on_sky_stop_time(self) -> datetime:
+        return self.scheduled_on_sky_stop_time if self.actual_on_sky_stop_time is None else self.actual_on_sky_stop_time
+
+    @property
+    def process_start_time(self) -> datetime:
+        return self.scheduled_process_start_time if self.actual_process_start_time is None else self.actual_process_start_time
+
+    @property
+    def process_stop_time(self) -> datetime:
+        return self.scheduled_process_stop_time if self.actual_process_stop_time is None else self.actual_process_stop_time
+
     def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
         creating = self._state.adding  # True on create, False on update
 
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/scheduling.py
index 04a943b9313..e41d21d8c8c 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/scheduling.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/scheduling.py
@@ -91,7 +91,7 @@ class SubtaskSerializer(DynamicRelationalHyperlinkedModelSerializer):
     class Meta:
         model = models.Subtask
         fields = '__all__'
-        extra_fields = ['input_dataproducts', 'output_dataproducts']
+        extra_fields = ['input_dataproducts', 'output_dataproducts', 'on_sky_start_time', 'on_sky_stop_time', 'process_start_time', 'process_stop_time']
         expandable_fields = {
             'input_dataproducts': ('lofar.sas.tmss.tmss.tmssapp.serializers.DataproductSerializer', {'many': True}),
             'output_dataproducts': ('lofar.sas.tmss.tmss.tmssapp.serializers.DataproductSerializer', {'many': True})
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
index fa1ee8088b4..cf1f3c93157 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
@@ -1482,6 +1482,10 @@ def schedule_observation_subtask(observation_subtask: Subtask):
 
     # 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)
+
+    # step 6: set scheduled_process_start/stop_time for MACScheduler
+    observation_subtask.scheduled_process_start_time = observation_subtask.scheduled_on_sky_start_time - timedelta(minutes=3)
+    observation_subtask.scheduled_process_stop_time = observation_subtask.scheduled_on_sky_stop_time + timedelta(minutes=1)
     observation_subtask.save()
 
     return observation_subtask
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
index 18f108dd5cd..f59734365aa 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
@@ -39,7 +39,7 @@ from lofar.sas.tmss.tmss.tmssapp.renderers import PlainTextRenderer
 from rest_framework.views import APIView
 from rest_framework.decorators import api_view, renderer_classes
 from django.core.exceptions import ObjectDoesNotExist
-
+import django_property_filter as property_filters
 
 class TextPlainAutoSchema(SwaggerAutoSchema):
     def get_produces(self):
@@ -131,12 +131,16 @@ class DataproductFeedbackTemplateViewSet(AbstractTemplateViewSet):
     serializer_class = serializers.DataproductFeedbackTemplateSerializer
 
 
-class SubTaskFilter(filters.FilterSet):
+class SubTaskFilter(property_filters.PropertyFilterSet):
     id = NumberInFilter(field_name='id', lookup_expr='in')
     id_min = filters.NumberFilter(field_name='id', lookup_expr='gte')
     id_max = filters.NumberFilter(field_name='id', lookup_expr='lte')
     state = filters.ModelMultipleChoiceFilter(field_name='state', queryset=models.SubtaskState.objects.all())
     name = filters.CharFilter(field_name='task_blueprint__scheduling_unit_blueprint__name', lookup_expr='icontains')   # todo: correct name?
+    on_sky_start_time__lt = property_filters.PropertyDateTimeFilter(field_name='on_sky_start_time', lookup_expr='lt')
+    on_sky_start_time__gt = property_filters.PropertyDateTimeFilter(field_name='on_sky_start_time', lookup_expr='gt')
+    process_start_time__lt = property_filters.PropertyDateTimeFilter(field_name='process_start_time', lookup_expr='lt')
+    process_stop_time__gt = property_filters.PropertyDateTimeFilter(field_name='process_start_time', lookup_expr='gt')
 
     class Meta:
         model = Subtask
-- 
GitLab