Refactor vetted status
* Renamed verified to good * Renamed no_data to bad * Add failed status * Remove unused data_not_verified * Expose vetted status to API * Simplify vetted status template markup * Simplify vetted methods * Utilize sass for vetted statuses * Add option to undo vettingmerge-requests/451/head
parent
334958e225
commit
b1eebfe0ae
|
@ -2,7 +2,7 @@ FROM centos:7
|
|||
RUN yum makecache
|
||||
RUN yum -y install epel-release
|
||||
RUN yum -y install python python-pip python-devel git gcc libjpeg-turbo-devel \
|
||||
libxml2-devel libxslt-devel mysql-devel mysql ruby-sass
|
||||
libxml2-devel libxslt-devel mysql-devel mysql rubygem-sass
|
||||
RUN yum -y clean all
|
||||
RUN pip install --upgrade pip
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
FROM fedora:latest
|
||||
RUN dnf upgrade -y libsolv
|
||||
RUN dnf -y install python python-pip python-devel git gcc libjpeg-turbo-devel findutils \
|
||||
libxml2-devel libxslt-devel mysql-devel mysql npm redhat-rpm-config ruby-sass
|
||||
libxml2-devel libxslt-devel mysql-devel mysql npm redhat-rpm-config rubygem-sass
|
||||
RUN dnf -y clean all
|
||||
|
||||
RUN npm install -g eslint stylelint
|
||||
|
|
|
@ -21,7 +21,7 @@ class ObservationSerializer(serializers.ModelSerializer):
|
|||
model = Observation
|
||||
fields = ('id', 'start', 'end', 'ground_station', 'transmitter',
|
||||
'norad_cat_id', 'payload', 'waterfall', 'demoddata', 'station_name',
|
||||
'station_lat', 'station_lng')
|
||||
'station_lat', 'station_lng', 'vetted_status')
|
||||
read_only_fields = ['id', 'start', 'end', 'observation', 'ground_station',
|
||||
'transmitter', 'norad_cat_id', 'station_name',
|
||||
'station_lat', 'station_lng']
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2017-12-24 17:01
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0029_auto_20171216_1712'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='observation',
|
||||
name='vetted_status',
|
||||
field=models.CharField(choices=[(b'unknown', b'Unknown'), (b'verified', b'Verified'), (b'no_data', b'No Data'), (b'good', b'Good'), (b'bad', b'Bad'), (b'failed', b'Failed')], default=b'unknown', max_length=20),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2017-12-23 23:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def move_vetted(apps, schema_editor):
|
||||
Observation = apps.get_model('base', 'Observation')
|
||||
for obs in Observation.objects.all():
|
||||
if obs.vetted_status == 'verified':
|
||||
obs.vetted_status='good'
|
||||
if obs.vetted_status == 'no_data':
|
||||
obs.vetted_status='bad'
|
||||
obs.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0030_auto_20171224_1701'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(move_vetted),
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.7 on 2017-12-24 17:03
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0031_migrate_vetted'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='observation',
|
||||
name='vetted_status',
|
||||
field=models.CharField(choices=[(b'unknown', b'Unknown'), (b'good', b'Good'), (b'bad', b'Bad'), (b'failed', b'Failed')], default=b'unknown', max_length=20),
|
||||
),
|
||||
]
|
|
@ -33,9 +33,9 @@ ANTENNA_TYPES = (
|
|||
)
|
||||
OBSERVATION_STATUSES = (
|
||||
('unknown', 'Unknown'),
|
||||
('verified', 'Verified'),
|
||||
('data_not_verified', 'Has Data, Not Verified'),
|
||||
('no_data', 'No Data'),
|
||||
('good', 'Good'),
|
||||
('bad', 'Bad'),
|
||||
('failed', 'Failed'),
|
||||
)
|
||||
SATELLITE_STATUS = ['alive', 'dead', 're-entered']
|
||||
|
||||
|
@ -122,10 +122,10 @@ class Station(models.Model):
|
|||
|
||||
@property
|
||||
def success_rate(self):
|
||||
observations = self.observations.all().count()
|
||||
success = self.observations.exclude(payload='').count()
|
||||
observations = self.observations.all()
|
||||
success = observations.filter(id__in=(o.id for o in observations if o.has_audio)).count()
|
||||
if observations:
|
||||
return int(100 * (float(success) / float(observations)))
|
||||
return int(100 * (float(success) / float(observations.count())))
|
||||
else:
|
||||
return False
|
||||
|
||||
|
@ -198,14 +198,14 @@ class Satellite(models.Model):
|
|||
return Observation.objects.filter(satellite=self).count()
|
||||
|
||||
@property
|
||||
def verified_count(self):
|
||||
def good_count(self):
|
||||
data = Observation.objects.filter(satellite=self)
|
||||
return data.filter(vetted_status='verified').count()
|
||||
return data.filter(vetted_status='good').count()
|
||||
|
||||
@property
|
||||
def empty_count(self):
|
||||
def bad_count(self):
|
||||
data = Observation.objects.filter(satellite=self)
|
||||
return data.filter(vetted_status='no_data').count()
|
||||
return data.filter(vetted_status='bad').count()
|
||||
|
||||
@property
|
||||
def unknown_count(self):
|
||||
|
@ -215,14 +215,14 @@ class Satellite(models.Model):
|
|||
@property
|
||||
def success_rate(self):
|
||||
try:
|
||||
return int(100 * (float(self.verified_count) / float(self.data_count)))
|
||||
return int(100 * (float(self.good_count) / float(self.data_count)))
|
||||
except (ZeroDivisionError, TypeError):
|
||||
return 0
|
||||
|
||||
@property
|
||||
def empty_rate(self):
|
||||
def bad_rate(self):
|
||||
try:
|
||||
return int(100 * (float(self.empty_count) / float(self.data_count)))
|
||||
return int(100 * (float(self.bad_count) / float(self.data_count)))
|
||||
except (ZeroDivisionError, TypeError):
|
||||
return 0
|
||||
|
||||
|
@ -308,20 +308,25 @@ class Observation(models.Model):
|
|||
def is_future(self):
|
||||
return self.end > now()
|
||||
|
||||
# this payload has been vetted good/bad by someone
|
||||
# this payload has been vetted good/bad/failed by someone
|
||||
@property
|
||||
def is_vetted(self):
|
||||
return not self.vetted_status == 'unknown'
|
||||
|
||||
# this payload has been vetted as good by someone
|
||||
@property
|
||||
def is_verified(self):
|
||||
return self.vetted_status == 'verified'
|
||||
def is_good(self):
|
||||
return self.vetted_status == 'good'
|
||||
|
||||
# this payload has been vetted as bad by someone
|
||||
@property
|
||||
def is_no_data(self):
|
||||
return self.vetted_status == 'no_data'
|
||||
def is_bad(self):
|
||||
return self.vetted_status == 'bad'
|
||||
|
||||
# this payload has been vetted as failed by someone
|
||||
@property
|
||||
def is_failed(self):
|
||||
return self.vetted_status == 'failed'
|
||||
|
||||
@property
|
||||
def has_audio(self):
|
||||
|
|
|
@ -142,6 +142,6 @@ def clean_observations():
|
|||
observations = Observation.objects.filter(end__lt=threshold)
|
||||
for obs in observations:
|
||||
if settings.ENVIRONMENT == 'stage':
|
||||
if not obs.is_verified:
|
||||
if not obs.is_good:
|
||||
obs.delete()
|
||||
archive_audio.delay(obs.id)
|
||||
|
|
|
@ -241,11 +241,11 @@ class ObservationsListViewTest(TestCase):
|
|||
for x in xrange(1, 10):
|
||||
self.stations.append(StationFactory())
|
||||
for x in xrange(1, 10):
|
||||
obs = ObservationFactory(vetted_status='no_data')
|
||||
obs = ObservationFactory(vetted_status='bad')
|
||||
self.observations_bad.append(obs)
|
||||
self.observations.append(obs)
|
||||
for x in xrange(1, 10):
|
||||
obs = ObservationFactory(vetted_status='verified')
|
||||
obs = ObservationFactory(vetted_status='good')
|
||||
self.observations_good.append(obs)
|
||||
self.observations.append(obs)
|
||||
for x in xrange(1, 10):
|
||||
|
@ -434,9 +434,9 @@ class SettingsSiteViewTest(TestCase):
|
|||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
class ObservationVerifyViewtest(TestCase):
|
||||
class ObservationVetViewtest(TestCase):
|
||||
"""
|
||||
Test vetting data as good
|
||||
Test vetting data
|
||||
"""
|
||||
client = Client()
|
||||
user = None
|
||||
|
@ -456,48 +456,39 @@ class ObservationVerifyViewtest(TestCase):
|
|||
self.transmitters.append(TransmitterFactory())
|
||||
for x in xrange(1, 10):
|
||||
self.stations.append(StationFactory())
|
||||
|
||||
self.observation = ObservationFactory()
|
||||
for x in xrange(1, 5):
|
||||
self.observations.append(ObservationFactory(vetted_status='unknown'))
|
||||
|
||||
def test_get_observation_vet_good(self):
|
||||
response = self.client.get('/observation_vet_good/%d/' % self.observation.id)
|
||||
self.assertRedirects(response, '/observations/%d/' % self.observation.id)
|
||||
observation = Observation.objects.get(id=self.observation.id)
|
||||
obs = self.observations[0]
|
||||
response = self.client.get('/observation_vet/%d/good/' % obs.id)
|
||||
self.assertRedirects(response, '/observations/%d/' % obs.id)
|
||||
observation = Observation.objects.get(id=obs.id)
|
||||
self.assertEqual(observation.vetted_user.username, self.user.username)
|
||||
self.assertEqual(observation.vetted_status, 'verified')
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
class ObservationMarkBadViewtest(TestCase):
|
||||
"""
|
||||
Test vetting data as bad
|
||||
"""
|
||||
client = Client()
|
||||
user = None
|
||||
satellites = []
|
||||
stations = []
|
||||
transmitters = []
|
||||
|
||||
def setUp(self):
|
||||
self.user = UserFactory()
|
||||
g = Group.objects.get(name='Moderators')
|
||||
g.user_set.add(self.user)
|
||||
self.client.force_login(self.user)
|
||||
for x in xrange(1, 10):
|
||||
self.satellites.append(SatelliteFactory())
|
||||
for x in xrange(1, 10):
|
||||
self.transmitters.append(TransmitterFactory())
|
||||
for x in xrange(1, 10):
|
||||
self.stations.append(StationFactory())
|
||||
|
||||
self.observation = ObservationFactory()
|
||||
self.assertEqual(observation.vetted_status, 'good')
|
||||
|
||||
def test_get_observation_vet_bad(self):
|
||||
response = self.client.get('/observation_vet_bad/%d/' % self.observation.id)
|
||||
self.assertRedirects(response, '/observations/%d/' % self.observation.id)
|
||||
observation = Observation.objects.get(id=self.observation.id)
|
||||
obs = self.observations[1]
|
||||
response = self.client.get('/observation_vet/%d/bad/' % obs.id)
|
||||
self.assertRedirects(response, '/observations/%d/' % obs.id)
|
||||
observation = Observation.objects.get(id=obs.id)
|
||||
self.assertEqual(observation.vetted_user.username, self.user.username)
|
||||
self.assertEqual(observation.vetted_status, 'no_data')
|
||||
self.assertEqual(observation.vetted_status, 'bad')
|
||||
|
||||
def test_get_observation_vet_failed(self):
|
||||
obs = self.observations[2]
|
||||
response = self.client.get('/observation_vet/%d/failed/' % obs.id)
|
||||
self.assertRedirects(response, '/observations/%d/' % obs.id)
|
||||
observation = Observation.objects.get(id=obs.id)
|
||||
self.assertEqual(observation.vetted_user.username, self.user.username)
|
||||
self.assertEqual(observation.vetted_status, 'failed')
|
||||
|
||||
def test_get_observation_undo_vet(self):
|
||||
obs = self.observations[0]
|
||||
response = self.client.get('/observation_vet/%d/unknown/' % obs.id)
|
||||
self.assertRedirects(response, '/observations/%d/' % obs.id)
|
||||
observation = Observation.objects.get(id=obs.id)
|
||||
self.assertEqual(observation.vetted_status, 'unknown')
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
|
|
|
@ -24,10 +24,8 @@ base_urlpatterns = ([
|
|||
views.prediction_windows, name='prediction_windows'),
|
||||
url(r'^pass_predictions/(?P<id>[\w.@+-]+)/$',
|
||||
views.pass_predictions, name='pass_predictions'),
|
||||
url(r'^observation_vet_good/(?P<id>[0-9]+)/$', views.observation_vet_good,
|
||||
name='observation_vet_good'),
|
||||
url(r'^observation_vet_bad/(?P<id>[0-9]+)/$', views.observation_vet_bad,
|
||||
name='observation_vet_bad'),
|
||||
url(r'^observation_vet/(?P<id>[0-9]+)/(?P<status>[a-z]+)/$', views.observation_vet,
|
||||
name='observation_vet'),
|
||||
|
||||
# Stations
|
||||
url(r'^stations/$', views.stations_list, name='stations_list'),
|
||||
|
|
|
@ -159,7 +159,7 @@ class ObservationListView(ListView):
|
|||
if not bad:
|
||||
observations = observations.exclude(vetted_status='no_data')
|
||||
if not good:
|
||||
observations = observations.exclude(vetted_status='verified')
|
||||
observations = observations.exclude(vetted_status='good')
|
||||
if not unvetted:
|
||||
observations = observations.exclude(vetted_status='unknown',
|
||||
id__in=(o.id for
|
||||
|
@ -493,30 +493,18 @@ def observation_delete(request, id):
|
|||
|
||||
|
||||
@login_required
|
||||
def observation_vet_good(request, id):
|
||||
def observation_vet(request, id, status):
|
||||
observation = get_object_or_404(Observation, id=id)
|
||||
can_vet = vet_perms(request.user, observation)
|
||||
if can_vet:
|
||||
observation.vetted_status = 'verified'
|
||||
if status in ['good', 'bad', 'failed', 'unknown'] and can_vet:
|
||||
observation.vetted_status = status
|
||||
observation.vetted_user = request.user
|
||||
observation.vetted_datetime = datetime.today()
|
||||
observation.vetted_datetime = now()
|
||||
observation.save(update_fields=['vetted_status', 'vetted_user', 'vetted_datetime'])
|
||||
messages.success(request, 'Observation vetted successfully.')
|
||||
else:
|
||||
messages.error(request, 'Permission denied.')
|
||||
return redirect(reverse('base:observation_view', kwargs={'id': observation.id}))
|
||||
|
||||
|
||||
@login_required
|
||||
def observation_vet_bad(request, id):
|
||||
observation = get_object_or_404(Observation, id=id)
|
||||
can_vet = vet_perms(request.user, observation)
|
||||
if can_vet:
|
||||
observation.vetted_status = 'no_data'
|
||||
observation.vetted_user = request.user
|
||||
observation.vetted_datetime = datetime.today()
|
||||
observation.save(update_fields=['vetted_status', 'vetted_user', 'vetted_datetime'])
|
||||
messages.success(request, 'Observation vetted successfully.')
|
||||
if not status == 'unknown':
|
||||
messages.success(request, 'Observation vetted successfully. [<a href="{0}">Undo</a>]'
|
||||
.format(reverse('base:observation_vet',
|
||||
kwargs={'id': observation.id, 'status': 'unknown'})))
|
||||
else:
|
||||
messages.error(request, 'Permission denied.')
|
||||
return redirect(reverse('base:observation_view', kwargs={'id': observation.id}))
|
||||
|
@ -642,10 +630,10 @@ def pass_predictions(request, id):
|
|||
'id': str(satellite.id),
|
||||
'success_rate': str(satellite.success_rate),
|
||||
'unknown_rate': str(satellite.unknown_rate),
|
||||
'empty_rate': str(satellite.empty_rate),
|
||||
'bad_rate': str(satellite.bad_rate),
|
||||
'data_count': str(satellite.data_count),
|
||||
'verified_count': str(satellite.verified_count),
|
||||
'empty_count': str(satellite.empty_count),
|
||||
'good_count': str(satellite.good_count),
|
||||
'bad_count': str(satellite.bad_count),
|
||||
'unknown_count': str(satellite.unknown_count),
|
||||
'norad_cat_id': str(satellite.norad_cat_id),
|
||||
'tr': tr.datetime(), # Rise time
|
||||
|
@ -720,8 +708,8 @@ def satellite_view(request, id):
|
|||
'names': sat.names,
|
||||
'image': sat.image,
|
||||
'success_rate': sat.success_rate,
|
||||
'verified_count': sat.verified_count,
|
||||
'empty_count': sat.empty_count,
|
||||
'good_count': sat.good_count,
|
||||
'bad_count': sat.bad_count,
|
||||
'unknown_count': sat.unknown_count,
|
||||
'data_count': sat.data_count,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
body {
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
font-family: ClearSans;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.alert-debug {
|
||||
color: black;
|
||||
background-color: white;
|
||||
border-color: #d6e9c6;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
color: #3a87ad;
|
||||
background-color: #d9edf7;
|
||||
border-color: #bce8f1;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
color: #468847;
|
||||
background-color: #dff0d8;
|
||||
border-color: #d6e9c6;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
color: black;
|
||||
background-color: orange;
|
||||
border-color: #d6e9c6;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
color: #b94a48;
|
||||
background-color: #f2dede;
|
||||
border-color: #eed3d7;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin-top: 40px auto 0 auto;
|
||||
width: 500px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.help-block {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.bottom-details {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.img-avatar {
|
||||
border: 2px solid white;
|
||||
border-radius: 50px;
|
||||
float: left;
|
||||
margin: 0 auto;
|
||||
margin-top: -7px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.form-avatar {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.footer-options {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.navbar-default {
|
||||
background-color: white;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
@font-face {
|
||||
font-family: ClearSans;
|
||||
src: url('../fonts/ClearSans-Regular.eot');
|
||||
src: url('../fonts/staticClearSans-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');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ClearSans;
|
||||
src: url('../fonts/ClearSans-Bold.eot');
|
||||
src: url('../fonts/ClearSans-Bold.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/ClearSans-Bold.woff') format('woff'),
|
||||
url('../fonts/ClearSans-Bold.ttf') format('truetype'),
|
||||
url('../fonts/ClearSans-Bold.svg#open_sansbold') format('svg');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ClearSans;
|
||||
src: url('../fonts/ClearSans-BoldItalic.eot');
|
||||
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');
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ClearSans;
|
||||
src: url('../fonts/ClearSans-Italic.eot');
|
||||
src: url('../fonts/ClearSans-Italic.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/ClearSans-Italic.woff') format('woff'),
|
||||
url('../fonts/ClearSans-Italic.ttf') format('truetype'),
|
||||
url('../fonts/ClearSans-Italic.svg#open_sansitalic') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
.label-good {
|
||||
background-color: #5cb85c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-good {
|
||||
@extend .label-good;
|
||||
|
||||
&:hover {
|
||||
background-color: #449d44;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.label-bad {
|
||||
background-color: #d9534f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-bad {
|
||||
@extend .label-bad;
|
||||
|
||||
&:hover {
|
||||
background-color: #c9302c;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.label-failed {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-failed {
|
||||
@extend .label-failed;
|
||||
|
||||
&:hover {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.label-unknown {
|
||||
background-color: darkorange;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-unknown {
|
||||
@extend .label-unknown;
|
||||
|
||||
&:hover {
|
||||
background-color: orange;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.label-future {
|
||||
background-color: #5bc0de;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-future {
|
||||
@extend .label-future;
|
||||
|
||||
&:hover {
|
||||
background-color: #4ba0be;
|
||||
color: white;
|
||||
}
|
||||
}
|
|
@ -1,140 +1,6 @@
|
|||
/* Fonts
|
||||
==================== */
|
||||
|
||||
@font-face {
|
||||
font-family: ClearSans;
|
||||
src: url('../fonts/ClearSans-Regular.eot');
|
||||
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');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ClearSans;
|
||||
src: url('../fonts/ClearSans-Bold.eot');
|
||||
src: url('../fonts/ClearSans-Bold.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/ClearSans-Bold.woff') format('woff'),
|
||||
url('../fonts/ClearSans-Bold.ttf') format('truetype'),
|
||||
url('../fonts/ClearSans-Bold.svg#open_sansbold') format('svg');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ClearSans;
|
||||
src: url('../fonts/ClearSans-BoldItalic.eot');
|
||||
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');
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: ClearSans;
|
||||
src: url('../fonts/ClearSans-Italic.eot');
|
||||
src: url('../fonts/ClearSans-Italic.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/ClearSans-Italic.woff') format('woff'),
|
||||
url('../fonts/ClearSans-Italic.ttf') format('truetype'),
|
||||
url('../fonts/ClearSans-Italic.svg#open_sansitalic') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Generic
|
||||
==================== */
|
||||
|
||||
body {
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
font-family: ClearSans;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.alert-debug {
|
||||
color: black;
|
||||
background-color: white;
|
||||
border-color: #d6e9c6;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
color: #3a87ad;
|
||||
background-color: #d9edf7;
|
||||
border-color: #bce8f1;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
color: #468847;
|
||||
background-color: #dff0d8;
|
||||
border-color: #d6e9c6;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
color: black;
|
||||
background-color: orange;
|
||||
border-color: #d6e9c6;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
color: #b94a48;
|
||||
background-color: #f2dede;
|
||||
border-color: #eed3d7;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin-top: 40px auto 0 auto;
|
||||
width: 500px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.help-block {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.bottom-details {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.img-avatar {
|
||||
border: 2px solid white;
|
||||
border-radius: 50px;
|
||||
float: left;
|
||||
margin: 0 auto;
|
||||
margin-top: -7px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.form-avatar {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.footer-options {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.navbar-default {
|
||||
background-color: white;
|
||||
}
|
||||
@import 'fonts';
|
||||
@import 'common';
|
||||
@import 'vetted';
|
||||
|
||||
#main-navbar {
|
||||
margin-top: 2%;
|
||||
|
@ -418,10 +284,6 @@ span.datetime-time {
|
|||
font-size: 12px;
|
||||
}
|
||||
|
||||
.label-warning {
|
||||
background-color: darkorange;
|
||||
}
|
||||
|
||||
#timeline2 .axis {
|
||||
transform: translate(0, 30px);
|
||||
-ms-transform: translate(0, 30px); /* IE 9 */
|
|
@ -17,9 +17,9 @@ $(document).ready(function() {
|
|||
modal.find('#new-obs-link').attr('href', '/observations/new/?norad=' + satlink.data('id'));
|
||||
modal.find('#old-obs-link').attr('href', '/observations/?norad=' + satlink.data('id'));
|
||||
modal.find('.satellite-success').text(data.success_rate + '% success on ' + data.data_count + ' observations');
|
||||
modal.find('.satellite-verified').text(data.verified_count);
|
||||
modal.find('.satellite-good').text(data.good_count);
|
||||
modal.find('.satellite-unknown').text(data.unknown_count);
|
||||
modal.find('.satellite-empty').text(data.empty_count);
|
||||
modal.find('.satellite-bad').text(data.bad_count);
|
||||
if (data.image) {
|
||||
modal.find('.satellite-img-full').attr('src', data.image);
|
||||
} else {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<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' %}">
|
||||
<link rel="stylesheet" type="text/scss" href="{% static 'css/app.scss' %}">
|
||||
{% block css %}
|
||||
{% endblock %}
|
||||
{% endcompress %}
|
||||
|
|
|
@ -122,17 +122,13 @@
|
|||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'base:observation_view' id=observation.id %}">
|
||||
{% if observation.is_verified %}
|
||||
<span class="label label-success" title="There is known good data in this observation"
|
||||
{% if observation.is_vetted %}
|
||||
<span class="label label-{{observation.vetted_status }}">{{ observation.id }}</span>
|
||||
{% elif observation.is_future %}
|
||||
<span class="label label-info" title="This observation is in the future"
|
||||
{% elif not observation.is_vetted %}
|
||||
<span class="label label-warning" title="There is data that needs vetting in this observation"
|
||||
<span class="label label-future">{{ observation.id }}</span>
|
||||
{% else %}
|
||||
<span class="label label-danger" title="No good data in this observation"
|
||||
{% endif %}>
|
||||
{{ observation.id }}
|
||||
</span>
|
||||
<span class="label label-unknown">{{ observation.id }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -74,42 +74,41 @@
|
|||
<div class="front-line">
|
||||
<span class="label label-default">Status</span>
|
||||
<span class="front-data">
|
||||
{% if observation.is_verified %}
|
||||
<span class="label label-xs label-success" aria-hidden="true"
|
||||
{% if observation.is_vetted %}
|
||||
<span class="label label-xs label-{{ observation.vetted_status }}" aria-hidden="true"
|
||||
data-toggle="tooltip" data-placement="right"
|
||||
title="Vetted good on {{ observation.vetted_datetime|date:"Y-m-d H:i:s" }} by {{ observation.vetted_user.displayname }}">Good</span>
|
||||
{% elif observation.is_no_data %}
|
||||
<span class="label label-xs label-danger" aria-hidden="true"
|
||||
data-toggle="tooltip" data-placement="right"
|
||||
title="Vetted bad on {{ observation.vetted_datetime|date:"Y-m-d H:i:s" }} by {{ observation.vetted_user.displayname }}">Bad</span>
|
||||
title="Vetted {{ observation.vetted_status }} on {{ observation.vetted_datetime|date:"Y-m-d H:i:s" }} by {{ observation.vetted_user.displayname }}">{{ observation.get_vetted_status_display }}</span>
|
||||
{% elif observation.is_future %}
|
||||
<span class="label label-xs label-info" aria-hidden="true"
|
||||
data-toggle="tooltip" data-placement="right"
|
||||
title="This observation is in the future">Pending</span>
|
||||
{% else %}
|
||||
<span class="label label-xs label-warning" aria-hidden="true"
|
||||
<span class="label label-xs label-{{ observation.vetted_status }}" aria-hidden="true"
|
||||
data-toggle="tooltip" data-placement="right"
|
||||
title="This observation needs vetting">Unvetted</span>
|
||||
{% if observation.is_past %}
|
||||
{% if not observation.is_vetted and can_vet %}
|
||||
{% if can_vet %}
|
||||
<div class="pull-right">
|
||||
<a href="https://wiki.satnogs.org/Operation#Rating_observations" target="_blank">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
|
||||
</a>
|
||||
<a href="{% url 'base:observation_vet_good' id=observation.id %}">
|
||||
<button id="good-data" type="button" class="btn btn-xs btn-success">
|
||||
<a href="{% url 'base:observation_vet' id=observation.id status='good' %}">
|
||||
<button id="good-data" type="button" class="btn btn-xs btn-success" title="good" data-toggle="tooltip">
|
||||
<span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span>
|
||||
</button>
|
||||
</a>
|
||||
<a href="{% url 'base:observation_vet_bad' id=observation.id %}">
|
||||
<button id="bad-data" type="button" class="btn btn-xs btn-danger">
|
||||
<a href="{% url 'base:observation_vet' id=observation.id status='bad' %}">
|
||||
<button id="bad-data" type="button" class="btn btn-xs btn-danger" title="bad" data-toggle="tooltip">
|
||||
<span class="glyphicon glyphicon-thumbs-down" aria-hidden="true"></span>
|
||||
</button>
|
||||
</a>
|
||||
<a href="{% url 'base:observation_vet' id=observation.id status='failed' %}">
|
||||
<button id="bad-failed" type="button" class="btn btn-xs btn-failed" title="failed" data-toggle="tooltip">
|
||||
<span class="glyphicon glyphicon-minus" aria-hidden="true"></span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="front-line">
|
||||
|
|
|
@ -27,16 +27,16 @@
|
|||
<div class="form-group col-md-3">
|
||||
<label for="data-selector">Data</label>
|
||||
<div id="data-selector" class="btn-group" data-toggle="buttons">
|
||||
<label class="btn btn-info btn-sm {% if future == '1' %}active{% endif %}" aria-expanded="true" aria-controls="future">
|
||||
<label class="btn btn-future btn-sm {% if future == '1' %}active{% endif %}" aria-expanded="true" aria-controls="future">
|
||||
<input type="checkbox" name="future" {% if future == '1' %}checked{% endif %} autocomplete="off">Future
|
||||
</label>
|
||||
<label class="btn btn-success btn-sm {% if good == '1' %}active{% endif %}" aria-expanded="true" aria-controls="good">
|
||||
<label class="btn btn-good btn-sm {% if good == '1' %}active{% endif %}" aria-expanded="true" aria-controls="good">
|
||||
<input type="checkbox" name="good" {% if good == '1' %}checked{% endif %} autocomplete="off">Good
|
||||
</label>
|
||||
<label class="btn btn-danger btn-sm {% if bad == '1' %}active{% endif %}" aria-expanded="true" aria-controls="bad">
|
||||
<label class="btn btn-bad btn-sm {% if bad == '1' %}active{% endif %}" aria-expanded="true" aria-controls="bad">
|
||||
<input type="checkbox" name="bad" {% if bad == '1' %}checked{% endif %} autocomplete="off">Bad
|
||||
</label>
|
||||
<label class="btn btn-warning btn-sm {% if unvetted == '1' %}active{% endif %}" aria-expanded="true" aria-controls="unvetted">
|
||||
<label class="btn btn-unknown btn-sm {% if unvetted == '1' %}active{% endif %}" aria-expanded="true" aria-controls="unvetted">
|
||||
<input type="checkbox" name="unvetted" {% if unvetted == '1' %}checked{% endif %} autocomplete="off">Unvetted
|
||||
</label>
|
||||
</div>
|
||||
|
@ -100,17 +100,13 @@
|
|||
{% if observation.id in scheduled %}class="bg-info"{% endif %}>
|
||||
<td>
|
||||
<a href="{% url 'base:observation_view' id=observation.id %}" class="obs-link">
|
||||
{% if observation.is_verified %}
|
||||
<span class="label label-success" title="There is known good data in this observation"
|
||||
{% if observation.is_vetted %}
|
||||
<span class="label label-{{observation.vetted_status }}">{{ observation.id }}</span>
|
||||
{% elif observation.is_future %}
|
||||
<span class="label label-info" title="This observation is in the future"
|
||||
{% elif not observation.is_vetted %}
|
||||
<span class="label label-warning" title="There is data that needs vetting in this observation"
|
||||
<span class="label label-future">{{ observation.id }}</span>
|
||||
{% else %}
|
||||
<span class="label label-danger" title="No good data in this observation"
|
||||
{% endif %}>
|
||||
{{ observation.id }}
|
||||
</span>
|
||||
<span class="label label-unknown">{{ observation.id }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -8,16 +8,19 @@
|
|||
<div class="modal-body">
|
||||
<h4>The ID number of each observation is listed below, colored as such:</h4>
|
||||
<p>
|
||||
<span class="label label-info" title="This observation is in the future">43</span> This observation is in the future</span>
|
||||
<span class="label label-future" title="This observation is in the future">43</span> This observation is in the future</span>
|
||||
</p>
|
||||
<p>
|
||||
<span class="label label-success" title="There is known good data in this observation">12</span> There is some known good data in this observation
|
||||
<span class="label label-good" title="There is known good data in this observation">12</span> There is some known good data in this observation
|
||||
</p>
|
||||
<p>
|
||||
<span class="label label-warning" title="There is data that needs vetting in this observation">56</span> There is data that needs vetting in this observation
|
||||
<span class="label label-unknown" title="There is data that needs vetting in this observation">56</span> There is data that needs vetting in this observation
|
||||
</p>
|
||||
<p>
|
||||
<span class="label label-danger" title="No good data in this observation">93</span> No good data in this observation
|
||||
<span class="label label-bad" title="No good data in this observation">78</span> No good data in this observation
|
||||
</p>
|
||||
<p>
|
||||
<span class="label label-failed" title="This observation failed">93</span> Observation failed
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
<span class="satellite-success"></span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="label label-success satellite-verified" data-toggle="tooltip" data-placement="bottom" title="Successful observations"></span>
|
||||
<span class="label label-success satellite-good" data-toggle="tooltip" data-placement="bottom" title="Successful observations"></span>
|
||||
<span class="label label-warning satellite-unknown" data-toggle="tooltip" data-placement="bottom" title="Unknown observations"></span>
|
||||
<span class="label label-danger satellite-empty" data-toggle="tooltip" data-placement="bottom" title="Empty observations"></span>
|
||||
<span class="label label-danger satellite-bad" data-toggle="tooltip" data-placement="bottom" title="Bad observations"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -123,6 +123,8 @@
|
|||
label-success" title="There is known good data in this observation"
|
||||
{% elif observation.is_future %}
|
||||
label-info" title="This observation is in the future"
|
||||
{% elif observation.is_failed %}
|
||||
label-default" title="This observation failed"
|
||||
{% elif not observation.is_vetted %}
|
||||
label-warning" title="There is data that needs vetting in this observation"
|
||||
{% else %}
|
||||
|
|
Loading…
Reference in New Issue