1
0
Fork 0

Add JSON-LD post API endpoint for Transmitter suggestions

Signed-off-by: Alfredos-Panagiotis Damkalis <fredy@fredy.gr>
spacecruft
Alfredos-Panagiotis Damkalis 2021-05-20 14:33:43 +03:00
parent 4141e275cc
commit 804b69e442
4 changed files with 215 additions and 3 deletions

18
db/api/parsers.py 100644
View File

@ -0,0 +1,18 @@
"""SatNOGS DB django rest framework API custom parsers"""
from pyld import jsonld
from rest_framework.parsers import JSONParser
from db.base.structured_data import get_structured_data
class JSONLDParser(JSONParser): # pylint: disable=R0903
""" Parser for JSONLD. """
media_type = 'application/ld+json'
def parse(self, stream, media_type=None, parser_context=None):
""" Render `data` into JSONLD, returning a bytestring. """
raw_data = super().parse(stream, media_type, parser_context)
structured_data = get_structured_data(parser_context['view'].basename, [])
data = jsonld.frame(raw_data, structured_data.frame, {'omitGraph': False})
return data

View File

@ -8,7 +8,7 @@ from drf_spectacular.utils import OpenApiExample, extend_schema_field, extend_sc
from rest_framework import serializers
from db.base.models import TRANSMITTER_STATUS, Artifact, DemodData, LatestTleSet, Mode, \
Satellite, Telemetry, Transmitter
Satellite, Telemetry, Transmitter, TransmitterEntry
@extend_schema_serializer(
@ -181,6 +181,18 @@ class SatelliteSerializer(serializers.ModelSerializer):
]
class TransmitterEntrySerializer(serializers.ModelSerializer):
"""SatNOGS DB TransmitterEntry API Serializer"""
class Meta:
model = TransmitterEntry
fields = (
'uuid', 'description', 'status', 'type', 'uplink_low', 'uplink_high', 'uplink_drift',
'downlink_low', 'downlink_high', 'downlink_drift', 'downlink_mode', 'uplink_mode',
'invert', 'baud', 'satellite', 'citation', 'service', 'coordination',
'coordination_url', 'created_by'
)
@extend_schema_serializer(
examples=[
OpenApiExample(

View File

@ -15,6 +15,7 @@ from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from db.api import filters, pagination, serializers
from db.api.parsers import JSONLDParser
from db.api.perms import SafeMethodsWithPermission
from db.api.renderers import BrowserableJSONLDRenderer, JSONLDRenderer
from db.base.helpers import gridsquare
@ -152,14 +153,17 @@ class SatelliteViewSet(viewsets.ReadOnlyModelViewSet): # pylint: disable=R0901
],
),
)
class TransmitterViewSet(viewsets.ReadOnlyModelViewSet): # pylint: disable=R0901
class TransmitterViewSet( # pylint: disable=R0901
mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin,
viewsets.GenericViewSet):
"""
Read-only view into the Transmitter entities in the SatNOGS DB database.
View into the Transmitter entities in the SatNOGS DB database.
Transmitters are inclusive of Transceivers and Transponders
"""
renderer_classes = [
JSONRenderer, BrowsableAPIRenderer, JSONLDRenderer, BrowserableJSONLDRenderer
]
parser_classes = [JSONLDParser]
queryset = Transmitter.objects.filter(
satellite__associated_satellite__isnull=True, satellite__satellite_entry__approved=True
)
@ -167,6 +171,170 @@ class TransmitterViewSet(viewsets.ReadOnlyModelViewSet): # pylint: disable=R090
filterset_class = filters.TransmitterViewFilter
lookup_field = 'uuid'
def create(self, request, *args, **kwargs): # noqa: C901; pylint: disable=R0911,R0912,R0915
"""
Creates a transmitter suggestion.
"""
transmitters_data = []
for transmitter_entry in request.data['@graph']:
if 'transmitter' not in transmitter_entry:
data = 'Transmitter Entry without "transmitter" key'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if not transmitter_entry['transmitter']:
data = 'One or more of the required fields are missing.\n Required fields: \
description, status, citation, service, satellite'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
transmitter = transmitter_entry['transmitter']
transmitter_data = {}
if "@id" not in transmitter:
data = 'Missing "@id" for one or more entries'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if 'uuid' in transmitter:
if isinstance(transmitter['uuid'], list):
data = 'Multiple values for "http://schema.org/identifier" or multiple \
entries with the same "@id" and different \
"http://schema.org/identifier" values'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
transmitter_uuid = transmitter['uuid']
if Transmitter.objects.filter(uuid=transmitter_uuid).exists():
transmitter_data['uuid'] = transmitter_uuid
transmitter_data['description'] = transmitter['description']
transmitter_data['status'] = transmitter['status']
transmitter_data['citation'] = transmitter['citation']
transmitter_data['service'] = transmitter['service']
transmitter_data['coordination'] = ''
transmitter_data['coordination_url'] = ''
transmitter_data['created_by'] = request.user.pk
try:
if transmitter['satellite']:
if isinstance(transmitter['satellite'], list):
data = 'Multiple values for "https://schema.space/metasat/satellite" \
or multiple entries with the same "@id" and different \
"https://schema.space/metasat/satellite" values'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
transmitter_data['satellite'] = Satellite.objects.get(
satellite_entry__norad_cat_id=transmitter['satellite']['norad_cat_id']
).pk
else:
data = 'Missing NORAD ID value for Satellite'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
except Satellite.DoesNotExist:
data = 'Unknown NORAD ID: {}'.format(transmitter['satellite']['norad_cat_id'])
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if 'baud' in transmitter:
transmitter_data['baud'] = transmitter['baud']
if 'invert' in transmitter:
transmitter_data['invert'] = transmitter['invert']
if 'uplink' not in transmitter and 'downlink' not in transmitter:
data = 'Missing "https://schema.space/metasat/uplink" or \
"https://schema.space/metasat/downlink"'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if 'uplink' in transmitter:
if isinstance(transmitter['uplink'], list):
data = 'Multiple values for "https://schema.space/metasat/uplink" or multiple \
entries with the same "@id" and different \
"https://schema.space/metasat/uplink" values'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if 'frequency' not in transmitter['uplink']:
data = 'Missing "https://schema.space/metasat/frequency" from \
"https://schema.space/metasat/uplink" value'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if isinstance(transmitter['uplink']['frequency'], int):
transmitter_data['type'] = 'Transceiver'
transmitter_data['uplink_low'] = transmitter['uplink']['frequency']
else:
transmitter_data['type'] = 'Transponder'
if 'minimum' not in transmitter['uplink'][
'frequency'] or 'maximum' not in transmitter['uplink']['frequency']:
data = 'Missing "https://schema.org/minimum" or \
"https://schema.org/maximum" from \
"https://schema.space/metasat/frequency" value of \
"https://schema.space/metasat/uplink"'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
transmitter_data['uplink_low'] = transmitter['uplink']['frequency']['minimum']
transmitter_data['uplink_high'] = transmitter['uplink']['frequency']['maximum']
if 'mode' in transmitter['uplink']:
try:
transmitter_data['uplink_mode'] = Mode.objects.get(
name=transmitter['uplink']['mode']
).pk
except Mode.DoesNotExist:
data = 'Unknown Mode: {}'.format(transmitter['uplink']['mode'])
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if 'drift' in transmitter['uplink']:
transmitter_data['uplink_drift'] = transmitter['uplink']['drift']
else:
transmitter_data['type'] = 'Transmitter'
if 'downlink' in transmitter:
if isinstance(transmitter['downlink'], list):
data = 'Multiple values for "https://schema.space/metasat/downlink" or \
multiple entries with the same "@id" and different \
"https://schema.space/metasat/downlink" values'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if 'frequency' not in transmitter['downlink']:
data = 'Missing "https://schema.space/metasat/frequency" from \
"https://schema.space/metasat/downlink" value'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if isinstance(transmitter['downlink']['frequency'],
int) and not transmitter_data['type'] == 'Transponder':
transmitter_data['downlink_low'] = transmitter['downlink']['frequency']
elif transmitter_data['type'] == 'Transponder':
if 'minimum' not in transmitter['downlink'][
'frequency'] or 'maximum' not in transmitter['downlink']['frequency']:
data = 'Missing "https://schema.org/minimum" or \
"https://schema.org/maximum" from \
"https://schema.space/metasat/frequency" value of \
"https://schema.space/metasat/downlink"'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
transmitter_data['downlink_low'] = transmitter['downlink']['frequency'][
'minimum']
transmitter_data['downlink_high'] = transmitter['downlink']['frequency'][
'maximum']
else:
data = 'Expected integer for "https://schema.space/metasat/frequency" value \
of "https://schema.space/metasat/downlink"'
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if 'mode' in transmitter['downlink']:
try:
transmitter_data['downlink_mode'] = Mode.objects.get(
name=transmitter['downlink']['mode']
).pk
except Mode.DoesNotExist:
data = 'Unknown Mode: {}'.format(transmitter['downlink']['mode'])
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if 'drift' in transmitter['downlink']:
transmitter_data['downlink_drift'] = transmitter['downlink']['drift']
transmitters_data.append(transmitter_data)
serializer = serializers.TransmitterEntrySerializer(
data=transmitters_data, many=True, allow_empty=True
)
if serializer.is_valid():
serializer.save()
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_201_CREATED)
class LatestTleSetViewSet(viewsets.ReadOnlyModelViewSet): # pylint: disable=R0901
"""

View File

@ -131,6 +131,20 @@ class TransmitterStructuredData(StructuredData):
"citation": "schema:citation",
"service": "service"
}
self.frame = {
"@context": self.context,
'transmitter': {
"@requireAll": True,
"description": {},
"status": {},
"citation": {},
"service": {},
"satellite": {
"@requireAll": True,
"norad_cat_id": {}
},
}
}
structured_data = []
transmitter_id_domain = Site.objects.get_current().domain + '/transmitter/'
satellite_id_domain = Site.objects.get_current().domain + '/satellite/'