Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

preserve source query order on detail view #364

Merged
merged 2 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions templates/source_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,22 @@ <h1 class="h3 mb-2 text-gray-800"><strong>Source:</strong> {{ source.name }}</h1
</span>
<span class="text">NED</span>
</a>
<a href="{% url 'vast_pipeline:source_detail' source.id %}prev"
class="d-none d-sm-inline-block btn btn-sm btn-light btn-icon-split shadow-sm">
{% if source_previous_id %}
<a href="{% url 'vast_pipeline:source_detail' source_previous_id %}" class="d-none d-sm-inline-block btn btn-sm btn-light btn-icon-split shadow-sm">
<span class="icon text-gray-600">
<i class="fas fa-arrow-left fa-sm"></i>
</span>
<span class="text">Previous</span>
</a>
<a href="{% url 'vast_pipeline:source_detail' source.id %}next"
class="d-none d-sm-inline-block btn btn-sm btn-light btn-icon-split shadow-sm">
{% endif %}
{% if source_next_id %}
<a href="{% url 'vast_pipeline:source_detail' source_next_id %}" class="d-none d-sm-inline-block btn btn-sm btn-light btn-icon-split shadow-sm">
<span class="text">Next</span>
<span class="icon text-gray-600">
<i class="fas fa-arrow-right fa-sm"></i>
</span>
</a>
{% endif %}
</div>
</div>

Expand Down
6 changes: 1 addition & 5 deletions vast_pipeline/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,7 @@
name='measurement_detail'
),
path('sources/query/', views.SourceQuery, name='source_query'),
re_path(
r'^sources/(?P<id>\d+)(?:/(?P<action>[\w]+))?/$',
views.SourceDetail,
name='source_detail'
),
path('sources/<int:pk>/', views.SourceDetail, name='source_detail'),
path('sources/favs/', views.UserSourceFavsList, name='source_favs'),
path('cutout/<str:measurement_name>/', views.ImageCutout.as_view(), name='cutout'),
path('cutout/<str:measurement_name>/<str:size>/', views.ImageCutout.as_view(), name='cutout'),
Expand Down
94 changes: 40 additions & 54 deletions vast_pipeline/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from django.contrib import messages

from rest_framework import status
from rest_framework.decorators import action
import rest_framework.decorators
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
Expand Down Expand Up @@ -199,7 +199,7 @@ class RunViewSet(ModelViewSet):
queryset = Run.objects.all()
serializer_class = RunSerializer

@action(detail=True, methods=['get'])
@rest_framework.decorators.action(detail=True, methods=['get'])
def images(self, request, pk=None):
qs = Image.objects.filter(run__id=pk).order_by('id')
qs = self.filter_queryset(qs)
Expand All @@ -211,7 +211,7 @@ def images(self, request, pk=None):
serializer = ImageSerializer(qs, many=True)
return Response(serializer.data)

@action(detail=True, methods=['get'])
@rest_framework.decorators.action(detail=True, methods=['get'])
def measurements(self, request, pk=None):
qs = Measurement.objects.filter(image__run__in=[pk]).order_by('id')
qs = self.filter_queryset(qs)
Expand Down Expand Up @@ -464,7 +464,7 @@ class ImageViewSet(ModelViewSet):
queryset = Image.objects.all()
serializer_class = ImageSerializer

@action(detail=True, methods=['get'])
@rest_framework.decorators.action(detail=True, methods=['get'])
def measurements(self, request, pk=None):
qs = Measurement.objects.filter(image__in=[pk], forced=False).order_by('id')
qs = self.filter_queryset(qs)
Expand All @@ -476,7 +476,7 @@ def measurements(self, request, pk=None):
serializer = MeasurementSerializer(qs, many=True)
return Response(serializer.data)

@action(detail=True, methods=['get'])
@rest_framework.decorators.action(detail=True, methods=['get'])
def runs(self, request, pk=None):
image = self.queryset.get(pk=pk)
qs = image.run.all().order_by('id')
Expand Down Expand Up @@ -709,7 +709,7 @@ def get_queryset(self):
run_id = self.request.query_params.get('run_id', None)
return self.queryset.filter(source__id=run_id) if run_id else self.queryset

@action(detail=True, methods=['get'])
@rest_framework.decorators.action(detail=True, methods=['get'])
def siblings(self, request, pk=None):
measurement = self.queryset.get(pk=pk)
image_id = measurement.image_id
Expand All @@ -726,7 +726,7 @@ def siblings(self, request, pk=None):
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data)

@action(detail=True, methods=['get'])
@rest_framework.decorators.action(detail=True, methods=['get'])
def sources(self, request, pk=None):
measurement = self.queryset.get(pk=pk)
qs = measurement.source.all()
Expand Down Expand Up @@ -984,7 +984,18 @@ def get_queryset(self):

