Skip to content
Snippets Groups Projects
Commit fa98e431 authored by Jorrit Schaap's avatar Jorrit Schaap
Browse files

Merge branch 'TMSS-2893' into 'master'

Resolve TMSS-2893

Closes TMSS-2893

See merge request !1289
parents ca56009a e19b9a72
No related branches found
No related tags found
1 merge request!1289Resolve TMSS-2893
...@@ -335,30 +335,6 @@ class SchedulingUnitBlueprintSlimSerializer(serializers.ModelSerializer): ...@@ -335,30 +335,6 @@ class SchedulingUnitBlueprintSlimSerializer(serializers.ModelSerializer):
raise NotImplementedError("This serializer should only be used for fast querying existing scheduling units") raise NotImplementedError("This serializer should only be used for fast querying existing scheduling units")
class SchedulingUnitBlueprintPublicSerializer(serializers.ModelSerializer):
'''A read-only serializer exposing public non-sensitive properties.'''
project = serializers.StringRelatedField(source='draft.scheduling_set.project.name', label='project', read_only=True)
on_sky_duration = FloatDurationField(read_only=True)
target_pointings_astropy = serializers.StringRelatedField(label='targets', read_only=True)
class Meta:
model = models.SchedulingUnitBlueprint
read_only_fields = ['id',
'status',
'on_sky_start_time',
'on_sky_stop_time',
'on_sky_duration',
'target_pointings_astropy',
'project']
fields = read_only_fields
def create(self, validated_data):
raise NotImplementedError("This is a read-only serializer")
def update(self, instance, validated_data):
raise NotImplementedError("This is a read-only serializer")
class TaskBlueprintSlimSerializer(serializers.ModelSerializer): class TaskBlueprintSlimSerializer(serializers.ModelSerializer):
'''A small 'slim' serializer for the most relevant properties.''' '''A small 'slim' serializer for the most relevant properties.'''
duration = FloatDurationField(read_only=True) duration = FloatDurationField(read_only=True)
......
...@@ -755,40 +755,59 @@ def failure_report(request): ...@@ -755,40 +755,59 @@ def failure_report(request):
@swagger_auto_schema(responses={200: 'A json list of the current public schedule.'}, @swagger_auto_schema(responses={200: 'A json list of the current public schedule.'},
operation_description="Get a json list of the current public schedule.") operation_description="Get a json list of the current public schedule.")
def public_schedule(request): 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''' '''return the current and future(2 weeks) running and scheduled units in a simple html table'''
# get all current and future running and scheduled units return __create_html_schedule(request, past=False)
scheduled_or_running_units = models.SchedulingUnitBlueprint.objects.prefetch_related('task_blueprints__specifications_template__type') \
.select_related(models.SchedulingUnitBlueprint.path_to_project, 'status', 'specifications_template', 'specifications_template') \
.filter(status__value__in=models.SchedulingUnitStatus.ACTIVE_STATUS_VALUES) \ @permission_classes([AllowAny])
.filter(obsolete_since__isnull=True) \ @authentication_classes([AllowAny])
.filter(on_sky_stop_time__gte=datetime.utcnow()) \ @swagger_auto_schema(responses={200: 'A json list of the current public schedule.'},
.filter(on_sky_start_time__lte=datetime.utcnow()+timedelta(days=7)) \ operation_description="Get a json list of the current public schedule.")
.order_by('on_sky_start_time').all() def public_past_schedule(request):
'''return the past schedule of observed units in the cycle in a simple html table'''
return __create_html_schedule(request, past=True)
# return json if requested
def __create_html_schedule(request, past: bool=False):
# get all current and future running and scheduled units
if 'json' in request.headers.get('Accept','').lower(): if 'json' in request.headers.get('Accept','').lower():
result = serializers.SchedulingUnitBlueprintPublicSerializer(scheduled_or_running_units, many=True, context={'request': request}).data return JsonResponse("Sorry, Json is not supported", status=status.HTTP_501_NOT_IMPLEMENTED, safe=False)
return JsonResponse(result, status=status.HTTP_200_OK, safe=False)
# else, return html table
base_url = request._current_scheme_host base_url = request._current_scheme_host
table_rows = '''<tr><th>Project</th><th>ID</th><th>Name</th><th>Start [UTC]</th><th>End [UTC]</th><th>Duration [min]</th><th>Center [LST]</th><th>Antenna</th><th>Target(s)</th></tr>\n''' title = 'LOFAR observing schedule %sUTC' %(datetime.utcnow().strftime("%Y-%m-%d %H:%M"), )
scheduling_units = models.SchedulingUnitBlueprint.objects.prefetch_related('task_blueprints__specifications_template__type') \
.select_related(models.SchedulingUnitBlueprint.path_to_project, 'status', 'specifications_template', 'specifications_template') \
.filter(status__value__in=models.SchedulingUnitStatus.ACTIVE_OR_FINISHED_STATUS_VALUES) \
.filter(obsolete_since__isnull=True)
if past:
current_cycle = models.Cycle.objects.filter(start__lte=datetime.utcnow()).order_by('-start').first()
scheduling_units = scheduling_units.filter(on_sky_stop_time__gte=current_cycle.start) \
.filter(on_sky_start_time__lte=datetime.utcnow())
description = '''This page shows all %s's past observations. Please find the current observations <a href="%s">here</a>''' % (current_cycle.name, base_url.rstrip('/')+'/schedule')
else:
scheduling_units = scheduling_units.filter(on_sky_stop_time__gte=datetime.utcnow()) \
.filter(on_sky_start_time__lte=datetime.utcnow()+timedelta(days=14))
description = '''This page shows all observations for the upcoming 2 weeks. Please find all past observations <a href="%s">here</a>''' % (base_url.rstrip('/')+'/past_schedule', )
description += '''<br>This schedule is subject to change based on changing telescope operational circumstances'''
scheduling_units = scheduling_units.order_by('on_sky_start_time').all()
table_rows = '''<tr><th>Project</th><th>ID</th><th>Name</th><th>Status</th><th>Start [UTC]</th><th>End [UTC]</th><th>Antenna</th><th>Target(s)</th></tr>\n'''
row_date = date.min row_date = date.min
for su in scheduled_or_running_units: for su in scheduling_units:
if su.on_sky_start_time.date() > row_date: if su.on_sky_start_time.date() > row_date:
row_date = su.on_sky_start_time.date() row_date = su.on_sky_start_time.date()
table_rows += '''<tr><td style="font-weight: bold; background-color: #999999;" colspan=9>%s</td></tr>\n''' % (row_date.strftime('%Y-%m-%d')) table_rows += '''<tr><td style="font-weight: bold; background-color: #999999;" colspan=9>%s</td></tr>\n''' % (row_date.strftime('%Y-%m-%d'))
table_rows += '''<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><td>%s</td></tr>\n''' % ( table_rows += '''<tr><td><a href="%s/project/view/%s" title="%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>%s</td><td>%s</td><td>%s</td></tr>\n''' % (
base_url, su.project, su.project, base_url, su.project.name, 'description hidden' if su.project.private_data else su.project.description , su.project.name,
base_url, su.id, su.id, base_url, su.id, su.id,
su.name, 'hidden' if su.project.private_data else su.name, su.status.value,
su.on_sky_start_time.strftime('%H:%M') if su.on_sky_start_time else '', su.on_sky_start_time.strftime('%H:%M') if su.on_sky_start_time else '',
su.on_sky_stop_time.strftime('%H:%M') if su.on_sky_stop_time else '', su.on_sky_stop_time.strftime('%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 '',
', '.join(sorted(list([q for q in set([x['specifications_doc__station_configuration__antenna_set'] for x in su.observation_tasks.values('specifications_doc__station_configuration__antenna_set')]) if q is not None]))), ', '.join(sorted(list([q for q in set([x['specifications_doc__station_configuration__antenna_set'] for x in su.observation_tasks.values('specifications_doc__station_configuration__antenna_set')]) if q is not None]))),
su.target_pointings_astropy) 'hidden' if su.project.private_data else su.target_pointings_astropy)
html_doc = '''<!DOCTYPE html> html_doc = '''<!DOCTYPE html>
<html> <html>
...@@ -817,15 +836,20 @@ def public_schedule(request): ...@@ -817,15 +836,20 @@ def public_schedule(request):
} }
</style> </style>
<title>LOFAR observing schedule %sUTC</title> <title>LOFAR observing schedule %sUTC</title>
<meta http-equiv="refresh" content="60"> <meta http-equiv="refresh" content="300">
</head> </head>
<body> <body>
<h1>%s</h1>
<p>%s</p>
<table> <table>
%s %s
</table> </table>
</body> </body>
</html> </html>
''' % (datetime.utcnow().strftime("%Y-%m-%d %H:%M"), table_rows,) ''' % (title,
title,
description,
table_rows,)
return HttpResponse(html_doc, content_type='text/html') return HttpResponse(html_doc, content_type='text/html')
...@@ -270,6 +270,7 @@ urlpatterns = [url(r'^api$', RedirectView.as_view(url='/api/')), ...@@ -270,6 +270,7 @@ urlpatterns = [url(r'^api$', RedirectView.as_view(url='/api/')),
RedirectView.as_view(url='/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'^schedule', views.public_schedule),
url(r'^past_schedule', views.public_past_schedule),
url(r'', include(frontend_urls)), url(r'', include(frontend_urls)),
url(r'^.*', include(frontend_urlpatterns)), url(r'^.*', include(frontend_urlpatterns)),
] ]
......
...@@ -163,6 +163,9 @@ export class Login extends Component { ...@@ -163,6 +163,9 @@ export class Login extends Component {
<div className="col-md-6 forget-paswd login-form-link"> <div className="col-md-6 forget-paswd login-form-link">
<a href={process.env.REACT_APP_FORGOT_PASSWORD_URL} >Forgot Password?</a> <a href={process.env.REACT_APP_FORGOT_PASSWORD_URL} >Forgot Password?</a>
</div> </div>
<div className="col-md-6 login-form-link">
<a href="/schedule">Public Schedule</a>
</div>
</div> </div>
{this.state.error && {this.state.error &&
<div className="row error"> <div className="row error">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment