From e9733266684cb2d74840735100298647f3de5c99 Mon Sep 17 00:00:00 2001
From: Jorrit Schaap <schaap@astron.nl>
Date: Tue, 16 Jan 2024 16:01:15 +0100
Subject: [PATCH] TMSS-2886: added public '/schedule' endpoint to get the
 current schedule

---
 SAS/TMSS/backend/src/tmss/tmssapp/views.py | 72 +++++++++++++++++++++-
 SAS/TMSS/backend/src/tmss/urls.py          |  3 +-
 2 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/views.py b/SAS/TMSS/backend/src/tmss/tmssapp/views.py
index c529071eaf5..6be8abb8cb6 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/views.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/views.py
@@ -7,7 +7,7 @@ from django.http import HttpResponse, JsonResponse, Http404, FileResponse, HttpR
 from rest_framework.response import Response as RestResponse
 from rest_framework import status
 from django.shortcuts import get_object_or_404, render
-from lofar.sas.tmss.tmss.tmssapp import models
+from lofar.sas.tmss.tmss.tmssapp import models, serializers
 from lofar.common.json_utils import get_default_json_object_for_schema
 from lofar.common.util import single_line_with_single_spaces
 from lofar.sas.tmss.tmss.tmssapp.adapters.parset import convert_to_parset
@@ -747,4 +747,72 @@ def failure_report(request):
             return RestResponse('Error: please specify an isoformat timestamp for stop_time', status=400)
 
     result = create_failure_report(start, stop, ignore_time_range_for_histogram)
-    return JsonResponse(result, status=status.HTTP_200_OK)
\ No newline at end of file
+    return JsonResponse(result, status=status.HTTP_200_OK)
+
+
+@permission_classes([AllowAny])
+@authentication_classes([AllowAny])
+@swagger_auto_schema(responses={200: 'A json list of the current public schedule.'},
+                     operation_description="Get a json list of the current public schedule.")
+def public_schedule(request):
+    '''return the current and future running and scheduled units in either html table or json depending on the request accept header'''
+    # get all current and future running and scheduled units
+    scheduled_or_running_units = models.SchedulingUnitBlueprint.objects.filter(status__value__in=models.SchedulingUnitStatus.ACTIVE_STATUS_VALUES) \
+                                                                       .filter(obsolete_since__isnull=True) \
+                                                                       .filter(on_sky_stop_time__gte=datetime.utcnow()) \
+                                                                       .order_by('on_sky_start_time').all()
+
+    if 'html' in request.headers.get('Accept','').lower():
+        base_url = request._current_scheme_host
+        table_rows = '''<tr><th>Project</th><th>ID</th><th>Status</th><th>Start [UTC]</th><th>End [UTC]</th><th>Duration [min]</th><th>Center [LST]</th><th>Target(s)</th></tr>\n'''
+        table_rows += '\n'.join(['''<tr><td><a href="%s/project/view/%s">%s</a></td><td><a href="%s/schedulingunit/view/blueprint/%s">%s</a></td><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td></tr>\n''' % (
+                                    base_url, su.project, su.project,
+                                    base_url, su.id, su.id,
+                                    su.status.value,
+                                    su.on_sky_start_time.strftime('%Y-%m-%d %H:%M') if su.on_sky_start_time else '',
+                                    su.on_sky_stop_time.strftime('%Y-%m-%d %H:%M') if su.on_sky_stop_time else '',
+                                    round(su.on_sky_duration.total_seconds()/60.0),
+                                    su.main_observation_scheduled_central_lst.strftime('%H:%M') if su.main_observation_scheduled_central_lst else '',
+                                    su.target_pointings_astropy) for su in scheduled_or_running_units])
+        html_doc = '''<!DOCTYPE html>
+        <html>
+        <head>
+        <style>
+        table {
+          border-collapse: collapse;
+          height: 100%%;
+          width: 100%%;
+        }
+        th {
+          background-color: #AAAAAA;
+          font-weight: bold; 
+          text-align: left; 
+        }
+        tr:nth-child(even) {
+          background-color: #EEEEEE;
+        }
+        tr:nth-child(odd) {
+          background-color: #CCCCCC;
+        }
+        tr:hover {
+          background-color: #DDDDDD;
+        }     
+        </style>    
+        <title>LOFAR observing schedule</title>
+        </head>
+        <body>
+        <table>
+        %s
+        </table>
+        </body>
+        </html>
+            ''' % (table_rows,)
+
+        return HttpResponse(html_doc, content_type='text/html')
+
+    if 'json' in request.headers.get('Accept','').lower():
+        result = serializers.SchedulingUnitBlueprintPublicSerializer(scheduled_or_running_units, many=True, context={'request': request}).data
+        return JsonResponse(result, status=status.HTTP_200_OK, safe=False)
+
+    return HttpResponseServerError()
+
diff --git a/SAS/TMSS/backend/src/tmss/urls.py b/SAS/TMSS/backend/src/tmss/urls.py
index 1917123caf8..51c1d660121 100644
--- a/SAS/TMSS/backend/src/tmss/urls.py
+++ b/SAS/TMSS/backend/src/tmss/urls.py
@@ -268,7 +268,8 @@ frontend_urlpatterns = [
 urlpatterns = [url(r'^api$', RedirectView.as_view(url='/api/')),
                 url(r'^api/', include(urlpatterns)), url(r'^oidc$',
                 RedirectView.as_view(url='/oidc/')),
-                url(r'^oidc/', include('mozilla_django_oidc.urls')), 
+                url(r'^oidc/', include('mozilla_django_oidc.urls')),
+                url(r'^schedule', views.public_schedule),
                 url(r'', include(frontend_urls)),
                 url(r'^.*', include(frontend_urlpatterns)),
 ]
-- 
GitLab