return qs

@action(detail=True, methods=['get'])
def list(self, request, *args, **kwargs):
"""Override the DRF ModelViewSet.list function to store the source ID order in the
user session to retain the source order for source detail view next and previous
button links. Then, call the original list function.
"""
queryset = self.filter_queryset(self.get_queryset())
self.request.session["source_query_result_ids"] = list(
queryset.values_list("id", flat=True)
)
return super().list(request, *args, **kwargs)

@rest_framework.decorators.action(detail=True, methods=['get'])
def related(self, request, pk=None):
qs = Source.objects.filter(related__id=pk).order_by('id')
qs = self.filter_queryset(qs)
Expand Down Expand Up @@ -1084,47 +1095,9 @@ def SourceQuery(request):

# Source detail
@login_required
def SourceDetail(request, id, action=None):
def SourceDetail(request, pk):
# source data
source = Source.objects.all()
if action:
if action == 'next':
src = source.filter(id__gt=id)
if src.exists():
source = (
src.annotate(run_name=F('run__name'))
.values()
.first()
)
else:
source = (
source.filter(id=id)
.annotate(run_name=F('run__name'))
.values()
.get()
)
elif action == 'prev':
src = source.filter(id__lt=id)
if src.exists():
source = (
src.annotate(run_name=F('run__name'))
.values()
.last()
)
else:
source = (
source.filter(id=id)
.annotate(run_name=F('run__name'))
.values()
.get()
)
else:
source = (
source.filter(id=id)
.annotate(run_name=F('run__name'))
.values()
.get()
)
source = Source.objects.filter(id=pk).annotate(run_name=F('run__name')).values().get()
source['aladin_ra'] = source['wavg_ra']
source['aladin_dec'] = source['wavg_dec']
source['aladin_zoom'] = 0.15
Expand Down Expand Up @@ -1154,7 +1127,7 @@ def SourceDetail(request, id, action=None):
'image_id'
]
measurements = list(
Measurement.objects.filter(source__id=id).annotate(
Measurement.objects.filter(source__id=pk).annotate(
datetime=F('image__datetime'),
image_name=F('image__name'),
frequency=F('image__band__frequency'),
Expand Down Expand Up @@ -1256,8 +1229,21 @@ def SourceDetail(request, id, action=None):
'search': True,
}

# find next and previous sources
source_query_result_id_list = request.session.get("source_query_result_ids", [])
source_next_id, source_previous_id = None, None
for i, source_id in enumerate(source_query_result_id_list):
if source_id == source['id']:
if i + 1 < len(source_query_result_id_list):
source_next_id = source_query_result_id_list[i + 1]
if i - 1 >= 0:
source_previous_id = source_query_result_id_list[i - 1]
break

context = {
'source': source,
'source_next_id': source_next_id,
'source_previous_id': source_previous_id,
'datatables': [measurements, related_datatables],
# flag to deactivate starring and render yellow star
'sourcefav': (
Expand Down Expand Up @@ -1521,7 +1507,7 @@ class RunConfigSet(ViewSet):
permission_classes = [IsAuthenticated]
queryset = Run.objects.all()

@action(detail=True, methods=['get'])
@rest_framework.decorators.action(detail=True, methods=['get'])
def validate(self, request, pk=None):
if not pk:
return Response(
Expand Down Expand Up @@ -1578,7 +1564,7 @@ def validate(self, request, pk=None):

return Response(msg, status=status.HTTP_202_ACCEPTED)

@action(detail=True, methods=['post'])
@rest_framework.decorators.action(detail=True, methods=['post'])
def write(self, request, pk=None):
# this post is for writing the config text (modified or not)
# from the UI to a config.py file
Expand Down Expand Up @@ -1722,7 +1708,7 @@ class UtilitiesSet(ViewSet):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]

@action(methods=['get'], detail=False)
@rest_framework.decorators.action(methods=['get'], detail=False)
def sesame_search(self, request: Request) -> Response:
"""Query the Sesame name resolver service and return a coordinate.

Expand Down Expand Up @@ -1750,7 +1736,7 @@ def sesame_search(self, request: Request) -> Response:

return Response(serializer.data)

@action(methods=['get'], detail=False)
@rest_framework.decorators.action(methods=['get'], detail=False)
def coordinate_validator(self, request: Request) -> Response:
"""Validate a coordinate string.

Expand All @@ -1774,7 +1760,7 @@ def coordinate_validator(self, request: Request) -> Response:
serializer.is_valid(raise_exception=True)
return Response()

@action(methods=["get"], detail=False)
@rest_framework.decorators.action(methods=["get"], detail=False)
def simbad_ned_search(self, request: Request) -> Response:
"""Perform a cone search with SIMBAD and NED and return the combined results.

Expand Down