1
0
Fork 0

New SatNOGS DB user interface

Initial commit of new UI. There is still some work to be done before this goes into dev, but here is the work so far:

* Updated dependencies to latest 2.x django
* Updated to Bootstrap 4
* New home screen to display most recent satellite entries, most recent data, and contributors
* Adopted django-bootstrap-modal-forms for handling satellite and transmitter creation and update, with more of an emphasis on django's model/view/form model - and a dynamic flow where the modals and details are only loaded when the proper icon is clicked, reducing the overall page size
* Adopted AdminLTE 3.x framework atop Bootstrap 4
* Created reusable cards for satellite and transmitters
* Cards and Modals are organized into subdirectories for template includes and base templates, respectively
* New stats display widgets using BS4 and AdminLTE 3
* Satellite search is redesigned and now accessible from any page of the site
* Introduced datatables for an "All Satellites" view and a modification of the new "All Transmitters" view
* Focus on all UI scaling down to mobile devices
* New model created for Operator (/ Owner): name, names, description, website
* Added django-countries for support of CountryField
* Satellite model expanded to include: Operator, (satellite) website, countries, launched datetime, deployed datetime
* Transmitter suggestions can now be approved in the UI by superusers
* Satellite entries can now be edited in the UI by users with the change satellite permission
* Satellite page is now broken into 'tabbed' panels (Profile, Map, Transmitters, etc) - with the tab menu options appearing in the sidebar or at the top depending on screen size
* Other cleanup and changes that I'm missing for sure.

Signed-off-by: Corey Shields <cshields@gmail.com>
spacecruft
Corey Shields 2020-07-25 22:08:44 +00:00
parent 63a93fa1ed
commit a7141c5b30
65 changed files with 6213 additions and 2379 deletions

1
.gitignore vendored
View File

@ -6,6 +6,7 @@ env
.env
.cache
*.egg-info
*.DS_Store
# Logs
*.log

View File

@ -9,7 +9,7 @@ from django.shortcuts import redirect
from django.urls import reverse
from db.base.models import Artifact, DemodData, ExportedFrameset, Mode, \
Satellite, Telemetry, Transmitter, TransmitterEntry, \
Operator, Satellite, Telemetry, Transmitter, TransmitterEntry, \
TransmitterSuggestion
from db.base.tasks import check_celery, decode_all_data
@ -20,6 +20,13 @@ class ModeAdmin(admin.ModelAdmin):
list_display = ('name', )
@admin.register(Operator)
class OperatorAdmin(admin.ModelAdmin):
"""Defines Operator view in django admin UI"""
list_display = ('name', 'names', 'website')
search_fields = ('name', 'names')
@admin.register(Satellite)
class SatelliteAdmin(admin.ModelAdmin):
"""Defines Satellite view in django admin UI"""

View File

@ -0,0 +1,22 @@
[
{
"model": "base.operator",
"pk": 1,
"fields": {
"name": "Libre Space Foundation",
"names": "LSF",
"description": "The Libre Space Foundation promotes open source space technologies.",
"website": "https://libre.space"
}
},
{
"model": "base.operator",
"pk": 2,
"fields": {
"name": "Radio Amateur Satellite Corporation",
"names": "AMSAT",
"description": "The goal of AMSAT is to foster Amateur Radios participation in space research and communication.",
"website": "https://www.amsat.org"
}
}
]

View File

@ -1,9 +1,10 @@
"""SatNOGS DB django base Forms class"""
from django import forms
from bootstrap_modal_forms.forms import BSModalModelForm
from django.core.exceptions import ValidationError
from django.forms import NumberInput, TextInput
from django.utils.translation import ugettext_lazy as _
from db.base.models import Transmitter, TransmitterEntry
from db.base.models import Satellite, Transmitter, TransmitterEntry
def existing_uuid(value):
@ -18,15 +19,52 @@ def existing_uuid(value):
)
class TransmitterEntryForm(forms.ModelForm):
class TransmitterModelForm(BSModalModelForm): # pylint: disable=too-many-ancestors
"""Model Form class for TransmitterEntry objects"""
uuid = forms.CharField(required=False, validators=[existing_uuid])
class Meta:
model = TransmitterEntry
fields = [
'description', 'status', 'type', 'uplink_low', 'uplink_high', 'downlink_low',
'downlink_high', 'uplink_drift', 'downlink_drift', 'downlink_mode', 'uplink_mode',
'invert', 'baud', 'satellite', 'citation', 'service'
'invert', 'baud', 'citation', 'service'
]
widgets = {
'description': TextInput(),
}
class TransmitterUpdateForm(BSModalModelForm): # pylint: disable=too-many-ancestors
"""Model Form class for TransmitterEntry objects"""
class Meta:
model = TransmitterEntry
fields = [
'description', 'status', 'type', 'service', 'uplink_low', 'uplink_drift',
'uplink_high', 'downlink_low', 'uplink_mode', 'downlink_drift', 'downlink_high',
'downlink_mode', 'invert', 'baud', 'created', 'citation'
]
widgets = {
'description': TextInput(),
'created': TextInput(attrs={'readonly': True}),
}
class SatelliteModelForm(BSModalModelForm):
"""Form that uses django-bootstrap-modal-forms for satellite editing"""
class Meta:
model = Satellite
fields = [
'norad_cat_id', 'name', 'names', 'operator', 'status', 'description', 'countries',
'website', 'dashboard_url', 'launched', 'deployed', 'decayed', 'image'
]
labels = {
'norad_cat_id': _('Norad ID'),
'names': _('Other names'),
'countries': _('Countries of Origin'),
'launched': _('Launch Date'),
'deployed': _('Deploy Date'),
'decayed': _('Re-entry Date'),
'description': _('Description'),
'dashboard_url': _('Dashboard URL'),
'operator': _('Owner/Operator'),
}
widgets = {'norad_cat_id': NumberInput(attrs={'readonly': True}), 'names': TextInput()}

View File

@ -0,0 +1,50 @@
# Generated by Django 2.2.14 on 2020-07-15 12:02
from django.db import migrations, models
import django.db.models.deletion
import django_countries.fields
class Migration(migrations.Migration):
dependencies = [
('base', '0018_artifact'),
]
operations = [
migrations.CreateModel(
name='Operator',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, unique=True)),
('names', models.TextField(blank=True)),
('description', models.TextField(blank=True)),
('website', models.URLField(blank=True)),
],
),
migrations.AddField(
model_name='satellite',
name='countries',
field=django_countries.fields.CountryField(blank=True, max_length=746, multiple=True),
),
migrations.AddField(
model_name='satellite',
name='deployed',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='satellite',
name='launched',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='satellite',
name='website',
field=models.URLField(blank=True),
),
migrations.AddField(
model_name='satellite',
name='operator',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='satellite_operator', to='base.Operator'),
),
]

View File

@ -12,6 +12,7 @@ from django.db.models import OuterRef, Subquery
from django.db.models.signals import post_save, pre_save
from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now
from django_countries.fields import CountryField
from markdown import markdown
from shortuuidfield import ShortUUIDField
@ -86,6 +87,18 @@ class Mode(models.Model):
return self.name
@python_2_unicode_compatible
class Operator(models.Model):
"""Satellite Owner/Operator"""
name = models.CharField(max_length=255, unique=True)
names = models.TextField(blank=True)
description = models.TextField(blank=True)
website = models.URLField(blank=True)
def __str__(self):
return self.name
@python_2_unicode_compatible
class Satellite(models.Model):
"""Model for all the satellites."""
@ -103,6 +116,20 @@ class Satellite(models.Model):
)
decayed = models.DateTimeField(null=True, blank=True)
# new fields below, metasat etc
# countries is multiple for edge cases like ISS/Zarya
countries = CountryField(blank=True, multiple=True, blank_label='(select countries)')
website = models.URLField(blank=True)
launched = models.DateTimeField(null=True, blank=True)
deployed = models.DateTimeField(null=True, blank=True)
operator = models.ForeignKey(
Operator,
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='satellite_operator'
)
class Meta:
ordering = ['norad_cat_id']
@ -182,6 +209,47 @@ class Satellite(models.Model):
'redistributable': self.tle_redistributable
}
@property
def latest_data(self):
"""Returns the latest DemodData for this Satellite
:returns: dict with most recent DemodData for this Satellite
"""
data = DemodData.objects.filter(satellite=self.id).order_by('-id')[:1]
return {
'data_id': data[0].data_id,
'payload_frame': data[0].payload_frame,
'timestamp': data[0].timestamp,
'is_decoded': data[0].is_decoded,
'station': data[0].station,
'observer': data[0].observer,
}
@property
def needs_help(self):
"""Returns a boolean based on whether or not this Satellite could
use some editorial help based on a configurable threshold
:returns: bool
"""
score = 0
if self.description and self.description != '':
score += 1
if self.countries and self.countries != '':
score += 1
if self.website and self.website != '':
score += 1
if self.names and self.names != '':
score += 1
if self.launched and self.launched != '':
score += 1
if self.operator and self.operator != '':
score += 1
if self.image and self.image != '':
score += 1
return score <= 2
def __str__(self):
return '{0} - {1}'.format(self.norad_cat_id, self.name)

View File

