1
0
Fork 0
satnogs-network/network/base/views.py

882 lines
35 KiB
Python

import urllib2
from collections import defaultdict
from datetime import datetime, timedelta
from operator import itemgetter
import ephem
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.db.models import Count, Prefetch
from django.forms import ValidationError, formset_factory
from django.http import HttpResponse, HttpResponseNotFound, \
HttpResponseServerError, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.text import slugify
from django.utils.timezone import make_aware, now, utc
from django.views.generic import ListView
from rest_framework import serializers, viewsets
from network.base.db_api import DBConnectionError, get_transmitter_by_uuid, \
get_transmitters_by_norad_id, get_transmitters_by_status
from network.base.decorators import admin_required, ajax_required
from network.base.forms import BaseObservationFormSet, ObservationForm, \
SatelliteFilterForm, StationForm
from network.base.models import Antenna, LatestTle, Observation, Satellite, \
Station, StationStatusLog
from network.base.perms import delete_perms, schedule_perms, \
schedule_station_perms, vet_perms
from network.base.scheduling import create_new_observation, \
get_available_stations, predict_available_observation_windows
from network.base.stats import satellite_stats_by_transmitter_list, \
transmitter_stats_by_uuid
from network.base.tasks import fetch_data, update_all_tle
from network.base.validators import NegativeElevationError, \
ObservationOverlapError, SinglePassError, \
is_transmitter_in_station_range
from network.users.models import User
class StationSerializer(serializers.ModelSerializer):
status_display = serializers.SerializerMethodField()
class Meta:
model = Station
fields = ('name', 'lat', 'lng', 'id', 'status', 'status_display')
def get_status_display(self, obj):
try:
return obj.get_status_display()
except AttributeError:
return None
class StationAllView(viewsets.ReadOnlyModelViewSet):
queryset = Station.objects.exclude(status=0)
serializer_class = StationSerializer
def index(request):
"""View to render index page."""
return render(request, 'base/home.html', {'mapbox_id': settings.MAPBOX_MAP_ID,
'mapbox_token': settings.MAPBOX_TOKEN})
def custom_404(request):
"""Custom 404 error handler."""
return HttpResponseNotFound(render(request, '404.html'))
def custom_500(request):
"""Custom 500 error handler."""
return HttpResponseServerError(render(request, '500.html'))
def robots(request):
data = render(request, 'robots.txt', {'environment': settings.ENVIRONMENT})
response = HttpResponse(data,
content_type='text/plain; charset=utf-8')
return response
@admin_required
def settings_site(request):
"""View to render settings page."""
if request.method == 'POST':
fetch_data.delay()
update_all_tle.delay()
messages.success(request, 'Data fetching task was triggered successfully!')
return redirect(reverse('users:view_user', kwargs={"username": request.user.username}))
return render(request, 'base/settings_site.html')
class ObservationListView(ListView):
"""
Displays a list of observations with pagination
"""
model = Observation
context_object_name = "observations"
paginate_by = settings.ITEMS_PER_PAGE
template_name = 'base/observations.html'
def get_queryset(self):
"""
Optionally filter based on norad get argument
Optionally filter based on future/good/bad/unvetted/failed
"""
norad_cat_id = self.request.GET.get('norad', '')
observer = self.request.GET.get('observer', '')
station = self.request.GET.get('station', '')
start = self.request.GET.get('start', '')
end = self.request.GET.get('end', '')
self.filtered = False
bad = self.request.GET.get('bad', '1')
if bad == '0':
bad = False
else:
bad = True
good = self.request.GET.get('good', '1')
if good == '0':
good = False
else:
good = True
unvetted = self.request.GET.get('unvetted', '1')
if unvetted == '0':
unvetted = False
else:
unvetted = True
future = self.request.GET.get('future', '1')
if future == '0':
future = False
else:
future = True
failed = self.request.GET.get('failed', '1')
if failed == '0':
failed = False
else:
failed = True
results = self.request.GET.getlist('results')
if False in (bad, good, unvetted, future, failed):
self.filtered = True
if results:
self.filtered = True
observations = Observation.objects.all()
if not norad_cat_id == '':
observations = observations.filter(
satellite__norad_cat_id=norad_cat_id)
self.filtered = True
if not observer == '':
observations = observations.filter(
author=observer)
self.filtered = True
if not station == '':
observations = observations.filter(
ground_station_id=station)
self.filtered = True
if not start == '':
observations = observations.filter(
start__gt=start)
self.filtered = True
if not end == '':
observations = observations.filter(
end__lt=end)
self.filtered = True
if not bad:
observations = observations.exclude(vetted_status='bad')
if not good:
observations = observations.exclude(vetted_status='good')
if not failed:
observations = observations.exclude(vetted_status='failed')
if not unvetted:
observations = observations.exclude(vetted_status='unknown', end__lte=now())
if not future:
observations = observations.exclude(vetted_status='unknown', end__gt=now())
if results:
if 'w0' in results:
observations = observations.filter(waterfall='')
elif 'w1' in results:
observations = observations.exclude(waterfall='')
if 'a0' in results:
observations = observations.exclude(archived=True).filter(payload='')
elif 'a1' in results:
observations = observations.exclude(archived=False, payload='')
if 'd0' in results:
observations = observations.filter(demoddata__payload_demod__isnull=True)
elif 'd1' in results:
observations = observations.exclude(demoddata__payload_demod__isnull=True)
return observations
def get_context_data(self, **kwargs):
"""
Need to add a list of satellites to the context for the template
"""
context = super(ObservationListView, self).get_context_data(**kwargs)
context['satellites'] = Satellite.objects.all()
context['authors'] = User.objects.annotate(obs_count=Count('observations')) \
.filter(obs_count__gt=0) \
.order_by('first_name', 'last_name', 'username')
context['stations'] = Station.objects.all().order_by('id')
norad_cat_id = self.request.GET.get('norad', None)
observer = self.request.GET.get('observer', None)
station = self.request.GET.get('station', None)
start = self.request.GET.get('start', None)
end = self.request.GET.get('end', None)
context['future'] = self.request.GET.get('future', '1')
context['bad'] = self.request.GET.get('bad', '1')
context['good'] = self.request.GET.get('good', '1')
context['unvetted'] = self.request.GET.get('unvetted', '1')
context['failed'] = self.request.GET.get('failed', '1')
context['results'] = self.request.GET.getlist('results')
context['filtered'] = self.filtered
if norad_cat_id is not None and norad_cat_id != '':
context['norad'] = int(norad_cat_id)
if observer is not None and observer != '':
context['observer_id'] = int(observer)
if station is not None and station != '':
context['station_id'] = int(station)
if start is not None and start != '':
context['start'] = start
if end is not None and end != '':
context['end'] = end
if 'scheduled' in self.request.session:
context['scheduled'] = self.request.session['scheduled']
try:
del self.request.session['scheduled']
except KeyError:
pass
context['can_schedule'] = schedule_perms(self.request.user)
return context
def observation_new_post(request):
ObservationFormSet = formset_factory(ObservationForm, formset=BaseObservationFormSet,
min_num=1, validate_min=True)
formset = ObservationFormSet(request.user, request.POST, prefix='obs')
try:
if formset.is_valid():
new_observations = []
for observation_data in formset.cleaned_data:
station = observation_data['ground_station']
start = observation_data['start']
end = observation_data['end']
transmitter_uuid = observation_data['transmitter_uuid']
transmitter = formset.transmitters[transmitter_uuid]
author = request.user
observation = create_new_observation(station=station, transmitter=transmitter,
start=start, end=end, author=author)
new_observations.append(observation)
total = formset.total_form_count()
for observation in new_observations:
observation.save()
try:
del request.session['scheduled']
except KeyError:
pass
request.session['scheduled'] = list(obs.id for obs in new_observations)
# If it's a single observation redirect to that one
if total == 1:
messages.success(request, 'Observation was scheduled successfully.')
return redirect(reverse('base:observation_view',
kwargs={'id': new_observations[0].id}))
messages.success(request, str(total) + ' Observations were scheduled successfully.')
return redirect(reverse('base:observations_list'))
else:
errors_list = [error for error in formset.errors if error]
if errors_list:
for field in errors_list[0]:
messages.error(request, '{0}'.format(errors_list[0][field][0]))
else:
messages.error(request, '{0}'.format(formset.non_form_errors()[0]))
return redirect(reverse('base:observation_new'))
except ValidationError as e:
messages.error(request, '{0}'.format(e.message))
return redirect(reverse('base:observation_new'))
except LatestTle.DoesNotExist:
message = 'Scheduling failed: Satellite without TLE'
messages.error(request, '{0}'.format(message))
return redirect(reverse('base:observation_new'))
except ObservationOverlapError as e:
messages.error(request, '{0}'.format(e.message))
return redirect(reverse('base:observation_new'))
except NegativeElevationError as e:
messages.error(request, '{0}'.format(e.message))
return redirect(reverse('base:observation_new'))
except SinglePassError as e:
messages.error(request, '{0}'.format(e.message))
return redirect(reverse('base:observation_new'))
@login_required
def observation_new(request):
"""View for new observation"""
can_schedule = schedule_perms(request.user)
if not can_schedule:
messages.error(request, 'You don\'t have permissions to schedule observations')
return redirect(reverse('base:observations_list'))
if request.method == 'POST':
return observation_new_post(request)
satellites = Satellite.objects.filter(status='alive')
obs_filter = {}
if request.method == 'GET':
filter_form = SatelliteFilterForm(request.GET)
if filter_form.is_valid():
start = filter_form.cleaned_data['start']
end = filter_form.cleaned_data['end']
ground_station = filter_form.cleaned_data['ground_station']
transmitter = filter_form.cleaned_data['transmitter']
norad = filter_form.cleaned_data['norad']
obs_filter['dates'] = False
if start and end: # Either give both dates or ignore if only one is given
start = datetime.strptime(start, '%Y/%m/%d %H:%M').strftime('%Y-%m-%d %H:%M')
end = (datetime.strptime(end, '%Y/%m/%d %H:%M') +
timedelta(minutes=1)).strftime('%Y-%m-%d %H:%M')
obs_filter['start'] = start
obs_filter['end'] = end
obs_filter['dates'] = True
obs_filter['exists'] = True
if norad:
obs_filter['norad'] = norad
obs_filter['transmitter'] = transmitter # Add transmitter only if norad exists
if ground_station:
obs_filter['ground_station'] = ground_station
else:
obs_filter['exists'] = False
return render(request, 'base/observation_new.html',
{'satellites': satellites, 'obs_filter': obs_filter,
'date_min_start': settings.OBSERVATION_DATE_MIN_START,
'date_min_end': settings.OBSERVATION_DATE_MIN_END,
'date_max_range': settings.OBSERVATION_DATE_MAX_RANGE,
'warn_min_obs': settings.OBSERVATION_WARN_MIN_OBS})
@ajax_required
def prediction_windows(request):
sat_norad_id = request.POST['satellite']
transmitter = request.POST['transmitter']
start = request.POST['start']
end = request.POST['end']
station_ids = request.POST.getlist('stations[]', [])
min_horizon = request.POST.get('min_horizon', None)
overlapped = int(request.POST.get('overlapped', 0))
try:
sat = Satellite.objects.filter(status='alive').get(norad_cat_id=sat_norad_id)
except Satellite.DoesNotExist:
data = [{
'error': 'You should select a Satellite first.'
}]
return JsonResponse(data, safe=False)
try:
tle = LatestTle.objects.get(satellite_id=sat.id)
except LatestTle.DoesNotExist:
data = [{
'error': 'No TLEs for this satellite yet.'
}]
return JsonResponse(data, safe=False)
try:
transmitter = get_transmitter_by_uuid(transmitter)
if len(transmitter) == 0:
data = [{
'error': 'You should select a valid Transmitter.'
}]
return JsonResponse(data, safe=False)
else:
downlink = transmitter[0]['downlink_low']
except DBConnectionError as e:
data = [{
'error': e.message
}]
return JsonResponse(data, safe=False)
start = make_aware(datetime.strptime(start, '%Y-%m-%d %H:%M'), utc)
end = make_aware(datetime.strptime(end, '%Y-%m-%d %H:%M'), utc)
data = []
scheduled_obs_queryset = Observation.objects.filter(end__gt=now())
stations = Station.objects.filter(status__gt=0).prefetch_related(
Prefetch('observations',
queryset=scheduled_obs_queryset,
to_attr='scheduled_obs'),
'antenna')
if len(station_ids) > 0 and station_ids != ['']:
stations = stations.filter(id__in=station_ids)
if len(stations) == 0:
if len(station_ids) == 1:
data = [{
'error': 'Station is offline or it doesn\'t exist.'
}]
else:
data = [{
'error': 'Stations are offline or they don\'t exist.'
}]
return JsonResponse(data, safe=False)
passes_found = defaultdict(list)
available_stations = get_available_stations(stations, downlink, request.user)
for station in available_stations:
station_passes, station_windows = predict_available_observation_windows(station,
min_horizon,
overlapped,
tle,
start,
end,
sat)
passes_found[station.id] = station_passes
if station_windows:
data.append({'id': station.id,
'name': station.name,
'status': station.status,
'lng': station.lng,
'lat': station.lat,
'alt': station.alt,
'window': station_windows})
if not data:
error_message = 'Satellite is always below horizon or ' \
'no free observation time available on visible stations.'
error_details = {}
for station in available_stations:
if station.id not in passes_found.keys():
error_details[station.id] = 'Satellite is always above or below horizon.\n'
else:
error_details[station.id] = 'No free observation time during passes available.\n'
data = [{'error': error_message,
'error_details': error_details,
'passes_found': passes_found}]
return JsonResponse(data, safe=False)
def observation_view(request, id):
"""View for single observation page."""
observation = get_object_or_404(Observation, id=id)
can_vet = vet_perms(request.user, observation)
can_delete = delete_perms(request.user, observation)
if observation.has_audio and not observation.audio_url:
messages.error(request, 'Audio file is not currently available,'
' if the problem persists please contact an administrator.')
if settings.ENVIRONMENT == 'production':
discuss_slug = 'https://community.libre.space/t/observation-{0}-{1}-{2}' \
.format(observation.id, slugify(observation.satellite.name),
observation.satellite.norad_cat_id)
discuss_url = ('https://community.libre.space/new-topic?title=Observation {0}: {1}'
' ({2})&body=Regarding [Observation {3}](http://{4}{5}) ...'
'&category=observations') \
.format(observation.id, observation.satellite.name,
observation.satellite.norad_cat_id, observation.id,
request.get_host(), request.path)
has_comments = True
apiurl = '{0}.json'.format(discuss_slug)
try:
urllib2.urlopen(apiurl).read()
except urllib2.URLError:
has_comments = False
return render(request, 'base/observation_view.html',
{'observation': observation, 'has_comments': has_comments,
'discuss_url': discuss_url, 'discuss_slug': discuss_slug,
'can_vet': can_vet, 'can_delete': can_delete})
return render(request, 'base/observation_view.html',
{'observation': observation, 'can_vet': can_vet,
'can_delete': can_delete})
@login_required
def observation_delete(request, id):
"""View for deleting observation."""
observation = get_object_or_404(Observation, id=id)
can_delete = delete_perms(request.user, observation)
if can_delete:
observation.delete()
messages.success(request, 'Observation deleted successfully.')
else:
messages.error(request, 'Permission denied.')
return redirect(reverse('base:observations_list'))
@login_required
@ajax_required
def observation_vet(request, id):
try:
observation = Observation.objects.get(id=id)
except Observation.DoesNotExist:
data = {
'error': 'Observation does not exist.'
}
return JsonResponse(data, safe=False)
status = request.POST.get('status', None)
can_vet = vet_perms(request.user, observation)
if status not in ['good', 'bad', 'failed', 'unknown']:
data = {
'error': 'Invalid status, select one of \'good\', \'bad\', \'failed\' and \'unknown\'.'
}
return JsonResponse(data, safe=False)
if not can_vet:
data = {
'error': 'Permission denied.'
}
return JsonResponse(data, safe=False)
observation.vetted_status = status
observation.vetted_user = request.user
observation.vetted_datetime = now()
observation.save(update_fields=['vetted_status', 'vetted_user', 'vetted_datetime'])
data = {
'vetted_status': observation.vetted_status,
'vetted_status_display': observation.get_vetted_status_display(),
'vetted_user': observation.vetted_user.displayname,
'vetted_datetime': observation.vetted_datetime.strftime('%Y-%m-%d %H:%M:%S')
}
return JsonResponse(data, safe=False)
def stations_list(request):
"""View to render Stations page."""
stations = Station.objects.annotate(total_obs=Count('observations'))
form = StationForm()
antennas = Antenna.objects.all()
online = stations.filter(status=2).count()
testing = stations.filter(status=1).count()
return render(request, 'base/stations.html',
{'stations': stations, 'form': form, 'antennas': antennas,
'online': online, 'testing': testing,
'mapbox_id': settings.MAPBOX_MAP_ID, 'mapbox_token': settings.MAPBOX_TOKEN})
def station_view(request, id):
"""View for single station page."""
station = get_object_or_404(Station, id=id)
form = StationForm(instance=station)
antennas = Antenna.objects.all()
unsupported_frequencies = request.GET.get('unsupported_frequencies', '0')
can_schedule = schedule_station_perms(request.user, station)
# Calculate uptime
uptime = '-'
try:
latest = StationStatusLog.objects.filter(station=station)[0]
except IndexError:
latest = None
if latest:
if latest.status:
try:
offline = StationStatusLog.objects.filter(station=station, status=0)[0]
uptime = latest.changed - offline.changed
except IndexError:
uptime = now() - latest.changed
uptime = str(uptime).split('.')[0]
if request.user.is_authenticated():
if request.user == station.owner:
wiki_help = ('<a href="{0}" target="_blank" class="wiki-help"><span class="glyphicon '
'glyphicon-question-sign" aria-hidden="true"></span>'
'</a>'.format(settings.WIKI_STATION_URL))
if station.is_offline:
messages.error(request, ('Your Station is offline. You should make '
'sure it can successfully connect to the Network API. '
'{0}'.format(wiki_help)))
if station.is_testing:
messages.warning(request, ('Your Station is in Testing mode. Once you are sure '
'it returns good observations you can put it online. '
'{0}'.format(wiki_help)))
return render(request, 'base/station_view.html',
{'station': station, 'form': form, 'antennas': antennas,
'mapbox_id': settings.MAPBOX_MAP_ID,
'mapbox_token': settings.MAPBOX_TOKEN,
'can_schedule': can_schedule,
'unsupported_frequencies': unsupported_frequencies,
'uptime': uptime})
def station_log(request, id):
"""View for single station status log."""
station = get_object_or_404(Station, id=id)
station_log = StationStatusLog.objects.filter(station=station)
return render(request, 'base/station_log.html',
{'station': station, 'station_log': station_log})
@ajax_required
def scheduling_stations(request):
"""Returns json with stations on which user has permissions to schedule"""
uuid = request.POST.get('transmitter', None)
if uuid is None:
data = [{
'error': 'You should select a Transmitter.'
}]
return JsonResponse(data, safe=False)
else:
try:
transmitter = get_transmitter_by_uuid(uuid)
if len(transmitter) == 0:
data = [{
'error': 'You should select a Transmitter.'
}]
return JsonResponse(data, safe=False)
else:
downlink = transmitter[0]['downlink_low']
if downlink is None:
data = [{
'error': 'You should select a valid Transmitter.'
}]
return JsonResponse(data, safe=False)
except DBConnectionError as e:
data = [{
'error': e.message
}]
return JsonResponse(data, safe=False)
stations = Station.objects.filter(status__gt=0)
available_stations = get_available_stations(stations, downlink, request.user)
import sys
sys.stdout.flush()
data = {
'stations': StationSerializer(available_stations, many=True).data,
}
return JsonResponse(data, safe=False)
@ajax_required
def pass_predictions(request, id):
"""Endpoint for pass predictions"""
scheduled_obs_queryset = Observation.objects.filter(end__gt=now())
station = get_object_or_404(
Station.objects.prefetch_related(
Prefetch('observations',
queryset=scheduled_obs_queryset,
to_attr='scheduled_obs'),
'antenna'),
id=id)
unsupported_frequencies = request.GET.get('unsupported_frequencies', '0')
try:
latest_tle_queryset = LatestTle.objects.all()
satellites = Satellite.objects.filter(
status='alive'
).prefetch_related(
Prefetch('tles', queryset=latest_tle_queryset, to_attr='tle')
)
except Satellite.DoesNotExist:
pass # we won't have any next passes to display
# Load the station information and invoke ephem so we can
# calculate upcoming passes for the station
observer = ephem.Observer()
observer.lon = str(station.lng)
observer.lat = str(station.lat)
observer.elevation = station.alt
observer.horizon = str(station.horizon)
nextpasses = []
start = make_aware(datetime.utcnow(), utc)
end = make_aware(datetime.utcnow() + timedelta(hours=settings.STATION_UPCOMING_END), utc)
observation_min_start = (
datetime.utcnow() + timedelta(minutes=settings.OBSERVATION_DATE_MIN_START)
).strftime("%Y-%m-%d %H:%M:%S.%f")
try:
all_transmitters = get_transmitters_by_status('active')
except DBConnectionError:
all_transmitters = []
if all_transmitters:
for satellite in satellites:
# look for a match between transmitters from the satellite and
# ground station antenna frequency capabilities
if int(unsupported_frequencies) == 0:
norad_id = satellite.norad_cat_id
transmitters = [t for t in all_transmitters if t['norad_cat_id'] == norad_id and
is_transmitter_in_station_range(t, station)]
if not transmitters:
continue
if satellite.tle:
tle = satellite.tle[0]
else:
continue
station_passes, station_windows = predict_available_observation_windows(station,
None,
2,
tle,
start,
end,
satellite)
if station_windows:
satellite_stats = satellite_stats_by_transmitter_list(transmitters)
for window in station_windows:
valid = window['start'] > observation_min_start and window['valid_duration']
window_start = datetime.strptime(window['start'], '%Y-%m-%d %H:%M:%S.%f')
window_end = datetime.strptime(window['end'], '%Y-%m-%d %H:%M:%S.%f')
sat_pass = {'name': str(satellite.name),
'id': str(satellite.id),
'success_rate': str(satellite_stats['success_rate']),
'bad_rate': str(satellite_stats['bad_rate']),
'unvetted_rate': str(satellite_stats['unvetted_rate']),
'future_rate': str(satellite_stats['future_rate']),
'total_count': str(satellite_stats['total_count']),
'good_count': str(satellite_stats['good_count']),
'bad_count': str(satellite_stats['bad_count']),
'unvetted_count': str(satellite_stats['unvetted_count']),
'future_count': str(satellite_stats['future_count']),
'norad_cat_id': str(satellite.norad_cat_id),
'tle1': window['tle1'],
'tle2': window['tle2'],
'tr': window_start, # Rise time
'azr': window['az_start'], # Rise Azimuth
'altt': window['elev_max'], # Max altitude
'ts': window_end, # Set time
'azs': window['az_end'], # Set azimuth
'valid': valid,
'overlapped': window['overlapped'],
'overlap_ratio': window['overlap_ratio']}
nextpasses.append(sat_pass)
data = {
'id': id,
'nextpasses': sorted(nextpasses, key=itemgetter('tr')),
'ground_station': {'lng': str(station.lng),
'lat': str(station.lat),
'alt': station.alt}
}
return JsonResponse(data, safe=False)
def station_edit(request, id=None):
"""Edit or add a single station."""
station = None
antennas = Antenna.objects.all()
if id:
station = get_object_or_404(Station, id=id, owner=request.user)
if request.method == 'POST':
if station:
form = StationForm(request.POST, request.FILES, instance=station)
else:
form = StationForm(request.POST, request.FILES)
if form.is_valid():
f = form.save(commit=False)
if not station:
f.testing = True
f.owner = request.user
f.save()
form.save_m2m()
messages.success(request, 'Ground Station saved successfully.')
return redirect(reverse('base:station_view', kwargs={'id': f.id}))
else:
messages.error(request, ('Your Ground Station submission has some '
'errors. {0}').format(form.errors))
return render(request, 'base/station_edit.html',
{'form': form, 'station': station, 'antennas': antennas})
else:
if station:
form = StationForm(instance=station)
else:
form = StationForm()
return render(request, 'base/station_edit.html',
{'form': form, 'station': station, 'antennas': antennas})
@login_required
def station_delete(request, id):
"""View for deleting a station."""
me = request.user
station = get_object_or_404(Station, id=id, owner=request.user)
station.delete()
messages.success(request, 'Ground Station deleted successfully.')
return redirect(reverse('users:view_user', kwargs={'username': me}))
def transmitters_with_stats(transmitters_list):
transmitters_with_stats_list = []
for transmitter in transmitters_list:
transmitter_stats = transmitter_stats_by_uuid(transmitter['uuid'])
transmitter_with_stats = dict(transmitter, **transmitter_stats)
transmitters_with_stats_list.append(transmitter_with_stats)
return transmitters_with_stats_list
def satellite_view(request, id):
try:
sat = Satellite.objects.get(norad_cat_id=id)
except Satellite.DoesNotExist:
data = {
'error': 'Unable to find that satellite.'
}
return JsonResponse(data, safe=False)
try:
transmitters = get_transmitters_by_norad_id(norad_id=id)
except DBConnectionError as e:
data = [{
'error': e.message
}]
return JsonResponse(data, safe=False)
satellite_stats = satellite_stats_by_transmitter_list(transmitters)
data = {
'id': id,
'name': sat.name,
'names': sat.names,
'image': sat.image,
'success_rate': satellite_stats['success_rate'],
'good_count': satellite_stats['good_count'],
'bad_count': satellite_stats['bad_count'],
'unvetted_count': satellite_stats['unvetted_count'],
'future_count': satellite_stats['future_count'],
'total_count': satellite_stats['total_count'],
'transmitters': transmitters_with_stats(transmitters)
}
return JsonResponse(data, safe=False)
def transmitters_view(request):
norad_id = request.POST['satellite']
station_id = request.POST.get('station_id', None)
try:
Satellite.objects.get(norad_cat_id=norad_id)
except Satellite.DoesNotExist:
data = {
'error': 'Unable to find that satellite.'
}
return JsonResponse(data, safe=False)
try:
transmitters = get_transmitters_by_norad_id(norad_id)
except DBConnectionError as e:
data = [{
'error': e.message
}]
return JsonResponse(data, safe=False)
transmitters = [t for t in transmitters
if t['status'] == 'active' and t['downlink_low'] is not None]
if station_id:
supported_transmitters = []
station = Station.objects.get(id=station_id)
for transmitter in transmitters:
transmitter_supported = is_transmitter_in_station_range(transmitter, station)
if transmitter_supported:
supported_transmitters.append(transmitter)
transmitters = supported_transmitters
data = {
'transmitters': transmitters_with_stats(transmitters)
}
return JsonResponse(data, safe=False)