diff options
author | luxagraf <sng@luxagraf.net> | 2018-01-28 10:30:57 -0600 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2018-01-28 10:30:57 -0600 |
commit | 916474036c1d2e041ed6425a3c0370a2e4c7e7bc (patch) | |
tree | 0536598b9a114303102f6f66242fb31d51beea36 | |
parent | a278fc64c1dcd5321492a7dc384305b8719cb85e (diff) |
abstracted birds out to sightings
-rw-r--r-- | app/birds/admin.py | 52 | ||||
-rw-r--r-- | app/birds/models.py | 149 | ||||
-rw-r--r-- | app/jrnl/views.py | 10 | ||||
-rw-r--r-- | app/sightings/__init__.py | 0 | ||||
-rw-r--r-- | app/sightings/admin.py | 49 | ||||
-rw-r--r-- | app/sightings/autocomplete_light_registry.py | 24 | ||||
-rw-r--r-- | app/sightings/build.py | 21 | ||||
-rw-r--r-- | app/sightings/migrations/0001_initial.py | 72 | ||||
-rw-r--r-- | app/sightings/migrations/__init__.py | 0 | ||||
-rw-r--r-- | app/sightings/models.py | 175 | ||||
-rw-r--r-- | app/sightings/urls.py | 34 | ||||
-rw-r--r-- | app/sightings/views.py | 48 | ||||
-rw-r--r-- | config/base_urls.py | 1 | ||||
-rw-r--r-- | design/sass/_writing_details.scss | 4 | ||||
-rw-r--r-- | design/templates/admin/index.html | 2 | ||||
-rw-r--r-- | design/templates/archives/sightings.html | 56 | ||||
-rw-r--r-- | design/templates/details/entry.html | 6 | ||||
-rw-r--r-- | design/templates/details/sighting.html | 56 |
18 files changed, 744 insertions, 15 deletions
diff --git a/app/birds/admin.py b/app/birds/admin.py index 77ebc8a..50bd755 100644 --- a/app/birds/admin.py +++ b/app/birds/admin.py @@ -1,12 +1,53 @@ from django.contrib import admin from django.contrib.gis.admin import OSMGeoAdmin -from birds.models import BirdSighting, BirdAudio, BirdClass, Bird +from birds.models import BirdSighting, BirdAudio, BirdClass, Bird, APClass, AP, Sighting from photos.forms import GalleryForm from utils.util import get_latlon from utils.widgets import CustomSelectMultiple +class GalleryFormPlus(GalleryForm): + def __init__(self, *args, **kwargs): + super(GalleryFormPlus, self).__init__(*args, **kwargs) + self.base_fields['seen_by'].widget = CustomSelectMultiple() + + class Meta: + model = Sighting + fields = '__all__' + + +@admin.register(APClass) +class APClassAdmin(admin.ModelAdmin): + list_display = ('common_name', 'scientific_name', 'kind') + list_filter = ('kind',) + + +@admin.register(AP) +class APAdmin(admin.ModelAdmin): + list_display = ('pk', 'common_name', 'scientific_name', 'kind', 'code', 'apclass') + list_filter = ('apclass__kind','apclass') + + +@admin.register(Sighting) +class SightingAdmin(OSMGeoAdmin): + form = GalleryFormPlus + list_filter = ('seen_by',('location', admin.RelatedOnlyFieldListFilter),) + list_display = ('ap', 'location') + # options for OSM map Using custom ESRI topo map + lat, lon = get_latlon() + print(lat, lon) + default_lon = lon + default_lat = lat + default_zoom = 13 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + + class BirdClassAdmin(admin.ModelAdmin): list_display = ('common_name', 'scientific_name',) @@ -19,15 +60,6 @@ class BirdAdmin(admin.ModelAdmin): list_display = ('pk', 'common_name', 'scientific_name', 'code', 'bird_class') list_filter = ('bird_class',) -class GalleryFormPlus(GalleryForm): - def __init__(self, *args, **kwargs): - super(GalleryFormPlus, self).__init__(*args, **kwargs) - self.base_fields['seen_by'].widget = CustomSelectMultiple() - - class Meta: - model = BirdSighting - fields = '__all__' - class BirdSightingAdmin(OSMGeoAdmin): form = GalleryFormPlus list_filter = ( diff --git a/app/birds/models.py b/app/birds/models.py index c73e7d7..5a14786 100644 --- a/app/birds/models.py +++ b/app/birds/models.py @@ -17,6 +17,7 @@ def get_upload_path(self, filename): # from http://aba.org/checklist/codes.html ABA_CODES = ( + (0, 'unknown'), (1, 'regular occurring - common'), (2, 'regular occurring - less common'), (3, 'rare'), @@ -25,6 +26,154 @@ ABA_CODES = ( (6, 'Cannot be found'), ) +KIND_LIST = ( + (1, 'Bird'), + (2, 'Mammal'), + (3, 'Reptile'), + (4, 'Amphibian'), + (5, 'Plant'), +) + + +class APClass(models.Model): + common_name = models.CharField(max_length=200) + scientific_name = models.CharField(max_length=200) + kind = models.IntegerField(choices=KIND_LIST, default=1) + + class Meta: + verbose_name_plural = 'Animal/Plant Class' + ordering = ["kind", "common_name"] + + def __str__(self): + return self.common_name + +class AP(models.Model): + common_name = models.CharField(max_length=200) + slug = models.SlugField() + scientific_name = models.CharField(max_length=200) + code = models.IntegerField(choices=ABA_CODES, default=0) + apclass = models.ForeignKey(APClass, on_delete=models.CASCADE) + image = models.FileField(upload_to=get_upload_path, null=True, blank=True, help_text="width of high res is 1360px") + image_credit = models.CharField(max_length=200, blank=True, null=True) + + def __str__(self): + return self.common_name + + def get_image_url(self): + return "%s%s" % (settings.IMAGES_URL, self.image.url.split("media")[1][8:]) + + def get_absolute_url(self): + return reverse("sightings:detail", kwargs={"slug": self.slug}) + + def kind(self): + return self.apclass.kind + + class Meta: + verbose_name_plural = 'Animal/Plant' + verbose_name = 'Animal/Plant' + ordering = ["common_name", ] + + def save(self, *args, **kwargs): + self.slug = slugify(self.common_name[:50]) + super(AP, self).save(*args, **kwargs) + + +class Sighting(models.Model): + ap = models.ForeignKey(AP, on_delete=models.CASCADE) + point = models.PointField(blank=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True, related_name="location_old") + date = models.DateTimeField('Date', default=timezone.now) + seen_by = models.ManyToManyField(User, related_name="seenby_old") + images = models.ManyToManyField(LuxImage, blank=True, related_name="images_old") + #audio = models.ManyToManyField(BirdAudio, blank=True) + + class Meta: + ordering = ["-date", ] + + @property + def state(self): + return self.location.state + + @property + def country(self): + return self.location.state.country + + @property + def region(self): + return self.location.state.country.lux_region + + @property + def longitude(self): + '''Get the site's longitude.''' + return self.point.x + + @property + def latitude(self): + '''Get the site's latitude.''' + return self.point.y + + def get_small_image(self): + for img in self.images.all(): + for size in img.sizes.all(): + if size.width > 360 and size.width < 700: + return img.get_image_by_size(size) + + def get_absolute_url(self): + return reverse("birds:detail", kwargs={"slug": self.bird.slug}) + + def __str__(self): + return self.ap.common_name + + def save(self): + if not self.point: + self.point = Sighting.objects.latest().point + try: + self.location = Location.objects.filter( + geometry__contains=self.point + ).get() + except Location.DoesNotExist: + raise forms.ValidationError("There is no location associated with that point, add it: %sadmin/locations/location/add/" % (settings.BASE_URL)) + super(Sighting, self).save() + +""" +Migration from Birds to abstract: +birdclass = BirdClass.objects.all() +for b in birdclass: + APClass.objects.create( + common_name = b.common_name, + scientific_name = b.scientific_name, + kind = 1 + ) + +birds = Bird.objects.all() +for b in birds: + ap = APClass.objects.get(scientific_name=b.bird_class.scientific_name) + print(ap) + AP.objects.create( + common_name = b.common_name, + scientific_name = b.scientific_name, + code = b.code, + apclass = ap, + image = b.image, + image_credit = b.image_credit, + ) + print(t) + +birdsighting = BirdSighting.objects.all() +for bird in birdsighting: + ap = AP.objects.get(scientific_name=bird.bird.scientific_name) + s = Sighting.objects.create( + ap = ap, + point = bird.point, + location = bird.location, + date = bird.date, + ) + for t in bird.images.all(): + s.images.add(t) + for t in bird.seen_by.all(): + s.seen_by.add(t) +""" + class BirdClass(models.Model): common_name = models.CharField(max_length=200) diff --git a/app/jrnl/views.py b/app/jrnl/views.py index 72a64d7..253aa5e 100644 --- a/app/jrnl/views.py +++ b/app/jrnl/views.py @@ -8,9 +8,8 @@ from django.conf import settings from utils.views import PaginatedListView from .models import Entry, HomepageCurrator -from locations.models import CheckIn - -from locations.models import Country, Region +from locations.models import CheckIn, Country, Region +from birds.models import BirdSighting class EntryList(PaginatedListView): @@ -81,6 +80,11 @@ class EntryDetailView(DetailView): ) return obj + def get_context_data(self, **kwargs): + context = super(EntryDetailView, self).get_context_data(**kwargs) + context['wildlife'] = BirdSighting.objects.filter(location=self.get_object().location) + return context + class EntryDetailViewTXT(EntryDetailView): template_name = "details/entry.txt" diff --git a/app/sightings/__init__.py b/app/sightings/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/sightings/__init__.py diff --git a/app/sightings/admin.py b/app/sightings/admin.py new file mode 100644 index 0000000..196712a --- /dev/null +++ b/app/sightings/admin.py @@ -0,0 +1,49 @@ +from django.contrib import admin +from django.contrib.gis.admin import OSMGeoAdmin +from .models import APClass, AP, Sighting + +from photos.forms import GalleryForm +from utils.util import get_latlon +from utils.widgets import CustomSelectMultiple + + +class GalleryFormPlus(GalleryForm): + def __init__(self, *args, **kwargs): + super(GalleryFormPlus, self).__init__(*args, **kwargs) + self.base_fields['seen_by'].widget = CustomSelectMultiple() + + class Meta: + model = Sighting + fields = '__all__' + + +@admin.register(APClass) +class APClassAdmin(admin.ModelAdmin): + list_display = ('common_name', 'scientific_name', 'kind') + list_filter = ('kind',) + + +@admin.register(AP) +class APAdmin(admin.ModelAdmin): + list_display = ('pk', 'common_name', 'scientific_name', 'kind', 'code', 'apclass') + list_filter = ('apclass__kind','apclass') + search_fields = ['common_name', 'scientific_name'] + + +@admin.register(Sighting) +class SightingAdmin(OSMGeoAdmin): + form = GalleryFormPlus + list_filter = ('seen_by',('location', admin.RelatedOnlyFieldListFilter),) + list_display = ('ap', 'location') + # options for OSM map Using custom ESRI topo map + lat, lon = get_latlon() + print(lat, lon) + default_lon = lon + default_lat = lat + default_zoom = 13 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' diff --git a/app/sightings/autocomplete_light_registry.py b/app/sightings/autocomplete_light_registry.py new file mode 100644 index 0000000..1cfa881 --- /dev/null +++ b/app/sightings/autocomplete_light_registry.py @@ -0,0 +1,24 @@ +import autocomplete_light.shortcuts as al +from .models import Bird + +# This will generate a PersonAutocomplete class +al.register(Bird, + # Just like in ModelAdmin.search_fields + search_fields=['common_name','scientific_name'], + attrs={ + # This will set the input placeholder attribute: + 'placeholder': 'Tags...', + # This will set the yourlabs.Autocomplete.minimumCharacters + # options, the naming conversion is handled by jQuery + 'data-autocomplete-minimum-characters': 1, +}, + # This will set the data-widget-maximum-values attribute on the + # widget container element, and will be set to + # yourlabs.Widget.maximumValues (jQuery handles the naming + # conversion). + widget_attrs={ + 'data-widget-maximum-values': 4, + # Enable modern-style widget ! + 'class': 'modern-style', + }, +) diff --git a/app/sightings/build.py b/app/sightings/build.py new file mode 100644 index 0000000..565b675 --- /dev/null +++ b/app/sightings/build.py @@ -0,0 +1,21 @@ +import os +from django.urls import reverse +from builder.base import BuildNew + + +class BuildBirds(BuildNew): + + def build(self): + self.build_detail_view() + self.build_list_view( + base_path=reverse("birds:list_redirect"), + paginate_by=24 + ) + print("building birds") + + def get_model_queryset(self): + return self.model.objects.all() + +def builder(): + j = BuildBirds("birds", "birdsighting") + j.build() diff --git a/app/sightings/migrations/0001_initial.py b/app/sightings/migrations/0001_initial.py new file mode 100644 index 0000000..4c25926 --- /dev/null +++ b/app/sightings/migrations/0001_initial.py @@ -0,0 +1,72 @@ +# Generated by Django 2.0.1 on 2018-01-28 10:05 + +from django.conf import settings +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import sightings.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('photos', '0018_auto_20161130_1218'), + ('locations', '0002_checkin'), + ] + + operations = [ + migrations.CreateModel( + name='AP', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('common_name', models.CharField(max_length=200)), + ('slug', models.SlugField()), + ('scientific_name', models.CharField(max_length=200)), + ('code', models.IntegerField(choices=[(0, 'unknown'), (1, 'regular occurring - common'), (2, 'regular occurring - less common'), (3, 'rare'), (4, 'casual'), (5, 'accidental'), (6, 'Cannot be found')], default=0)), + ('image', models.FileField(blank=True, help_text='width of high res is 1360px', null=True, upload_to=sightings.models.get_upload_path)), + ('image_credit', models.CharField(blank=True, max_length=200, null=True)), + ], + options={ + 'verbose_name': 'Animal/Plant', + 'verbose_name_plural': 'Animal/Plant', + 'ordering': ['common_name'], + }, + ), + migrations.CreateModel( + name='APClass', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('common_name', models.CharField(max_length=200)), + ('scientific_name', models.CharField(max_length=200)), + ('kind', models.IntegerField(choices=[(1, 'Bird'), (2, 'Mammal'), (3, 'Reptile'), (4, 'Amphibian'), (5, 'Plant')], default=1)), + ], + options={ + 'verbose_name_plural': 'Animal/Plant Class', + 'ordering': ['kind', 'common_name'], + }, + ), + migrations.CreateModel( + name='Sighting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, srid=4326)), + ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date')), + ('ap', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sightings.AP')), + ('images', models.ManyToManyField(blank=True, to='photos.LuxImage')), + ('location', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ('seen_by', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['-date'], + }, + ), + migrations.AddField( + model_name='ap', + name='apclass', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sightings.APClass'), + ), + ] diff --git a/app/sightings/migrations/__init__.py b/app/sightings/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/sightings/migrations/__init__.py diff --git a/app/sightings/models.py b/app/sightings/models.py new file mode 100644 index 0000000..7f93fa8 --- /dev/null +++ b/app/sightings/models.py @@ -0,0 +1,175 @@ +import datetime +from django.urls import reverse +from django.template.defaultfilters import slugify +from django.contrib.gis.db import models +from django.contrib.auth.models import User +from django.utils import timezone +from locations.models import Location +from django import forms +from django.conf import settings + +from photos.models import LuxImage + + +def get_upload_path(self, filename): + return "images/sightings-images/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +# from http://aba.org/checklist/codes.html +ABA_CODES = ( + (0, 'unknown'), + (1, 'regular occurring - common'), + (2, 'regular occurring - less common'), + (3, 'rare'), + (4, 'casual'), + (5, 'accidental'), + (6, 'Cannot be found'), +) + +KIND_LIST = ( + (1, 'Bird'), + (2, 'Mammal'), + (3, 'Reptile'), + (4, 'Amphibian'), + (5, 'Plant'), +) + + +class APClass(models.Model): + common_name = models.CharField(max_length=200) + scientific_name = models.CharField(max_length=200) + kind = models.IntegerField(choices=KIND_LIST, default=1) + + class Meta: + verbose_name_plural = 'Animal/Plant Class' + ordering = ["kind", "common_name"] + + def __str__(self): + return self.common_name + +class AP(models.Model): + common_name = models.CharField(max_length=200) + slug = models.SlugField() + scientific_name = models.CharField(max_length=200) + code = models.IntegerField(choices=ABA_CODES, default=0) + apclass = models.ForeignKey(APClass, on_delete=models.CASCADE) + image = models.FileField(upload_to=get_upload_path, null=True, blank=True, help_text="width of high res is 1360px") + image_credit = models.CharField(max_length=200, blank=True, null=True) + + def __str__(self): + return self.common_name + + def get_image_url(self): + return "%s%s" % (settings.IMAGES_URL, self.image.url.split("media")[1][8:]) + + def get_absolute_url(self): + return reverse("sightings:detail", kwargs={"slug": self.slug}) + + def kind(self): + return self.apclass.kind + + class Meta: + verbose_name_plural = 'Animal/Plant' + verbose_name = 'Animal/Plant' + ordering = ["common_name", ] + + def save(self, *args, **kwargs): + self.slug = slugify(self.common_name[:50]) + super(AP, self).save(*args, **kwargs) + + +class Sighting(models.Model): + ap = models.ForeignKey(AP, on_delete=models.CASCADE) + point = models.PointField(blank=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True) + date = models.DateTimeField('Date', default=timezone.now) + seen_by = models.ManyToManyField(User) + images = models.ManyToManyField(LuxImage, blank=True) + #audio = models.ManyToManyField(BirdAudio, blank=True) + + class Meta: + ordering = ["-date", ] + + @property + def state(self): + return self.location.state + + @property + def country(self): + return self.location.state.country + + @property + def region(self): + return self.location.state.country.lux_region + + @property + def longitude(self): + '''Get the site's longitude.''' + return self.point.x + + @property + def latitude(self): + '''Get the site's latitude.''' + return self.point.y + + def get_small_image(self): + for img in self.images.all(): + for size in img.sizes.all(): + if size.width > 360 and size.width < 700: + return img.get_image_by_size(size) + + def get_absolute_url(self): + return reverse("sightings:detail", kwargs={"slug": self.bird.slug}) + + def __str__(self): + return self.ap.common_name + + def save(self, *args, **kwargs): + if not self.point: + self.point = Sighting.objects.latest().point + try: + self.location = Location.objects.filter( + geometry__contains=self.point + ).get() + except Location.DoesNotExist: + raise forms.ValidationError("There is no location associated with that point, add it: %sadmin/locations/location/add/" % (settings.BASE_URL)) + super(Sighting, self).save() + +""" +Migration from Birds to abstract: +apclass = OLDAPClass.objects.all() +for b in apclass: + NewAPClass.objects.create( + common_name = b.common_name, + scientific_name = b.scientific_name, + kind = 1 + ) + +ap = OLDAP.objects.all() +for b in ap: + apnew = NewAPClass.objects.get(scientific_name=b.apclass.scientific_name) + print(ap) + NewAP.objects.create( + common_name = b.common_name, + scientific_name = b.scientific_name, + code = b.code, + apclass = apnew, + image = b.image, + image_credit = b.image_credit, + ) + print(t) + +oldsighting = OLDSighting.objects.all() +for bird in oldsighting: + ap = NEWAP.objects.get(scientific_name=bird.ap.scientific_name) + s = NEWSighting.objects.create( + ap = ap, + point = bird.point, + location = bird.location, + date = bird.date, + ) + for t in bird.images.all(): + s.images.add(t) + for t in bird.seen_by.all(): + s.seen_by.add(t) +""" diff --git a/app/sightings/urls.py b/app/sightings/urls.py new file mode 100644 index 0000000..51b29d7 --- /dev/null +++ b/app/sightings/urls.py @@ -0,0 +1,34 @@ +from django.urls import path +from . import views + +app_name = "sightings" + +urlpatterns = [ + path( + r'', + views.SightingListView.as_view(), + {'page':1}, + name="list" + ), + path( + r'<int:page>/', + views.SightingListView.as_view(), + name="list" + ), + path( + r'<str:slug>', + views.SightingDetailView.as_view(), + name='detail' + ), + path( + r'<str:user>/', + views.SightingListUserView.as_view(), + {'page':1}, + name='list_by_person' + ), + path( + r'<str:user>/<int:page>/', + views.SightingListUserView.as_view(), + name='list_by_person' + ), +] diff --git a/app/sightings/views.py b/app/sightings/views.py new file mode 100644 index 0000000..f23898c --- /dev/null +++ b/app/sightings/views.py @@ -0,0 +1,48 @@ +from django.views.generic.detail import DetailView +from django.contrib.auth.models import User +from utils.views import PaginatedListView +from .models import AP, Sighting + +class SightingListView(PaginatedListView): + template_name = 'archives/sightings.html' + + def get_queryset(self): + return Sighting.objects.all() + + +class SightingListUserView(PaginatedListView): + template_name = 'archives/sightings.html' + + def get_queryset(self): + return Sighting.objects.filter( + seen_by__username=self.kwargs['user'] + ) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(SightingListUserView, self).get_context_data(**kwargs) + context['user'] = User.objects.get(username=self.kwargs['user']) + return context + + +class SightingDetailView(DetailView): + model = AP + template_name = "details/sighting.html" + slug_field = "slug" + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(SightingDetailView, self).get_context_data(**kwargs) + #try: + # context['recording'] = SightingAudio.objects.get( + # ap__slug=self.kwargs['slug'] + # ) + #except SightingAudio.DoesNotExist: + # pass + try: + context['sighting'] = Sighting.objects.filter( + ap__slug=self.kwargs['slug'] + ) + except Sighting.DoesNotExist: + pass + return context diff --git a/config/base_urls.py b/config/base_urls.py index 1a0ea83..d709f8c 100644 --- a/config/base_urls.py +++ b/config/base_urls.py @@ -46,6 +46,7 @@ urlpatterns = [ path(r'books/', include('books.urls')), path(r'field-notes/', include('notes.urls')), path(r'birds/', include('birds.urls', namespace='birds')), + path(r'sightings/', include('sightings.urls', namespace='sightings')), path(r'src/', include('src.urls', namespace='src')), path(r'figments/', include('figments.urls', namespace='figments')), path(r'resume/', include('resume.urls', namespace='resume')), diff --git a/design/sass/_writing_details.scss b/design/sass/_writing_details.scss index 8e011e5..348d617 100644 --- a/design/sass/_writing_details.scss +++ b/design/sass/_writing_details.scss @@ -746,3 +746,7 @@ figure.picwide img.picwide { float: none; } } +.wildlife { + @include constrain_narrow; + text-align: left; +} diff --git a/design/templates/admin/index.html b/design/templates/admin/index.html index 6642012..54f9798 100644 --- a/design/templates/admin/index.html +++ b/design/templates/admin/index.html @@ -25,9 +25,7 @@ td, th { {% block content %} <div id="content-main"> -{% load admin_reorder %} {% if app_list %} - {% app_order %} <div class="module"> <table> <caption><a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">Frequently Used</a></caption> diff --git a/design/templates/archives/sightings.html b/design/templates/archives/sightings.html new file mode 100644 index 0000000..71cd389 --- /dev/null +++ b/design/templates/archives/sightings.html @@ -0,0 +1,56 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load pagination_tags %} + +{% block pagetitle %}Luxagraf | Plants and Animals seen {% if region %}in {{region.name|title|smartypants|safe}}{%else%}by {{user}}{%endif%}{% if page != "1" %} -- Page {{page}}{%endif%}{% endblock %} +{% block metadescription %}Plants and Animals seen {% if region %} in {{region.name|title|smartypants|safe}}{%else%}by {{user}}{%endif%} Page {{page}}{% endblock %} +{%block bodyid%}id="birds"{%endblock%} + + +{% block primary %}<ul class="bl" id="breadcrumbs" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"> + <li><a href="/" title="luxagraf homepage" itemprop="url"><span itemprop="title">Home</span></a> → </li> + {% if region %}{%if region.name == 'United States'%} <li><a href="{% url 'sightings:list' %}" title="See all" itemprop="url"><span itemprop="title">Plants and Animals</span></a> →</li> + <li itemprop="title">the United States</li>{%else%}<li><a href="/jrnl/" title="See all Journal Entries" itemprop="url"><span>Plants and Animals</span></a> →</li> + <li>{{region.name|title|smartypants|safe}}</li>{%endif%}{%else%}<li>Plants and Animals</li>{%endif%} + </ul> + <main role="main" class="archive">{% autopaginate object_list 40 %} + <h1 class="hide">Plants and Animals seen {% if region %}in {%if region.name == 'United States'%}the United States{%else%}{{region.name|title|smartypants|safe}}{%endif%}{%else%} by {{user}}{%endif%}</h1> {% for object in object_list %} + <article class="{% cycle 'odd' 'even' %} {% cycle 'first' 'second' 'third' %}"> + <div class="post--image"> +{% load get_image_by_size %} +{% load get_image_width %} +<figure> +<a href="{{object.ap.get_absolute_url}}" title="{{object.ap}}"> +<img class="picfull" sizes="(max-width: 680px) 100vw, (min-width: 681) 680px" srcset="{% for size in object.image.sizes.all%}{% get_image_by_size object.image size.name %} {{size.width}}w{% if forloop.last%}"{%else%}, {%endif%}{%endfor%} + {% for size in object.image.sizes.all%}{%if forloop.first %} src="{% get_image_by_size object.image size.name %}"{%endif%}{%endfor%} alt="{{object.image.alt}} photographed by {% if object.image.photo_credit_source %}{{object.image.photo_credit_source}}{%else%}luxagraf{%endif%}" > +</a> +{% if object.image.photo_credit_source %}<figcaption>photo by <a href="{{object.image.photo_credit_url}}">{{object.image.photo_credit_source}}</a></figcaption>{%endif%} +</figure> + </div> + <h3 class="post--title"><a href="{{object.ap.get_absolute_url}}" title="{%if object.title_keywords%}{{object.title_keywords}}{%else%}{{object.ap}}{%endif%}">{{object.ap|safe|smartypants|widont}}</a> (<span class="sci">{{object.ap.scientific_name}}</span>)</h3> + <time class="post--date" datetime="{{object.date|date:'c'}}">Seen: {{object.date|date:"F"}} <span>{{object.date|date:"j, Y"}}</span></time> + <p> + <span class="sighting location place post--location" itemscope itemtype="http://schema.org/Place">Loc: + {% if object.country.name == "United States" %}<span class="p-locality locality">{{object.location.name|smartypants|safe}}</span>, {{object.state.name}}, <span class="p-country-name">U.S.</span>{%else%}<span class="p-region">{{object.location.name|smartypants|safe}}</span>, {{object.country.name}}</a>{%endif%} + <span style="display: none;" itemprop="geo" itemscope itemtype="http://schema.org/GeoCoordinates"> + <data itemprop="latitude" class="p-latitude" value="{{object.latitude}}">{{object.latitude}}</data> + <data itemprop="longitude" class="p-longitude" value="{{object.longitude}}">{{object.longitude}}</data> + </span> + </span> + </p> + <div class="audio-figure">{% for recording in object.ap.recordings.all %} + <audio controls="controls"> + <source src="/media/{{recording.audio}}" /> + </audio> + <small>Audio recorded by {{recording.recorder}} on {{recording.pub_date|date:"F j, Y"}} in {{recording.location}}. <a href="{{recording.link}}">© {{recording.copyright}}</a></small> + </div>{% endfor %} + </article> {% endfor %} + </main> + <nav class="pagination"> + {% paginate %} + </nav> +{% endblock %} + + + +{% block js %}<script src="/media/js/hyphenate.min.js" type="text/javascript"></script>{% endblock%} diff --git a/design/templates/details/entry.html b/design/templates/details/entry.html index d5038f2..b2a1058 100644 --- a/design/templates/details/entry.html +++ b/design/templates/details/entry.html @@ -53,6 +53,12 @@ class="{%if t == 1 or t == 3 or t == 5 %}double{%else%}single{%endif%}{%if t == <div id="article" class="e-content entry-content post--body post--body--{% with object.template_name as t %}{%if t == 0 or t == 2 %}single{%endif%}{%if t == 1 or t == 3 %}double{%endif%}{%endwith%}" itemprop="articleBody"> {{object.body_html|safe|smartypants|widont}} </div> + <aside class="wildlife"> + <h3>Fauna and Flora</h3> + <ul>{% for obj in wildlife %} + <li>{{obj}} </li> + {% endfor %}</ul> + </aside> </article> {% with object.get_next_published as next %} {% with object.get_previous_published as prev %} diff --git a/design/templates/details/sighting.html b/design/templates/details/sighting.html new file mode 100644 index 0000000..a9bbef8 --- /dev/null +++ b/design/templates/details/sighting.html @@ -0,0 +1,56 @@ +{% extends 'base.html' %} +{% load get_image_by_size %} +{% block extrahead %} +<style> +#detail-map-canvas { height: 100%;} +</style> +{% endblock %} +{% block bodyid %}class="birds"{%endblock%} +{% block primary %} +<ul class="bl" id="breadcrumbs" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"> + <li><a href="/" title="luxagraf homepage" itemprop="url"><span itemprop="title">Home</span></a> → </li> + <li><a href="{% url 'sightings:list' %}" title="See all" itemprop="url"><span itemprop="title">Plants and Animals</span></a></li> + </ul> + +<main> +<article> +<h1>{{object.common_name}}</h1> +<h2 class="sci">{{object.scientific_name}}</h2> +<h3 class="sci">Family {{object.apclass.scientific_name}} ({{object.apclass}})</h3> +{% load get_image_by_size %} +{% load get_image_width %} +<div class="post--image"> +<figure> +<img class="picfull" sizes="(max-width: 680px) 100vw, (min-width: 681) 680px" srcset="{% for size in sighting.image.sizes.all%}{% get_image_by_size sighting.image size.name %} {{size.width}}w{% if forloop.last%}"{%else%}, {%endif%}{%endfor%} + {% for size in sighting.image.sizes.all%}{%if forloop.first %} src="{% get_image_by_size sighting.image size.name %}"{%endif%}{%endfor%} alt="{{sighting.image.alt}} photographed by {% if sighting.image.photo_credit_source %}{{sighting.image.photo_credit_source}}{%else%}luxagraf{%endif%}" > +{% if sighting.image.photo_credit_source %}<figcaption>photo by <a href="{{sighting.image.photo_credit_url}}">{{sighting.image.photo_credit_source}}</a></figcaption>{%endif%} +</figure> +</div> +{%if recording %} +<div class="audio-figure"> +<audio autoplay="autoplay" controls="controls"> + <source src="/media/{{recording.audio}}" /> +</audio> +<small>Audio recorded by {{recording.recorder}} on {{recording.pub_date|date:"F j, Y"}} in {{recording.location}}. <a href="{{recording.link}}">© {{recording.copyright}}</a></small> +</div> +{%endif%} +<p id="endnode">Seen at {%for sight in sighting %}{{sight.location}}, {{sight.location.comma_name}} in {{sight.date|date:"M Y"}} by {% for person in sight.seen_by.all %}<a href="{% url 'sightings:list_by_person' person %}">{% if person.username == "luxagraf"%}Scott{%else%}{{person.username|capfirst}}{%endif%}</a>{%if forloop.last %}. {%else%}{% if forloop.revcounter == 2 %}, and {%else%}, {%endif%}{%endif%}{%endfor%}{%endfor%} </p> +{% if recording.audio %} +{%endif%} +</article> +</main> +{% endblock %} + +{% block js %} +<script src="/media/js/leaflet-master/leaflet-mod.js"></script> +<script src="/media/js/detail.js"></script> +<script type="text/javascript"> +sightings = []; +{% for s in sighting %} +sightings.push(['{{s.latitude}}', '{{s.longitude}}']); +{% endfor %} +window.onload = function() { + createBirdMap({% for s in sighting %}{% if forloop.first %}{{s.latitude}},{{s.longitude}}{%endif%}{%endfor%}, 5, { type:'point', lat:'{{sighting.latitude}}', lon:'{{sighting.longitude}}'}, sightings); return false; +} +</script> +{% endblock %} |