@ -1,5 +1,7 @@
"""Django base URL routings for SatNOGS DB"""
from django.conf.urls import url
from django.contrib.auth.decorators import permission_required
from django.urls import path
from db.base import views
@ -7,8 +9,8 @@ BASE_URLPATTERNS = (
[
url(r'^$', views.home, name='home'),
url(r'^about/$', views.about, name='about'),
url(r'^transmitters/$', views.transmitters_list, name='transmitters_list'),
url(r'^faq/$', views.faq, name='faq'),
url(r'^satellites/$', views.satellites, name='satellites'),
url(r'^satellite/(?P<norad>[0-9]+)/$', views.satellite, name='satellite'),
url(r'^frames/(?P<norad>[0-9]+)/$', views.request_export, name='request_export_all'),
url(
@ -16,14 +18,32 @@ BASE_URLPATTERNS = (
views.request_export,
name='request_export'
),
url(r'^help/$', views.satnogs_help, name='help'),
url(
r'^transmitter_suggestion/$',
views.transmitter_suggestion,
name='transmitter_suggestion'
r'^transmitter_suggestion_handler/$',
views.transmitter_suggestion_handler,
name='transmitter_suggestion_handler'
),
url(r'^transmitters/$', views.transmitters_list, name='transmitters_list'),
url(r'^statistics/$', views.statistics, name='statistics'),
url(r'^stats/$', views.stats, name='stats'),
url(r'^users/edit/$', views.users_edit, name='users_edit'),
url(r'^robots\.txt$', views.robots, name='robots'),
url(r'^search/$', views.search, name='search_results'),
url(
r'^update_satellite/(?P<pk>[0-9]+)/$',
permission_required('base.change_satellite')(views.SatelliteUpdateView.as_view()),
name='update_satellite'
),
path(
'create_transmitter/<int:satellite_pk>',
views.TransmitterCreateView.as_view(),
name='create_transmitter'
),
path(
'update_transmitter/<int:pk>',
views.TransmitterUpdateView.as_view(),
name='update_transmitter'
),
]
)

View File

@ -1,40 +1,62 @@
"""Base django views for SatNOGS DB"""
import logging
from datetime import timedelta
from bootstrap_modal_forms.generic import BSModalCreateView, BSModalUpdateView
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.mixins import LoginRequiredMixin, \
PermissionRequiredMixin
from django.contrib.auth.models import User
from django.contrib.sites.shortcuts import get_current_site
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist
from django.db import OperationalError
from django.db.models import Count
from django.db.models import Count, Max, Q
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils import timezone
from django.views.decorators.http import require_POST
from db.base.forms import TransmitterEntryForm
from db.base.forms import SatelliteModelForm, TransmitterModelForm, \
TransmitterUpdateForm
from db.base.helpers import get_apikey
from db.base.models import SERVICE_TYPE, TRANSMITTER_STATUS, \
TRANSMITTER_TYPE, DemodData, Mode, Satellite, Transmitter, \
TransmitterSuggestion
TransmitterEntry, TransmitterSuggestion
from db.base.tasks import export_frames
from db.base.utils import cache_statistics
LOGGER = logging.getLogger('db')
def superuser_check(user):
"""Returns True if user is a superuser, for use with @user_passes_test
"""
return user.is_superuser
def home(request):
"""View to render home page.
:returns: base/home.html
"""
satellites = Satellite.objects.annotate(transmitters_count=Count('transmitter_entries'))
transmitter_suggestions = TransmitterSuggestion.objects.count()
contributors = User.objects.filter(is_active=1).count()
newest_sats = Satellite.objects.all().order_by('-id')[:5].annotate(
transmitters_count=Count('transmitter_entries')
)
latest_data = Satellite.objects.annotate(
latest=Max('telemetry_data__timestamp'), transmitters_count=Count('transmitter_entries')
).order_by('-latest')[:5]
# Calculate latest contributors
date_from = timezone.now() - timedelta(days=1)
latest_submitters = DemodData.objects.filter(timestamp__gte=date_from
).values('station').annotate(c=Count('station')
).order_by('-c')
suggestion_count = TransmitterSuggestion.objects.count()
contributor_count = User.objects.filter(is_active=1).count()
cached_stats = cache.get('stats_transmitters')
if not cached_stats:
try:
@ -44,10 +66,12 @@ def home(request):
pass
return render(
request, 'base/home.html', {
'satellites': satellites,
'newest_sats': newest_sats,
'latest_data': latest_data,
'latest_submitters': latest_submitters,
'statistics': cached_stats,
'contributors': contributors,
'transmitter_suggestions': transmitter_suggestions
'contributor_count': contributor_count,
'suggestion_count': suggestion_count
}
)
@ -72,6 +96,33 @@ def robots(request):
return response
def satellites(request):
"""View to render satellites page.
:returns: base/satellites.html
"""
satellite_objects = Satellite.objects.annotate(
transmitters_count=Count('transmitter_entries')
).prefetch_related('operator')
suggestion_count = TransmitterSuggestion.objects.count()
contributor_count = User.objects.filter(is_active=1).count()
cached_stats = cache.get('stats_transmitters')
if not cached_stats:
try:
cache_statistics()
cached_stats = cache.get('stats_transmitters')
except OperationalError:
pass
return render(
request, 'base/satellites.html', {
'satellites': satellite_objects,
'statistics': cached_stats,
'contributor_count': contributor_count,
'suggestion_count': suggestion_count
}
)
def satellite(request, norad):
"""View to render satellite page.
@ -132,68 +183,106 @@ def request_export(request, norad, period=None):
return redirect(reverse('satellite', kwargs={'norad': norad}))
# <cshields> leaving this in place for reference while the New UI is fixed up
# and the functionality below is moved into new modals accordingly.
# @login_required
# @require_POST
# def transmitter_suggestion(request):
# """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)
# transmitter.user = request.user
# transmitter.reviewed = False
# transmitter.approved = False
# uuid = transmitter_form.cleaned_data['uuid']
# if uuid:
# transmitter.uuid = uuid
# transmitter.save()
# # Notify admins
# admins = User.objects.filter(is_superuser=True)
# site = get_current_site(request)
# subject = '[{0}] A new suggestion for {1} was submitted'.format(
# site.name, transmitter.satellite.name
# )
# template = 'emails/new_transmitter_suggestion.txt'
# saturl = '{0}{1}'.format(
# site.domain,
# reverse('satellite', kwargs={'norad': transmitter.satellite.norad_cat_id})
# )
# data = {
# 'satname': transmitter.satellite.name,
# 'saturl': saturl,
# 'sitedomain': site.domain,
# 'contributor': transmitter.user
# }
# message = render_to_string(template, {'data': data})
# for user in admins:
# try:
# user.email_user(subject, message, from_email=settings.DEFAULT_FROM_EMAIL)
# except Exception: # pylint: disable=W0703
# LOGGER.error('Could not send email to user', exc_info=True)
# messages.success(
# request,
# ('Your transmitter suggestion was stored successfully. '
# 'Thanks for contibuting!')
# )
# redirect_page = redirect(
# reverse('satellite', kwargs={'norad': transmitter.satellite.norad_cat_id})
# )
# else:
# LOGGER.error(
# 'Suggestion form was not valid %s',
# format(transmitter_form.errors),
# exc_info=True,
# extra={
# 'form': transmitter_form.errors,
# }
# )
# messages.error(request, 'We are sorry, but some error occured :(')
# redirect_page = redirect(reverse('home'))
# return redirect_page
@login_required
@require_POST
def transmitter_suggestion(request):
"""View to process transmitter suggestion form
@user_passes_test(superuser_check)
def transmitter_suggestion_handler(request):
"""Returns the Satellite page after approving or rejecting a suggestion
:returns: the originating satellite page unless an error occurs
:returns: Satellite page
"""
transmitter_form = TransmitterEntryForm(request.POST)
if transmitter_form.is_valid():
transmitter = transmitter_form.save(commit=False)
transmitter.user = request.user
transmitter.reviewed = False
transmitter = TransmitterSuggestion.objects.get(uuid=request.POST['uuid'])
if 'approve' in request.POST:
transmitter.approved = True
messages.success(request, ('Transmitter approved.'))
elif 'reject' in request.POST:
transmitter.approved = False
uuid = transmitter_form.cleaned_data['uuid']
if uuid:
transmitter.uuid = uuid
transmitter.save()
messages.success(request, ('Transmitter rejected.'))
transmitter.reviewed = True
transmitter.created = timezone.now()
transmitter.user = request.user
# Notify admins
admins = User.objects.filter(is_superuser=True)
site = get_current_site(request)
subject = '[{0}] A new suggestion for {1} was submitted'.format(
site.name, transmitter.satellite.name
)
template = 'emails/new_transmitter_suggestion.txt'
saturl = '{0}{1}'.format(
site.domain,
reverse('satellite', kwargs={'norad': transmitter.satellite.norad_cat_id})
)
data = {
'satname': transmitter.satellite.name,
'saturl': saturl,
'sitedomain': site.domain,
'contributor': transmitter.user
}
message = render_to_string(template, {'data': data})
for user in admins:
try:
user.email_user(subject, message, from_email=settings.DEFAULT_FROM_EMAIL)
except Exception: # pylint: disable=W0703
LOGGER.error('Could not send email to user', exc_info=True)
# Need to determine if we will attribute the suggestion or the approval
# transmitter.user = request.user
messages.success(
request,
('Your transmitter suggestion was stored successfully. '
'Thanks for contibuting!')
)
redirect_page = redirect(
reverse('satellite', kwargs={'norad': transmitter.satellite.norad_cat_id})
)
else:
LOGGER.error(
'Suggestion form was not valid %s',
format(transmitter_form.errors),
exc_info=True,
extra={
'form': transmitter_form.errors,
}
)
messages.error(request, 'We are sorry, but some error occured :(')
redirect_page = redirect(reverse('home'))
transmitter.save()
# the way we handle suggestions in admin is to update the suggestion as
# reviewed and save a new object. This feels hacky but preserves the
# admin workflow
TransmitterSuggestion.objects.filter(uuid=request.POST['uuid']).update(
reviewed=True, approved=transmitter.approved
)
redirect_page = redirect(
reverse('satellite', kwargs={'norad': transmitter.satellite.norad_cat_id})
)
return redirect_page
@ -213,25 +302,68 @@ def faq(request):
return render(request, 'base/faq.html')
def satnogs_help(request):
"""View to render help modal. Have to avoid builtin 'help' name
:returns: base/modals/help.html
"""
return render(request, 'base/modals/satnogs_help.html')
def search(request):
"""View to render search page.
:returns: base/search.html
"""
if ('q' in request.GET) and request.GET['q'].strip():
query_string = request.GET['q']
results = Satellite.objects.filter(
Q(name__icontains=query_string) | Q(names__icontains=query_string)
| Q(norad_cat_id__icontains=query_string) # noqa: W503 google W503 it is evil
).order_by('name').annotate(transmitters_count=Count('transmitter_entries'))
if results.count() == 1:
return redirect(reverse('satellite', kwargs={'norad': results[0].norad_cat_id}))
# else (no-else-return)
return render(request, 'base/search.html', {
'results': results,
})
def stats(request):
"""View to render stats page.
:returns: base/stats.html
"""
satellites = []
cached_satellites = []
ids = cache.get('satellites_ids')
observers = cache.get('stats_observers')
suggestion_count = TransmitterSuggestion.objects.count()
contributor_count = User.objects.filter(is_active=1).count()
cached_stats = cache.get('stats_transmitters')
if not ids or not observers:
try:
cache_statistics()
cached_stats = cache.get('stats_transmitters')
ids = cache.get('satellites_ids')
observers = cache.get('stats_observers')
except OperationalError:
pass
else:
for sid in ids:
stat = cache.get(sid['id'])
satellites.append(stat)
for sid in ids:
stat = cache.get(sid['id'])
cached_satellites.append(stat)
return render(request, 'base/stats.html', {'satellites': satellites, 'observers': observers})
return render(
request, 'base/stats.html', {
'satellites': cached_satellites,
'observers': observers,
'statistics': cached_stats,
'contributor_count': contributor_count,
'suggestion_count': suggestion_count,
}
)
def statistics(request):
@ -253,4 +385,74 @@ def users_edit(request):
:returns: base/users_edit.html
"""
token = get_apikey(request.user)
return render(request, 'base/users_edit.html', {'token': token})
return render(request, 'base/modals/users_edit.html', {'token': token})
class TransmitterCreateView(LoginRequiredMixin, BSModalCreateView):
"""A django-bootstrap-modal-forms view for creating transmitter suggestions"""
template_name = 'base/modals/transmitter_create.html'
model = TransmitterEntry
form_class = TransmitterModelForm
success_message = 'Your transmitter suggestion was stored successfully and will be \
reviewed by a moderator. Thanks for contibuting!'
satellite = Satellite()
user = User()
def dispatch(self, request, *args, **kwargs):
"""
Overridden so we can make sure the `Satellite` instance exists first
"""
self.satellite = get_object_or_404(Satellite, pk=kwargs['satellite_pk'])
self.user = request.user
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
"""
Overridden to add the `Satellite` relation to the `Transmitter` instance.
"""
form.instance.satellite = self.satellite
form.instance.user = self.user
return super().form_valid(form)
def get_success_url(self):
return self.request.META.get('HTTP_REFERER')
class TransmitterUpdateView(LoginRequiredMixin, BSModalUpdateView):
"""A django-bootstrap-modal-forms view for updating transmitter entries"""
template_name = 'base/modals/transmitter_update.html'
model = TransmitterEntry
form_class = TransmitterUpdateForm
success_message = 'Your transmitter suggestion was stored successfully and will be \
reviewed by a moderator. Thanks for contibuting!'
user = User()
def get_initial(self):
initial = {}
initial['created'] = timezone.now()
return initial
def dispatch(self, request, *args, **kwargs):
self.user = request.user
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
form.instance.user = self.user
return super().form_valid(form)
def get_success_url(self):
return self.request.META.get('HTTP_REFERER')
class SatelliteUpdateView(PermissionRequiredMixin, BSModalUpdateView):
"""A django-bootstrap-modal-forms view for updating satellite fields"""
permission_required = 'base.change_satellite'
model = Satellite
template_name = 'base/modals/satellite_update.html'
form_class = SatelliteModelForm
success_message = 'Satellite was updated.'
def get_success_url(self):
return self.request.META.get('HTTP_REFERER')

View File

