From dfb39c2dd752a8d363fda4a788a61966c5fc7889 Mon Sep 17 00:00:00 2001
From: Jorrit Schaap <schaap@astron.nl>
Date: Tue, 9 Nov 2021 10:23:53 +0100
Subject: [PATCH] TMSS-781: added mark_as_obsolete at subtask level in API.

---
 SAS/TMSS/backend/src/tmss/tmssapp/tasks.py    |  8 ++++---
 .../src/tmss/tmssapp/viewsets/scheduling.py   | 14 +++++++++++
 SAS/TMSS/client/bin/CMakeLists.txt            |  1 +
 .../client/bin/tmss_mark_subtask_as_obsolete  | 23 +++++++++++++++++++
 SAS/TMSS/client/lib/mains.py                  | 14 +++++++++++
 SAS/TMSS/client/lib/tmss_http_rest_client.py  | 10 ++------
 6 files changed, 59 insertions(+), 11 deletions(-)
 create mode 100755 SAS/TMSS/client/bin/tmss_mark_subtask_as_obsolete

diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py
index bec379fc7e4..1426b7377e9 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py
@@ -13,6 +13,7 @@ from django.db.utils import IntegrityError
 from django.db import transaction
 from django.db.models import Q
 from lofar.common.util import dict_with_overrides, subdict_of_pointer_items
+from lofar.common.datetimeutils import round_to_second_precision
 
 logger = logging.getLogger(__name__)
 
@@ -512,10 +513,11 @@ def cancel_task_blueprint(task_blueprint: TaskBlueprint) -> TaskBlueprint:
 
 def mark_task_blueprint_as_obsolete(task_blueprint: TaskBlueprint) -> TaskBlueprint:
     '''Convenience method: mark all cancelled/error subtasks in the task_blueprint as obsolete'''
-    now = datetime.utcnow()
+    now = round_to_second_precision(datetime.utcnow()) # no need for precision, make human readable
     for subtask in task_blueprint.subtasks.all():
-        subtask.obsolete_since = now
-        subtask.save()
+        if subtask.obsolete_since is None:
+            subtask.obsolete_since = now
+            subtask.save()
     task_blueprint.refresh_from_db()
     return task_blueprint
 
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
index 531c63c3fc5..5f3c7fee43c 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
@@ -41,6 +41,7 @@ from rest_framework.decorators import api_view, renderer_classes
 from django.core.exceptions import ObjectDoesNotExist
 import django_property_filter as property_filters
 from django.db import transaction
+from lofar.common.datetimeutils import round_to_second_precision
 
 class TextPlainAutoSchema(SwaggerAutoSchema):
     def get_produces(self):
@@ -251,6 +252,19 @@ class SubtaskViewSet(LOFARViewSet):
         return RestResponse(serializer.data)
 
 
+    @swagger_auto_schema(responses={200: 'The obsolete version of this subtask',
+                                    403: 'forbidden',
+                                    500: 'The subtask could not be marked as obsolete'},
+                         operation_description="Try to mark this subtask as obsolete.")
+    @action(methods=['post'], detail=True, url_name="mark_as_obsolete")
+    def mark_as_obsolete(self, request, pk=None):
+        subtask = get_object_or_404(models.Subtask, pk=pk)
+        subtask.obsolete_since = round_to_second_precision(datetime.utcnow()) # no need for precision, make human readable
+        subtask.save()
+        serializer = self.get_serializer(subtask)
+        return RestResponse(serializer.data)
+
+
     @swagger_auto_schema(responses={200: 'The state log for this Subtask.',
                                     403: 'forbidden'},
                          operation_description="Get the state log for this Subtask.")
diff --git a/SAS/TMSS/client/bin/CMakeLists.txt b/SAS/TMSS/client/bin/CMakeLists.txt
index 356d20d3db0..f5299935717 100644
--- a/SAS/TMSS/client/bin/CMakeLists.txt
+++ b/SAS/TMSS/client/bin/CMakeLists.txt
@@ -7,6 +7,7 @@ lofar_add_bin_scripts(tmss_get_subtask_successors)
 lofar_add_bin_scripts(tmss_schedule_subtask)
 lofar_add_bin_scripts(tmss_unschedule_subtask)
 lofar_add_bin_scripts(tmss_cancel_subtask)
