Add artifact model and API endpoint for new waterfall artifacts
Signed-off-by: Pierros Papadeas <pierros@papadeas.gr>spacecruft
parent
4891efd75e
commit
d8aef58025
|
@ -6,7 +6,7 @@ import django_filters
|
|||
from django_filters import rest_framework as filters
|
||||
from django_filters.rest_framework import FilterSet
|
||||
|
||||
from db.base.models import DemodData, Mode, Satellite, Transmitter
|
||||
from db.base.models import Artifact, DemodData, Mode, Satellite, Transmitter
|
||||
|
||||
|
||||
class TransmitterViewFilter(FilterSet):
|
||||
|
@ -57,3 +57,12 @@ class TelemetryViewFilter(FilterSet):
|
|||
class Meta:
|
||||
model = DemodData
|
||||
fields = ['satellite', 'app_source', 'observer', 'transmitter']
|
||||
|
||||
|
||||
class ArtifactViewFilter(FilterSet):
|
||||
"""SatNOGS DB Artifact API View Filter"""
|
||||
class Meta:
|
||||
model = Artifact
|
||||
fields = [
|
||||
'network_obs_id',
|
||||
]
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
import h5py
|
||||
from rest_framework import serializers
|
||||
|
||||
from db.base.models import TRANSMITTER_STATUS, DemodData, Mode, Satellite, \
|
||||
Telemetry, Transmitter
|
||||
from db.base.models import TRANSMITTER_STATUS, Artifact, DemodData, Mode, \
|
||||
Satellite, Telemetry, Transmitter
|
||||
|
||||
|
||||
class ModeSerializer(serializers.ModelSerializer):
|
||||
|
@ -125,3 +126,33 @@ class SidsSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = DemodData
|
||||
fields = ('satellite', 'payload_frame', 'station', 'lat', 'lng', 'timestamp', 'app_source')
|
||||
|
||||
|
||||
class ArtifactSerializer(serializers.ModelSerializer):
|
||||
"""SatNOGS DB Artifacts API Serializer"""
|
||||
class Meta:
|
||||
model = Artifact
|
||||
fields = ('id', 'network_obs_id', 'artifact_file')
|
||||
|
||||
|
||||
class NewArtifactSerializer(serializers.ModelSerializer):
|
||||
"""SatNOGS Network New Artifact API Serializer"""
|
||||
def validate(self, attrs):
|
||||
"""Validates data of incoming artifact"""
|
||||
|
||||
try:
|
||||
with h5py.File(self.initial_data['artifact_file'], 'r') as h5_file:
|
||||
if 'artifact_version' not in h5_file.attrs:
|
||||
raise serializers.ValidationError(
|
||||
'Not a valid SatNOGS Artifact.', code='invalid'
|
||||
)
|
||||
except OSError as error:
|
||||
raise serializers.ValidationError(
|
||||
'Not a valid HDF5 file: {}'.format(error), code='invalid'
|
||||
)
|
||||
|
||||
return attrs
|
||||
|
||||
class Meta:
|
||||
model = Artifact
|
||||
fields = ('artifact_file', )
|
||||
|
|
|
@ -8,6 +8,7 @@ from db.api import views
|
|||
|
||||
ROUTER = routers.DefaultRouter()
|
||||
|
||||
ROUTER.register(r'artifacts', views.ArtifactView)
|
||||
ROUTER.register(r'modes', views.ModeView)
|
||||
ROUTER.register(r'satellites', views.SatelliteView)
|
||||
ROUTER.register(r'transmitters', views.TransmitterView)
|
||||
|
|
|
@ -4,12 +4,15 @@ from __future__ import absolute_import, division, print_function, \
|
|||
|
||||
from django.core.files.base import ContentFile
|
||||
from rest_framework import mixins, status, viewsets
|
||||
from rest_framework.parsers import FileUploadParser, FormParser
|
||||
from rest_framework.parsers import FileUploadParser, FormParser, \
|
||||
MultiPartParser
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ValidationError
|
||||
|
||||
from db.api import filters, pagination, serializers
|
||||
from db.api.perms import SafeMethodsWithPermission
|
||||
from db.base.models import DemodData, Mode, Satellite, Transmitter
|
||||
from db.base.models import Artifact, DemodData, Mode, Satellite, Transmitter
|
||||
from db.base.tasks import update_satellite
|
||||
|
||||
|
||||
|
@ -89,3 +92,36 @@ class TelemetryView( # pylint: disable=R0901
|
|||
self.perform_create(serializer)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(status=status.HTTP_201_CREATED, headers=headers)
|
||||
|
||||
|
||||
class ArtifactView( # pylint: disable=R0901
|
||||
mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin,
|
||||
viewsets.GenericViewSet):
|
||||
"""SatNOGS DB Artifact API view class"""
|
||||
queryset = Artifact.objects.all()
|
||||
filter_class = filters.ArtifactViewFilter
|
||||
permission_classes = [IsAuthenticated]
|
||||
parser_classes = (FormParser, MultiPartParser)
|
||||
pagination_class = pagination.LinkedHeaderPageNumberPagination
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""Returns the right serializer depending on http method that is used"""
|
||||
if self.action == 'create':
|
||||
return serializers.NewArtifactSerializer
|
||||
return serializers.ArtifactSerializer
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""Creates artifact"""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
try:
|
||||
if serializer.is_valid():
|
||||
data = serializer.save()
|
||||
http_response = {}
|
||||
http_response['id'] = data.id
|
||||
response = Response(http_response, status=status.HTTP_200_OK)
|
||||
else:
|
||||
data = serializer.errors
|
||||
response = Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
except (ValidationError, ValueError, OSError) as error:
|
||||
response = Response(str(error), status=status.HTTP_400_BAD_REQUEST)
|
||||
return response
|
||||
|
|
|
@ -11,8 +11,9 @@ from django.http import HttpResponseRedirect
|
|||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
|
||||
from db.base.models import DemodData, ExportedFrameset, Mode, Satellite, \
|
||||
Telemetry, Transmitter, TransmitterEntry, TransmitterSuggestion
|
||||
from db.base.models import Artifact, DemodData, ExportedFrameset, Mode, \
|
||||
Satellite, Telemetry, Transmitter, TransmitterEntry, \
|
||||
TransmitterSuggestion
|
||||
from db.base.tasks import check_celery, decode_all_data
|
||||
|
||||
|
||||
|
@ -243,3 +244,9 @@ class ExportedFramesetAdmin(admin.ModelAdmin):
|
|||
list_display = ('id', 'created', 'user', 'satellite', 'exported_file', 'start', 'end')
|
||||
search_fields = ('user', 'satellite__norad_cat_id')
|
||||
list_filter = ('satellite', 'user')
|
||||
|
||||
|
||||
@admin.register(Artifact)
|
||||
class ArtifactAdmin(admin.ModelAdmin):
|
||||
"""Defines Artifact view in django admin UI"""
|
||||
list_display = ('id', 'network_obs_id', 'artifact_file')
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 2.2.12 on 2020-05-20 16:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0017_exported_frameset'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Artifact',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('artifact_file', models.FileField(blank=True, null=True, upload_to='artifacts/')),
|
||||
('network_obs_id', models.BigIntegerField(blank=True, null=True)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -6,6 +6,7 @@ import logging
|
|||
from os import path
|
||||
from uuid import uuid4
|
||||
|
||||
import h5py
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
|
@ -67,6 +68,18 @@ def _set_is_decoded(sender, instance, **kwargs): # pylint: disable=W0613
|
|||
instance.is_decoded = instance.payload_decoded != ''
|
||||
|
||||
|
||||
def _extract_network_obs_id(sender, instance, created, **kwargs): # pylint: disable=W0613
|
||||
post_save.disconnect(_extract_network_obs_id, sender=Artifact)
|
||||
try:
|
||||
with h5py.File(instance.artifact_file, 'r') as h5_file:
|
||||
instance.network_obs_id = h5_file.attrs["observation_id"]
|
||||
except OSError as error:
|
||||
LOGGER.warning(error)
|
||||
|
||||
instance.save()
|
||||
post_save.connect(_extract_network_obs_id, sender=Artifact)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Mode(models.Model):
|
||||
"""A satellite transmitter RF mode. For example: FM"""
|
||||
|
@ -377,3 +390,18 @@ class ExportedFrameset(models.Model):
|
|||
exported_file = models.FileField(upload_to=_name_exported_frames, blank=True, null=True)
|
||||
start = models.DateTimeField(blank=True, null=True)
|
||||
end = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Artifact(models.Model):
|
||||
"""Model for observation artifacts."""
|
||||
|
||||
artifact_file = models.FileField(upload_to='artifacts/', blank=True, null=True)
|
||||
|
||||
network_obs_id = models.BigIntegerField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return 'artifact-{0}'.format(self.id)
|
||||
|
||||
|
||||
post_save.connect(_extract_network_obs_id, sender=Artifact)
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
apipkg==1.5
|
||||
appdirs==1.4.3
|
||||
attrs==19.3.0
|
||||
click==7.1.1
|
||||
coverage==5.0.4
|
||||
click==7.1.2
|
||||
coverage==5.1
|
||||
distlib==0.3.0
|
||||
docopt==0.6.1
|
||||
docopts==0.6.1
|
||||
|
@ -22,7 +22,7 @@ packaging==20.3
|
|||
pluggy==0.13.1
|
||||
pur==5.2.2
|
||||
py==1.8.1
|
||||
pyparsing==2.4.6
|
||||
pyparsing==2.4.7
|
||||
pytest==5.4.1
|
||||
pytest-cov==2.7.1
|
||||
pytest-django==3.5.1
|
||||
|
@ -30,6 +30,6 @@ pytest-forked==1.0.2
|
|||
pytest-xdist==1.29.0
|
||||
text-unidecode==1.3
|
||||
toml==0.10.0
|
||||
tox==3.14.5
|
||||
virtualenv==20.0.13
|
||||
tox==3.14.6
|
||||
virtualenv==20.0.20
|
||||
wcwidth==0.1.9
|
||||
|
|
|
@ -6,15 +6,15 @@
|
|||
amqp==2.5.2
|
||||
billiard==3.6.3.0
|
||||
celery==4.3.0
|
||||
certifi==2019.11.28
|
||||
certifi==2020.4.5.1
|
||||
cffi==1.14.0
|
||||
chardet==3.0.4
|
||||
cryptography==2.8
|
||||
cryptography==2.9.2
|
||||
defusedxml==0.6.0
|
||||
dj-database-url==0.5.0
|
||||
Django==2.2.11
|
||||
Django==2.2.12
|
||||
django-allauth==0.40.0
|
||||
django-appconf==1.0.3
|
||||
django-appconf==1.0.4
|
||||
django-avatar==4.1.0
|
||||
django-compressor==2.3
|
||||
django-crispy-forms==1.7.2
|
||||
|
@ -27,11 +27,11 @@ django-shortuuidfield==0.1.3
|
|||
djangorestframework==3.10.3
|
||||
dnspython==1.16.0
|
||||
enum34==1.1.10
|
||||
eventlet==0.25.1
|
||||
eventlet==0.25.2
|
||||
greenlet==0.4.15
|
||||
gunicorn==19.9.0
|
||||
h5py==2.10.0
|
||||
idna==2.9
|
||||
importlib-metadata==1.5.0
|
||||
influxdb==5.2.3
|
||||
kaitaistruct==0.8
|
||||
kombu==4.6.8
|
||||
|
@ -39,19 +39,20 @@ Logbook==1.5.3
|
|||
Markdown==3.1.1
|
||||
monotonic==1.5
|
||||
mysqlclient==1.4.6
|
||||
numpy==1.18.4
|
||||
oauthlib==3.1.0
|
||||
Pillow==7.0.0
|
||||
Pillow==7.1.2
|
||||
pycparser==2.20
|
||||
PyJWT==1.7.1
|
||||
python-dateutil==2.8.1
|
||||
python-decouple==3.1
|
||||
python-dotenv==0.10.5
|
||||
python3-openid==3.1.0
|
||||
pytz==2019.3
|
||||
pytz==2020.1
|
||||
PyYAML==5.1.2
|
||||
ratelimiter==1.2.0.post0
|
||||
rcssmin==1.0.6
|
||||
redis==3.4.1
|
||||
redis==3.5.0
|
||||
Represent==1.6.0
|
||||
requests==2.23.0
|
||||
requests-oauthlib==1.3.0
|
||||
|
@ -64,12 +65,10 @@ shortuuid==1.0.1
|
|||
simplejson==3.16.0
|
||||
six==1.14.0
|
||||
social-auth-app-django==3.1.0
|
||||
social-auth-core==3.3.0
|
||||
social-auth-core==3.3.3
|
||||
spacetrack==0.13.6
|
||||
sqlparse==0.3.1
|
||||
Unidecode==1.1.1
|
||||
Unipath==1.1
|
||||
uritemplate==3.0.1
|
||||
urllib3==1.24.3
|
||||
vine==1.3.0
|
||||
zipp==3.1.0
|
||||
|
|
Loading…
Reference in New Issue