146 lines
6.9 KiB
Python
146 lines
6.9 KiB
Python
"""SatNOGS Network API django rest framework Views"""
|
|
from __future__ import absolute_import
|
|
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
from django.shortcuts import get_object_or_404
|
|
from django.utils.timezone import now
|
|
from requests.exceptions import RequestException
|
|
from rest_framework import mixins, status, viewsets
|
|
from rest_framework.response import Response
|
|
from rest_framework.serializers import ValidationError
|
|
|
|
from network.api import filters, pagination, serializers
|
|
from network.api.perms import StationOwnerPermission
|
|
from network.base.models import LatestTle, Observation, Station, Transmitter
|
|
from network.base.utils import sync_demoddata_to_db
|
|
from network.base.validators import NegativeElevationError, \
|
|
ObservationOverlapError, SinglePassError
|
|
|
|
|
|
class ObservationView( # pylint: disable=R0901
|
|
mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin,
|
|
mixins.CreateModelMixin, viewsets.GenericViewSet):
|
|
"""SatNOGS Network Observation API view class"""
|
|
queryset = Observation.objects.prefetch_related('satellite', 'demoddata', 'ground_station')
|
|
filter_class = filters.ObservationViewFilter
|
|
permission_classes = [StationOwnerPermission]
|
|
pagination_class = pagination.LinkedHeaderPageNumberPagination
|
|
|
|
def get_serializer_class(self):
|
|
"""Returns the right serializer depending on http method that is used"""
|
|
if self.request.method == 'POST':
|
|
return serializers.NewObservationSerializer
|
|
return serializers.ObservationSerializer
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
"""Creates observations from a list of observation data"""
|
|
serializer = self.get_serializer(data=request.data, many=True, allow_empty=False)
|
|
try:
|
|
if serializer.is_valid():
|
|
observations = serializer.save()
|
|
serialized_obs = serializers.ObservationSerializer(observations, many=True)
|
|
data = serialized_obs.data
|
|
response = Response(data, status=status.HTTP_200_OK)
|
|
else:
|
|
data = serializer.errors
|
|
response = Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
except ValueError as error:
|
|
data = error.message
|
|
response = Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
except ValidationError as error:
|
|
data = error.message
|
|
response = Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
except LatestTle.DoesNotExist:
|
|
data = 'Scheduling failed: Satellite without TLE'
|
|
response = Response(data, status=status.HTTP_501_NOT_IMPLEMENTED)
|
|
except ObservationOverlapError as error:
|
|
data = error.message
|
|
response = Response(data, status=status.HTTP_409_CONFLICT)
|
|
except NegativeElevationError as error:
|
|
data = error.message
|
|
response = Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
except SinglePassError as error:
|
|
data = error.message
|
|
response = Response(data, status=status.HTTP_400_BAD_REQUEST)
|
|
return response
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
"""Updates observation with audio, waterfall or demoded data"""
|
|
instance = self.get_object()
|
|
if request.data.get('client_version'):
|
|
instance.ground_station.client_version = request.data.get('client_version')
|
|
instance.ground_station.save()
|
|
if request.data.get('demoddata'):
|
|
try:
|
|
file_path = 'data_obs/{0}/{1}'.format(instance.id, request.data.get('demoddata'))
|
|
instance.demoddata.get(payload_demod=file_path)
|
|
return Response(
|
|
data='This data file has already been uploaded',
|
|
status=status.HTTP_403_FORBIDDEN
|
|
)
|
|
except ObjectDoesNotExist:
|
|
demoddata = instance.demoddata.create(payload_demod=request.data.get('demoddata'))
|
|
if Transmitter.objects.get(uuid=instance.transmitter_uuid).sync_to_db:
|
|
try:
|
|
sync_demoddata_to_db(demoddata.id)
|
|
except RequestException:
|
|
# Sync to db failed, let the periodic task handle the sync to db later
|
|
pass
|
|
if request.data.get('waterfall'):
|
|
if instance.has_waterfall:
|
|
return Response(
|
|
data='Watefall has already been uploaded', status=status.HTTP_403_FORBIDDEN
|
|
)
|
|
if request.data.get('payload'):
|
|
if instance.has_audio:
|
|
return Response(
|
|
data='Audio has already been uploaded', status=status.HTTP_403_FORBIDDEN
|
|
)
|
|
|
|
super(ObservationView, self).update(request, *args, **kwargs)
|
|
return Response(status=status.HTTP_200_OK)
|
|
|
|
|
|
class StationView( # pylint: disable=R0901
|
|
mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
|
"""SatNOGS Network Station API view class"""
|
|
queryset = Station.objects.prefetch_related('antenna', 'observations')
|
|
serializer_class = serializers.StationSerializer
|
|
filter_class = filters.StationViewFilter
|
|
pagination_class = pagination.LinkedHeaderPageNumberPagination
|
|
|
|
|
|
class TransmitterView( # pylint: disable=R0901
|
|
mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
|
"""SatNOGS Network Transmitter API view class"""
|
|
queryset = Transmitter.objects.all().order_by('uuid')
|
|
serializer_class = serializers.TransmitterSerializer
|
|
filter_class = filters.TransmitterViewFilter
|
|
pagination_class = pagination.LinkedHeaderPageNumberPagination
|
|
|
|
|
|
class JobView(viewsets.ReadOnlyModelViewSet): # pylint: disable=R0901
|
|
"""SatNOGS Network Job API view class"""
|
|
queryset = Observation.objects.filter(payload='')
|
|
serializer_class = serializers.JobSerializer
|
|
filter_class = filters.ObservationViewFilter
|
|
filter_fields = ('ground_station')
|
|
|
|
def get_queryset(self):
|
|
"""Returns queryset for Job API view"""
|
|
queryset = self.queryset.filter(start__gte=now()).prefetch_related('tle')
|
|
ground_station_id = self.request.query_params.get('ground_station', None)
|
|
if ground_station_id and self.request.user.is_authenticated():
|
|
ground_station = get_object_or_404(Station, id=ground_station_id)
|
|
if ground_station.owner == self.request.user:
|
|
lat = self.request.query_params.get('lat', None)
|
|
lon = self.request.query_params.get('lon', None)
|
|
alt = self.request.query_params.get('alt', None)
|
|
if not (lat is None or lon is None or alt is None):
|
|
ground_station.lat = float(lat)
|
|
ground_station.lng = float(lon)
|
|
ground_station.alt = int(alt)
|
|
ground_station.last_seen = now()
|
|
ground_station.save()
|
|
return queryset
|