2015-08-28 02:38:49 -06:00
|
|
|
import urllib2
|
2014-10-25 15:47:24 -06:00
|
|
|
import ephem
|
2015-11-16 06:18:46 -07:00
|
|
|
from operator import itemgetter
|
2014-10-25 15:47:24 -06:00
|
|
|
from datetime import datetime, timedelta
|
2018-12-12 05:16:35 -07:00
|
|
|
from collections import defaultdict
|
2014-10-25 15:47:24 -06:00
|
|
|
|
2018-03-06 11:01:21 -07:00
|
|
|
from django.db.models import Count
|
2015-04-11 11:32:44 -06:00
|
|
|
from django.conf import settings
|
2014-12-13 11:45:52 -07:00
|
|
|
from django.contrib import messages
|
2014-10-26 18:01:59 -06:00
|
|
|
from django.contrib.auth.decorators import login_required
|
2017-03-20 09:36:54 -06:00
|
|
|
from django.core.urlresolvers import reverse
|
2019-07-15 05:10:17 -06:00
|
|
|
from django.db.models import Prefetch
|
2017-02-21 15:21:09 -07:00
|
|
|
from django.http import JsonResponse, HttpResponseNotFound, HttpResponseServerError, HttpResponse
|
|
|
|
from django.shortcuts import get_object_or_404, render, redirect
|
|
|
|
from django.utils.timezone import now, make_aware, utc
|
|
|
|
from django.utils.text import slugify
|
2016-12-20 19:51:43 -07:00
|
|
|
from django.views.generic import ListView
|
2019-06-21 12:14:52 -06:00
|
|
|
from django.forms import formset_factory, ValidationError
|
2016-12-20 19:51:43 -07:00
|
|
|
|
2015-08-11 02:31:03 -06:00
|
|
|
from rest_framework import serializers, viewsets
|
2017-12-21 15:45:14 -07:00
|
|
|
from network.base.decorators import admin_required, ajax_required
|
2019-05-02 14:05:25 -06:00
|
|
|
from network.base.db_api import (get_transmitter_by_uuid, get_transmitters_by_norad_id,
|
2019-06-21 14:27:09 -06:00
|
|
|
get_transmitters_by_status, DBConnectionError)
|
2019-06-21 12:14:52 -06:00
|
|
|
from network.base.forms import (ObservationForm, BaseObservationFormSet, StationForm,
|
|
|
|
SatelliteFilterForm)
|
2019-06-21 14:37:03 -06:00
|
|
|
from network.base.validators import is_transmitter_in_station_range, ObservationOverlapError
|
2019-07-24 04:57:18 -06:00
|
|
|
from network.base.models import (Station, Observation, Satellite, Antenna, StationStatusLog, Tle,
|
|
|
|
LatestTle)
|
2019-06-21 12:14:52 -06:00
|
|
|
from network.base.scheduling import (create_new_observation, predict_available_observation_windows,
|
|
|
|
get_available_stations)
|
2019-02-24 13:41:35 -07:00
|
|
|
from network.base.perms import schedule_perms, schedule_station_perms, delete_perms, vet_perms
|
2017-11-17 07:31:21 -07:00
|
|
|
from network.base.tasks import update_all_tle, fetch_data
|
2019-07-19 08:53:39 -06:00
|
|
|
from network.base.stats import transmitter_stats_by_uuid, satellite_stats_by_transmitter_list
|
2019-05-02 14:05:25 -06:00
|
|
|
from network.users.models import User
|
2014-09-08 11:36:12 -06:00
|
|
|
|
2014-09-08 13:07:15 -06:00
|
|
|
|
2015-08-11 02:31:03 -06:00
|
|
|
class StationSerializer(serializers.ModelSerializer):
|
2019-01-08 07:33:04 -07:00
|
|
|
status_display = serializers.SerializerMethodField()
|
|
|
|
|
2015-08-11 02:31:03 -06:00
|
|
|
class Meta:
|
|
|
|
model = Station
|
2019-01-08 07:33:04 -07:00
|
|
|
fields = ('name', 'lat', 'lng', 'id', 'status', 'status_display')
|
|
|
|
|
|
|
|
def get_status_display(self, obj):
|
|
|
|
try:
|
|
|
|
return obj.get_status_display()
|
|
|
|
except AttributeError:
|
|
|
|
return None
|
2015-08-11 02:31:03 -06:00
|
|
|
|
|
|
|
|
|
|
|
class StationAllView(viewsets.ReadOnlyModelViewSet):
|
2018-01-05 12:31:35 -07:00
|
|
|
queryset = Station.objects.exclude(status=0)
|
2015-08-11 02:31:03 -06:00
|
|
|
serializer_class = StationSerializer
|
|
|
|
|
|
|
|
|
2014-09-08 11:36:12 -06:00
|
|
|
def index(request):
|
|
|
|
"""View to render index page."""
|
2018-02-18 13:35:45 -07:00
|
|
|
return render(request, 'base/home.html', {'mapbox_id': settings.MAPBOX_MAP_ID,
|
|
|
|
'mapbox_token': settings.MAPBOX_TOKEN})
|
2014-09-08 13:07:15 -06:00
|
|
|
|
|
|
|
|
2015-07-21 02:42:23 -06:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2015-07-22 05:16:15 -06:00
|
|
|
@admin_required
|
|
|
|
def settings_site(request):
|
|
|
|
"""View to render settings page."""
|
|
|
|
if request.method == 'POST':
|
2017-11-17 07:31:21 -07:00
|
|
|
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}))
|
2015-07-22 05:16:15 -06:00
|
|
|
return render(request, 'base/settings_site.html')
|
|
|
|
|
|
|
|
|
2016-12-20 19:51:43 -07:00
|
|
|
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
|
2017-12-27 08:13:29 -07:00
|
|
|
Optionally filter based on future/good/bad/unvetted/failed
|
2016-12-20 19:51:43 -07:00
|
|
|
"""
|
2017-02-03 20:20:32 -07:00
|
|
|
norad_cat_id = self.request.GET.get('norad', '')
|
2017-10-10 10:46:19 -06:00
|
|
|
observer = self.request.GET.get('observer', '')
|
|
|
|
station = self.request.GET.get('station', '')
|
2019-06-18 05:31:13 -06:00
|
|
|
start = self.request.GET.get('start', '')
|
|
|
|
end = self.request.GET.get('end', '')
|
2017-12-27 08:13:29 -07:00
|
|
|
self.filtered = False
|
2017-10-17 10:55:20 -06:00
|
|
|
|
2017-02-03 20:20:32 -07:00
|
|
|
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
|
2017-10-17 10:55:20 -06:00
|
|
|
future = self.request.GET.get('future', '1')
|
|
|
|
if future == '0':
|
|
|
|
future = False
|
|
|
|
else:
|
|
|
|
future = True
|
2017-12-27 08:13:29 -07:00
|
|
|
failed = self.request.GET.get('failed', '1')
|
|
|
|
if failed == '0':
|
|
|
|
failed = False
|
|
|
|
else:
|
|
|
|
failed = True
|
2019-03-06 12:00:08 -07:00
|
|
|
results = self.request.GET.getlist('results')
|
2017-12-27 08:13:29 -07:00
|
|
|
|
2019-03-06 12:00:08 -07:00
|
|
|
if False in (bad, good, unvetted, future, failed):
|
|
|
|
self.filtered = True
|
|
|
|
if results:
|
2017-12-27 08:13:29 -07:00
|
|
|
self.filtered = True
|
2017-02-03 20:20:32 -07:00
|
|
|
|
2017-10-10 10:46:19 -06:00
|
|
|
observations = Observation.objects.all()
|
|
|
|
if not norad_cat_id == '':
|
|
|
|
observations = observations.filter(
|
2017-09-11 04:33:49 -06:00
|
|
|
satellite__norad_cat_id=norad_cat_id)
|
2017-12-27 08:13:29 -07:00
|
|
|
self.filtered = True
|
2017-10-10 10:46:19 -06:00
|
|
|
if not observer == '':
|
|
|
|
observations = observations.filter(
|
|
|
|
author=observer)
|
2017-12-27 08:13:29 -07:00
|
|
|
self.filtered = True
|
2017-10-10 10:46:19 -06:00
|
|
|
if not station == '':
|
|
|
|
observations = observations.filter(
|
|
|
|
ground_station_id=station)
|
2017-12-27 08:13:29 -07:00
|
|
|
self.filtered = True
|
2019-06-18 05:31:13 -06:00
|
|
|
if not start == '':
|
2018-09-11 09:32:05 -06:00
|
|
|
observations = observations.filter(
|
2019-06-18 05:31:13 -06:00
|
|
|
start__gt=start)
|
2018-09-11 09:32:05 -06:00
|
|
|
self.filtered = True
|
2019-06-18 05:31:13 -06:00
|
|
|
if not end == '':
|
2018-09-11 09:32:05 -06:00
|
|
|
observations = observations.filter(
|
2019-06-18 05:31:13 -06:00
|
|
|
end__lt=end)
|
2018-09-11 09:32:05 -06:00
|
|
|
self.filtered = True
|
2017-02-24 03:57:19 -07:00
|
|
|
|
2017-09-11 04:33:49 -06:00
|
|
|
if not bad:
|
2017-12-27 11:15:05 -07:00
|
|
|
observations = observations.exclude(vetted_status='bad')
|
2017-09-11 04:33:49 -06:00
|
|
|
if not good:
|
2017-12-24 12:17:37 -07:00
|
|
|
observations = observations.exclude(vetted_status='good')
|
2017-12-27 08:13:29 -07:00
|
|
|
if not failed:
|
|
|
|
observations = observations.exclude(vetted_status='failed')
|
2017-09-11 04:33:49 -06:00
|
|
|
if not unvetted:
|
2019-02-16 02:41:09 -07:00
|
|
|
observations = observations.exclude(vetted_status='unknown', end__lte=now())
|
2017-10-17 10:55:20 -06:00
|
|
|
if not future:
|
2019-02-16 02:41:09 -07:00
|
|
|
observations = observations.exclude(vetted_status='unknown', end__gt=now())
|
2019-03-06 12:00:08 -07:00
|
|
|
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)
|
2017-09-11 04:33:49 -06:00
|
|
|
return observations
|
2016-12-20 19:51:43 -07:00
|
|
|
|
|
|
|
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()
|
2018-05-13 20:02:38 -06:00
|
|
|
context['authors'] = User.objects.annotate(obs_count=Count('observations')) \
|
|
|
|
.filter(obs_count__gt=0) \
|
|
|
|
.order_by('first_name', 'last_name', 'username')
|
2017-10-15 04:21:16 -06:00
|
|
|
context['stations'] = Station.objects.all().order_by('id')
|
2016-12-20 19:51:43 -07:00
|
|
|
norad_cat_id = self.request.GET.get('norad', None)
|
2017-10-10 10:46:19 -06:00
|
|
|
observer = self.request.GET.get('observer', None)
|
|
|
|
station = self.request.GET.get('station', None)
|
2019-06-18 05:31:13 -06:00
|
|
|
start = self.request.GET.get('start', None)
|
|
|
|
end = self.request.GET.get('end', None)
|
2017-10-17 10:55:20 -06:00
|
|
|
context['future'] = self.request.GET.get('future', '1')
|
2017-02-03 20:20:32 -07:00
|
|
|
context['bad'] = self.request.GET.get('bad', '1')
|
|
|
|
context['good'] = self.request.GET.get('good', '1')
|
|
|
|
context['unvetted'] = self.request.GET.get('unvetted', '1')
|
2017-12-27 08:13:29 -07:00
|
|
|
context['failed'] = self.request.GET.get('failed', '1')
|
2019-03-06 12:00:08 -07:00
|
|
|
context['results'] = self.request.GET.getlist('results')
|
2017-12-27 08:13:29 -07:00
|
|
|
context['filtered'] = self.filtered
|
2016-12-20 19:51:43 -07:00
|
|
|
if norad_cat_id is not None and norad_cat_id != '':
|
|
|
|
context['norad'] = int(norad_cat_id)
|
2017-10-10 10:46:19 -06:00
|
|
|
if observer is not None and observer != '':
|
|
|
|
context['observer_id'] = int(observer)
|
|
|
|
if station is not None and station != '':
|
|
|
|
context['station_id'] = int(station)
|
2019-06-18 05:31:13 -06:00
|
|
|
if start is not None and start != '':
|
|
|
|
context['start'] = start
|
|
|
|
if end is not None and end != '':
|
|
|
|
context['end'] = end
|
2017-09-18 11:30:02 -06:00
|
|
|
if 'scheduled' in self.request.session:
|
|
|
|
context['scheduled'] = self.request.session['scheduled']
|
|
|
|
try:
|
|
|
|
del self.request.session['scheduled']
|
|
|
|
except KeyError:
|
|
|
|
pass
|
2017-11-18 12:16:21 -07:00
|
|
|
context['can_schedule'] = schedule_perms(self.request.user)
|
2016-12-20 19:51:43 -07:00
|
|
|
return context
|
2014-09-18 07:34:39 -06:00
|
|
|
|
|
|
|
|
2018-08-18 06:24:07 -06:00
|
|
|
def observation_new_post(request):
|
2019-06-21 12:14:52 -06:00
|
|
|
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()
|
2016-04-23 11:14:50 -06:00
|
|
|
|
2019-06-21 12:14:52 -06:00
|
|
|
try:
|
|
|
|
del request.session['scheduled']
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
request.session['scheduled'] = list(obs.id for obs in new_observations)
|
2016-04-23 11:14:50 -06:00
|
|
|
|
2019-06-21 12:14:52 -06:00
|
|
|
# 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}))
|
2017-12-26 12:25:33 -07:00
|
|
|
|
2019-06-21 12:14:52 -06:00
|
|
|
messages.success(request, str(total) + ' Observations were scheduled successfully.')
|
|
|
|
return redirect(reverse('base:observations_list'))
|
2018-06-17 12:54:40 -06:00
|
|
|
|
2018-08-18 06:24:07 -06:00
|
|
|
else:
|
2019-06-21 12:14:52 -06:00
|
|
|
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 ObservationOverlapError as e:
|
|
|
|
messages.error(request, '{0}'.format(e.message))
|
2018-08-18 06:24:07 -06:00
|
|
|
return redirect(reverse('base:observation_new'))
|
2017-09-18 11:30:02 -06:00
|
|
|
|
2017-09-11 04:33:49 -06:00
|
|
|
|
2018-08-18 06:24:07 -06:00
|
|
|
@login_required
|
|
|
|
def observation_new(request):
|
|
|
|
"""View for new observation"""
|
2019-02-24 13:41:35 -07:00
|
|
|
can_schedule = schedule_perms(request.user)
|
2018-08-18 06:24:07 -06:00
|
|
|
if not can_schedule:
|
|
|
|
messages.error(request, 'You don\'t have permissions to schedule observations')
|
2017-09-11 04:33:49 -06:00
|
|
|
return redirect(reverse('base:observations_list'))
|
2014-10-26 18:01:59 -06:00
|
|
|
|
2018-08-18 06:24:07 -06:00
|
|
|
if request.method == 'POST':
|
|
|
|
return observation_new_post(request)
|
|
|
|
|
2019-05-02 14:05:25 -06:00
|
|
|
satellites = Satellite.objects.filter(status='alive')
|
2014-10-26 14:07:42 -06:00
|
|
|
|
2016-05-09 06:29:23 -06:00
|
|
|
obs_filter = {}
|
2016-04-08 04:14:19 -06:00
|
|
|
if request.method == 'GET':
|
2016-05-09 06:29:23 -06:00
|
|
|
filter_form = SatelliteFilterForm(request.GET)
|
|
|
|
if filter_form.is_valid():
|
2019-06-18 05:31:13 -06:00
|
|
|
start = filter_form.cleaned_data['start']
|
|
|
|
end = filter_form.cleaned_data['end']
|
2017-01-28 09:53:19 -07:00
|
|
|
ground_station = filter_form.cleaned_data['ground_station']
|
2018-09-03 14:46:40 -06:00
|
|
|
transmitter = filter_form.cleaned_data['transmitter']
|
2017-01-28 09:53:19 -07:00
|
|
|
norad = filter_form.cleaned_data['norad']
|
|
|
|
|
2018-09-05 06:37:53 -06:00
|
|
|
obs_filter['dates'] = False
|
2019-06-18 05:31:13 -06:00
|
|
|
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
|
2018-09-05 06:37:53 -06:00
|
|
|
obs_filter['dates'] = True
|
|
|
|
|
2016-05-09 06:29:23 -06:00
|
|
|
obs_filter['exists'] = True
|
2018-09-05 06:37:53 -06:00
|
|
|
if norad:
|
|
|
|
obs_filter['norad'] = norad
|
|
|
|
obs_filter['transmitter'] = transmitter # Add transmitter only if norad exists
|
2017-03-04 12:10:26 -07:00
|
|
|
if ground_station:
|
|
|
|
obs_filter['ground_station'] = ground_station
|
2016-05-09 06:29:23 -06:00
|
|
|
else:
|
|
|
|
obs_filter['exists'] = False
|
2016-04-08 04:14:19 -06:00
|
|
|
|
2015-04-22 16:19:56 -06:00
|
|
|
return render(request, 'base/observation_new.html',
|
2019-01-04 03:59:12 -07:00
|
|
|
{'satellites': satellites, 'obs_filter': obs_filter,
|
2017-12-16 04:51:14 -07:00
|
|
|
'date_min_start': settings.OBSERVATION_DATE_MIN_START,
|
|
|
|
'date_min_end': settings.OBSERVATION_DATE_MIN_END,
|
2019-01-24 18:47:53 -07:00
|
|
|
'date_max_range': settings.OBSERVATION_DATE_MAX_RANGE,
|
|
|
|
'warn_min_obs': settings.OBSERVATION_WARN_MIN_OBS})
|
2014-10-26 14:07:42 -06:00
|
|
|
|
|
|
|
|
2017-12-21 15:45:14 -07:00
|
|
|
@ajax_required
|
2019-01-03 13:30:14 -07:00
|
|
|
def prediction_windows(request):
|
|
|
|
sat_id = request.POST['satellite']
|
|
|
|
transmitter = request.POST['transmitter']
|
2019-06-18 05:31:13 -06:00
|
|
|
start = request.POST['start']
|
|
|
|
end = request.POST['end']
|
2019-01-08 07:33:04 -07:00
|
|
|
station_ids = request.POST.getlist('stations[]', [])
|
|
|
|
min_horizon = request.POST.get('min_horizon', None)
|
2019-01-23 13:52:47 -07:00
|
|
|
overlapped = int(request.POST.get('overlapped', 0))
|
2015-04-16 15:44:10 -06:00
|
|
|
try:
|
2019-05-02 14:05:25 -06:00
|
|
|
sat = Satellite.objects.filter(status='alive').get(norad_cat_id=sat_id)
|
2017-11-17 07:31:21 -07:00
|
|
|
except Satellite.DoesNotExist:
|
2019-01-03 13:36:57 -07:00
|
|
|
data = [{
|
2015-04-16 15:44:10 -06:00
|
|
|
'error': 'You should select a Satellite first.'
|
2019-01-03 13:36:57 -07:00
|
|
|
}]
|
2015-04-16 15:44:10 -06:00
|
|
|
return JsonResponse(data, safe=False)
|
2016-01-22 10:48:07 -07:00
|
|
|
|
2016-01-26 06:52:06 -07:00
|
|
|
try:
|
2019-07-15 04:26:54 -06:00
|
|
|
tle = sat.latest_tle
|
2019-07-15 04:21:08 -06:00
|
|
|
except (ValueError, AttributeError, Tle.DoesNotExist):
|
2019-01-03 13:36:57 -07:00
|
|
|
data = [{
|
2016-01-26 06:52:06 -07:00
|
|
|
'error': 'No TLEs for this satellite yet.'
|
2019-01-03 13:36:57 -07:00
|
|
|
}]
|
2016-01-26 06:52:06 -07:00
|
|
|
return JsonResponse(data, safe=False)
|
2014-10-25 15:47:24 -06:00
|
|
|
|
2019-06-21 14:27:09 -06:00
|
|
|
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:
|
2019-05-02 14:05:25 -06:00
|
|
|
data = [{
|
2019-06-21 14:27:09 -06:00
|
|
|
'error': e.message
|
2019-05-02 14:05:25 -06:00
|
|
|
}]
|
|
|
|
return JsonResponse(data, safe=False)
|
2017-03-19 12:03:58 -06:00
|
|
|
|
2019-06-18 05:31:13 -06:00
|
|
|
start = make_aware(datetime.strptime(start, '%Y-%m-%d %H:%M'), utc)
|
|
|
|
end = make_aware(datetime.strptime(end, '%Y-%m-%d %H:%M'), utc)
|
2014-10-25 15:47:24 -06:00
|
|
|
|
2014-10-26 14:07:42 -06:00
|
|
|
data = []
|
2014-10-25 15:47:24 -06:00
|
|
|
|
2019-07-15 05:10:17 -06:00
|
|
|
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')
|
2019-01-08 07:33:04 -07:00
|
|
|
if len(station_ids) > 0 and station_ids != ['']:
|
|
|
|
stations = stations.filter(id__in=station_ids)
|
2019-01-04 03:55:11 -07:00
|
|
|
if len(stations) == 0:
|
2019-01-08 07:33:04 -07:00
|
|
|
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.'
|
|
|
|
}]
|
2019-01-04 03:55:11 -07:00
|
|
|
return JsonResponse(data, safe=False)
|
2018-12-11 07:02:05 -07:00
|
|
|
|
2018-12-12 05:16:35 -07:00
|
|
|
passes_found = defaultdict(list)
|
2019-01-08 07:33:04 -07:00
|
|
|
available_stations = get_available_stations(stations, downlink, request.user)
|
|
|
|
for station in available_stations:
|
2018-12-15 17:00:29 -07:00
|
|
|
station_passes, station_windows = predict_available_observation_windows(station,
|
2019-01-08 07:33:04 -07:00
|
|
|
min_horizon,
|
2019-01-14 03:35:20 -07:00
|
|
|
overlapped,
|
2019-01-08 08:28:15 -07:00
|
|
|
tle,
|
2019-06-18 05:31:13 -06:00
|
|
|
start,
|
|
|
|
end,
|
2018-12-15 17:00:29 -07:00
|
|
|
sat)
|
|
|
|
passes_found[station.id] = station_passes
|
2018-12-09 16:47:22 -07:00
|
|
|
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})
|
2014-10-25 15:47:24 -06:00
|
|
|
|
2018-12-12 05:16:35 -07:00
|
|
|
if not data:
|
|
|
|
error_message = 'Satellite is always below horizon or ' \
|
|
|
|
'no free observation time available on visible stations.'
|
|
|
|
error_details = {}
|
2019-01-08 07:33:04 -07:00
|
|
|
for station in available_stations:
|
|
|
|
if station.id not in passes_found.keys():
|
2018-12-12 05:16:35 -07:00
|
|
|
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}]
|
2018-12-11 06:31:26 -07:00
|
|
|
|
2014-10-26 14:07:42 -06:00
|
|
|
return JsonResponse(data, safe=False)
|
2014-10-25 15:47:24 -06:00
|
|
|
|
|
|
|
|
2014-12-19 06:06:58 -07:00
|
|
|
def observation_view(request, id):
|
2014-09-21 14:18:16 -06:00
|
|
|
"""View for single observation page."""
|
|
|
|
observation = get_object_or_404(Observation, id=id)
|
2015-08-28 02:38:49 -06:00
|
|
|
|
2017-11-18 12:16:21 -07:00
|
|
|
can_vet = vet_perms(request.user, observation)
|
Initial data vetting/verification system
Model change (with migration 0006) adds 3 fields to Data:
vetted_status (charfield with options for data status, default "unknown")
vetted_user (who vetted the data)
vetted_datetime (when it was vetted)
In addition, various boolean functions are added for the Data model
to check statuses. More functions are added to the Observation model
to check status of verification within an observation as well, assuming
multiple data entries in an Observation. With these, I also changed
"has_data" to "has_submitted_data" to be more specific alongside the
others.
For UX, we add a green check sign or red removal sign to the data
header in Observation view (along with green/red datetime in the footer)
if a data is verified good or bad, respectively. If there is an unknown
status, the data header is given a thumbs-up and thumbs-down button to
verify the data good or bad. These icons are only offered to is_staff,
the observation requestor, and any station owner in the observation.
These buttons trigger new URLs/functions in view:
data_verify(id)
data_mark_bad(id)
Returning the user back to the originating Observation page.
In the observation lists I changed the coloring of the ID button to be:
Future: light blue (same)
No uploaded data and/or all vetted bad data: red
Some or all unvetted data with no verified good data: orange
Some or all verified good data: green
These changes are reflected in the observations.html, home.html, and
user_detail.html templates.
solves satnogs/satnogs-network#171
2016-03-25 13:52:45 -06:00
|
|
|
|
2017-11-18 12:16:21 -07:00
|
|
|
can_delete = delete_perms(request.user, observation)
|
2017-02-09 05:33:44 -07:00
|
|
|
|
2019-06-25 06:03:06 -06:00
|
|
|
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.')
|
|
|
|
|
2017-09-24 14:14:24 -06:00
|
|
|
if settings.ENVIRONMENT == 'production':
|
2017-09-11 04:33:49 -06:00
|
|
|
discuss_slug = 'https://community.libre.space/t/observation-{0}-{1}-{2}' \
|
2015-09-02 09:34:37 -06:00
|
|
|
.format(observation.id, slugify(observation.satellite.name),
|
|
|
|
observation.satellite.norad_cat_id)
|
2017-05-06 12:14:22 -06:00
|
|
|
discuss_url = ('https://community.libre.space/new-topic?title=Observation {0}: {1}'
|
2016-05-05 08:50:11 -06:00
|
|
|
' ({2})&body=Regarding [Observation {3}](http://{4}{5}) ...'
|
|
|
|
'&category=observations') \
|
2015-09-02 09:34:37 -06:00
|
|
|
.format(observation.id, observation.satellite.name,
|
|
|
|
observation.satellite.norad_cat_id, observation.id,
|
|
|
|
request.get_host(), request.path)
|
2017-09-24 14:14:24 -06:00
|
|
|
has_comments = True
|
|
|
|
apiurl = '{0}.json'.format(discuss_slug)
|
2015-09-02 09:34:37 -06:00
|
|
|
try:
|
|
|
|
urllib2.urlopen(apiurl).read()
|
2017-11-17 07:31:21 -07:00
|
|
|
except urllib2.URLError:
|
2015-09-02 09:34:37 -06:00
|
|
|
has_comments = False
|
|
|
|
|
|
|
|
return render(request, 'base/observation_view.html',
|
2017-09-11 04:33:49 -06:00
|
|
|
{'observation': observation, 'has_comments': has_comments,
|
|
|
|
'discuss_url': discuss_url, 'discuss_slug': discuss_slug,
|
2017-11-18 12:16:21 -07:00
|
|
|
'can_vet': can_vet, 'can_delete': can_delete})
|
2014-09-21 14:18:16 -06:00
|
|
|
|
2014-10-16 07:42:44 -06:00
|
|
|
return render(request, 'base/observation_view.html',
|
2017-11-18 12:16:21 -07:00
|
|
|
{'observation': observation, 'can_vet': can_vet,
|
|
|
|
'can_delete': can_delete})
|
2014-10-27 18:45:22 -06:00
|
|
|
|
|
|
|
|
2015-08-14 12:58:54 -06:00
|
|
|
@login_required
|
|
|
|
def observation_delete(request, id):
|
|
|
|
"""View for deleting observation."""
|
|
|
|
observation = get_object_or_404(Observation, id=id)
|
2017-11-18 12:16:21 -07:00
|
|
|
can_delete = delete_perms(request.user, observation)
|
|
|
|
if can_delete:
|
2015-08-14 12:58:54 -06:00
|
|
|
observation.delete()
|
|
|
|
messages.success(request, 'Observation deleted successfully.')
|
|
|
|
else:
|
|
|
|
messages.error(request, 'Permission denied.')
|
|
|
|
return redirect(reverse('base:observations_list'))
|
|
|
|
|
|
|
|
|
Initial data vetting/verification system
Model change (with migration 0006) adds 3 fields to Data:
vetted_status (charfield with options for data status, default "unknown")
vetted_user (who vetted the data)
vetted_datetime (when it was vetted)
In addition, various boolean functions are added for the Data model
to check statuses. More functions are added to the Observation model
to check status of verification within an observation as well, assuming
multiple data entries in an Observation. With these, I also changed
"has_data" to "has_submitted_data" to be more specific alongside the
others.
For UX, we add a green check sign or red removal sign to the data
header in Observation view (along with green/red datetime in the footer)
if a data is verified good or bad, respectively. If there is an unknown
status, the data header is given a thumbs-up and thumbs-down button to
verify the data good or bad. These icons are only offered to is_staff,
the observation requestor, and any station owner in the observation.
These buttons trigger new URLs/functions in view:
data_verify(id)
data_mark_bad(id)
Returning the user back to the originating Observation page.
In the observation lists I changed the coloring of the ID button to be:
Future: light blue (same)
No uploaded data and/or all vetted bad data: red
Some or all unvetted data with no verified good data: orange
Some or all verified good data: green
These changes are reflected in the observations.html, home.html, and
user_detail.html templates.
solves satnogs/satnogs-network#171
2016-03-25 13:52:45 -06:00
|
|
|
@login_required
|
2019-02-15 12:59:22 -07:00
|
|
|
@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)
|
2017-11-18 12:16:21 -07:00
|
|
|
can_vet = vet_perms(request.user, observation)
|
2019-02-15 12:59:22 -07:00
|
|
|
|
|
|
|
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)
|
Initial data vetting/verification system
Model change (with migration 0006) adds 3 fields to Data:
vetted_status (charfield with options for data status, default "unknown")
vetted_user (who vetted the data)
vetted_datetime (when it was vetted)
In addition, various boolean functions are added for the Data model
to check statuses. More functions are added to the Observation model
to check status of verification within an observation as well, assuming
multiple data entries in an Observation. With these, I also changed
"has_data" to "has_submitted_data" to be more specific alongside the
others.
For UX, we add a green check sign or red removal sign to the data
header in Observation view (along with green/red datetime in the footer)
if a data is verified good or bad, respectively. If there is an unknown
status, the data header is given a thumbs-up and thumbs-down button to
verify the data good or bad. These icons are only offered to is_staff,
the observation requestor, and any station owner in the observation.
These buttons trigger new URLs/functions in view:
data_verify(id)
data_mark_bad(id)
Returning the user back to the originating Observation page.
In the observation lists I changed the coloring of the ID button to be:
Future: light blue (same)
No uploaded data and/or all vetted bad data: red
Some or all unvetted data with no verified good data: orange
Some or all verified good data: green
These changes are reflected in the observations.html, home.html, and
user_detail.html templates.
solves satnogs/satnogs-network#171
2016-03-25 13:52:45 -06:00
|
|
|
|
|
|
|
|
2014-10-27 18:45:22 -06:00
|
|
|
def stations_list(request):
|
|
|
|
"""View to render Stations page."""
|
2018-03-06 11:01:21 -07:00
|
|
|
stations = Station.objects.annotate(total_obs=Count('observations'))
|
2014-12-14 05:50:39 -07:00
|
|
|
form = StationForm()
|
|
|
|
antennas = Antenna.objects.all()
|
2018-03-16 06:32:52 -06:00
|
|
|
online = stations.filter(status=2).count()
|
|
|
|
testing = stations.filter(status=1).count()
|
2014-10-27 18:45:22 -06:00
|
|
|
|
2014-12-14 05:50:39 -07:00
|
|
|
return render(request, 'base/stations.html',
|
2018-03-16 06:32:52 -06:00
|
|
|
{'stations': stations, 'form': form, 'antennas': antennas,
|
2018-03-19 13:38:32 -06:00
|
|
|
'online': online, 'testing': testing,
|
|
|
|
'mapbox_id': settings.MAPBOX_MAP_ID, 'mapbox_token': settings.MAPBOX_TOKEN})
|
2014-12-01 13:20:38 -07:00
|
|
|
|
|
|
|
|
2014-12-19 06:06:58 -07:00
|
|
|
def station_view(request, id):
|
2014-12-01 13:20:38 -07:00
|
|
|
"""View for single station page."""
|
|
|
|
station = get_object_or_404(Station, id=id)
|
2014-12-13 11:45:52 -07:00
|
|
|
form = StationForm(instance=station)
|
2014-12-14 05:47:50 -07:00
|
|
|
antennas = Antenna.objects.all()
|
2017-03-19 12:03:58 -06:00
|
|
|
unsupported_frequencies = request.GET.get('unsupported_frequencies', '0')
|
2014-12-01 13:20:38 -07:00
|
|
|
|
2019-02-24 13:41:35 -07:00
|
|
|
can_schedule = schedule_station_perms(request.user, station)
|
2017-12-23 03:35:42 -07:00
|
|
|
|
2018-03-28 11:55:29 -06:00
|
|
|
# Calculate uptime
|
2018-03-23 07:28:23 -06:00
|
|
|
uptime = '-'
|
2018-03-28 11:55:29 -06:00
|
|
|
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]
|
2018-03-23 07:28:23 -06:00
|
|
|
|
2018-03-10 07:01:42 -07:00
|
|
|
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)))
|
2018-03-07 08:46:35 -07:00
|
|
|
|
2017-12-23 03:35:42 -07:00
|
|
|
return render(request, 'base/station_view.html',
|
|
|
|
{'station': station, 'form': form, 'antennas': antennas,
|
|
|
|
'mapbox_id': settings.MAPBOX_MAP_ID,
|
|
|
|
'mapbox_token': settings.MAPBOX_TOKEN,
|
2018-12-06 11:39:37 -07:00
|
|
|
'can_schedule': can_schedule,
|
2018-03-23 07:28:23 -06:00
|
|
|
'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})
|
2017-12-23 03:35:42 -07:00
|
|
|
|
|
|
|
|
2019-01-08 07:33:04 -07:00
|
|
|
@ajax_required
|
|
|
|
def scheduling_stations(request):
|
|
|
|
"""Returns json with stations on which user has permissions to schedule"""
|
2019-05-02 14:05:25 -06:00
|
|
|
uuid = request.POST.get('transmitter', None)
|
|
|
|
if uuid is None:
|
2019-01-08 07:33:04 -07:00
|
|
|
data = [{
|
2019-05-02 14:05:25 -06:00
|
|
|
'error': 'You should select a Transmitter.'
|
2019-01-08 07:33:04 -07:00
|
|
|
}]
|
|
|
|
return JsonResponse(data, safe=False)
|
2019-05-02 14:05:25 -06:00
|
|
|
else:
|
2019-06-21 14:27:09 -06:00
|
|
|
try:
|
|
|
|
transmitter = get_transmitter_by_uuid(uuid)
|
|
|
|
if len(transmitter) == 0:
|
2019-05-02 14:05:25 -06:00
|
|
|
data = [{
|
2019-06-21 14:27:09 -06:00
|
|
|
'error': 'You should select a Transmitter.'
|
2019-05-02 14:05:25 -06:00
|
|
|
}]
|
|
|
|
return JsonResponse(data, safe=False)
|
2019-06-21 14:27:09 -06:00
|
|
|
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)
|
2019-05-02 14:05:25 -06:00
|
|
|
|
2019-01-08 07:33:04 -07:00
|
|
|
stations = Station.objects.filter(status__gt=0)
|
|
|
|
available_stations = get_available_stations(stations, downlink, request.user)
|
2019-05-02 14:05:25 -06:00
|
|
|
import sys
|
|
|
|
sys.stdout.flush()
|
2019-01-08 07:33:04 -07:00
|
|
|
data = {
|
|
|
|
'stations': StationSerializer(available_stations, many=True).data,
|
|
|
|
}
|
|
|
|
|
|
|
|
return JsonResponse(data, safe=False)
|
|
|
|
|
|
|
|
|
2017-12-23 03:35:42 -07:00
|
|
|
@ajax_required
|
|
|
|
def pass_predictions(request, id):
|
|
|
|
"""Endpoint for pass predictions"""
|
2019-07-15 05:10:17 -06:00
|
|
|
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)
|
2017-12-23 03:35:42 -07:00
|
|
|
unsupported_frequencies = request.GET.get('unsupported_frequencies', '0')
|
|
|
|
|
2015-11-16 06:18:46 -07:00
|
|
|
try:
|
2019-07-24 04:57:18 -06:00
|
|
|
latest_tle_queryset = LatestTle.objects.all()
|
|
|
|
satellites = Satellite.objects.filter(
|
|
|
|
status='alive'
|
|
|
|
).prefetch_related(
|
|
|
|
Prefetch('tles', queryset=latest_tle_queryset, to_attr='tle')
|
|
|
|
)
|
2017-11-17 07:31:21 -07:00
|
|
|
except Satellite.DoesNotExist:
|
2015-11-16 06:18:46 -07:00
|
|
|
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
|
2019-01-23 00:00:45 -07:00
|
|
|
observer.horizon = str(station.horizon)
|
2015-11-16 06:18:46 -07:00
|
|
|
|
|
|
|
nextpasses = []
|
2019-06-18 05:31:13 -06:00
|
|
|
start = make_aware(datetime.utcnow(), utc)
|
|
|
|
end = make_aware(datetime.utcnow() + timedelta(hours=settings.STATION_UPCOMING_END), utc)
|
2019-05-02 14:05:25 -06:00
|
|
|
observation_min_start = (
|
2019-01-23 00:00:45 -07:00
|
|
|
datetime.utcnow() + timedelta(minutes=settings.OBSERVATION_DATE_MIN_START)
|
|
|
|
).strftime("%Y-%m-%d %H:%M:%S.%f")
|
2015-11-16 06:18:46 -07:00
|
|
|
|
2019-06-21 14:27:09 -06:00
|
|
|
try:
|
|
|
|
all_transmitters = get_transmitters_by_status('active')
|
|
|
|
except DBConnectionError:
|
|
|
|
all_transmitters = []
|
|
|
|
|
|
|
|
if all_transmitters:
|
2019-05-02 14:05:25 -06:00
|
|
|
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
|
2019-07-19 08:53:39 -06:00
|
|
|
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:
|
2019-05-02 14:05:25 -06:00
|
|
|
continue
|
2017-03-19 12:03:58 -06:00
|
|
|
|
2019-07-24 04:57:18 -06:00
|
|
|
if satellite.tle:
|
|
|
|
tle = satellite.tle[0]
|
|
|
|
else:
|
2019-05-02 14:05:25 -06:00
|
|
|
continue
|
2019-01-23 00:00:45 -07:00
|
|
|
|
2019-05-02 14:05:25 -06:00
|
|
|
station_passes, station_windows = predict_available_observation_windows(station,
|
|
|
|
None,
|
|
|
|
2,
|
|
|
|
tle,
|
2019-06-18 05:31:13 -06:00
|
|
|
start,
|
|
|
|
end,
|
2019-05-02 14:05:25 -06:00
|
|
|
satellite)
|
|
|
|
|
|
|
|
if station_windows:
|
2019-07-19 08:53:39 -06:00
|
|
|
satellite_stats = satellite_stats_by_transmitter_list(transmitters)
|
2019-05-02 14:05:25 -06:00
|
|
|
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),
|
2019-07-19 08:53:39 -06:00
|
|
|
'success_rate': str(satellite_stats['success_rate']),
|
|
|
|
'bad_rate': str(satellite_stats['bad_rate']),
|
2019-07-19 13:15:13 -06:00
|
|
|
'unvetted_rate': str(satellite_stats['unvetted_rate']),
|
|
|
|
'future_rate': str(satellite_stats['future_rate']),
|
2019-07-19 08:53:39 -06:00
|
|
|
'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']),
|
2019-07-19 13:15:13 -06:00
|
|
|
'future_count': str(satellite_stats['future_count']),
|
2019-05-02 14:05:25 -06:00
|
|
|
'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)
|
2015-11-16 06:18:46 -07:00
|
|
|
|
2017-12-23 03:35:42 -07:00
|
|
|
data = {
|
|
|
|
'id': id,
|
2018-06-10 15:53:01 -06:00
|
|
|
'nextpasses': sorted(nextpasses, key=itemgetter('tr')),
|
|
|
|
'ground_station': {'lng': str(station.lng),
|
|
|
|
'lat': str(station.lat),
|
|
|
|
'alt': station.alt}
|
2017-12-23 03:35:42 -07:00
|
|
|
}
|
2017-11-18 12:16:21 -07:00
|
|
|
|
2017-12-23 03:35:42 -07:00
|
|
|
return JsonResponse(data, safe=False)
|
2014-12-13 11:45:52 -07:00
|
|
|
|
|
|
|
|
2018-03-16 06:18:48 -06:00
|
|
|
def station_edit(request, id=None):
|
2014-12-13 11:45:52 -07:00
|
|
|
"""Edit or add a single station."""
|
2018-03-16 06:18:48 -06:00
|
|
|
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',
|
2018-12-06 11:39:37 -07:00
|
|
|
{'form': form, 'station': station, 'antennas': antennas})
|
2014-12-14 05:47:50 -07:00
|
|
|
else:
|
2018-03-16 06:18:48 -06:00
|
|
|
if station:
|
|
|
|
form = StationForm(instance=station)
|
|
|
|
else:
|
|
|
|
form = StationForm()
|
|
|
|
return render(request, 'base/station_edit.html',
|
2018-12-06 11:39:37 -07:00
|
|
|
{'form': form, 'station': station, 'antennas': antennas})
|
2015-08-24 06:13:59 -06:00
|
|
|
|
|
|
|
|
|
|
|
@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}))
|
2016-04-09 10:19:47 -06:00
|
|
|
|
|
|
|
|
2019-05-02 14:05:25 -06:00
|
|
|
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
|
2018-08-28 13:23:43 -06:00
|
|
|
|
|
|
|
|
2016-04-09 10:19:47 -06:00
|
|
|
def satellite_view(request, id):
|
|
|
|
try:
|
2017-11-17 07:31:21 -07:00
|
|
|
sat = Satellite.objects.get(norad_cat_id=id)
|
|
|
|
except Satellite.DoesNotExist:
|
2016-04-09 10:19:47 -06:00
|
|
|
data = {
|
|
|
|
'error': 'Unable to find that satellite.'
|
|
|
|
}
|
|
|
|
return JsonResponse(data, safe=False)
|
|
|
|
|
2019-06-21 14:27:09 -06:00
|
|
|
try:
|
|
|
|
transmitters = get_transmitters_by_norad_id(norad_id=id)
|
|
|
|
except DBConnectionError as e:
|
|
|
|
data = [{
|
|
|
|
'error': e.message
|
|
|
|
}]
|
2019-05-02 14:05:25 -06:00
|
|
|
return JsonResponse(data, safe=False)
|
2019-07-19 08:53:39 -06:00
|
|
|
satellite_stats = satellite_stats_by_transmitter_list(transmitters)
|
2016-04-09 10:19:47 -06:00
|
|
|
data = {
|
|
|
|
'id': id,
|
|
|
|
'name': sat.name,
|
|
|
|
'names': sat.names,
|
|
|
|
'image': sat.image,
|
2019-07-19 08:53:39 -06:00
|
|
|
'success_rate': satellite_stats['success_rate'],
|
|
|
|
'good_count': satellite_stats['good_count'],
|
|
|
|
'bad_count': satellite_stats['bad_count'],
|
2019-07-19 13:15:13 -06:00
|
|
|
'unvetted_count': satellite_stats['unvetted_count'],
|
|
|
|
'future_count': satellite_stats['future_count'],
|
2019-07-19 08:53:39 -06:00
|
|
|
'total_count': satellite_stats['total_count'],
|
2019-05-02 14:05:25 -06:00
|
|
|
'transmitters': transmitters_with_stats(transmitters)
|
2016-04-09 10:19:47 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return JsonResponse(data, safe=False)
|
2018-09-03 05:39:49 -06:00
|
|
|
|
|
|
|
|
2019-01-03 13:29:38 -07:00
|
|
|
def transmitters_view(request):
|
2019-05-02 14:05:25 -06:00
|
|
|
norad_id = request.POST['satellite']
|
2019-01-03 13:29:38 -07:00
|
|
|
station_id = request.POST.get('station_id', None)
|
2018-09-03 05:39:49 -06:00
|
|
|
try:
|
2019-05-02 14:05:25 -06:00
|
|
|
Satellite.objects.get(norad_cat_id=norad_id)
|
2018-09-03 05:39:49 -06:00
|
|
|
except Satellite.DoesNotExist:
|
|
|
|
data = {
|
|
|
|
'error': 'Unable to find that satellite.'
|
|
|
|
}
|
|
|
|
return JsonResponse(data, safe=False)
|
|
|
|
|
2019-06-21 14:27:09 -06:00
|
|
|
try:
|
|
|
|
transmitters = get_transmitters_by_norad_id(norad_id)
|
|
|
|
except DBConnectionError as e:
|
|
|
|
data = [{
|
|
|
|
'error': e.message
|
|
|
|
}]
|
2019-05-02 14:05:25 -06:00
|
|
|
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:
|
2019-06-21 14:37:03 -06:00
|
|
|
transmitter_supported = is_transmitter_in_station_range(transmitter, station)
|
2019-05-02 14:05:25 -06:00
|
|
|
if transmitter_supported:
|
|
|
|
supported_transmitters.append(transmitter)
|
|
|
|
transmitters = supported_transmitters
|
|
|
|
|
|
|
|
data = {
|
|
|
|
'transmitters': transmitters_with_stats(transmitters)
|
|
|
|
}
|
2018-09-03 05:39:49 -06:00
|
|
|
|
|
|
|
return JsonResponse(data, safe=False)
|