summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/birds/admin.py52
-rw-r--r--app/birds/models.py149
-rw-r--r--app/jrnl/views.py10
-rw-r--r--app/sightings/__init__.py0
-rw-r--r--app/sightings/admin.py49
-rw-r--r--app/sightings/autocomplete_light_registry.py24
-rw-r--r--app/sightings/build.py21
-rw-r--r--app/sightings/migrations/0001_initial.py72
-rw-r--r--app/sightings/migrations/__init__.py0
-rw-r--r--app/sightings/models.py175
-rw-r--r--app/sightings/urls.py34
-rw-r--r--app/sightings/views.py48
12 files changed, 621 insertions, 13 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