pylint C0111 docstrings
phew, fixed all of the missing-docstring and found some other things along the way (for future changes)merge-requests/398/head
parent
dcccb1770e
commit
2ec2fd2a4d
|
@ -4,7 +4,6 @@ load-plugins=pylint_django
|
|||
|
||||
[MESSAGES CONTROL]
|
||||
disable=
|
||||
C0111,
|
||||
C0411,
|
||||
C0412,
|
||||
E1101,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""SatNOGS DB Auth0 login module admin class"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# from django.contrib import admin
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""SatNOGS DB Auth0 login app config"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class Auth0LoginConfig(AppConfig):
|
||||
"""Set the name of the django app for auth0login"""
|
||||
name = 'auth0login'
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB Auth0 login module auth backend"""
|
||||
import requests
|
||||
from social_core.backends.oauth import BaseOAuth2
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""SatNOGS DB Auth0 login module models"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# from django.db import models
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""SatNOGS DB Auth0 login module test suites"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# from django.test import TestCase
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB Auth0 login module URL routers"""
|
||||
from django.conf.urls import include, url
|
||||
|
||||
from . import views
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
"""SatNOGS DB Auth0 login module views"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.shortcuts import render
|
||||
|
||||
|
||||
def index(request):
|
||||
"""Returns the index view"""
|
||||
return render(request, 'index.html')
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""The core django app for SatNOGS DB"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
from ._version import get_versions
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB django rest framework Filters class"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -9,9 +10,11 @@ from db.base.models import DemodData, Satellite, Transmitter
|
|||
|
||||
|
||||
class TransmitterViewFilter(FilterSet):
|
||||
"""SatNOGS DB Transmitter API View Filter"""
|
||||
alive = filters.BooleanFilter(field_name='status', label='Alive', method='filter_status')
|
||||
|
||||
def filter_status(self, queryset, name, value):
|
||||
"""Returns Transmitters that are either functional or non-functional"""
|
||||
if value:
|
||||
return queryset.filter(status='functional')
|
||||
else:
|
||||
|
@ -23,7 +26,10 @@ class TransmitterViewFilter(FilterSet):
|
|||
|
||||
|
||||
class SatelliteViewFilter(FilterSet):
|
||||
''' filter on decayed field '''
|
||||
"""SatNOGS DB Satellite API View Filter
|
||||
|
||||
filter on decayed field
|
||||
"""
|
||||
in_orbit = filters.BooleanFilter(field_name='decayed', label='In orbit', lookup_expr='isnull')
|
||||
|
||||
class Meta:
|
||||
|
@ -32,6 +38,7 @@ class SatelliteViewFilter(FilterSet):
|
|||
|
||||
|
||||
class TelemetryViewFilter(FilterSet):
|
||||
"""SatNOGS DB Telemetry API View Filter"""
|
||||
satellite = django_filters.NumberFilter(
|
||||
field_name='satellite__norad_cat_id', lookup_expr='exact'
|
||||
)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB API serializers, django rest framework"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -8,18 +9,21 @@ from db.base.models import TRANSMITTER_STATUS, DemodData, Mode, Satellite, \
|
|||
|
||||
|
||||
class ModeSerializer(serializers.ModelSerializer):
|
||||
"""SatNOGS DB Mode API Serializer"""
|
||||
class Meta:
|
||||
model = Mode
|
||||
fields = ('id', 'name')
|
||||
|
||||
|
||||
class SatelliteSerializer(serializers.ModelSerializer):
|
||||
"""SatNOGS DB Satellite API Serializer"""
|
||||
class Meta:
|
||||
model = Satellite
|
||||
fields = ('norad_cat_id', 'name', 'names', 'image', 'status', 'decayed')
|
||||
|
||||
|
||||
class TransmitterSerializer(serializers.ModelSerializer):
|
||||
"""SatNOGS DB Transmitter API Serializer"""
|
||||
norad_cat_id = serializers.SerializerMethodField()
|
||||
mode_id = serializers.SerializerMethodField()
|
||||
mode = serializers.SerializerMethodField()
|
||||
|
@ -36,25 +40,30 @@ class TransmitterSerializer(serializers.ModelSerializer):
|
|||
|
||||
# Keeping alive field for compatibility issues
|
||||
def get_alive(self, obj):
|
||||
"""Returns transmitter status"""
|
||||
return obj.status == TRANSMITTER_STATUS[0]
|
||||
|
||||
def get_mode_id(self, obj):
|
||||
"""Returns mode ID"""
|
||||
try:
|
||||
return obj.mode.id
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_mode(self, obj):
|
||||
"""Returns mode name"""
|
||||
try:
|
||||
return obj.mode.name
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_norad_cat_id(self, obj):
|
||||
"""Returns Satellite NORAD ID"""
|
||||
return obj.satellite.norad_cat_id
|
||||
|
||||
|
||||
class TelemetrySerializer(serializers.ModelSerializer):
|
||||
"""SatNOGS DB Telemetry API Serializer"""
|
||||
norad_cat_id = serializers.SerializerMethodField()
|
||||
transmitter = serializers.SerializerMethodField()
|
||||
schema = serializers.SerializerMethodField()
|
||||
|
@ -69,28 +78,36 @@ class TelemetrySerializer(serializers.ModelSerializer):
|
|||
)
|
||||
|
||||
def get_norad_cat_id(self, obj):
|
||||
"""Returns Satellite NORAD ID for this Transmitter"""
|
||||
return obj.satellite.norad_cat_id
|
||||
|
||||
def get_transmitter(self, obj):
|
||||
"""Returns Transmitter UUID"""
|
||||
try:
|
||||
return obj.transmitter.uuid
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
# TODO: this is a relic of the old data decoding method, needs revisiting
|
||||
def get_schema(self, obj):
|
||||
"""Returns Transmitter telemetry schema"""
|
||||
try:
|
||||
return obj.payload_telemetry.schema
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
# TODO: this is a relic of the old data decoding method, needs revisiting
|
||||
def get_decoded(self, obj):
|
||||
"""Returns the payload_decoded field"""
|
||||
return obj.payload_decoded
|
||||
|
||||
def get_frame(self, obj):
|
||||
"""Returns the payload frame"""
|
||||
return obj.display_frame()
|
||||
|
||||
|
||||
class SidsSerializer(serializers.ModelSerializer):
|
||||
"""SatNOGS DB SiDS API Serializer"""
|
||||
class Meta:
|
||||
model = DemodData
|
||||
fields = ('satellite', 'payload_frame', 'station', 'lat', 'lng', 'timestamp', 'app_source')
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB API test suites"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -21,10 +22,12 @@ class ModeViewApiTest(TestCase):
|
|||
self.mode.save()
|
||||
|
||||
def test_list(self):
|
||||
"""Test the API modes list"""
|
||||
response = self.client.get('/api/modes/', format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
def test_retrieve(self):
|
||||
"""Test the API mode retrieval"""
|
||||
response = self.client.get('/api/modes/{0}/'.format(self.mode.id), format='json')
|
||||
self.assertContains(response, self.mode.name)
|
||||
|
||||
|
@ -41,10 +44,12 @@ class SatelliteViewApiTest(TestCase):
|
|||
self.satellite.save()
|
||||
|
||||
def test_list(self):
|
||||
"""Test the Satellite API listing"""
|
||||
response = self.client.get('/api/satellites/', format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
def test_retrieve(self):
|
||||
"""Test the Satellite API retrieval"""
|
||||
response = self.client.get(
|
||||
'/api/satellites/{0}/'.format(self.satellite.norad_cat_id), format='json'
|
||||
)
|
||||
|
@ -64,10 +69,12 @@ class TransmitterViewApiTest(TestCase):
|
|||
self.transmitter.save()
|
||||
|
||||
def test_list(self):
|
||||
"""Test the Transmitter API listing"""
|
||||
response = self.client.get('/api/transmitters/', format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
def test_retrieve(self):
|
||||
"""Test the Transmitter API retrieval"""
|
||||
response = self.client.get(
|
||||
'/api/transmitters/{0}/'.format(self.transmitter.uuid), format='json'
|
||||
)
|
||||
|
@ -86,9 +93,11 @@ class TelemetryViewApiTest(TestCase):
|
|||
self.datum.save()
|
||||
|
||||
def test_list(self):
|
||||
"""Test the Telemetry API listing"""
|
||||
response = self.client.get('/api/telemetry/', format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
def test_retrieve(self):
|
||||
"""Test the Telemetry API retrieval"""
|
||||
response = self.client.get('/api/telemetry/{0}/'.format(self.datum.id), format='json')
|
||||
self.assertContains(response, self.datum.observer)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB django rest framework API url routings"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB API django rest framework Views"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -13,11 +14,13 @@ from db.base.tasks import update_satellite
|
|||
|
||||
|
||||
class ModeView(viewsets.ReadOnlyModelViewSet):
|
||||
"""SatNOGS DB Mode API view class"""
|
||||
queryset = Mode.objects.all()
|
||||
serializer_class = serializers.ModeSerializer
|
||||
|
||||
|
||||
class SatelliteView(viewsets.ReadOnlyModelViewSet):
|
||||
"""SatNOGS DB Satellite API view class"""
|
||||
queryset = Satellite.objects.all()
|
||||
serializer_class = serializers.SatelliteSerializer
|
||||
filter_class = filters.SatelliteViewFilter
|
||||
|
@ -25,6 +28,7 @@ class SatelliteView(viewsets.ReadOnlyModelViewSet):
|
|||
|
||||
|
||||
class TransmitterView(viewsets.ReadOnlyModelViewSet):
|
||||
"""SatNOGS DB Transmitter API view class"""
|
||||
queryset = Transmitter.objects.all()
|
||||
serializer_class = serializers.TransmitterSerializer
|
||||
filter_class = filters.TransmitterViewFilter
|
||||
|
@ -33,6 +37,7 @@ class TransmitterView(viewsets.ReadOnlyModelViewSet):
|
|||
|
||||
class TelemetryView(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin,
|
||||
viewsets.GenericViewSet):
|
||||
"""SatNOGS DB Telemetry API view class"""
|
||||
queryset = DemodData.objects.all()
|
||||
serializer_class = serializers.TelemetrySerializer
|
||||
filter_class = filters.TelemetryViewFilter
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Defines functions and settings for the django admin interface"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -17,16 +18,25 @@ from db.base.tasks import check_celery, decode_all_data
|
|||
|
||||
@admin.register(Mode)
|
||||
class ModeAdmin(admin.ModelAdmin):
|
||||
"""Defines Mode view in django admin UI"""
|
||||
list_display = ('name', )
|
||||
|
||||
|
||||
@admin.register(Satellite)
|
||||
class SatelliteAdmin(admin.ModelAdmin):
|
||||
"""Defines Satellite view in django admin UI"""
|
||||
list_display = ('name', 'norad_cat_id', 'status', 'decayed')
|
||||
search_fields = ('name', 'norad_cat_id')
|
||||
list_filter = ('status', 'decayed')
|
||||
|
||||
def get_urls(self):
|
||||
"""Returns django urls for the Satellite view
|
||||
|
||||
check_celery -- url for the check_celery function
|
||||
decode_all_data -- url for the decode_all_data function
|
||||
|
||||
:returns: Django urls for the Satellite admin view
|
||||
"""
|
||||
urls = super(SatelliteAdmin, self).get_urls()
|
||||
my_urls = [
|
||||
url(r'^check_celery/$', self.check_celery, name='check_celery'),
|
||||
|
@ -39,6 +49,14 @@ class SatelliteAdmin(admin.ModelAdmin):
|
|||
return my_urls + urls
|
||||
|
||||
def check_celery(self, request):
|
||||
"""Returns status of Celery workers
|
||||
|
||||
Check the delay for celery workers, return an error if a connection
|
||||
can not be made or if the delay is too long. Otherwise return that
|
||||
Celery is OK.
|
||||
|
||||
:returns: admin home page redirect with popup message
|
||||
"""
|
||||
try:
|
||||
investigator = check_celery.delay()
|
||||
except socket_error as error:
|
||||
|
@ -54,9 +72,15 @@ class SatelliteAdmin(admin.ModelAdmin):
|
|||
finally:
|
||||
return HttpResponseRedirect(reverse('admin:index'))
|
||||
|
||||
# force a decode of all data for a norad ID. This could be very resource
|
||||
# intensive but necessary when catching a satellite up with a new decoder
|
||||
def decode_all_data(self, request, norad):
|
||||
"""Returns the admin home page, while triggering a Celery decode task
|
||||
|
||||
Forces a decode of all data for a norad ID. This could be very resource
|
||||
intensive but necessary when catching a satellite up with a new decoder
|
||||
|
||||
:param norad: the NORAD ID for the satellite to decode
|
||||
:returns: Admin home page
|
||||
"""
|
||||
decode_all_data.delay(norad)
|
||||
messages.success(request, 'Decode task was triggered successfully!')
|
||||
return redirect(reverse('admin:index'))
|
||||
|
@ -64,6 +88,7 @@ class SatelliteAdmin(admin.ModelAdmin):
|
|||
|
||||
@admin.register(TransmitterEntry)
|
||||
class TransmitterEntryAdmin(admin.ModelAdmin):
|
||||
"""Defines TransmitterEntry view in django admin UI"""
|
||||
list_display = (
|
||||
'uuid', 'description', 'satellite', 'service', 'type', 'mode', 'baud', 'downlink_low',
|
||||
'downlink_high', 'downlink_drift', 'uplink_low', 'uplink_high', 'uplink_drift', 'reviewed',
|
||||
|
@ -84,6 +109,7 @@ class TransmitterEntryAdmin(admin.ModelAdmin):
|
|||
|
||||
@admin.register(TransmitterSuggestion)
|
||||
class TransmitterSuggestionAdmin(admin.ModelAdmin):
|
||||
"""Defines TransmitterSuggestion view in django admin UI"""
|
||||
list_display = (
|
||||
'uuid', 'description', 'satellite', 'service', 'type', 'mode', 'baud', 'downlink_low',
|
||||
'downlink_high', 'downlink_drift', 'uplink_low', 'uplink_high', 'uplink_drift', 'status',
|
||||
|
@ -104,6 +130,12 @@ class TransmitterSuggestionAdmin(admin.ModelAdmin):
|
|||
actions = ['approve_suggestion', 'reject_suggestion']
|
||||
|
||||
def get_actions(self, request):
|
||||
"""Returns the actions a user can take on a TransmitterSuggestion
|
||||
|
||||
For example, delete, approve, or reject
|
||||
|
||||
:returns: list of actions the user can take on TransmitterSuggestion
|
||||
"""
|
||||
actions = super(TransmitterSuggestionAdmin, self).get_actions(request)
|
||||
if not request.user.has_perm('base.delete_transmittersuggestion'):
|
||||
if 'delete_selected' in actions:
|
||||
|
@ -111,6 +143,11 @@ class TransmitterSuggestionAdmin(admin.ModelAdmin):
|
|||
return actions
|
||||
|
||||
def approve_suggestion(self, request, queryset):
|
||||
"""Returns the TransmitterSuggestion page after approving suggestions
|
||||
|
||||
:param queryset: the TransmitterSuggestion entries to be approved
|
||||
:returns: TransmitterSuggestion admin page
|
||||
"""
|
||||
queryset_size = len(queryset)
|
||||
for entry in queryset:
|
||||
entry.approved = True
|
||||
|
@ -129,6 +166,11 @@ class TransmitterSuggestionAdmin(admin.ModelAdmin):
|
|||
approve_suggestion.short_description = 'Approve selected transmitter suggestions'
|
||||
|
||||
def reject_suggestion(self, request, queryset):
|
||||
"""Returns the TransmitterSuggestion page after rejecting suggestions
|
||||
|
||||
:param queryset: the TransmitterSuggestion entries to be rejected
|
||||
:returns: TransmitterSuggestion admin page
|
||||
"""
|
||||
queryset_size = len(queryset)
|
||||
for entry in queryset:
|
||||
entry.created = datetime.utcnow()
|
||||
|
@ -149,6 +191,7 @@ class TransmitterSuggestionAdmin(admin.ModelAdmin):
|
|||
|
||||
@admin.register(Transmitter)
|
||||
class TransmitterAdmin(admin.ModelAdmin):
|
||||
"""Defines Transmitter view in django admin UI"""
|
||||
list_display = (
|
||||
'uuid', 'description', 'satellite', 'service', 'type', 'mode', 'baud', 'downlink_low',
|
||||
'downlink_high', 'downlink_drift', 'uplink_low', 'uplink_high', 'uplink_drift', 'status',
|
||||
|
@ -167,13 +210,20 @@ class TransmitterAdmin(admin.ModelAdmin):
|
|||
|
||||
@admin.register(Telemetry)
|
||||
class TelemetryAdmin(admin.ModelAdmin):
|
||||
"""Defines Telemetry view in django admin UI"""
|
||||
list_display = ('name', 'decoder')
|
||||
|
||||
|
||||
@admin.register(DemodData)
|
||||
class DemodDataAdmin(admin.ModelAdmin):
|
||||
"""Defines DemodData view in django admin UI"""
|
||||
list_display = ('id', 'satellite', 'app_source', 'observer')
|
||||
search_fields = ('transmitter__uuid', 'satellite__norad_cat_id', 'observer')
|
||||
|
||||
def satellite(self, obj):
|
||||
"""Returns the Satellite object associated with this DemodData
|
||||
|
||||
:param obj: DemodData object
|
||||
:returns: Satellite object
|
||||
"""
|
||||
return obj.satellite
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB django context processors"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB django base Forms class"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -9,7 +10,9 @@ from db.base.models import Transmitter, TransmitterEntry
|
|||
|
||||
|
||||
class TransmitterEntryForm(forms.ModelForm):
|
||||
"""Model Form class for TransmitterEntry objects"""
|
||||
def existing_uuid(value):
|
||||
"""ensures the UUID is existing and valid"""
|
||||
try:
|
||||
Transmitter.objects.get(uuid=value)
|
||||
except Transmitter.DoesNotExist:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Helper functions for SatNOGS DB"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -9,6 +10,13 @@ LOWER = 'abcdefghijklmnopqrstuvwx'
|
|||
|
||||
|
||||
def gridsquare(lat, lng):
|
||||
"""Calculates a maidenhead grid square from a lat/long
|
||||
|
||||
Used when we get a SiDS submission, we want to store and display the
|
||||
location of the submitter as a grid square.
|
||||
|
||||
:returns: a string of the grid square, ie: EM69uf
|
||||
"""
|
||||
if not -180 <= lng < 180:
|
||||
return False
|
||||
if not -90 <= lat < 90:
|
||||
|
@ -38,6 +46,11 @@ def gridsquare(lat, lng):
|
|||
|
||||
|
||||
def get_apikey(user):
|
||||
"""If necessary, create, then return an API key for a user
|
||||
|
||||
:param user: a SatNOGS DB User object
|
||||
:returns: user API token
|
||||
"""
|
||||
try:
|
||||
token = Token.objects.get(user=user)
|
||||
except Exception:
|
||||
|
@ -45,7 +58,9 @@ def get_apikey(user):
|
|||
return token
|
||||
|
||||
|
||||
# TODO: remove
|
||||
def cache_get_key(*args, **kwargs):
|
||||
"""Unused, needs removing"""
|
||||
import hashlib
|
||||
serialise = []
|
||||
for arg in args:
|
||||
|
@ -57,9 +72,13 @@ def cache_get_key(*args, **kwargs):
|
|||
return key
|
||||
|
||||
|
||||
# TODO: remove
|
||||
def cache_for(time):
|
||||
"""Unused, needs removing"""
|
||||
def decorator(func):
|
||||
"""Unused, needs removing"""
|
||||
def wrapper(*args, **kwargs):
|
||||
"""Unused, needs removing"""
|
||||
key = cache_get_key(func.__name__, *args, **kwargs)
|
||||
result = cache.get(key)
|
||||
if not result:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB django management command to delete satellites"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -7,6 +8,7 @@ from db.base.models import Satellite
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""django management command to delete satellites"""
|
||||
help = 'Delete selected Satellites'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB django management command to fetch data (TLEs, etc)"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -13,6 +14,7 @@ from db.base.models import DemodData, Satellite, TransmitterEntry
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""django management command to fetch data (TLEs, etc)"""
|
||||
help = 'Fetch Satellite data from Network'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB django management command to fetch satellites"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -7,6 +8,7 @@ from db.base.tasks import update_satellite
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""django management command to fetch satellites"""
|
||||
help = 'Updates/Inserts Name for certain Satellites'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB django management command to initialize a new database"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -6,6 +7,7 @@ from django.core.management.base import BaseCommand
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""django management command to initialize a new database"""
|
||||
help = 'Create initial fixtures'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB django management command to update TLE entries"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -7,6 +8,7 @@ from db.base.tasks import update_all_tle
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""django management command to update TLE entries"""
|
||||
help = 'Update TLEs for existing Satellites'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Django database model for SatNOGS DB"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -33,6 +34,11 @@ SERVICE_TYPE = [
|
|||
|
||||
|
||||
def _name_payload_frame(instance, filename):
|
||||
"""Returns a unique, timestamped path and filename for a payload
|
||||
|
||||
:param filename: the original filename submitted
|
||||
:returns: path string with timestamped subfolders and filename
|
||||
"""
|
||||
today = now()
|
||||
folder = 'payload_frames/{0}/{1}/{2}/'.format(today.year, today.month, today.day)
|
||||
ext = 'raw'
|
||||
|
@ -53,11 +59,13 @@ def _gen_observer(sender, instance, created, **kwargs):
|
|||
|
||||
|
||||
def _set_is_decoded(sender, instance, **kwargs):
|
||||
"""Returns true if payload_decoded has data"""
|
||||
instance.is_decoded = instance.payload_decoded != ''
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Mode(models.Model):
|
||||
"""A satellite transmitter RF mode. For example: FM"""
|
||||
name = models.CharField(max_length=10, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -84,9 +92,17 @@ class Satellite(models.Model):
|
|||
ordering = ['norad_cat_id']
|
||||
|
||||
def get_description(self):
|
||||
"""Returns the markdown-processed satellite description
|
||||
|
||||
:returns: the markdown-processed satellite description
|
||||
"""
|
||||
return markdown(self.description)
|
||||
|
||||
def get_image(self):
|
||||
"""Returns an image for the satellite
|
||||
|
||||
:returns: the saved image for the satellite, or a default
|
||||
"""
|
||||
if self.image and hasattr(self.image, 'url'):
|
||||
return self.image.url
|
||||
else:
|
||||
|
@ -94,20 +110,39 @@ class Satellite(models.Model):
|
|||
|
||||
@property
|
||||
def transmitters(self):
|
||||
"""Returns valid transmitters for this Satellite
|
||||
|
||||
:returns: the valid transmitters for this Satellite
|
||||
"""
|
||||
return Transmitter.objects.filter(satellite=self.id).exclude(status='invalid')
|
||||
|
||||
# TODO: rename this to sound more like a count
|
||||
@property
|
||||
def pending_transmitter_suggestions(self):
|
||||
"""Returns number of pending transmitter suggestions for this Satellite
|
||||
|
||||
:returns: number of pending transmitter suggestions for this Satellite
|
||||
"""
|
||||
pending = TransmitterSuggestion.objects.filter(satellite=self.id).count()
|
||||
return pending
|
||||
|
||||
# TODO: rename this to sound more like a count
|
||||
@property
|
||||
def has_telemetry_data(self):
|
||||
"""Returns number of DemodData for this Satellite
|
||||
|
||||
:returns: number of DemodData for this Satellite
|
||||
"""
|
||||
has_data = DemodData.objects.filter(satellite=self.id).count()
|
||||
return has_data
|
||||
|
||||
# TODO: rename this to sound more like a count
|
||||
@property
|
||||
def has_telemetry_decoders(self):
|
||||
"""Returns number of Telemetry objects for this Satellite
|
||||
|
||||
:returns: number of Telemetry objects for this Satellite
|
||||
"""
|
||||
has_decoders = Telemetry.objects.filter(satellite=self.id).exclude(decoder='').count()
|
||||
return has_decoders
|
||||
|
||||
|
@ -164,11 +199,18 @@ class TransmitterEntry(models.Model):
|
|||
|
||||
|
||||
class TransmitterSuggestionManager(models.Manager):
|
||||
"""Django Manager for TransmitterSuggestions
|
||||
|
||||
TransmitterSuggestions are TransmitterEntry objects that have been
|
||||
submitted (suggested) but not yet reviewed
|
||||
"""
|
||||
def get_queryset(self):
|
||||
"""Returns TransmitterEntries that have not been reviewed"""
|
||||
return TransmitterEntry.objects.filter(reviewed=False)
|
||||
|
||||
|
||||
class TransmitterSuggestion(TransmitterEntry):
|
||||
"""TransmitterSuggestion is an unreviewed TransmitterEntry object"""
|
||||
objects = TransmitterSuggestionManager()
|
||||
|
||||
class Meta:
|
||||
|
@ -177,7 +219,14 @@ class TransmitterSuggestion(TransmitterEntry):
|
|||
|
||||
|
||||
class TransmitterManager(models.Manager):
|
||||
"""Django Manager for Transmitter objects"""
|
||||
def get_queryset(self):
|
||||
"""Returns query of TransmitterEntries
|
||||
|
||||
:returns: the latest revision of a TransmitterEntry for each
|
||||
TransmitterEntry uuid associated with this Satellite that is
|
||||
both reviewed and approved
|
||||
"""
|
||||
subquery = TransmitterEntry.objects.filter(
|
||||
reviewed=True, approved=True
|
||||
).filter(uuid=OuterRef('uuid')).order_by('-created')
|
||||
|
@ -187,6 +236,9 @@ class TransmitterManager(models.Manager):
|
|||
|
||||
|
||||
class Transmitter(TransmitterEntry):
|
||||
"""Associates a generic Transmitter object with their TransmitterEntries
|
||||
that are managed by TransmitterManager
|
||||
"""
|
||||
objects = TransmitterManager()
|
||||
|
||||
class Meta:
|
||||
|
@ -195,7 +247,7 @@ class Transmitter(TransmitterEntry):
|
|||
|
||||
@python_2_unicode_compatible
|
||||
class Telemetry(models.Model):
|
||||
"""Model for satellite telemtry decoders."""
|
||||
"""Model for satellite telemetry decoders."""
|
||||
satellite = models.ForeignKey(
|
||||
Satellite, null=True, related_name='telemetries', on_delete=models.SET_NULL
|
||||
)
|
||||
|
@ -244,13 +296,24 @@ class DemodData(models.Model):
|
|||
def __str__(self):
|
||||
return 'data-for-{0}'.format(self.satellite.norad_cat_id)
|
||||
|
||||
# TODO: this is a relic of the first attempt at payload decoding and
|
||||
# should be refactored out or changed to actually fetch the decoded
|
||||
# frame (from influx?)
|
||||
def display_decoded(self):
|
||||
"""Returns the contents of payload_decoded
|
||||
|
||||
:returns: json-formatted contents of payload_decoded
|
||||
"""
|
||||
try:
|
||||
json.dumps(self.payload_decoded)
|
||||
except Exception:
|
||||
'{}'
|
||||
|
||||
def display_frame(self):
|
||||
"""Returns the contents of the saved frame file for this DemodData
|
||||
|
||||
:returns: the contents of the saved frame file for this DemodData
|
||||
"""
|
||||
try:
|
||||
with open(self.payload_frame.path) as frame_file:
|
||||
return frame_file.read()
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB Celery task functions"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Django template tags for SatNOGS DB"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -10,6 +11,7 @@ register = template.Library()
|
|||
|
||||
@register.simple_tag
|
||||
def active(request, urls):
|
||||
"""Returns if this is an active URL"""
|
||||
if request.path in (reverse(url) for url in urls.split()):
|
||||
return 'active'
|
||||
return None
|
||||
|
@ -17,6 +19,7 @@ def active(request, urls):
|
|||
|
||||
@register.filter
|
||||
def frq(value):
|
||||
"""Returns Hz formatted frequency html string"""
|
||||
try:
|
||||
to_format = float(value)
|
||||
except (TypeError, ValueError):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB test suites"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -18,6 +19,7 @@ DATA_SOURCE_IDS = [c[0] for c in DATA_SOURCES]
|
|||
|
||||
|
||||
def generate_payload():
|
||||
"""Create data payloads"""
|
||||
payload = '{0:b}'.format(random.randint(500000000, 510000000))
|
||||
digits = 1824
|
||||
while digits:
|
||||
|
@ -28,6 +30,7 @@ def generate_payload():
|
|||
|
||||
|
||||
def generate_payload_name():
|
||||
"""Create payload names"""
|
||||
filename = datetime.strftime(
|
||||
fuzzy.FuzzyDateTime(now() - timedelta(days=10), now()).fuzz(), '%Y%m%dT%H%M%SZ'
|
||||
)
|
||||
|
@ -35,6 +38,7 @@ def generate_payload_name():
|
|||
|
||||
|
||||
def get_valid_satellites():
|
||||
"""Returns valid satellites"""
|
||||
qs = Transmitter.objects.all()
|
||||
satellites = Satellite.objects.filter(transmitters__in=qs).distinct()
|
||||
return satellites
|
||||
|
@ -89,6 +93,7 @@ class TransmitterFactory(factory.django.DjangoModelFactory):
|
|||
|
||||
|
||||
class TransmitterSuggestionFactory(factory.django.DjangoModelFactory):
|
||||
"""TransmitterSuggestion model factory."""
|
||||
description = fuzzy.FuzzyText()
|
||||
status = fuzzy.FuzzyChoice(choices=['active', 'inactive', 'invalid'])
|
||||
type = fuzzy.FuzzyChoice(choices=['Transmitter', 'Transceiver', 'Transponder'])
|
||||
|
@ -111,6 +116,7 @@ class TransmitterSuggestionFactory(factory.django.DjangoModelFactory):
|
|||
|
||||
|
||||
class TelemetryFactory(factory.django.DjangoModelFactory):
|
||||
"""Telemetry model factory."""
|
||||
satellite = factory.SubFactory(SatelliteFactory)
|
||||
name = fuzzy.FuzzyText()
|
||||
schema = '{}'
|
||||
|
@ -121,6 +127,7 @@ class TelemetryFactory(factory.django.DjangoModelFactory):
|
|||
|
||||
|
||||
class DemodDataFactory(factory.django.DjangoModelFactory):
|
||||
"""DemodData model factory."""
|
||||
satellite = factory.SubFactory(SatelliteFactory)
|
||||
transmitter = factory.SubFactory(TransmitterFactory)
|
||||
app_source = fuzzy.FuzzyChoice(choices=DATA_SOURCE_IDS)
|
||||
|
@ -144,6 +151,7 @@ class HomeViewTest(TestCase):
|
|||
"""
|
||||
|
||||
def test_home_page(self):
|
||||
"""Tests for a known string in the SatNOGS DB home page template"""
|
||||
response = self.client.get('/')
|
||||
self.assertContains(response, 'SatNOGS DB is, and will always be, an open database.')
|
||||
|
||||
|
@ -160,6 +168,7 @@ class SatelliteViewTest(TestCase):
|
|||
self.satellite.save()
|
||||
|
||||
def test_satellite_page(self):
|
||||
"""Tests for satellite name in a SatNOGS DB satellite page"""
|
||||
response = self.client.get('/satellite/%s/' % self.satellite.norad_cat_id)
|
||||
self.assertContains(response, self.satellite.name)
|
||||
|
||||
|
@ -171,6 +180,7 @@ class AboutViewTest(TestCase):
|
|||
"""
|
||||
|
||||
def test_about_page(self):
|
||||
"""Tests for a known string in the SatNOGS DB about page template"""
|
||||
response = self.client.get('/about/')
|
||||
self.assertContains(response, 'SatNOGS DB is an effort to create an hollistic')
|
||||
|
||||
|
@ -182,5 +192,6 @@ class FaqViewTest(TestCase):
|
|||
"""
|
||||
|
||||
def test_faq_page(self):
|
||||
"""Tests for a known string in the SatNOGS DB FAQ page template"""
|
||||
response = self.client.get('/faq/')
|
||||
self.assertContains(response, 'How do I suggest a new transmitter?')
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Django base URL routings for SatNOGS DB"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Miscellaneous functions for SatNOGS DB"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -19,7 +20,10 @@ LOGGER = logging.getLogger('db')
|
|||
|
||||
|
||||
def calculate_statistics():
|
||||
"""View to create statistics endpoint."""
|
||||
"""Calculates statistics about the data we have in DB
|
||||
|
||||
:returns: a dictionary of statistics
|
||||
"""
|
||||
satellites = Satellite.objects.all()
|
||||
transmitters = Transmitter.objects.all()
|
||||
modes = Mode.objects.all()
|
||||
|
@ -140,7 +144,10 @@ def calculate_statistics():
|
|||
|
||||
|
||||
def create_point(fields, satellite, telemetry, demoddata, version):
|
||||
"""Create a decoded data point"""
|
||||
"""Create a decoded data point in JSON format that is influxdb compatible
|
||||
|
||||
:returns: a JSON formatted time series data point
|
||||
"""
|
||||
point = [
|
||||
{
|
||||
'time': demoddata.timestamp.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
||||
|
@ -174,7 +181,12 @@ def write_influx(json_obj):
|
|||
|
||||
|
||||
def decode_data(norad, period=None):
|
||||
"""Decode data for a satellite, with an option to limit the scope."""
|
||||
"""Decode data for a satellite, with an option to limit the scope.
|
||||
|
||||
:param norad: the NORAD ID of the satellite to decode data for
|
||||
:param period: if period exists, only attempt to decode the last 4 hours,
|
||||
otherwise attempt to decode everything
|
||||
"""
|
||||
sat = Satellite.objects.get(norad_cat_id=norad)
|
||||
if sat.has_telemetry_decoders:
|
||||
now = datetime.utcnow()
|
||||
|
@ -241,6 +253,10 @@ def decode_data(norad, period=None):
|
|||
|
||||
# Caches stats about satellites and data
|
||||
def cache_statistics():
|
||||
"""Populate a django cache with statistics from data in DB
|
||||
|
||||
.. seealso:: calculate_statistics
|
||||
"""
|
||||
statistics = calculate_statistics()
|
||||
cache.set('stats_transmitters', statistics, 60 * 60 * 2)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""Base django views for SatNOGS DB"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -29,7 +30,10 @@ LOGGER = logging.getLogger('db')
|
|||
|
||||
|
||||
def home(request):
|
||||
"""View to render home page."""
|
||||
"""View to render home page.
|
||||
|
||||
:returns: base/home.html
|
||||
"""
|
||||
satellites = Satellite.objects.all()
|
||||
transmitter_suggestions = TransmitterSuggestion.objects.count()
|
||||
contributors = User.objects.filter(is_active=1).count()
|
||||
|
@ -51,23 +55,36 @@ def home(request):
|
|||
|
||||
|
||||
def custom_404(request):
|
||||
"""Custom 404 error handler."""
|
||||
"""Custom 404 error handler.
|
||||
|
||||
:returns: 404.html
|
||||
"""
|
||||
return HttpResponseNotFound(render(request, '404.html'))
|
||||
|
||||
|
||||
def custom_500(request):
|
||||
"""Custom 500 error handler."""
|
||||
"""Custom 500 error handler.
|
||||
|
||||
:returns: 500.html
|
||||
"""
|
||||
return HttpResponseServerError(render(request, '500.html'))
|
||||
|
||||
|
||||
def robots(request):
|
||||
"""robots.txt handler
|
||||
|
||||
:returns: robots.txt
|
||||
"""
|
||||
data = render(request, 'robots.txt', {'environment': settings.ENVIRONMENT})
|
||||
response = HttpResponse(data, content_type='text/plain; charset=utf-8')
|
||||
return response
|
||||
|
||||
|
||||
def satellite(request, norad):
|
||||
"""View to render satellite page."""
|
||||
"""View to render satellite page.
|
||||
|
||||
:returns: base/satellite.html
|
||||
"""
|
||||
satellite = get_object_or_404(Satellite.objects, norad_cat_id=norad)
|
||||
transmitter_suggestions = TransmitterSuggestion.objects.filter(satellite=satellite)
|
||||
for transmitter_suggestion in transmitter_suggestions:
|
||||
|
@ -108,7 +125,13 @@ def satellite(request, norad):
|
|||
|
||||
@login_required
|
||||
def request_export(request, norad, period=None):
|
||||
"""View to request frames export download."""
|
||||
"""View to request frames export download.
|
||||
|
||||
This triggers a request to collect and zip up the requested data for
|
||||
download, which the user is notified of via email when the celery task is
|
||||
completed.
|
||||
:returns: the originating satellite page
|
||||
"""
|
||||
export_frames.delay(norad, request.user.email, request.user.pk, period)
|
||||
messages.success(
|
||||
request, ('Your download request was received. '
|
||||
|
@ -120,7 +143,10 @@ def request_export(request, norad, period=None):
|
|||
@login_required
|
||||
@require_POST
|
||||
def transmitter_suggestion(request):
|
||||
"""View to process transmitter suggestion form"""
|
||||
"""View to process transmitter suggestion form
|
||||
|
||||
:returns: the originating satellite page unless an error occurs
|
||||
"""
|
||||
transmitter_form = TransmitterEntryForm(request.POST)
|
||||
if transmitter_form.is_valid():
|
||||
transmitter = transmitter_form.save(commit=False)
|
||||
|
@ -175,17 +201,28 @@ def transmitter_suggestion(request):
|
|||
|
||||
|
||||
def about(request):
|
||||
"""View to render about page."""
|
||||
"""View to render about page.
|
||||
|
||||
:returns: base/about.html
|
||||
"""
|
||||
return render(request, 'base/about.html')
|
||||
|
||||
|
||||
# TODO: replace this with a link to docs in the wiki which won't require code
|
||||
# updates to maintain
|
||||
def faq(request):
|
||||
"""View to render faq page."""
|
||||
"""View to render faq page.
|
||||
|
||||
:returns: base/faq.html
|
||||
"""
|
||||
return render(request, 'base/faq.html')
|
||||
|
||||
|
||||
def stats(request):
|
||||
"""View to render stats page."""
|
||||
"""View to render stats page.
|
||||
|
||||
:returns: base/stats.html
|
||||
"""
|
||||
satellites = cache.get('stats_satellites')
|
||||
observers = cache.get('stats_observers')
|
||||
# TODO this will never succeed, cache_statistics() runs too long to be live
|
||||
|
@ -198,6 +235,10 @@ def stats(request):
|
|||
|
||||
|
||||
def statistics(request):
|
||||
"""Triggers a refresh of cached statistics if the cache does not exist
|
||||
|
||||
:returns: JsonResponse of statistics
|
||||
"""
|
||||
statistics = cache.get('stats_transmitters')
|
||||
if not statistics:
|
||||
cache_statistics()
|
||||
|
@ -205,8 +246,12 @@ def statistics(request):
|
|||
return JsonResponse(statistics, safe=False)
|
||||
|
||||
|
||||
# TODO: this is confusing as we call it "edit" but it is the users "settings"
|
||||
@login_required
|
||||
def users_edit(request):
|
||||
"""View to render user settings page."""
|
||||
"""View to render user settings page.
|
||||
|
||||
:returns: base/users_edit.html
|
||||
"""
|
||||
token = get_apikey(request.user)
|
||||
return render(request, 'base/users_edit.html', {'token': token})
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
"""SatNOGS DB celery task workers"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
@ -20,6 +21,7 @@ APP.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
|
|||
|
||||
@APP.on_after_finalize.connect
|
||||
def setup_periodic_tasks(sender, **kwargs):
|
||||
"""Initializes celery tasks that need to run on a scheduled basis"""
|
||||
from db.base.tasks import update_all_tle, background_cache_statistics, decode_recent_data
|
||||
|
||||
sender.add_periodic_task(RUN_DAILY, update_all_tle.s(), name='update-all-tle')
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
"""SatNOGS DB Application django settings
|
||||
|
||||
For local installation settings please copy .env-dist to .env and edit
|
||||
the appropriate settings in that file. You should not need to edit this
|
||||
file for local settings!
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
""" Base Django URL mapping for SatNOGS DB"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
"""WSGI module for SatNOGS DB"""
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
|
|
Loading…
Reference in New Issue