+lofar_add_bin_scripts(tmss_mark_subtask_as_obsolete)
 lofar_add_bin_scripts(tmss_get_setting)
 lofar_add_bin_scripts(tmss_set_setting)
 lofar_add_bin_scripts(tmss_populate)
diff --git a/SAS/TMSS/client/bin/tmss_mark_subtask_as_obsolete b/SAS/TMSS/client/bin/tmss_mark_subtask_as_obsolete
new file mode 100755
index 00000000000..4e76470ea5f
--- /dev/null
+++ b/SAS/TMSS/client/bin/tmss_mark_subtask_as_obsolete
@@ -0,0 +1,23 @@
+#!/usr/bin/python3
+
+# Copyright (C) 2012-2015  ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The LOFAR software suite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+
+from lofar.sas.tmss.client.mains import main_mark_subtask_as_obsolete
+
+if __name__ == "__main__":
+    main_mark_subtask_as_obsolete()
diff --git a/SAS/TMSS/client/lib/mains.py b/SAS/TMSS/client/lib/mains.py
index 4b6ea7e3816..a03940d7391 100644
--- a/SAS/TMSS/client/lib/mains.py
+++ b/SAS/TMSS/client/lib/mains.py
@@ -174,6 +174,20 @@ def main_cancel_subtask():
         exit(1)
 
 
+def main_mark_subtask_as_obsolete():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("subtask_id", type=int, help="The ID of the TMSS subtask to be marked as obsolete")
+    parser.add_argument('-R', '--rest_api_credentials', type=str, default='TMSSClient', help='TMSS django REST API credentials name, default: TMSSClient')
+    args = parser.parse_args()
+
+    try:
+        with TMSSsession.create_from_dbcreds_for_ldap(dbcreds_name=args.rest_api_credentials) as session:
+            pprint(session.mark_subtask_as_obsolete(args.subtask_id, retry_count=3))
+    except Exception as e:
+        print(e)
+        exit(1)
+
+
 def main_get_setting():
     parser = argparse.ArgumentParser()
     parser.add_argument("setting_name", type=str, help="The name of the TMSS setting to get")
diff --git a/SAS/TMSS/client/lib/tmss_http_rest_client.py b/SAS/TMSS/client/lib/tmss_http_rest_client.py
index 20c9c3bc222..03ae0b2a055 100644
--- a/SAS/TMSS/client/lib/tmss_http_rest_client.py
+++ b/SAS/TMSS/client/lib/tmss_http_rest_client.py
@@ -448,16 +448,10 @@ class TMSSsession(object):
         returns the cancelled scheduling_unit_blueprint upon success, or raises."""
         return self.post_to_path_and_get_result_as_json_object('scheduling_unit_blueprint/%s/cancel' % (scheduling_unit_blueprint_id), retry_count=retry_count)
 
-    def mark_subtask_as_obsolete(self, subtask_id: int) -> {}:
+    def mark_subtask_as_obsolete(self, subtask_id: int, retry_count: int=0) -> {}:
         """mark the subtask for the given subtask_id as obsolete.
         returns the marked_as_obsolete subtask upon success, or raises."""
-        logger.info("marking subtask id=%s as obsolete", subtask_id)
-        response = self.session.patch(url='%s/subtask/%s/' % (self.api_url, subtask_id), json={'obsolete_since': "%s" % datetime.utcnow().isoformat()})
-
-        if response.status_code >= 200 and response.status_code < 300:
-            return json.loads(response.content.decode('utf-8'))
-
-        raise Exception("Could not mark subtask as obsolete with url %s - %s %s - %s" % (response.request.url, response.status_code, responses.get(response.status_code), response.text))
+        return self.post_to_path_and_get_result_as_json_object('subtask/%s/mark_as_obsolete' % (subtask_id), retry_count=retry_count)
 
     def mark_task_blueprint_as_obsolete(self, task_blueprint_id: int, retry_count: int=0) -> {}:
         """mark the task_blueprint for the given task_blueprint_id as obsolete.
-- 
GitLab