Add JSON-LD post API endpoint for Transmitter suggestions
Signed-off-by: Alfredos-Panagiotis Damkalis <fredy@fredy.gr>spacecruft
parent
4141e275cc
commit
804b69e442
|
@ -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
|
|
@ -8,7 +8,7 @@ from drf_spectacular.utils import OpenApiExample, extend_schema_field, extend_sc
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from db.base.models import TRANSMITTER_STATUS, Artifact, DemodData, LatestTleSet, Mode, \
|
from db.base.models import TRANSMITTER_STATUS, Artifact, DemodData, LatestTleSet, Mode, \
|
||||||
Satellite, Telemetry, Transmitter
|
Satellite, Telemetry, Transmitter, TransmitterEntry
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_serializer(
|
@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(
|
@extend_schema_serializer(
|
||||||
examples=[
|
examples=[
|
||||||
OpenApiExample(
|
OpenApiExample(
|
||||||
|
|
172
db/api/views.py
172
db/api/views.py
|
@ -15,6 +15,7 @@ from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
from db.api import filters, pagination, serializers
|
from db.api import filters, pagination, serializers
|
||||||
|
from db.api.parsers import JSONLDParser
|
||||||
from db.api.perms import SafeMethodsWithPermission
|
from db.api.perms import SafeMethodsWithPermission
|
||||||
from db.api.renderers import BrowserableJSONLDRenderer, JSONLDRenderer
|
from db.api.renderers import BrowserableJSONLDRenderer, JSONLDRenderer
|
||||||
from db.base.helpers import gridsquare
|
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
|
Transmitters are inclusive of Transceivers and Transponders
|
||||||
"""
|
"""
|
||||||
renderer_classes = [
|
renderer_classes = [
|
||||||
JSONRenderer, BrowsableAPIRenderer, JSONLDRenderer, BrowserableJSONLDRenderer
|
JSONRenderer, BrowsableAPIRenderer, JSONLDRenderer, BrowserableJSONLDRenderer
|
||||||
]
|
]
|
||||||
|
parser_classes = [JSONLDParser]
|
||||||
queryset = Transmitter.objects.filter(
|
queryset = Transmitter.objects.filter(
|
||||||
satellite__associated_satellite__isnull=True, satellite__satellite_entry__approved=True
|
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
|
filterset_class = filters.TransmitterViewFilter
|
||||||
lookup_field = 'uuid'
|
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
|
class LatestTleSetViewSet(viewsets.ReadOnlyModelViewSet): # pylint: disable=R0901
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -131,6 +131,20 @@ class TransmitterStructuredData(StructuredData):
|
||||||
"citation": "schema:citation",
|
"citation": "schema:citation",
|
||||||
"service": "service"
|
"service": "service"
|
||||||
}
|
}
|
||||||
|
self.frame = {
|
||||||
|
"@context": self.context,
|
||||||
|
'transmitter': {
|
||||||
|
"@requireAll": True,
|
||||||
|
"description": {},
|
||||||
|
"status": {},
|
||||||
|
"citation": {},
|
||||||
|
"service": {},
|
||||||
|
"satellite": {
|
||||||
|
"@requireAll": True,
|
||||||
|
"norad_cat_id": {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
structured_data = []
|
structured_data = []
|
||||||
transmitter_id_domain = Site.objects.get_current().domain + '/transmitter/'
|
transmitter_id_domain = Site.objects.get_current().domain + '/transmitter/'
|
||||||
satellite_id_domain = Site.objects.get_current().domain + '/satellite/'
|
satellite_id_domain = Site.objects.get_current().domain + '/satellite/'
|
||||||
|
|
Loading…
Reference in New Issue