diff --git a/docker/dev b/docker/dev index 38e561a..3be3789 100644 --- a/docker/dev +++ b/docker/dev @@ -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 diff --git a/docker/test b/docker/test index 99bd252..685eafd 100644 --- a/docker/test +++ b/docker/test @@ -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 diff --git a/network/api/serializers.py b/network/api/serializers.py index e2d4efc..8bcd551 100644 --- a/network/api/serializers.py +++ b/network/api/serializers.py @@ -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'] diff --git a/network/base/migrations/0030_auto_20171224_1701.py b/network/base/migrations/0030_auto_20171224_1701.py new file mode 100644 index 0000000..6a15478 --- /dev/null +++ b/network/base/migrations/0030_auto_20171224_1701.py @@ -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), + ), + ] diff --git a/network/base/migrations/0031_migrate_vetted.py b/network/base/migrations/0031_migrate_vetted.py new file mode 100644 index 0000000..3541aaf --- /dev/null +++ b/network/base/migrations/0031_migrate_vetted.py @@ -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), + ] diff --git a/network/base/migrations/0032_auto_20171224_1703.py b/network/base/migrations/0032_auto_20171224_1703.py new file mode 100644 index 0000000..06d6333 --- /dev/null +++ b/network/base/migrations/0032_auto_20171224_1703.py @@ -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), + ), + ] diff --git a/network/base/models.py b/network/base/models.py index e560f16..9cf5401 100644 --- a/network/base/models.py +++ b/network/base/models.py @@ -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): diff --git a/network/base/tasks.py b/network/base/tasks.py index 178168f..8f6e5d4 100644 --- a/network/base/tasks.py +++ b/network/base/tasks.py @@ -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) diff --git a/network/base/tests.py b/network/base/tests.py index afe4036..e8169c1 100644 --- a/network/base/tests.py +++ b/network/base/tests.py @@ -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) diff --git a/network/base/urls.py b/network/base/urls.py index 91ad5ea..6fcc4ea 100644 --- a/network/base/urls.py +++ b/network/base/urls.py @@ -24,10 +24,8 @@ base_urlpatterns = ([ views.prediction_windows, name='prediction_windows'), url(r'^pass_predictions/(?P[\w.@+-]+)/$', views.pass_predictions, name='pass_predictions'), - url(r'^observation_vet_good/(?P[0-9]+)/$', views.observation_vet_good, - name='observation_vet_good'), - url(r'^observation_vet_bad/(?P[0-9]+)/$', views.observation_vet_bad, - name='observation_vet_bad'), + url(r'^observation_vet/(?P[0-9]+)/(?P[a-z]+)/$', views.observation_vet, + name='observation_vet'), # Stations url(r'^stations/$', views.stations_list, name='stations_list'), diff --git a/network/base/views.py b/network/base/views.py index e371ec0..29a5219 100644 --- a/network/base/views.py +++ b/network/base/views.py @@ -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. [Undo]' + .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, } diff --git a/network/static/css/_common.scss b/network/static/css/_common.scss new file mode 100644 index 0000000..821735a --- /dev/null +++ b/network/static/css/_common.scss @@ -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; +} diff --git a/network/static/css/_fonts.scss b/network/static/css/_fonts.scss new file mode 100644 index 0000000..968aa94 --- /dev/null +++ b/network/static/css/_fonts.scss @@ -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; +} diff --git a/network/static/css/_vetted.scss b/network/static/css/_vetted.scss new file mode 100644 index 0000000..838f11b --- /dev/null +++ b/network/static/css/_vetted.scss @@ -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; + } +} diff --git a/network/static/css/app.css b/network/static/css/app.scss similarity index 65% rename from network/static/css/app.css rename to network/static/css/app.scss index f8dfcb4..a94adc8 100644 --- a/network/static/css/app.css +++ b/network/static/css/app.scss @@ -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 */ diff --git a/network/static/js/satellite.js b/network/static/js/satellite.js index 7413c81..b66b8b9 100644 --- a/network/static/js/satellite.js +++ b/network/static/js/satellite.js @@ -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 { diff --git a/network/templates/base.html b/network/templates/base.html index 6ba4616..53af466 100644 --- a/network/templates/base.html +++ b/network/templates/base.html @@ -11,7 +11,7 @@ {% compress css %} - + {% block css %} {% endblock %} {% endcompress %} diff --git a/network/templates/base/home.html b/network/templates/base/home.html index 56a6fda..30de14f 100644 --- a/network/templates/base/home.html +++ b/network/templates/base/home.html @@ -122,17 +122,13 @@ - {% if observation.is_verified %} - {{ observation.id }} {% elif observation.is_future %} - {{ observation.id }} {% else %} - - {{ observation.id }} - + {{ observation.id }} + {% endif %} diff --git a/network/templates/base/observation_view.html b/network/templates/base/observation_view.html index d04c520..39bda9b 100644 --- a/network/templates/base/observation_view.html +++ b/network/templates/base/observation_view.html @@ -74,40 +74,39 @@
Status - {% if observation.is_verified %} - - {% elif observation.is_no_data %} - + 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 }} {% elif observation.is_future %} {% else %} - - {% if observation.is_past %} - {% if not observation.is_vetted and can_vet %} - - {% endif %} + {% if can_vet %} + {% endif %} {% endif %} diff --git a/network/templates/base/observations.html b/network/templates/base/observations.html index 0164092..29cdb35 100644 --- a/network/templates/base/observations.html +++ b/network/templates/base/observations.html @@ -27,16 +27,16 @@
-
@@ -100,17 +100,13 @@ {% if observation.id in scheduled %}class="bg-info"{% endif %}> - {% if observation.is_verified %} - {{ observation.id }} {% elif observation.is_future %} - {{ observation.id }} {% else %} - - {{ observation.id }} - + {{ observation.id }} + {% endif %} diff --git a/network/templates/includes/legend.html b/network/templates/includes/legend.html index fa37cf9..666c4af 100644 --- a/network/templates/includes/legend.html +++ b/network/templates/includes/legend.html @@ -8,16 +8,19 @@ diff --git a/network/templates/users/user_detail.html b/network/templates/users/user_detail.html index 3ed16b8..e8ffb6e 100644 --- a/network/templates/users/user_detail.html +++ b/network/templates/users/user_detail.html @@ -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 %}