@ -30,9 +30,13 @@ DJANGO_APPS = (
)
THIRD_PARTY_APPS = (
'avatar',
'bootstrap_modal_forms',
'rest_framework',
'rest_framework.authtoken',
'django_countries',
'django_filters',
'fontawesome_5',
'widget_tweaks',
'allauth',
'allauth.account',
'allauth.socialaccount',
@ -159,8 +163,8 @@ STATICFILES_FINDERS = (
MEDIA_ROOT = config('MEDIA_ROOT', default=Path('media').resolve())
FILE_UPLOAD_TEMP_DIR = config('FILE_UPLOAD_TEMP_DIR', default=Path('/tmp').resolve())
MEDIA_URL = config('MEDIA_URL', default='/media/')
CRISPY_TEMPLATE_PACK = 'bootstrap3'
SATELLITE_DEFAULT_IMAGE = '/static/img/sat.png'
CRISPY_TEMPLATE_PACK = 'bootstrap4'
SATELLITE_DEFAULT_IMAGE = '/static/img/sat_purple.png'
COMPRESS_ENABLED = config('COMPRESS_ENABLED', default=False, cast=bool)
COMPRESS_OFFLINE = config('COMPRESS_OFFLINE', default=False, cast=bool)
COMPRESS_CACHE_BACKEND = config('COMPRESS_CACHE_BACKEND', default='default')
@ -279,6 +283,9 @@ CSP_DEFAULT_SRC = config(
cast=lambda v: tuple(s.strip() for s in v.split(',')),
default="'self',"
'https://*.mapbox.com,'
'https://kit-free.fontawesome.com,'
'https://ka-f.fontawesome.com,'
'https://fonts.gstatic.com,'
"'unsafe-inline'"
)
CSP_SCRIPT_SRC = config(
@ -286,8 +293,8 @@ CSP_SCRIPT_SRC = config(
cast=lambda v: tuple(s.strip() for s in v.split(',')),
default="'self',"
'https://*.google-analytics.com,'
"'unsafe-eval',"
"'unsafe-inline'"
'https://kit-free.fontawesome.com,'
'https://kit.fontawesome.com,'
)
CSP_IMG_SRC = config(
'CSP_IMG_SRC',
@ -302,6 +309,12 @@ CSP_IMG_SRC = config(
CSP_FRAME_SRC = config(
'CSP_FRAME_SRC', cast=lambda v: tuple(s.strip() for s in v.split(',')), default='blob:'
)
CSP_WORKER_SRC = config(
'CSP_WORKER_SRC',
cast=lambda v: tuple(s.strip() for s in v.split(',')),
default="'self',"
'blob:'
)
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True

View File

@ -4,7 +4,8 @@
@font-face {
font-family: ClearSans;
src: url('../fonts/ClearSans-Regular.eot');
src: url('../fonts/ClearSans-Regular.eot?#iefix') format('embedded-opentype'),
src: url('../fonts/ClearSans-Regular.eot?#iefix')
format('embedded-opentype'),
url('../fonts/ClearSans-Regular.woff') format('woff'),
url('../fonts/ClearSans-Regular.ttf') format('truetype'),
url('../fonts/ClearSans-Regular.svg#open_sans') format('svg');
@ -26,10 +27,12 @@
@font-face {
font-family: ClearSans;
src: url('../fonts/ClearSans-BoldItalic.eot');
src: url('../fonts/ClearSans-BoldItalic.eot?#iefix') format('embedded-opentype'),
src: url('../fonts/ClearSans-BoldItalic.eot?#iefix')
format('embedded-opentype'),
url('../fonts/ClearSans-BoldItalic.woff') format('woff'),
url('../fonts/ClearSans-BoldItalic.ttf') format('truetype'),
url('../fonts/ClearSans-BoldItalic.svg#open_sansbold_italic') format('svg');
url('../fonts/ClearSans-BoldItalic.svg#open_sansbold_italic')
format('svg');
font-weight: bold;
font-style: italic;
}
@ -48,12 +51,6 @@
/* Generic
==================== */
body {
font-size: 14px;
line-height: 1.3;
font-family: ClearSans;
}
.alert-debug {
color: black;
background-color: white;
@ -112,13 +109,13 @@ body {
}
#search {
margin: auto;
margin: 15px auto;
}
.statistics {
text-align: center;
text-shadow: 1px 1px 2px rgba(150, 150, 150, 0.77);
margin-top: 12px;
margin: 12px auto auto auto;
display: inline-block;
padding: 0 15px;
}
@ -166,6 +163,7 @@ a.satellite-item:hover {
min-height: 30px;
padding-top: 5px;
display: inline-block;
color: var(--satnogs-color-dark);
}
.satellite-title .badge {
@ -192,7 +190,7 @@ a.satellite-item:hover {
}
.satellite-status {
font-size: .9em;
font-size: 0.9em;
}
.satellite-status[data-status='alive'] {
@ -218,7 +216,9 @@ a.satellite-item:hover {
#map {
width: 100%;
height: 250px;
/* height: 250px; */
height: 100%;
min-height: 250px;
border-radius: 5px;
}
@ -232,13 +232,13 @@ a.satellite-item:hover {
}
.satellite-img-full {
width: 100%;
/* width: 100%; */
max-height: 250px;
border-radius: 5px;
}
.panel-transmitter {
margin: 20px 0;
margin: 10px 0;
}
.panel-tle {
@ -250,18 +250,14 @@ a.satellite-item:hover {
}
.suggest-transmitter {
margin-top: -6px;
margin-right: 6px;
}
.transmitter-uuid {
margin-top: -6px;
margin-left: 6px;
}
.transmitter-citation {
/* .transmitter-citation {
margin-top: -8px;
margin-left: 6px;
}
margin: 6px 10px 6px 10px;
} */
.transmitter-suggestions-counter {
display: inline-block;
@ -277,12 +273,19 @@ a.satellite-item:hover {
margin: 10px;
}
.transmitter-badge {
font-size: 1em;
}
.transmitter-element-suggest {
margin-bottom: 10px;
}
.transmitter-citation-footer {
font-size: .8em;
.transmitter-card-footer {
font-size: 0.8em;
height: 100%;
padding: 6px 10px 6px 10px;
background-color: var(--satnogs-color-white);
}
.tle-element {
@ -364,7 +367,7 @@ footer {
============================= */
.chart text {
color: #333;
color: var(--satnogs-color-dark);
font-size: 12px;
}
@ -401,7 +404,8 @@ svg.chart {
border-radius: 3px;
}
.tick line, .domain {
.tick line,
.domain {
fill: none;
stroke: #ddd;
}
@ -411,3 +415,583 @@ svg.chart {
stroke: #286090;
stroke-width: 2;
}
/* New UI CSS
============= */
/* Theme */
.bg-gradient-primary {
background-color: #4e73df;
background-image: linear-gradient(180deg, #4e73df 10%, #224abe 100%);
background-size: cover;
}
.bg-gradient-secondary {
background-color: #858796;
background-image: linear-gradient(180deg, #858796 10%, #60616f 100%);
background-size: cover;
}
.bg-gradient-success {
background-color: #1cc88a;
background-image: linear-gradient(180deg, #1cc88a 10%, #13855c 100%);
background-size: cover;
}
.bg-gradient-info {
background-color: #36b9cc;
background-image: linear-gradient(180deg, #36b9cc 10%, #258391 100%);
background-size: cover;
}
.bg-gradient-warning {
background-color: #f6c23e;
background-image: linear-gradient(180deg, #f6c23e 10%, #dda20a 100%);
background-size: cover;
}
.bg-gradient-danger {
background-color: #e74a3b;
background-image: linear-gradient(180deg, #e74a3b 10%, #be2617 100%);
background-size: cover;
}
.bg-gradient-light {
background-color: #f8f9fc;
background-image: linear-gradient(180deg, #f8f9fc 10%, #c2cbe5 100%);
background-size: cover;
}
.bg-gradient-dark {
background-color: #5a5c69;
background-image: linear-gradient(180deg, #5a5c69 10%, #373840 100%);
background-size: cover;
}
.bg-gray-100 {
background-color: #f8f9fc;
}
.bg-gray-200 {
background-color: #eaecf4;
}
.bg-gray-300 {
background-color: #dddfeb;
}
.bg-gray-400 {
background-color: #d1d3e2;
}
.bg-gray-500 {
background-color: #b7b9cc;
}
.bg-gray-600 {
background-color: #858796;
}
.bg-gray-700 {
background-color: #6e707e;
}
.bg-gray-800 {
background-color: #5a5c69;
}
.bg-gray-900 {
background-color: #3a3b45;
}
.o-hidden {
overflow: hidden;
}
.text-xs {
font-size: 0.7rem;
}
.text-lg {
font-size: 1.2rem;
}
.text-gray-100 {
color: #f8f9fc;
}
.text-gray-200 {
color: #eaecf4;
}
.text-gray-300 {
color: #dddfeb;
}
.text-gray-400 {
color: #d1d3e2;
}
.text-gray-500 {
color: #b7b9cc;
}
.text-gray-600 {
color: #858796;
}
.text-gray-700 {
color: #6e707e;
}
.text-gray-800 {
color: #5a5c69;
}
.text-gray-900 {
color: #3a3b45;
}
.icon-circle {
height: 2.5rem;
width: 2.5rem;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.border-left-primary {
border-left: 0.25rem solid var(--satnogs-color-primary);
}
.border-bottom-primary {
border-bottom: 0.25rem solid var(--satnogs-color-primary);
}
.border-left-active {
border-left: 0.25rem solid #dff0d8;
/* color: #3c763d;
background-color: #dff0d8;
border-color: #d6e9c6; */
}
.border-left-secondary {
border-left: 0.25rem solid #858796;
}
.border-bottom-secondary {
border-bottom: 0.25rem solid #858796;
}
.border-left-success {
border-left: 0.25rem solid #1cc88a;
}
.border-bottom-success {
border-bottom: 0.25rem solid #1cc88a;
}
.border-left-info {
border-left: 0.25rem solid #36b9cc;
}
.border-bottom-info {
border-bottom: 0.25rem solid #36b9cc;
}
.border-left-warning {
border-left: 0.25rem solid #f6c23e;
}
.border-bottom-warning {
border-bottom: 0.25rem solid #f6c23e;
}
.border-left-danger {
border-left: 0.25rem solid #e74a3b;
}
.border-bottom-danger {
border-bottom: 0.25rem solid #e74a3b;
}
.border-left-light {
border-left: 0.25rem solid #f8f9fc;
}
.border-bottom-light {
border-bottom: 0.25rem solid #f8f9fc;
}
.border-left-dark {
border-left: 0.25rem solid #5a5c69;
}
.border-bottom-dark {
border-bottom: 0.25rem solid #5a5c69;
}
@-webkit-keyframes grow_in {
0% {
transform: scale(0.9);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes grow_in {
0% {
transform: scale(0.9);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.animated--grow-in,
.sidebar .nav-item .collapse {
-webkit-animation-name: grow_in;
animation-name: grow_in;
-webkit-animation-duration: 200ms;
animation-duration: 200ms;
-webkit-animation-timing-function: transform
cubic-bezier(0.18, 1.25, 0.4, 1),
opacity cubic-bezier(0, 1, 0.4, 1);
animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1),
opacity cubic-bezier(0, 1, 0.4, 1);
}
@-webkit-keyframes fade_in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fade_in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.animated--fade-in {
-webkit-animation-name: fade_in;
animation-name: fade_in;
-webkit-animation-duration: 200ms;
animation-duration: 200ms;
-webkit-animation-timing-function: opacity cubic-bezier(0, 1, 0.4, 1);
animation-timing-function: opacity cubic-bezier(0, 1, 0.4, 1);
}
#satnogs-menu {
background-color: var(--satnogs-color-primary);
height: 30px;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 0;
margin-top: 0;
margin-bottom: 0;
}
#satnogs-navbar {
margin-right: 15px;
}
.satnogs-navbar-link {
margin-right: 5px;
margin-left: 5px;
font-family: Roboto, Helvetica, Arial, sans-serif;
font-weight: 700;
letter-spacing: 1.6px;
font-size: 13px;
color: var(--satnogs-color-white);
}
.satnogs-navbar-link:hover {
text-decoration: none;
color: var(--satnogs-color-white);
}
#db-menu {
background-color: var(--satnogs-color-menu-background);
height: 30px;
}
#db-menu-col {
height: 30px;
/* border-color: var(--satnogs-color-primary); */
padding-right: 0;
padding-left: 0;
}
#db-menu-navbar {
/* border-color: var(--satnogs-color-primary); */
padding-right: 0;
padding-left: 0;
height: 30px;
}
.db-menu-link {
font-family: Roboto, Helvetica, Arial, sans-serif;
font-weight: 700;
font-size: 13px;
letter-spacing: 1.6px;
height: 30px;
padding-left: 0;
/* color: rgba(0,0,0,0.5); */
color: var(--satnogs-color-menu-text);
}
.db-menu-link:hover {
text-decoration: none;
color: var(--satnogs-color-menu-hover);
}
.satnogs-card-header {
padding: 5px 15px 5px 15px;
height: 100%;
background-color: var(--satnogs-color-header);
border-color: var(--satnogs-color-border);
}
.satnogs-card-body {
padding: 5px 15px 5px 15px;
background-color: var(--satnogs-color-background);
border-color: var(--satnogs-color-border);
}
.satnogs-card {
background-color: var(--satnogs-color-card-background);
border-color: var(--satnogs-color-border);
}
.transmitter-card-header {
padding: 6px 10px 6px 10px;
margin-bottom: 0;
background-color: var(--satnogs-color-white);
/* height: 34px; */
}
.transmitter-title {
font-size: 16px;
}
.panel-title {
/* width: 100%; */
padding-right: 0;
padding-left: 0;
}
.satellite-panels,
.stats-panel {
margin-top: 15px;
}
.transmitter-inactive {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
.transmitter-active {
color: #3c763d;
background-color: #dff0d8;
border-color: #d6e9c6;
}
.transmitter-card-body {
padding: 10px 10px;
}
/* Color pallete */
.text-satnogs-white {
color: var(--satnogs-color-white);
}
.text-satnogs-dark {
color: var(--satnogs-color-dark);
}
.text-satnogs-text {
color: var(--satnogs-color-text);
}
.text-satnogs-background {
color: var(--satnogs-color-background);
}
.text-satnogs-primary {
color: var(--satnogs-color-primary);
}
.text-satnogs-active {
color: #3c763d;
}
.text-satnogs-inactive {
color: #a94442;
}
.background-satnogs {
background-color: var(--satnogs-color-background);
}
.background-satnogs-light {
background-color: var(--satnogs-color-white);
}
.background-satnogs-primary {
background-color: var(--satnogs-color-primary);
}
.background-satnogs-header {
background-color: var(--satnogs-color-header);
}
.text-satnogs-header {
color: var(--satnogs-color-text);
}
.text-satnogs {
color: var(--satnogs-color-text);
}
.text-satnogs-headline {
color: var(--satnogs-color-headline);
}
.satnogs-icon {
color: var(--satnogs-color-icon);
}
body {
background: var(--color-scheme-background);
color: var(--color-scheme-text-color);
font-size: 14px;
line-height: 1.3;
font-family: ClearSans;
}
.nav-link.active {
color: var(--satnogs-color-menu-hover);
}
:root {
--satnogs-color-primary: #656fdb;
--satnogs-color-dark: #4d4e59;
--satnogs-color-text: #7c81b2;
--satnogs-color-background: #f2f2f7;
--satnogs-color-header: #f2f2f7;
--satnogs-color-border: #f2f2f7;
--satnogs-color-white: #f2f2f7;
--satnogs-color-card-background: #f2f2f7;
--satnogs-color-icon: #656fdb;
--satnogs-color-headline: #656fdb;
--satnogs-color-menu-background: #f2f2f7;
--satnogs-color-menu-text: #4d4e59;
--satnogs-color-menu-hover: #656fdb;
--color-scheme-background: white;
--color-scheme-text-color: #4d4e59;
}
a {
color: var(--satnogs-color-primary);
}
.satellite-card-body a:link,
.satellite-card-body a:visited,
.satellite-card-body a:hover {
color: var(--color-scheme-text-color);
text-decoration: none;
}
.satellite-card-body-row {
width: 40px;
}
td.details-control {
text-align: center;
color: forestgreen;
cursor: pointer;
}
tr.shown td.details-control {
text-align: center;
color: red;
}
.table {
color: var(--satnogs-color-dark);
}
.page-item.active .page-link {
background-color: var(--satnogs-color-primary);
border-color: var(--satnogs-color-primary);
}
.page-link {
color: var(--satnogs-color-primary);
}
/* Dark mode */
/* @media (prefers-color-scheme: dark) {
:root {
--satnogs-color-primary:#656FDB;
--satnogs-color-dark: #4d4e59;
--satnogs-color-text: #F2F2F7;
--satnogs-color-background: #4D4E59;
--satnogs-color-header: #656fdb;
--satnogs-color-border: #656fdb;
--satnogs-color-white: #F2F2F7;
--satnogs-color-card-background: #7c81b2;
--satnogs-color-icon:#4d4e59;
--satnogs-color-headline:#f2f2f7;
--satnogs-color-menu-background: #7c81b2;
--satnogs-color-menu-text: #4d4e59;
--satnogs-color-menu-hover: #F2F2F7;
--color-scheme-background: #4d4e59;
--color-scheme-text-color: #F2F2F7;
}
} */
/* Light mode */
/* @media (prefers-color-scheme: light) {
:root {
--satnogs-color-primary:#656FDB;
--satnogs-color-dark: #4d4e59;
--satnogs-color-text: #7c81b2;
--satnogs-color-background: #F2F2F7;
--satnogs-color-header: #F2F2F7;
--satnogs-color-border: #F2F2F7;
--satnogs-color-white: #F2F2F7;
--satnogs-color-card-background: #f2f2f7;
--satnogs-color-icon: #656FDB;
--satnogs-color-headline:#656FDB;
--satnogs-color-menu-background: #F2F2F7;
--satnogs-color-menu-text: #4d4e59;
--satnogs-color-menu-hover: #656FDB;
--color-scheme-background: white;
--color-scheme-text-color: #4d4e59;
}
} */

View File

@ -0,0 +1,376 @@
/* transitional / new CSS file for the new SatNOGS-DB UI */
:root {
--satnogs-color-primary: #656fdb;
--satnogs-color-dark: #4d4e59;
--satnogs-color-text: #7c81b2;
--satnogs-color-background: #f2f2f7;
--satnogs-color-header: #f2f2f7;
--satnogs-color-border: #f2f2f7;
--satnogs-color-white: #f2f2f7;
--satnogs-color-card-background: #f2f2f7;
--satnogs-color-icon: #656fdb;
--satnogs-color-headline: #656fdb;
--satnogs-color-menu-background: #f2f2f7;
--satnogs-color-menu-text: #4d4e59;
--satnogs-color-menu-hover: #656fdb;
--color-scheme-background: white;
--color-scheme-text-color: #4d4e59;
}
.satellite-title {
font-weight: bold;
font-size: 1.2em;
color: var(--satnogs-color-dark);
}
.text-satnogs-white {
color: var(--satnogs-color-white);
}
.text-satnogs-dark {
color: var(--satnogs-color-dark);
}
.text-satnogs-text {
color: var(--satnogs-color-text);
}
.text-satnogs-background {
color: var(--satnogs-color-background);
}
.text-satnogs-primary {
color: var(--satnogs-color-primary);
}
.text-satnogs-active {
color: #3c763d;
}
.text-satnogs-inactive {
color: #a94442;
}
.background-satnogs {
background-color: var(--satnogs-color-background);
}
.background-satnogs-light {
background-color: var(--satnogs-color-white);
}
.background-satnogs-primary {
background-color: var(--satnogs-color-primary);
}
.background-satnogs-header {
background-color: var(--satnogs-color-header);
}
.text-satnogs-header {
color: var(--satnogs-color-text);
}
.text-satnogs {
color: var(--satnogs-color-text);
}
.text-satnogs-headline {
color: var(--satnogs-color-headline);
}
.satnogs-icon {
color: var(--satnogs-color-icon);
}
.c-app:not(.c-legacy-theme):not(.c-dark-theme) .c-header.c-header-fixed {
border: 0;
box-shadow: 0 2px 2px 0 rgba(60, 75, 100, 0.14),
0 3px 1px -2px rgba(60, 75, 100, 0.12),
0 1px 5px 0 rgba(60, 75, 100, 0.2);
}
#map {
min-height: 250px;
position: relative;
top: 0;
bottom: 0;
width: 100%;
border-radius: 5px;
}
.mapbox-improve-map,
.mapboxgl-ctrl-bottom-left {
display: none;
}
.mapboxgl-canvas-container {
height: 60vh;
}
.satellite-card-body a:link,
.satellite-card-body a:visited,
.satellite-card-body a:hover {
color: var(--color-scheme-text-color);
text-decoration: none;
}
.satellite-card-body-row {
width: 40px;
}
.satellite-img-full {
max-width: 100%;
max-height: 250px;
border-radius: 5px;
}
.c-avatar {
position: relative;
display: inline-flex;
-ms-flex-align: center;
align-items: center;
-ms-flex-pack: center;
justify-content: center;
border-radius: 50em;
width: 36px;
height: 36px;
font-size: 14.4px;
}
.c-avatar .c-avatar-status {
width: 10px;
height: 10px;
}
.c-avatar-img {
width: 100%;
height: auto;
border-radius: 50em;
}
.c-avatar-status {
position: absolute;
bottom: 0;
display: block;
border: 1px solid #fff;
border-radius: 50em;
}
.card-satnogs,
.card-satnogs > a,
.jumbotron-satnogs {
color: var(--satnogs-color-dark);
}
.card-satnogs.card-outline {
border-top: 3px solid var(--satnogs-color-primary);
}
.card-satnogs.card-outline-tabs > .card-header a:hover {
border-top: 3px solid #dee2e6;
}
.card-satnogs.card-outline-tabs > .card-header a.active {
border-top: 3px solid var(--satnogs-color-primary);
}
.sat-menu-panel {
position: relative;
}
[class*='sidebar-dark'] .sat-menu-panel {
border-top: 1px solid #4f5962;
}
[class*='sidebar-light'] .sat-menu-panel {
border-top: 1px solid #dee2e6;
}
.sat-menu-panel,
.sat-menu-panel .info {
overflow: hidden;
white-space: nowrap;
}
.sat-menu-panel .image {
display: inline-block;
padding-left: 0.8rem;
}
.sat-menu-panel img {
height: auto;
width: 2.1rem;
}
.sat-menu-panel .info {
display: inline-block;
padding: 5px 5px 5px 10px;
}
.sat-menu-panel .status,
.sat-menu-panel .dropdown-menu {
font-size: 0.875rem;
}
.sat-menu-panel .sat-name {
color: #c2c7d0;
font-weight: bold;
}
.navbar-nav > .nav-item > .nav-link.active,
.nav > .nav-item > .nav-link.active,
.sidebar-dark-primary .nav-sidebar > .nav-item > .nav-link.active,
.sidebar-light-primary .nav-sidebar > .nav-item > .nav-link.active {
background-color: var(--satnogs-color-primary);
color: #fff;
}
.nav-pills .nav-link:not(.active):hover {
color: var(--satnogs-color-primary);
}
.dropdown-item.active, .dropdown-item:active {
background-color: var(--satnogs-color-primary);
}
.brand-text {
/* color: var(--satnogs-color-primary); */
color: #b9b9b9;
font-weight: bold;
}
a {
color: var(--satnogs-color-primary);
}
.page-item.active .page-link {
z-index: 3;
color: #fff;
background-color: var(--satnogs-color-primary);
border-color: var(--satnogs-color-primary);
}
.page-link {
color: var(--satnogs-color-primary);
background-color: #fff;
border: 1px solid #dee2e6;
}
.satnogs-nav-sat {
font-size: small;
}
.satnogs-nav-sat > .nav-link {
padding-bottom: 0;
}
.satnogs-nav-sat .nav-link {
padding: 0;
line-height: 1;
}
.satnogs-nav-sat > .nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: var(--satnogs-color-primary);
}
.satnogs-nav-sat > .nav-pills .nav-link:not(.active):hover {
color: var(--satnogs-color-primary);
}
.satnogs-nav-sat > .nav-tabs .nav-link.active, .nav-tabs .show > .nav-link {
color: #fff;
background-color: var(--satnogs-color-primary);
padding-top: 2px;
}
.satnogs-nav-sat > .nav-tabs .nav-link:not(.active):hover {
color: var(--satnogs-color-primary);
}
.navbar-nav > .nav-item > .brand-link {
padding: 0 0 0 0;
}
.nav-sidebar > .nav-item .nav-icon {
font-size: 24px;
}
.small-box-satnogs {
background-color: var(--satnogs-color-text);
color: rgba(255, 255, 255, .8);
}
.sat-menu-panel > .image > .fas {
color: var(--satnogs-color-primary);
}
.timeline > .time-label > .background-satnogs-primary,
.timeline > div > .background-satnogs-primary {
background-color: var(--satnogs-color-primary);
color: rgba(255, 255, 255, .8);
}
.nav-sidebar .nav-item > .nav-link.disabled {
color: #6c757d;
pointer-events: none;
cursor: default;
}
.freqlabel {
display: inline-block;
width: 75px;
padding: 3px;
}
.btn-satnogs-primary,
.badge-satnogs-primary {
background-color: var(--satnogs-color-primary);
color: rgba(255, 255, 255, .8);
}
.nav-link.active > .badge-satnogs-primary {
color: rgba(255, 255, 255, .8);
background-color: var(--satnogs-color-text);
}
.btn-satnogs {
color: var(--satnogs-color-dark);
}
.btn-navbar-satnogs {
color: #939ba2;
opacity: 1;
border: 1px solid #56606a;
}
a.dropdown-item {
color: var(--satnogs-color-dark);
}
.transmitter-element-suggest {
margin-bottom: 10px;
}
[class*='sidebar-dark'] .user-panel {
border-bottom: none;
}
[class*='sidebar-dark'] .satnogs-sidebar-footer,
[class*='sidebar-dark'] .satnogs-sidebar-footer a {
color: rgba(255, 255, 255, .5);
font-size: .7rem;
}
.toasts-top-right {
position: absolute;
right: 1rem;
top: 4.25rem;
}
.toast.alert-warning > .toast-header {
background-color: rgba(255, 255, 255, .2);
color: #000;
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,3 +1,4 @@
/* eslint new-cap: "off" */
$(document).ready(function() {
'use strict';
@ -6,5 +7,39 @@ $(document).ready(function() {
$('#copy').text(current_year);
// Enable tooltips
$('[data-toggle="tooltip"]').tooltip();
// $('[data-toggle="tooltip"]').tooltip();
// $('[data-toggle="popover"]').popover();
// User Settings / API Modal Form Link
$('.basemodal-link').each(function () {
$(this).modalForm({
formURL: $(this).data('form-url'),
modalID: '#basemodal'
});
$(this).click(function() {
$('#control-sidebar-toggle').ControlSidebar('toggle');
});
});
// Transitional from inline alerts to Toasts
$('.alert').each(function() {
var alerticon = 'fas fa-question';
var alerttitle = 'Unknown';
if ($(this).data('alertclass') == 'alert-success') {
alerticon = 'far fa-thumbs-up';
alerttitle = 'Success';
}
if ($(this).data('alertclass') == 'alert-warning') {
alerticon = 'fas fa-exclamation';
alerttitle = 'Alert';
}
$(document).Toasts('create', {
class: $(this).data('alertclass'),
title: alerttitle,
autohide: true,
delay: 6000,
icon: alerticon,
body: $(this).data('alertmessage')
});
});
});

View File

@ -119,7 +119,8 @@ $(document).ready(function() {
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/pierros/cj8kftshl4zll2slbelhkndwo',
// style: 'mapbox://styles/pierros/cj8kftshl4zll2slbelhkndwo',
style: 'mapbox://styles/cshields/ckc1a24y45smb1ht9bbhrcrk6',
zoom: 2,
center: sat_location
});
@ -223,4 +224,10 @@ $(document).ready(function() {
}
setInterval(update_map, 5000);
});
// couldn't get this to work with shown.bs.tab, have to go with click
// timeout is necessary for the first click for some reason
document.getElementById('mapcontent-tab').addEventListener('click', function() {
setTimeout( function() { map.resize();}, 200);
});
});

View File

@ -1,3 +1,4 @@
/* eslint new-cap: "off" */
function copyToClipboard(text, el) {
var copyTest = document.queryCommandSupported('copy');
var elOriginalText = el.attr('data-original-title');
@ -41,7 +42,7 @@ function transmitter_suggestion_type(selection) {
$('.input-group').has('input[name=\'invert\']').hide();
$('.input-group').has('select[name=\'uplink_mode\']').hide();
$('.input-group-addon:contains(\'Downlink Low\')').html('Downlink');
$('.input-group-prepend:contains(\'Downlink Low\')').html('<span class="input-group-text">Downlink</span>');
break;
case 'Transceiver':
$('.input-group').show();
@ -54,15 +55,15 @@ function transmitter_suggestion_type(selection) {
$('.input-group').has('input[name=\'downlink_high\']').hide();
$('.input-group').has('input[name=\'invert\']').hide();
$('input[name=\'downlink_low\']').prev().html('Downlink');
$('input[name=\'uplink_low\']').prev().html('Uplink');
$('input[name=\'downlink_low\']').prev().html('<span class="input-group-text">Downlink</span>');
$('input[name=\'uplink_low\']').prev().html('<span class="input-group-text">Uplink</span>');
break;
case 'Transponder':
$('.input-group').show();
$('input').prop( 'disabled', false );
$('select').prop( 'disabled', false );
$('input[name=\'downlink_low\']').prev().html('Downlink Low');
$('input[name=\'uplink_low\']').prev().html('Uplink Low');
$('input[name=\'downlink_low\']').prev().html('<span class="input-group-text">Downlink Low</span>');
$('input[name=\'uplink_low\']').prev().html('<span class="input-group-text">Uplink Low</span>');
break;
}
}
@ -97,7 +98,7 @@ $(document).ready(function() {
transmitter_suggestion_type(selection);
});
$('.transmitter_suggestion-edit-modal').on('show.bs.modal', function(){
$('.transmitter_suggestion-modal').on('show.bs.modal', function(){
var selection = $(this).find('.transmitter_suggestion-type').val();
transmitter_suggestion_type(selection);
@ -114,10 +115,6 @@ $(document).ready(function() {
}
});
$('#NewSuggestionModal').on('show.bs.modal', function(){
transmitter_suggestion_type('Transmitter');
});
// Calculate the drifted frequencies
$('.drifted').each(function() {
var drifted = ppb_to_freq($(this).data('freq_or'),$(this).data('drift'));
@ -126,13 +123,13 @@ $(document).ready(function() {
$('.uplink-drifted-sugedit').on('change click', function(){
var freq_obs = parseInt($(this).val());
var freq = parseInt($('.in input[name=\'uplink_low\']').val());
var freq = parseInt($('input[name=\'uplink_low\']:visible').val());
$('.uplink-ppb-sugedit').val(freq_to_ppb(freq_obs,freq));
});
$('.downlink-drifted-sugedit').on('change click', function(){
var freq_obs = parseInt($(this).val());
var freq = parseInt($('.in input[name=\'downlink_low\']').val());
var freq = parseInt($('input[name=\'downlink_low\']:visible').val());
$('.downlink-ppb-sugedit').val(freq_to_ppb(freq_obs,freq));
});
@ -148,4 +145,39 @@ $(document).ready(function() {
var el = $(this);
copyToClipboard(text, el);
});
// Update Satellite
$('.bs-modal').each(function () {
$(this).modalForm({
formURL: $(this).data('form-url')
});
});
// Update Transmitter
$('.update-transmitter-link').each(function () {
$(this).modalForm({
formURL: $(this).data('form-url'),
modalID: '#update-transmitter-modal'
});
});
// New transmitter links
$('.create-transmitter-link').each(function () {
$(this).modalForm({
formURL: $(this).data('form-url'),
modalID: '#create-transmitter-modal'
});
});
// Ask for help in a toast if this Satellite object is flagged as in need
if ($('#satellite_name').data('needshelp') == 'True') {
$(document).Toasts('create', {
title: 'Please Help!',
class: 'alert-warning',
autohide: true,
delay: 6000,
icon: 'fas fa-hand-holding-medical',
body: 'This Satellite needs editing. <a href="https://wiki.satnogs.org/Get_In_Touch" target="_blank">Contact us</a> to become an editor.'
});
}
});

View File

@ -0,0 +1,42 @@
/* eslint new-cap: "off" */
$(document).ready(function() {
$('#sats').DataTable( {
// the dom field controls the layout and visibility of datatable items
// and is not intuitive at all. Without layout we have dom: 'Bftrilp'
// https://datatables.net/reference/option/dom
dom: '<"row"<"d-none d-md-block col-md-6"B><"col-sm-12 col-md-6"f>>' +
'<"row"<"col-sm-12"tr>>' +
'<"row"<"col-sm-12 col-xl-3"i><"col-sm-12 col-md-6 col-xl-3"l><"col-sm-12 col-md-6 col-xl-6"p>>',
buttons: [
'colvis'
],
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.childRow,
type: 'column'
}
},
columnDefs: [
{
className: 'control',
orderable: false,
targets: 0
},
],
language: {
search: 'Filter:',
buttons: {
colvis: 'Columns',
}
},
order: [ 1, 'asc' ],
pageLength: 25
} );
// Update Satellite
$('.bs-modal').each(function () {
$(this).modalForm({
formURL: $(this).data('form-url')
});
});
} );

View File

@ -75,30 +75,40 @@ $(document).ready(function() {
$('#transmitters-numbers').hide();
} else {
var i;
var r;
var g;
var b;
var a;
var h;
var s;
var l;
var color;
var mode_total = 0;
var band_total = 0;
// Create colors for Mode Chart
var mode_colors = [];
for (i = 0; i < data.mode_label.length; i++) {
r = Math.floor(data.mode_data[i]* 10);
b = Math.floor(0.3 * 255);
g = Math.floor(data.mode_data[i]* 10);
a = 0.5;
color = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
mode_total += data.mode_data[i];
}
for (i = 0; i < data.band_label.length; i++) {
band_total += data.band_data[i];
}
for (i = 0; i < data.mode_label.length; i++) {
// Switching to HSL to stick with hue of LSF logo
h = 235;
l = data.mode_data[i]/mode_total*100;
l *= 3; // adjust for better visibility
s = data.mode_data[i]/mode_total*100;
s *= 3; // adjust for better visibility
color = 'hsl(' + h + ',' + Math.floor(s) + '%,' + Math.floor(l) + '%)';
mode_colors.push(color);
}
// Create colors for Band Chart
var band_colors = [];
for (i = 0; i < data.band_label.length; i++) {
b = Math.floor(0.1 * 255);
g = Math.floor(data.band_data[i]);
r = Math.floor(data.band_data[i]);
a = 0.5;
color = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
h = 235;
l = data.band_data[i]/band_total*100;
l *= 1.25; // adjust for better visibility
s = data.band_data[i]/band_total*100;
s *= 1.25; // adjust for better visibility
color = 'hsl(' + h + ',' + s + '%,' + l + '%)';
band_colors.push(color);
}

View File

@ -3,23 +3,65 @@ function ppb_to_freq(freq, drift) {
return Math.round(freq_obs);
}
/* eslint-disable no-unused-vars */
/* Disabling eslint for the following function since it is called by the Bootstrap Table library */
function freqSorter(a, b) {
var aa = a.split(' ', 1);
var bb = b.split(' ', 1);
return aa - bb;
function format_freq(frequency) {
if (isNaN(frequency) || frequency == ''){
return 'None';
} else if (frequency < 1000) {
// Frequency is in Hz range
return frequency.toFixed(3) + ' Hz';
} else if (frequency < 1000000) {
return (frequency/1000).toFixed(3) + ' kHz';
} else {
return (frequency/1000000).toFixed(3) + ' MHz';
}
}
/* eslint-enable no-unused-vars */
/* eslint new-cap: "off" */
$(document).ready(function() {
$('#transmitters-table').bootstrapTable();
$('#transmitters-table').css('opacity','1');
// Calculate the drifted frequencies
$('.drifted').each(function() {
var drifted = ppb_to_freq($(this).data('freq_or'),$(this).data('drift'));
$(this).html(drifted);
});
});
// Format all frequencies
$('.frequency').each(function() {
var to_format = $(this).html();
$(this).html(format_freq(to_format));
});
$('#transmitters').DataTable( {
// the dom field controls the layout and visibility of datatable items
// and is not intuitive at all. Without layout we have dom: 'Bftrilp'
// https://datatables.net/reference/option/dom
dom: '<"row"<"d-none d-md-block col-md-6"B><"col-sm-12 col-md-6"f>>' +
'<"row"<"col-sm-12"tr>>' +
'<"row"<"col-sm-12 col-xl-3"i><"col-sm-12 col-md-6 col-xl-3"l><"col-sm-12 col-md-6 col-xl-6"p>>',
buttons: [
'colvis'
],
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.childRow,
type: 'column'
}
},
columnDefs: [
{
className: 'control',
orderable: false,
targets: 0
},
],
language: {
search: 'Filter:',
buttons: {
colvis: 'Columns',
}
},
order: [ 1, 'asc' ],
pageLength: 25
} );
} );

View File

@ -1,5 +1,5 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load static %}
{% block title %} - Page Not found{% endblock %}

View File

@ -1,5 +1,5 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load static %}
{% block title %} - Server Error{% endblock %}

View File

@ -33,7 +33,7 @@
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<a class="button secondaryAction" href="{% url 'account_reset_password' %}">Forgot Password?</a>
<button class="btn btn-primary" type="submit">Sign In</button>
<button class="btn btn-satnogs-primary" type="submit">Sign In</button>
</form>
</div>
</div>

View File

@ -1 +1,2 @@
<img src="{{ url }}" width="{{ size }}" height="{{ size }}" class="img-avatar">
<!-- <img src="{{ url }}" width="{{ size }}" height="{{ size }}" class="img-avatar"> -->
<img src="{{ url }}" class="user-image" alt="User Image">

View File

@ -1,129 +1,248 @@
{% load staticfiles %}
{% load static %}
{% load avatar_tags %}
{% load tags %}
{% load compress %}
{% load fontawesome_5 %}
<!DOCTYPE html>
<html lang="en" ng-app>
<head>
<meta charset="utf-8">
<title>SatNOGS DB{% block title %}{% endblock title %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% compress css %}
<link rel="stylesheet" href="{% static 'lib/bootstrap/dist/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/app.css' %}">
{% block css %}{% endblock %}
{% endcompress %}
<link rel="shortcut icon" href="{% static 'favicon.ico' %}">
<head>
<meta charset="utf-8">
<title>SatNOGS DB{% block title %}{% endblock title %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
{% compress css %}
<link rel="stylesheet" href="{% static 'lib/admin-lte/dist/css/adminlte.min.css' %}">
</head>
<link rel="stylesheet" href="{% static 'css/newapp.css' %}">
{% block css %}{% endblock %}
{% endcompress %}
{% fontawesome_5_static %}
<body>
{{ stage_notice }}
<link rel="shortcut icon" href="{% static 'favicon.ico' %}">
<div class="container">
<nav class="navbar navbar-default" role="navigation" id="main-navbar">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{% url 'home' %}">
<img id="navbar-logo" src="{% static 'img/satnogs_db.png' %}" alt="SatNOGS net">
</head>
<body class="layout-fixed">
<div class="wrapper bg-light">
<!-- Top Navbar menu -->
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<ul class="navbar-nav d-lg-none">
<a class="nav-link keychainify-checked" data-widget="pushmenu" href="#" role="button"><i
class="fas fa-bars"></i></a>
</ul>
<span class="d-lg-none font-weight-light pr-2">SatNOGS DB</span>
<!-- hacky, push dyamic content and/or user menu to the right -->
<div class="pl-3 mr-auto">
{% block top-menu-left %}
{% endblock %}
</div>
{% block top-menu-right %}
{% endblock %}
{% if request.user.is_authenticated %}
<ul class="nav navbar-nav py-2">
<li class="user user-menu">
<a href="#" id="control-sidebar-toggle" data-widget="control-sidebar">
{% avatar request.user %}
</a>
</div>
</li>
</ul>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="{% active request 'about' %}"><a href="{% url 'about' %}">About</a></li>
<li class="{% active request 'home' %}"><a href="{% url 'home' %}">Satellites</a></li>
<li class="{% active request 'transmitters_list' %}"><a href="{% url 'transmitters_list' %}">Transmitters</a></li>
<li class="{% active request 'stats' %}"><a href="{% url 'stats' %}">Statistics</a></li>
<li class="{% active request 'faq' %}"><a href="{% url 'faq' %}">FAQ</a></li>
<li><a href="https://community.libre.space/c/satnogs/" target="_blank">Community</a></li>
<li><a href="https://wiki.satnogs.org/" target="_blank">Wiki</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="https://www.timeanddate.com/worldclock" target="_blank" class="hidden-sm"><span id="current_utc">--:-- UTC</span></a></li>
{% if request.user.is_authenticated %}
<li class="dropdown">
<a href="#"
class="dropdown-toggle"
data-toggle="dropdown">{% avatar request.user 35 %} {{ request.user.username }}
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="{% url 'users_edit' %}">Settings/API Key</a></li>
{{ logout_block }}
</ul>
</li>
{% else %}
{{ auth_block }}
{% endif %}
</ul>
{% else %}
{{ auth_block }}
{% endif %}
</nav>
<!-- Sidebar menu -->
<aside class="main-sidebar sidebar-dark-primary elevation-4 h-100" id="sidebar">
<div class="brand-link border-bottom-0">
<img src="{% static 'img/satnogs-logo-only-light.png' %}" alt="SatNOGS Logo" class="brand-image"
style="opacity: .8;">
<span class="brand-text font-weight-light">SatNOGS DB</span>
</div>
<div class="sidebar d-flex flex-column">
<form class="form-inline mt-1" action="{% url 'search_results' %}" method="GET">
<div class="input-group input-group-sm">
<input class="form-control form-control-sidebar" type="search" name="q"
placeholder="Satellite name or ID" aria-label="Search">
<div class="input-group-append">
<button class="btn btn-navbar-satnogs" type="submit">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</form>
<nav class="mt-2">
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
<li class="nav-item">
<a class="nav-link{% if request.resolver_match.url_name == 'home' %} active{% endif %}"
href="{% url 'home' %}">
<i class="nav-icon fas fa-home"></i>
<p>Home</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.resolver_match.url_name == 'about' %} active{% endif %}"
href="{% url 'about' %}">
<i class="nav-icon fas fa-info-circle"></i>
<p>About</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.resolver_match.url_name == 'satellites' %} active{% endif %}"
href="{% url 'satellites' %}">
<i class="nav-icon fas fa-satellite"></i>
<p>All Satellites</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.resolver_match.url_name == 'transmitters_list' %} active{% endif %}"
href="{% url 'transmitters_list' %}">
<i class="nav-icon fas fa-satellite-dish"></i>
<p>All Transmitters</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.resolver_match.url_name == 'stats' %} active{% endif %}"
href="{% url 'stats' %}">
<i class="nav-icon fas fa-chart-bar"></i>
<p>Statistics</p>
</a>
</li>
<li class="nav-item">
<a class="disabled nav-link{% if request.resolver_match.url_name == 'news' %} active{% endif %}"
href="">
<i class="nav-icon fas fa-newspaper"></i>
<p>News</p>
</a>
</li>
<li class="nav-item has-treeview">
<a href="#" class="nav-link">
<i class="nav-icon fas fa-link"></i>
<p>
SatNOGS Links
<i class="right align-middle fas fa-angle-left"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a class="nav-link" href="https://network.satnogs.org">
<i class="nav-icon fas fa-external-link-alt"></i>
<p>Ground Station Control</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://wiki.satnogs.org">
<i class="nav-icon fas fa-external-link-alt"></i>
<p>Wiki</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://community.libre.space">
<i class="nav-icon fas fa-external-link-alt"></i>
<p>Forums</p>
</a>
</li>
</ul>
</li>
</ul>
<!-- /.nav-sidebar -->
</nav>
<div class="mt-auto satnogs-sidebar-footer">
<div>
<span class="align-self-center"><i class="far fa-copyright"></i> 2014<span id="copy"></span>
<a href="https://libre.space" target="_blank">Libre Space Foundation</a></span>
</div>
<div>
<span>
{{ version }}
<br>
{{ decoders_version }}
</span>
</div>
<!-- /.panel-collapse -->
</div>
</nav>
</div>
<!-- /.satnogs-sidebar-footer -->
</div>
<!-- /.sidebar -->
</aside>
<!-- /.main-sidebar -->
<div class="container">
<!-- Control Sidebar (Right Menu) -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Control sidebar content goes here -->
<div class="p-3">
<div class="row justify-content-center border-bottom">
<h5 class="mb-1 text-sm">{{ user.username }}</h5>
</div>
<nav>
<ul class="nav nav-pills nav-sidebar flex-column">
<li class="nav-item">
<a href="#" class="nav-link basemodal-link control-sidebar-link" data-form-url="/users/edit/">
<i class="nav-icon fas fa-cog"></i>
<p>Settings / API Key</p>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link basemodal-link control-sidebar-link" data-form-url="/help/">
<i class="nav-icon fas fa-question-circle"></i>
<p>Help</p>
</a>
</li>
<li class="nav-item">
{{ logout_block }}
</li>
</ul>
</nav>
</div>
</aside>
<!-- /.control-sidebar -->
<!-- Content window -->
<div class="content-wrapper bg-light">
{% if messages %}
<div class="row messages">
<div class="col-md-12">
{% for notification in messages %}
<div class="alert alert-{{ notification.tags }}" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
{{ notification.message }}
</div>
{% endfor %}
</div>
<!-- This is hidden, transitional to Toasts - see app.js -->
{% for notification in messages %}
<div hidden class="alert alert-{{ notification.tags }}" data-alertclass="alert-{{ notification.tags }}" data-alertmessage="{{ notification.message }}" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
{{ notification.message }}
</div>
<!-- /.alert -->
{% endfor %}
{% endif %}
{% block top %}{% endblock %}
{% block content %}{% endblock content %}
</div>
<footer>
<div class="container">
<hr>
<div class="row">
<div class="col-md-6">
<span class="glyphicon glyphicon-copyright-mark" aria-hidden="true"></span> 2014<span id="copy"></span>
<a href="http://librespacefoundation.org/" target="_blank">Libre Space Foundation</a>.<br>
<span class="glyphicon glyphicon-cloud" aria-hidden="true"></span>
Transmitter data are freely distributed under the
<a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank">CC BY-SA</a> license.
</div>
<div class="col-md-6 text-right footer-options">
<a href="https://satnogs.org/" target="_blank">SatNOGS</a> |
<a href="#top">Back to top</a>
<p>
{{ version }}
<br>
{{ decoders_version }}
</p>
</div>
</div>
<!-- /.content-wrapper -->
<div class="modal fade" tabindex="-1" role="dialog" id="basemodal">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content"></div>
</div>
</footer>
</div>
</div>
<!-- /.wrapper -->
{% compress js %}
<script src="{% static 'lib/jquery/dist/jquery.min.js' %}"></script>
<script src="{% static 'lib/bootstrap/dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/app.js' %}"></script>
<script src="{% static 'js/current_utc.js'%}"></script>
{% block javascript %}{% endblock javascript %}
{{ analytics_code }}
{% endcompress %}
{% compress js %}
<script src="{% static 'lib/jquery/dist/jquery.min.js' %}"></script>
<script src="{% static 'lib/popper.js/dist/umd/popper.min.js' %}"></script>
<script src="{% static 'lib/bootstrap/dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'lib/admin-lte/dist/js/adminlte.min.js' %}"></script>
<script src="{% static 'js/jquery.bootstrap.modal.forms.min.js' %}"></script>
<script src="{% static 'js/app.js' %}"></script>
<!-- <script src="{% static 'js/current_utc.js'%}"></script> -->
{% block javascript %}{% endblock javascript %}
{{ analytics_code }}
{% endcompress %}
</body>
</html>
</html>

View File

@ -2,24 +2,75 @@
{% block title %} - About{% endblock %}
{% block top-menu-left %}
<h5 class="mb-0 mr-3">About</h5>
{% endblock %}
{% block top-menu-right %}
<ul class="navbar-nav nav nav-pills" data-widget="treeview" role="menu" data-accordion="false" id="tabs"
role="tablist">
<li class="nav-item">
<a class="nav-link active" id="satnogsdb-tab" data-toggle="tab" href="#satnogsdb" role="tab" aria-controls="satnogsdb"
aria-selected="true"><i class="nav-icon fas fa-info-circle"></i>
<p class="d-none d-lg-inline-block">SatNOGS DB</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="api-tab" data-toggle="tab" href="#api" role="tab"
aria-controls="api" aria-selected="false">
<i class="nav-icon fas fa-info-circle"></i>
<p class="d-none d-lg-inline-block">API</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="lsf-tab" data-toggle="tab" href="#lsf" role="tab"
aria-controls="lsf" aria-selected="false">
<i class="nav-icon fas fa-info-circle"></i>
<p class="d-none d-lg-inline-block">Libre Space Foundation</p>
</a>
</li>
{% endblock %}
{% block content %}
<div class="row text">
<div class="col-md-12">
<h1>About</h1>
SatNOGS DB is an effort to create an hollistic, unified, global transmitter database for all satellite
transmitters. You can export the data or even connect your application using our API.
It's part of the <a href="https://satnogs.org/" target="_blank">SatNOGS project</a>.
<h3>Data</h3>
The DB is open to everyone. Anyone is able to submit suggestions or use the existing Transmitters
All data are public and freely under the
<a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank">Creative Commons Atribution-Share Alike</a> license.
<h3>Coding</h3>
Fluent in Python and/or JS? We need you to help with satnogs-db web application. Take a look to our
<a href="https://gitlab.com/librespacefoundation/satnogs/satnogs-db" target="_blank">source code</a> or visit
our <a href="https://gitlab.com/librespacefoundation/satnogs/satnogs-db/issues" target="_blank">issues tracker</a>
to start filling bugs, feature requests or code fixes for existing ones.
Detailed contribution and development documentation is also availble at
<a href="https://docs.satnogs.org/projects/satnogs-db/en/stable/" target="_blank">docs.satnogs.org</a>.
<!-- The following div is managed by the tab menus -->
<div class="col-12 tab-content pt-3 mx-2" id="myTabContent">
<!-- SatNOGS DB panel -->
<div class="tab-pane fade show active mx-1" id="satnogsdb" role="tabpanel" aria-labelledby="satnogsdb-tab">
<div class="row">
<div class="col-md-12">
SatNOGS DB is an effort to create an hollistic, unified, global transmitter database for all satellite
transmitters. You can export the data or even connect your application using our API.
It's part of the <a href="https://satnogs.org/" target="_blank">SatNOGS project</a>.
<h3>Data</h3>
The DB is open to everyone. Anyone is able to submit suggestions or use the existing Transmitters
All data are public and freely under the
<a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank">Creative Commons Atribution-Share Alike</a> license.
<h3>Coding</h3>
Fluent in Python and/or JS? We need you to help with satnogs-db web application. Take a look to our
<a href="https://gitlab.com/librespacefoundation/satnogs/satnogs-db" target="_blank">source code</a> or visit
our <a href="https://gitlab.com/librespacefoundation/satnogs/satnogs-db/issues" target="_blank">issues tracker</a>
to start filling bugs, feature requests or code fixes for existing ones.
Detailed contribution and development documentation is also availble at
<a href="https://docs.satnogs.org/projects/satnogs-db/en/stable/" target="_blank">docs.satnogs.org</a>.
</div>
</div>
</div>
<!-- API panel -->
<div class="tab-pane fademx-1" id="api" role="tabpanel" aria-labelledby="api-tab">
<div class="row">
API info coming soon..
</div>
</div>
<!-- LSF panel -->
<div class="tab-pane fade mx-1" id="lsf" role="tabpanel" aria-labelledby="lsf-tab">
<div class="row">
Libre Space Foundation info coming soon. Visit <a href="https://libre.space" target="_blank">https://libre.space</a>
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,5 +1,5 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load static %}
{% block title %} - FAQ{% endblock %}

View File

@ -1,67 +1,84 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load static %}
{% block title %}{% endblock %}
{% block title %} - Home{% endblock %}
{% block top %}
<div class="row" id="search">
<div class="col-md-7">
<input type="text" list="satellites-list"
class="form-control input-lg satellite-search"
placeholder="Filter by Name or NORAD Cat ID"
autocomplete="off">
</div>
<div class="col-md-5">
<div class="row hidden-xs hidden-sm">
<p class="statistics" title="satellites" data-toggle="tooltip">
<img src="{% static 'img/satellites.png' %}">
{{ statistics.total_satellites }}
</p>
<p class="statistics" title="transmitters" data-toggle="tooltip">
<img src="{% static 'img/transmitters.png' %}">
{{ statistics.transmitters }}
</p>
{% if transmitter_suggestions %}
<p class="statistics" title="transmitter suggestions" data-toggle="tooltip">
<img src="{% static 'img/transmitter_suggestions.png' %}">
{{ transmitter_suggestions }}
</p>
{% endif %}
<p class="statistics" title="telemetry" data-toggle="tooltip">
<img src="{% static 'img/payloads.png' %}">
{{ statistics.total_data }}
</p>
<p class="statistics" title="contributors" data-toggle="tooltip">
<img src="{% static 'img/contributors.png' %}">
{{ contributors }}
</p>
</div>
</div>
</div>
{% block css %}
<link rel="stylesheet" href="{% static 'lib/flag-icon-css/css/flag-icon.min.css' %}" />
{% endblock css %}
{% block top-menu-left %}
<h5 class="mb-0 mr-3">Welcome to SatNOGS DB</h5>
{% endblock %}
{% block content %}
<div class="row sats">
{% for sat in satellites %}
<div class="satellite-group-item"
data-selector="{{ sat.name|lower }}{{ sat.norad_cat_id }}{{ sat.names|lower }}"
data-image="{{ sat.get_image }}">
<a href="{% url 'satellite' norad=sat.norad_cat_id %}" class="panel panel-default satellite-item">
<div class="panel-heading"></div>
<div class="panel-body">
<div class="satellite-title">{{ sat.norad_cat_id }} - {{ sat.name }}</div>
<div class="satellite-names">{{ sat.names }}&nbsp;</div>
</div>
<div class="panel-footer">
{{ sat.transmitters_count }} Transmitters
</div>
</a>
</div>
<div class="row px-2 pt-2 card-deck">
<div class="col-md-6 col-xl-4 satellite-panels">
<h4 class="text-satnogs text-center mb-0">New Satellites</h4>
{% for satellite in newest_sats %}
{% include 'includes/cards/satellite_card.html' with satellite=satellite %}
{% endfor %}
</div>
<div class="info row">
<div class="col-md-4">
<div class="col-md-6 col-xl-4 satellite-panels">
<h4 class="text-satnogs text-center mb-0">Latest Data</h4>
{% for satellite in latest_data %}
{% include 'includes/cards/satellite_card.html' with satellite=satellite %}
{% endfor %}
</div>
<div class="col-xs-12 col-xl-4 satellite-panels">
<h4 class="text-satnogs text-center mb-0">Recent Contributors</h4>
<div class="card shadow card-outline card-satnogs my-3">
{% if latest_submitters.count %}
<div class="card-header py-1">
<div class="row">
<div class="pl-1">
<span class="card-title">Data - Last 24h</span><br />
</div>
</div>
</div>
<div class="card-body satellite-card-body py-2 px-2">
<div class="d-flex flex-row no-gutters text-nowrap align-items-center justify-content-between">
<div class="d-flex flex-column float-left align-self-center">
{% for contributor in latest_submitters %}
<div class="d-flex flex-row">
<div class="d-flex satellite-card-body-row align-self-center justify-content-center">
<i class="fas fa-user"></i>
</div>
<div class="d-inline-flex">
{{ contributor.station }} - {{ contributor.c }} frames
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% else %}
<div class="card-body d-flex flex-row">
<div class="d-inline-flex">
<p class="text-wrap">
It appears there have been no contributions in the past 24 hours.
Please visit
<a href="https://wiki.satnogs.org" target="_blank">wiki.satnogs.org</a>
to learn how to contribute data!
</p>
</div>
</div>
{% endif %}
<div class="card-footer">
<span class="text-value-sm">Thank you to our contributors! Please visit
<a href="https://wiki.satnogs.org" target="_blank">wiki.satnogs.org</a>
to learn how to contribute data.</span>
</div>
</div>
</div>
</div>
<!-- <div class="info row p-3">
<div class="col-lg-4">
<h3>Open</h3>
<p>
SatNOGS DB is, and will always be, an open database. We aspire to create an hollistic,
@ -69,14 +86,14 @@
data or even connect your application using our API.
</p>
</div>
<div class="col-md-4">
<div class="col-lg-4">
<h3>Crowd-sourced</h3>
<p>
We rely on user submitted crowd-sourced information. Create an account and start
suggesting additions and/or modifications on our Database. We need your help!
</p>
</div>
<div class="col-md-4">
<div class="col-lg-4">
<h3>Extensible</h3>
<p>
We designed SatNOGS-DB to be easily extensible to accommodate additions and modification
@ -84,9 +101,7 @@
<a href="https://gitlab.com/librespacefoundation/satnogs/satnogs-db">our repo</a>.
</p>
</div>
</div>
{% endblock %}
{% block javascript %}
<script src="{% static 'js/home.js' %}"></script>
{% endblock %}
</div> -->
{% endblock %} {% block javascript %}
<script src="{% static 'js/home.js' %}"></script>
{% endblock %}

View File

@ -0,0 +1,37 @@
{% load widget_tweaks %}
<form method="post" action="">
{% csrf_token %}
<div class="modal-header">
<h3 class="modal-title">Update Satellite</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="{% if form.non_field_errors %}invalid{% endif %} mb-2">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% for field in form %}
<div class="input-group my-1">
<label class="input-group-prepend input-group-text" for="{{ field.id_for_label }}">{{ field.label }}</label>
{% render_field field class="form-control" placeholder=field.label %}
<div class="{% if field.errors %} invalid{% endif %}">
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="submit-btn btn btn-satnogs-primary">Update</button>
</div>
</form>

View File

@ -0,0 +1,13 @@
<div class="modal-header">
<h3 class="modal-title">Help</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="row m-2">
<div class="col-md-12">
Coming soon..
</div>
</div>

View File

@ -0,0 +1,45 @@
{% load widget_tweaks %}
{% if request.user.is_authenticated %}
<form action="" method="post" id="transmitter_create-form">
{% csrf_token %}
<div class="modal-header">
<h3 class="modal-title">Create Transmitter</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="{% if form.non_field_errors %}invalid{% endif %} mb-2">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% for field in form %}
<div class="input-group my-1">
<label class="input-group-prepend input-group-text" for="{{ field.id_for_label }}">{{ field.label }}</label>
{% render_field field class="form-control" placeholder=field.label %}
<div class="{% if field.errors %} invalid{% endif %}">
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="submit-btn btn btn-satnogs-primary">Create</button>
</div>
</form>
{% else %}
<div class="modal-body">
<div class="text-danger">You need to login first to add a new transmitter suggestion.</div>
</div>
<div class="modal-footer">
<button class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
<a href="{% url 'account_login' %}" class="btn btn-satnogs-primary">Log In</a>
</div>
{% endif %}

View File

@ -0,0 +1,45 @@
{% load widget_tweaks %}
{% if request.user.is_authenticated %}
<form action="" method="post" id="transmitter_create-form">
{% csrf_token %}
<div class="modal-header">
<h3 class="modal-title">Update Transmitter</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="{% if form.non_field_errors %}invalid{% endif %} mb-2">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% for field in form %}
<div class="input-group my-1">
<label class="input-group-prepend input-group-text" for="{{ field.id_for_label }}">{{ field.label }}</label>
{% render_field field class="form-control" placeholder=field.label %}
<div class="{% if field.errors %} invalid{% endif %}">
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="submit-btn btn btn-satnogs-primary">Update</button>
</div>
</form>
{% else %}
<div class="modal-body">
<div class="text-danger">You need to login first to add a new transmitter suggestion.</div>
</div>
<div class="modal-footer">
<button class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
<a href="{% url 'account_login' %}" class="btn btn-satnogs-primary">Log In</a>
</div>
{% endif %}

View File

@ -0,0 +1,16 @@
<div class="modal-header">
<h3 class="modal-title">User Settings</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="row m-2">
<div class="col-md-12">
<div>You can use this token to interact with the API.</div>
<div>
<code>{{ token }}</code>
</div>
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
{% extends "base.html" %}
{% load static %}
{% block title %} - Satellite List{% endblock %}
{% block css %}
<link rel="stylesheet" href="{% static 'lib/datatables.net-bs4/css/dataTables.bootstrap4.min.css' %}">
<link rel="stylesheet" href="{% static 'lib/datatables.net-buttons-bs4/css/buttons.bootstrap4.min.css' %}">
<link rel="stylesheet" href="{% static 'lib/flag-icon-css/css/flag-icon.min.css' %}">
{% endblock %}
{% block content %}
<div class="p-3">
<table id="sats" class="table table-sm display responsive table-striped" width="100%">
<thead>
<tr>
<th data-priority="1"></th>
<th data-priority="2">Name</th>
<th data-priority="3">NORAD</th>
<th data-priority="4">Status</th>
<th data-priority="6">Alt. Names</th>
<th data-priority="5">XMit</th>
<th data-priority="10" data-sortable="true" data-visible="false">Operator</th>
<th data-priority="10" data-sortable="true" data-visible="false">Launched</th>
<th data-priority="10" data-sortable="true" data-visible="false">Website</th>
<th data-priority="10" data-sortable="true" data-visible="false">Dashboard</th>
<th data-priority="6" data-sortable="false"><i class="fa fa-flag-o px-0"></i></th>
{% if perms.base.change_satellite %}
<th data-priority="2" data-sortable="false"></th>
{% endif %}
</tr>
</thead>
<tbody>
{% for sat in satellites %}
<tr>
<td class='details-control'>
<i class="fa fa-plus-square" aria-hidden="true"></i>
</td>
<td><a href="{% url 'satellite' norad=sat.norad_cat_id %}">{{ sat.name|lower }}</a></td>
<td><a href="{% url 'satellite' norad=sat.norad_cat_id %}">{{ sat.norad_cat_id }}</a></td>
<td class="d-flex justify-content-center">
{% if sat.status == 'alive' %}
<img height="32" src="{% static 'img/status_alive.png' %}" alt="alive">
{% elif sat.status == 're-entered' %}
<img height="32" src="{% static 'img/status_decayed.png' %}" alt="re-entered">
{% elif sat.status == 'dead' %}
<img height="32" src="{% static 'img/status_dead.png' %}" alt="dead">
{% else %}
<img height="32" src="{% static 'img/status_unknown.png' %}" alt="unknown">
{% endif %}
</td>
<td>{{ sat.names|lower }}</td>
<td>{{ sat.transmitters_count }}</td>
<td>{{ sat.operator }}</td>
<td>{{ sat.launched }}</td>
<td>{{ sat.website }}</td>
<td>{{ sat.dashboard_url }}</td>
<td><span class="align-middle flag-icon flag-icon-{{ sat.country.code|lower }}"></span></td>
{% if perms.base.change_satellite %}
<td><button type="button" class="bs-modal btn btn-sm btn-satnogs-primary"
data-form-url="{% url 'update_satellite' sat.id %}">
<span class="fa fa-pencil"></span>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
<!-- Satellite update modal -->
<div class="modal fade" tabindex="-1" role="dialog" id="modal">
<div class="modal-dialog" role="document">
<div class="modal-content"></div>
</div>
</div>
</div>
{% endblock %}
{% block javascript %}
<script src="{% static 'lib/datatables.net/js/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'lib/datatables.net-buttons/js/dataTables.buttons.min.js' %}"></script>
<script src="{% static 'lib/datatables.net-responsive/js/dataTables.responsive.min.js' %}"></script>
<script src="{% static 'lib/datatables.net-buttons/js/buttons.colVis.js' %}"></script>
<script src="{% static 'lib/datatables.net-bs4/js/dataTables.bootstrap4.min.js' %}"></script>
<script src="{% static 'lib/datatables.net-buttons-bs4/js/buttons.bootstrap4.min.js' %}"></script>
<script src="{% static 'js/jquery.bootstrap.modal.forms.min.js' %}"></script>
<script src="{% static 'js/satellites.js' %}"></script>
{% endblock %}

View File

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% load static %}
{% block title %} - Search Results{% endblock %}
{% block css %}
<link rel="stylesheet" href="{% static 'lib/flag-icon-css/css/flag-icon.min.css' %}">
{% endblock css %}
{% block content %}
<div class="container-fluid m-2">
{% if results.count > 0 %}
<h4>Your search returned multiple results:</h4>
{% else %}
<h4>No results found</h4>
<div>
If you are looking for a satellite that we don't seem to have, please <a href="https://wiki.satnogs.org/Get_In_Touch" target="_blank">let us know</a>!
</div>
{% endif %}
<div class="d-flex justify-content-center">
<div class="w-75">
{% for satellite in results %}
{% include 'includes/cards/satellite_card.html' with satellite=satellite %}
{% endfor %}
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,59 +1,130 @@
{% extends "base.html" %}
{% load tags %}
{% load staticfiles %}
{% load static %}
{% block title %} - Stats{% endblock %}
{% block top-menu-left %}
<h5 class="mb-0 mr-3">Statistics</h5>
{% endblock %}
{% block top-menu-right %}
<ul class="navbar-nav nav nav-pills" data-widget="treeview" role="menu" data-accordion="false" id="tabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="dashboard-tab" data-toggle="tab" href="#dashboard" role="tab"
aria-controls="dashboard" aria-selected="true"><i class="nav-icon fas fa-chart-line"></i>
<p class="d-none d-lg-inline-block">Dashboard</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="sat-leaderboard-tab" data-toggle="tab" href="#sat-leaderboard" role="tab"
aria-controls="sat-leaderboard" aria-selected="false">
<i class="nav-icon fas fa-satellite"></i>
<p class="d-none d-lg-inline-block">Satellites</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="user-leaderboard-tab" data-toggle="tab" href="#user-leaderboard" role="tab"
aria-controls="user-leaderboard" aria-selected="false">
<i class="nav-icon fas fa-house-user"></i>
<p class="d-none d-lg-inline-block">Ground Stations</p>
</a>
</li>
</ul>
{% endblock %}
{% block content %}
<!-- Transmitter stats -->
<div class="row text" id="transmitters-charts">
<div class="col-md-12">
<h2>Transmitters</h2>
</div>
</div>
<div class="row stats" id="transmitters-numbers">
<div class="col-md-4">
<canvas id="modes" width="300" height="300"></canvas>
</div>
<div class="col-md-4">
<canvas id="bands" width="300" height="300"></canvas>
</div>
<div class="col-md-4">
<!-- SatNOGS DB Stats -->
<!-- The following div is managed by the tab menus -->
<div class="col-12 tab-content pt-3 mx-2" id="myTabContent">
<!-- Dashboard panel -->
<div class="tab-pane fade show active mx-1" id="dashboard" role="tabpanel" aria-labelledby="dashboard-tab">
<div class="container-fluid">
<div class="row">
<p class="stats-hud">
Total satellites
<span class="stats-hud-num" id="stats-satellites">120</span>
</p>
</div>
<div class="row">
<p class="stats-hud">
Total transmitters
<span class="stats-hud-num" id="stats-transmitters">230</span>
</p>
</div>
<div class="row">
<p class="stats-hud">
Alive transmitters
<span class="stats-hud-num" id="stats-alive"></span>
</p>
</div>
<div class="row">
<p class="stats-hud">
Total data
<span class="stats-hud-num" id="stats-data"></span>
</p>
<div class="col-xs-12 col-md-4">
<div class="small-box small-box-satnogs">
<div class="inner">
<h3 id="stats-satellites">#</h3>
<p>Total Satellites</p>
</div>
<div class="icon">
<i class="fas fa-satellite"></i>
</div>
<a href="{% url 'satellites' %}" class="small-box-footer">
Satellites <i class="fas fa-arrow-circle-right"></i>
</a>
</div>
<div class="small-box small-box-satnogs">
<div class="inner">
<h3 id="stats-transmitters">#</h3>
<p>Total Transmitters</p>
</div>
<div class="icon">
<i class="fas fa-satellite-dish"></i>
</div>
<a href="{% url 'transmitters_list' %}" class="small-box-footer">
Transmitters <i class="fas fa-arrow-circle-right"></i>
</a>
</div>
<div class="small-box small-box-satnogs">
<div class="inner">
<h3 id="stats-alive">#</h3>
<p>Alive Transmitters</p>
</div>
<div class="icon">
<i class="fas fa-satellite-dish"></i>
</div>
<a href="{% url 'transmitters_list' %}" class="small-box-footer">
Transmitters <i class="fas fa-arrow-circle-right"></i>
</a>
</div>
<div class="small-box small-box-satnogs">
<div class="inner">
<h3 id="stats-data">#</h3>
<p>Total Data</p>
</div>
<div class="icon">
<i class="fas fa-database"></i>
</div>
<!-- <a href="#" class="small-box-footer">
Data <i class="fas fa-arrow-circle-right"></i>
</a> -->
</div>
</div>
<div class="col-xs-12 col-md-4">
<div class="card satnogs-card mb-4">
<div class="card-header background-satnogs-header d-flex justify-content-between align-items-center">
<h6 class="text-satnogs-header font-weight-bold m-0">Transmitter Modes</h6>
</div>
<div class="card-body">
<canvas id="modes" width="300" height="300"></canvas>
</div>
</div>
</div>
<div class="col-xs-12 col-md-4">
<div class="card satnogs-card mb-4">
<div class="card-header background-satnogs-header d-flex justify-content-between align-items-center">
<h6 class="text-satnogs-header font-weight-bold m-0">Transmitter Bands</h6>
</div>
<div class="card-body">
<canvas id="bands" width="300" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
<hr>
{% if satellites and observers %}
<!-- Data paylod stats -->
<div class="row">
<div class="col-md-6">
<h2>Satellites</h2>
<table class="table table-hover">
<div class="tab-pane fade px-2" id="sat-leaderboard" role="tabpanel"
aria-labelledby="sat-leaderboard-tab">
<div class="row justify-content-center">
<h4>Satellites</h4>
<table class="table table-hover table-striped">
<thead>
<tr>
<th>Norad ID</th>
@ -64,22 +135,25 @@
</thead>
<tbody>
{% for sat in satellites %}
{% if sat.count != 0 %}
<tr>
<td>{{sat.norad_cat_id}}</td>
<td><a href="{% url 'satellite' norad=sat.norad_cat_id %}">{{sat.name}}</a></td>
<td>{{sat.count}}</td>
<td>{{sat.latest_payload|date:"Y-m-d H:i:s"}}</td>
</tr>
{% endif %}
{% if sat.count != 0 %}
<tr>
<td>{{sat.norad_cat_id}}</td>
<td><a href="{% url 'satellite' norad=sat.norad_cat_id %}">{{sat.name}}</a></td>
<td>{{sat.count}}</td>
<td>{{sat.latest_payload|date:"Y-m-d H:i:s"}}</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
<div class="col-md-6">
<h2>Stations</h2>
</div>
<div class="tab-pane fade px-2" id="user-leaderboard" role="tabpanel"
aria-labelledby="user-leaderboard-tab">
<div class="row justify-content-center">
<h4>Stations</h4>
<table class="table table-hover">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>#</th>
@ -90,21 +164,22 @@
</thead>
<tbody>
{% for obs in observers %}
<tr>
<td>{{forloop.counter}}</td>
<td>{{obs.observer}}</td>
<td>{{obs.count}}</td>
<td>{{obs.latest_payload|date:"Y-m-d H:i:s"}}</td>
</tr>
<tr>
<td>{{forloop.counter}}</td>
<td>{{obs.observer}}</td>
<td>{{obs.count}}</td>
<td>{{obs.latest_payload|date:"Y-m-d H:i:s"}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
{% endblock %}
{% block javascript %}
<script src="{% static 'lib/chart.js/dist/Chart.min.js' %}"></script>
<script src="{% static 'js/stats.js' %}"></script>
{% endblock %}
<script src="{% static 'lib/chart.js/dist/Chart.min.js' %}"></script>
<script src="{% static 'js/stats.js' %}"></script>
{% endblock %}

View File

@ -1,75 +1,76 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load tags %}
{% load static %}
{% block title %} - Transmitters List{% endblock %}
{% block title %} - Transmitter List{% endblock %}
{% block css %}
<link rel="stylesheet" href="{% static 'lib/bootstrap-table/dist/bootstrap-table.min.css' %}">
<link rel="stylesheet" href="{% static 'lib/bootstrap-table/dist/extensions/filter-control/bootstrap-table-filter-control.min.css' %}">
{% endblock css %}
<link rel="stylesheet" href="{% static 'lib/datatables.net-bs4/css/dataTables.bootstrap4.min.css' %}">
<link rel="stylesheet" href="{% static 'lib/datatables.net-buttons-bs4/css/buttons.bootstrap4.min.css' %}">
{% endblock %}
{% block content %}
<div class="row trans">
<table data-sortable="true"
data-filter-control="true"
data-search="true"
data-show-columns="true"
data-show-columns-search="true"
id="transmitters-table">
<thead>
<tr>
<th data-visible="false">UUID</th>
<th data-sortable="true">Satellite</th>
<th data-field="type"
data-sortable="true"
data-filter-control="select">Type</th>
<th data-sortable="true">Description</th>
<th data-sortable="true" data-sorter="freqSorter">Downlink</th>
<th data-sortable="true" data-visible="false">Downlink Drift</th>
<th data-sortable="true" data-visible="false">Uplink</th>
<th data-sortable="true" data-visible="false">Uplink Drift</th>
<th data-sortable="true" data-visible="false">Inverted</th>
<th data-field="mode" data-filter-control="select">Mode</th>
<th data-sortable="true">Baud</th>
<th data-field="service"
data-sortable="true"
data-filter-control="select">Service</th>
<th data-field="status"
data-sortable="true"
data-filter-control="select">Status</th>
</tr>
</thead>
<tbody>
{% for trans in transmitters %}
<tr>
<td>{{ trans.uuid }}</td>
<td>
<a href="{% url 'satellite' norad=trans.satellite.norad_cat_id %}">
{{ trans.satellite }}
</a>
</td>
<td>{{ trans.type }}</td>
<td>{{ trans.description }}</td>
<td class="frequency">{{ trans.downlink_low | frq }}</td>
<td>{{ trans.downlink_drift }}</td>
<td class="frequency">{{ trans.uplink }}</td>
<td>{{ trans.uplink_drift }}</td>
<td>{{ trans.invert }}</td>
<td>{{ trans.downlink_mode }}</td>
<td>{{ trans.baud | floatformat }}</td>
<td>{{ trans.service }}</td>
<td>{{ trans.status }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="p-3">
<table id="transmitters" class="table table-sm display responsive table-striped" width="100%">
<thead>
<tr>
<th data-priority="1"></th>
<th data-visible="false" data-priority="20">UUID</th>
<th data-sortable="true" data-priority="1">Satellite</th>
<th data-sortable="true" data-priority="4">Type</th>
<th data-sortable="true" data-priority="1">Description</th>
<th data-sortable="true" data-priority="2">Downlink</th>
<th data-sortable="true" data-visible="false" data-priority="10">Downlink Drift</th>
<th data-sortable="true" data-visible="false" data-priority="10">Uplink</th>
<th data-sortable="true" data-visible="false" data-priority="10">Uplink Drift</th>
<th data-sortable="true" data-visible="false" data-priority="10">Inverted</th>
<th data-sortable="true" data-priority="3">Mode</th>
<th data-sortable="true" data-priority="4">Baud</th>
<th data-sortable="true" data-priority="10">Service</th>
<th data-sortable="true" data-priority="10">Status</th>
</tr>
</thead>
<tbody>
{% for trans in transmitters %}
<tr>
<td class='details-control'>
<i class="fa fa-plus-square" aria-hidden="true"></i>
</td>
<td>{{ trans.uuid }}</td>
<td>
{% if trans.satellite.norad_cat_id %}
<a href="{% url 'satellite' norad=trans.satellite.norad_cat_id %}">
{{ trans.satellite }}
</a>
{% else %}
{{ trans.satellite }}
{% endif %}
</td>
<td>{{ trans.type }}</td>
<td>{{ trans.description }}</td>
<td class="frequency">{{ trans.downlink_low }}</td>
<td>{{ trans.downlink_drift }}</td>
<td class="frequency">{{ trans.uplink }}</td>
<td>{{ trans.uplink_drift }}</td>
<td>{{ trans.invert }}</td>
<td>{{ trans.downlink_mode }}</td>
<td>{{ trans.baud }}</td>
<td>{{ trans.service }}</td>
<td>{{ trans.status }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
{% block javascript %}
<script src="{% static 'lib/bootstrap-table/dist/bootstrap-table.min.js' %}"></script>
<script src="{% static 'lib/bootstrap-table/dist/extensions/filter-control/bootstrap-table-filter-control.min.js' %}"></script>
<script src="{% static 'js/transmitters.js' %}"></script>
{% endblock %}
<script src="{% static 'lib/datatables.net/js/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'lib/datatables.net-buttons/js/dataTables.buttons.min.js' %}"></script>
<script src="{% static 'lib/datatables.net-responsive/js/dataTables.responsive.min.js' %}"></script>
<script src="{% static 'lib/datatables.net-buttons/js/buttons.colVis.js' %}"></script>
<script src="{% static 'lib/datatables.net-bs4/js/dataTables.bootstrap4.min.js' %}"></script>
<script src="{% static 'lib/datatables.net-buttons-bs4/js/buttons.bootstrap4.min.js' %}"></script>
<script src="{% static 'js/transmitters.js' %}"></script>
{% endblock %}

View File

@ -1,24 +0,0 @@
{% extends "base.html" %}
{% load tags %}
{% load staticfiles %}
{% block title %} - Settings{% endblock %}
{% block content %}
<div class="row text">
<div class="col-md-12">
<h2>Settings</h2>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div>You can use this token to interact with the API.</div>
<div>
<code>{{ token }}</code>
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,4 +1,4 @@
{% load staticfiles %}
{% load static %}
<script src="{% static 'lib/dnt-helper/js/dnt-helper.js' %}"></script>
<script src="{% static 'js/ga.js' %}"></script>

View File

@ -1 +1,10 @@
<li><a href="/login/auth0">Sign Up / Log In</a></li>
<nav>
<ul class="nav nav-pills navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/login/auth0">
<i class="nav-icon fas fa-sign-in-alt"></i>
<p class="d-none d-lg-inline-block">Sign Up / Log In</p>
</a>
</li>
</ul>
</nav>

View File

@ -1,2 +1,12 @@
<li><a href="{% url 'account_signup' %}">Sign Up</a></li>
<li><a href="{% url 'account_login' %}">Log In</a></li>
<nav>
<ul class="nav nav-pills navbar-nav">
<li class="nav-item"><a class="nav-link" href="{% url 'account_signup' %}">
<i class="nav-icon fas fa-user-plus"></i>
<p class="d-none d-lg-inline-block">Sign Up</p>
</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'account_login' %}">
<i class="nav-icon fas fa-sign-in-alt"></i>
<p class="d-none d-lg-inline-block">Log In</p>
</a></li>
</ul>
</nav>

View File

@ -0,0 +1,91 @@
<div class="card card-outline card-satnogs shadow my-3">
<a href="{% url 'satellite' norad=satellite.norad_cat_id %}">
<!-- Card header (Satellite name etc) -->
<div class="card-header py-1">
<div class="row">
<div class="pl-1">
<span class="card-title mr-1">{{ satellite.name }} <span class="text-sm text-truncate ml-1">{{ satellite.names }}</span></span>
</div>
<div class="d-flex ml-auto">
{% if request.user.is_superuser and satellite.transmitter_suggestion_count %}
<div class="mx-1">
<span class="badge badge-warning" data-toggle="tooltip"
title="Transmitter Suggestions">{{ satellite.transmitter_suggestion_count }}</span>
</div>
{% endif %}
{% if satellite.countries %}
<div>
{% for country in satellite.countries %}
<i class="align-middle flag-icon flag-icon-{{ country.code|lower }}"></i>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
<!-- /.card-header -->
<!-- Satellite card body -->
<div class="card-body satellite-card-body py-2 px-2">
<div class="d-flex flex-row no-gutters text-nowrap align-items-center justify-content-between">
<div class="d-flex flex-column float-left w-75 align-self-center">
<div class="d-flex flex-row">
<div class="d-flex satellite-card-body-row align-self-center justify-content-center" data-toggle="tooltip"
title="Transmitters">
<i class="fas fa-wifi"></i>
</div>
<div class="d-inline-flex">
{{ satellite.transmitters_count }}
</div>
</div>
{% if satellite.telemetry_data_count %}
<div class="d-flex flex-row">
<div class="d-flex satellite-card-body-row align-self-center justify-content-center" data-toggle="tooltip"
title="Data frames">
<i class="fas fa-database"></i>
</div>
<div class="d-inline-flex">
{{ satellite.telemetry_data_count }}
</div>
</div>
<div class="d-flex flex-row">
<div class="d-flex satellite-card-body-row align-self-center justify-content-center" data-toggle="tooltip"
title="Latest data timestamp">
<i class="fas fa-clock"></i>
</div>
<div class="d-inline-flex text-truncate">
{{ satellite.latest_data.timestamp }}
</div>
</div>
<div class="d-flex flex-row">
<div class="d-flex satellite-card-body-row align-self-center justify-content-center" data-toggle="tooltip"
title="Latest data submitter">
<i class="fas fa-user"></i>
</div>
<div class="d-inline-flex text-truncate">
{{ satellite.latest_data.station }}
</div>
</div>
{% else %}
<div class="d-flex flex-row">
<div class="d-flex satellite-card-body-row align-self-center justify-content-center" data-toggle="tooltip"
title="Data frames">
<i class="fas fa-database"></i>
</div>
<div class="d-inline-flex">
No Data
</div>
</div>
{% endif %}
</div>
<div class="float-right d-flex flex-column align-content-center">
<img class="rounded img-fluid" src="{{ satellite.get_image }}" style="max-height: 82.5px;" />
</div>
</div>
</div>
<!-- /.card-body -->
</a>
</div>

View File

@ -0,0 +1,182 @@
<div class="card shadow card-outline {% if suggestion_card %}card-warning{% else %}card-satnogs{% endif %}">
{% comment %}
<div class="card-header align-items-center no-gutters">
<div class="row px-2">
<div class="col mr-2">
<h5 class="card-title">{% if suggestion_card %}Suggestion - {% endif %}{{ transmitter.description }}</h5>
</div>
<div class="col-auto">
<div class="row">
<i class="fas fa-satellite fa-2x {% if satellite.status == 're-entered' %}text-satnogs-inactive{% elif transmitter.status == 'active' %}text-satnogs-active{% else %}text-satnogs-inactive{% endif %}"></i>
<div class="btn-group" role="group">
<btn class="btn btn-satnogs dropdown-toggle ml-3" data-toggle="dropdown" aria-expanded="false"
id="dropdown-{{ transmitter.id }}"><i class="fas fa-bars"></i></btn>
<div class="dropdown-menu" aria-labelledby="dropdown-{{ transmitter.id }}">
{% if request.user.is_superuser and suggestion_card %}
<form action="{% url 'transmitter_suggestion_handler' %}" method="post"
id="transmitter_suggestion_handler-form">
{% csrf_token %}
<input type="hidden" name="uuid" value="{{ transmitter.uuid }}">
<button type="submit" name="approve" class="btn btn-satnogs btn-outline-success dropdown-item">
<i class="fas fa-check-square mr-2"></i>
Approve Suggestion
</button>
<button type="submit" name="reject" class="btn btn-satnogs btn-outline-danger dropdown-item">
<i class="fas fa-minus-square mr-2"></i>
Reject Suggestion
</button>
</form>
{% endif %}
{% if request.user.is_authenticated %}
<a class="dropdown-item" data-toggle="modal" data-target="#EditSuggestionModal-{{ transmitter.id }}">
<i class="fas fa-edit mr-2"></i>
Edit
</a>
{% endif %}
<a class="dropdown-item" data-toggle="modal" data-target="#TransmitterCitation-{{ transmitter.id }}">
<i class="fas fa-quote-left mr-2"></i>
Citation
</a>
</div>
</div>
</div>
</div>
</div>
</div>
{% endcomment %}
<div class="card-header align-items-center no-gutters py-1">
<div class="row">
<span class="card-title align-self-center">{% if suggestion_card %}Suggestion - {% endif %}{{ transmitter.description }}</span>
<div class="row ml-auto">
<i class="fas fa-satellite fa-lg align-self-center {% if satellite.status == 're-entered' %}text-satnogs-inactive{% elif transmitter.status == 'active' %}text-satnogs-active{% else %}text-satnogs-inactive{% endif %}"></i>
<div class="btn-group" role="group">
<btn class="btn btn-satnogs dropdown-toggle ml-3" data-toggle="dropdown" aria-expanded="false"
id="dropdown-{{ transmitter.id }}"><i class="fas fa-bars"></i></btn>
<div class="dropdown-menu" aria-labelledby="dropdown-{{ transmitter.id }}">
{% if request.user.is_superuser and suggestion_card %}
<form action="{% url 'transmitter_suggestion_handler' %}" method="post"
id="transmitter_suggestion_handler-form">
{% csrf_token %}
<input type="hidden" name="uuid" value="{{ transmitter.uuid }}">
<button type="submit" name="approve" class="btn btn-satnogs btn-outline-success dropdown-item">
<i class="fas fa-check-square mr-2"></i>
Approve Suggestion
</button>
<button type="submit" name="reject" class="btn btn-satnogs btn-outline-danger dropdown-item">
<i class="fas fa-minus-square mr-2"></i>
Reject Suggestion
</button>
</form>
{% endif %}
{% if request.user.is_authenticated %}
<a class="dropdown-item update-transmitter-link" data-toggle="modal" href="#"
data-form-url="{% url 'update_transmitter' transmitter.id %}">
<i class="fas fa-edit mr-2"></i>
Edit
</a>
{% endif %}
<a class="dropdown-item" data-toggle="modal" data-target="#TransmitterCitation-{{ transmitter.id }}">
<i class="fas fa-quote-left mr-2"></i>
Citation
</a>
</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="col-12">
{% if transmitter.type %}
{% include "includes/field.html" with name="Type" value=transmitter.type %}
{% endif %}
{% if transmitter.service %}
{% include "includes/field.html" with name="Service" value=transmitter.service %}
{% endif %}
{% if transmitter.downlink_mode %}
{% include "includes/field.html" with name="Downlink Mode" value=transmitter.downlink_mode %}
{% endif %}
{% if transmitter.uplink_mode %}
{% include "includes/field.html" with name="Uplink Mode" value=transmitter.uplink_mode %}
{% endif %}
{% if transmitter.baud %}
{% include "includes/field.html" with name="Baud" value=transmitter.baud %}
{% endif %}
{% if transmitter.type == 'Transmitter' %}
{% if transmitter.downlink_low %}
{% include "includes/field.html" with name="Downlink" value=transmitter.downlink_low hz=transmitter.downlink_low %}
{% endif %}
{% if transmitter.downlink_drift %}
{% include "includes/field.html" with name="Downlink Drifted" value=transmitter.downlink_drift hz=transmitter.downlink_drift class="drifted" freq_or=transmitter.downlink_low %}
{% endif %}
{% endif %}
{% if transmitter.type == 'Transceiver' %}
{% if transmitter.downlink_low %}
{% include "includes/field.html" with name="Downlink" value=transmitter.downlink_low hz=transmitter.downlink_low %}
{% endif %}
{% if transmitter.downlink_drift %}
{% include "includes/field.html" with name="Downlink Drifted" value=transmitter.downlink_drift hz=transmitter.downlink_drift class="drifted" freq_or=transmitter.downlink_low %}
{% endif %}
{% if transmitter.uplink_low %}
{% include "includes/field.html" with name="Uplink" value=transmitter.uplink_low hz=transmitter.uplink_low %}
{% endif %}
{% if transmitter.uplink_drift %}
{% include "includes/field.html" with name="Uplink Drifted" value=transmitter.uplink_drift hz=transmitter.uplink_drift class="drifted" freq_or=transmitter.uplink_low %}
{% endif %}
{% endif %}
{% if transmitter.type == 'Transponder' %}
{% if transmitter.downlink_low %}
{% include "includes/field.html" with name="Downlink Low" value=transmitter.downlink_low hz=transmitter.downlink_low %}
{% endif %}
{% if transmitter.downlink_high %}
{% include "includes/field.html" with name="Downlink High" value=transmitter.downlink_high hz=transmitter.downlink_high %}
{% endif %}
{% if transmitter.downlink_drift %}
{% include "includes/field.html" with name="Downlink Drift" value=transmitter.downlink_drift hz=transmitter.downlink_drift %}
{% endif %}
{% if transmitter.uplink_low %}
{% include "includes/field.html" with name="Uplink Low" value=transmitter.uplink_low hz=transmitter.uplink_low %}
{% endif %}
{% if transmitter.uplink_high %}
{% include "includes/field.html" with name="Uplink High" value=transmitter.uplink_high hz=transmitter.uplink_high %}
{% endif %}
{% if transmitter.uplink_drift %}
{% include "includes/field.html" with name="Uplink Drift" value=transmitter.uplink_drift hz=transmitter.uplink_drift %}
{% endif %}
{% if transmitter.invert %}
{% include "includes/field.html" with name="Invert" value=transmitter.invert %}
{% endif %}
{% endif %}
</div>
</div>
<div class="card-footer align-items-baseline justify-content-between transmitter-card-footer">
Updated on {{ transmitter.created|date:'Y-m-d H:i' }} by {{ transmitter.user }}
</div>
</div>
</div>
<!-- Citation Modal -->
<div class="modal fade" id="TransmitterCitation-{{ transmitter.id }}" tabindex="-1" role="dialog"
aria-labelledby="CitationModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="CitationModalLabel">Citation for {{ transmitter.uuid }}</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
{{ transmitter.citation }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
<!-- Edit Suggestion Modal -->
{% comment %}
<div class="modal fade transmitter_suggestion-modal" id="EditSuggestionModal-{{ transmitter.id }}" tabindex="-1"
role="dialog" aria-labelledby="EditSuggestionModalLabel" aria-hidden="true">
{% include 'includes/transmitter_suggestion_modal.html' with transmitter=transmitter %}
</div>
{% endcomment %}

View File

@ -1,8 +1,8 @@
<div class="row transmitter-element">
<div class="col-md-5">
<span class="label label-default">{{ name }}</span>
<div class="col-md-6">
<span class="badge badge-secondary satellite-detail-badge">{{ name }}</span>
</div>
<div class="col-md-7" {% if hz %}
<div class="col-md-6" {% if hz %}
{% if freq_or %}
data-toggle="tooltip" data-placement="bottom" title="{{ hz }} ppb"
{% else %}

View File

@ -1 +1,4 @@
<li><a href="/logout">Logout</a></li>
<a class="nav-link" href="/logout">
<i class="nav-icon fas fa-sign-out-alt"></i>
<p>Logout</p>
</a>

View File

@ -1 +1,4 @@
<li><a href="{% url 'account_logout' %}">Logout</a></li>
<a class="nav-link" href="{% url 'account_logout' %}">
<i class="nav-icon fas fa-sign-out-alt"></i>
<p>Logout</p>
</a>

View File

@ -0,0 +1,33 @@
{% load static %}
<div class="row d-flex" id="search">
<div class="col-lg-7">
<input type="text" list="satellites-list"
class="border rounded form-control form-control-sm satellite-search"
placeholder="Search by Name or NORAD Cat ID"
autocomplete="off">
</div>
<div class="col-lg-5 d-flex d-lg-flex align-self-center justify-content-lg-center align-items-lg-center">
<p class="d-inline-flex align-items-lg-center statistics" title="satellites" data-toggle="tooltip">
<img src="{% static 'img/satellites.png' %}">
{{ statistics.total_satellites }}
</p>
<p class="d-inline-flex align-items-lg-center statistics" title="transmitters" data-toggle="tooltip">
<img src="{% static 'img/transmitters.png' %}">
{{ statistics.transmitters }}
</p>
{% if transmitter_suggestions %}
<p class="d-inline-flex align-items-lg-center statistics" title="transmitter_suggestions" data-toggle="tooltip">
<img src="{% static 'img/transmitter_suggestions.png' %}">
{{ transmitter_suggestions }}
</p>
{% endif %}
<p class="d-inline-flex align-items-lg-center statistics" title="telemetry" data-toggle="tooltip">
<img src="{% static 'img/payloads.png' %}">
{{ statistics.total_data }}
</p>
<p class="d-inline-flex align-items-lg-center statistics" title="contributors" data-toggle="tooltip">
<img src="{% static 'img/contributors.png' %}">
{{ contributors }}
</p>
</div>
</div>

View File

@ -1,4 +1,4 @@
{% load staticfiles %}
{% load static %}
{% block css %}
<link rel="stylesheet" href="{% static 'css/stage.css' %}">

3991
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "satnogs-db",
"version": "0.1.0",
"description": "Satellite Transmitter Suggestions Appp",
"description": "Satellite and Transmitter Database",
"repository": {
"type": "git",
"url": "https://gitlab.com/librespacefoundation/satnogs/satnogs-db"
@ -11,20 +11,29 @@
"devDependencies": {
"gulp": "^4.0.2",
"gulp-eslint": "^6.0.0",
"gulp-stylelint": "^9.0.0",
"stylelint": "^11.0.0"
"gulp-stylelint": "^13.0.0",
"minimist": "^1.2.5",
"stylelint": "^13.6.1"
},
"dependencies": {
"bootstrap": "^3.4.1",
"bootstrap-table": "^1.17.0",
"chart.js": "^2.8.0",
"admin-lte": "^3.0",
"bootstrap": "^4.5.0",
"chart.js": "^2.9.3",
"datatables.net": "^1.10.21",
"datatables.net-bs4": "^1.10.21",
"datatables.net-buttons-bs4": "^1.6.2",
"datatables.net-responsive": "^2.2.5",
"dnt-helper": "github:schalkneethling/dnt-helper",
"flag-icon-css": "^3.5.0",
"flot": "^4.2.1",
"gpredict.js": "github:kerel-fs/gpredict.js",
"jquery": "^3.4.1",
"mapbox-gl": "^1.3.1",
"moment": "^2.24.0"
"jquery": "^3.5.1",
"mapbox-gl": "^1.11.0",
"moment": "^2.27.0",
"popper.js": "^1.16.1"
},
"assets": [
"admin-lte/**/*",
"bootstrap/dist/css/bootstrap.min.css",
"bootstrap/dist/js/bootstrap.min.js",
"bootstrap/dist/fonts/*",
@ -33,11 +42,25 @@
"bootstrap-table/dist/extensions/filter-control/bootstrap-table-filter-control.min.css",
"bootstrap-table/dist/extensions/filter-control/bootstrap-table-filter-control.min.js",
"chart.js/dist/Chart.min.js",
"d3/dist/d3.min.js",
"datatables.net/js/jquery.dataTables.min.js",
"datatables.net-bs4/css/dataTables.bootstrap4.min.css",
"datatables.net-bs4/js/dataTables.bootstrap4.min.js",
"datatables.net-buttons/js/*",
"datatables.net-buttons-bs4/css/buttons.bootstrap4.min.css",
"datatables.net-buttons-bs4/js/buttons.bootstrap4.min.js",
"datatables.net-responsive/js/dataTables.responsive.min.js",
"dnt-helper/js/dnt-helper.js",
"flag-icon-css/**/*",
"flot/dist/**/*",
"gpredict.js/dist/gpredict.min.js",
"jquery/dist/jquery.min.js",
"mapbox-gl/dist/mapbox-gl.css",
"mapbox-gl/dist/mapbox-gl.js",
"moment/min/moment.min.js"
"moment/min/moment.min.js",
"pace/dist/**/*",
"popper.js/dist/umd/popper.min.js",
"sparklines/dist/**/*",
"summernote/dist/**/*"
]
}

View File

@ -4,16 +4,16 @@
# './contrib/refresh-requirements.sh to regenerate this file
-r requirements.txt
Faker==4.1.1
apipkg==1.5
appdirs==1.4.4
click==7.1.2
coverage==5.2
coverage==5.2.1
distlib==0.3.1
docopt==0.6.1
docopts==0.6.1
execnet==1.7.1
factory-boy==2.12.0
Faker==4.1.1
filelock==3.0.12
mock==4.0.2
more-itertools==8.4.0
@ -22,13 +22,13 @@ pluggy==0.13.1
pur==5.3.0
py==1.9.0
pyparsing==2.4.7
pytest==5.4.3
pytest-cov==2.10.0
pytest-django==3.9.0
pytest-forked==1.2.0
pytest-xdist==1.33.0
pytest==5.4.3
text-unidecode==1.3
toml==0.10.1
tox==3.16.1
virtualenv==20.0.26
virtualenv==20.0.28
wcwidth==0.2.5

View File

@ -11,23 +11,27 @@ celery==4.3.0
certifi==2020.6.20
cffi==1.14.0
chardet==3.0.4
cryptography==2.9.2
cryptography==3.0
defusedxml==0.6.0
dj-database-url==0.5.0
Django==2.2.14
django-allauth==0.42.0
django-appconf==1.0.4
django-avatar==5.0.0
django-bootstrap-modal-forms==2.0.0
django-compressor==2.4
django-countries==6.1.2
django-crispy-forms==1.9.2
django-csp==3.6
django-debug-toolbar==2.2
django-filter==2.3.0
django-fontawesome-5==1.0.18
django-jsonfield==1.3.1
django-redis-cache==2.0.0
django-shortuuidfield==0.1.3
django-widget-tweaks==1.4.8
djangorestframework==3.10.3
dnspython==1.16.0
dnspython==2.0.0
enum34==1.1.10
eventlet==0.25.2
frozendict==1.2
@ -35,6 +39,7 @@ greenlet==0.4.16
gunicorn==19.9.0
h5py==2.10.0
idna==2.10
importlib-metadata==1.7.0
influxdb==5.3.0
kaitaistruct==0.8
kombu==4.6.11
@ -44,7 +49,7 @@ Markdown==3.2.2
monotonic==1.5
msgpack==0.6.1
mysqlclient==1.4.6
numpy==1.19.0
numpy==1.19.1
oauthlib==3.1.0
Pillow==7.2.0
pycparser==2.20
@ -64,11 +69,11 @@ requests-oauthlib==1.3.0
rjsmin==1.1.0
rush==2018.12.1
satellitetle==0.9.0
satnogs-decoders~=1.0
sentry-sdk==0.16.1
satnogs-decoders==1.7.0
sentry-sdk==0.16.2
sgp4==2.12
shortuuid==1.0.1
simplejson==3.17.0
simplejson==3.17.2
six==1.15.0
social-auth-app-django==4.0.0
social-auth-core==3.3.3
@ -78,3 +83,4 @@ Unipath==1.1
uritemplate==3.0.1
urllib3==1.24.3
vine==1.3.0
zipp==3.1.0

View File

@ -59,12 +59,17 @@ install_requires =
satellitetle~=0.9.0
# Unsorted
influxdb~=5.3.0
django-widget-tweaks~=1.4.2
django-bootstrap-modal-forms~=2.0.0
django-fontawesome-5
satnogs-decoders~=1.0
simplejson~=3.17.0
uritemplate~=3.0.0
PyYAML~=5.3.0
h5py~=2.10.0
PyLD~=2.0.0
PyLD~=2.0.2
# Metasat
django-countries~=6.1.2
# Debugging
django-debug-toolbar~=2.2.0
# Conflict workarounds

View File

@ -6,7 +6,7 @@ flake8 = 3.7.9
isort = 4.3.21
yapf = 0.28.0
pylint = 2.4.4
pylint_django = 2.0.14
pylint_django = 2.0.15
sphinx_rtd_theme = 0.4.3
twine = 3.1.1