summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/birds/admin.py8
-rw-r--r--app/birds/models.py62
-rw-r--r--app/birds/urls.py8
-rw-r--r--app/birds/views.py27
-rw-r--r--config/base_urls.py1
-rw-r--r--design/sass/_birds.scss22
-rw-r--r--design/templates/archives/birds.html48
-rw-r--r--design/templates/details/bird.html13
8 files changed, 184 insertions, 5 deletions
diff --git a/app/birds/admin.py b/app/birds/admin.py
index 01a6f4c..8c09b7b 100644
--- a/app/birds/admin.py
+++ b/app/birds/admin.py
@@ -1,14 +1,17 @@
from django.contrib import admin
from django.contrib.gis.admin import OSMGeoAdmin
-from birds.models import BirdSighting, BirdClass, Bird
+from birds.models import BirdSighting, BirdAudio, BirdClass, Bird
class BirdClassAdmin(admin.ModelAdmin):
list_display = ('common_name', 'scientific_name',)
+class BirdAudioAdmin(admin.ModelAdmin):
+ list_display = ('bird', 'recorder',)
class BirdAdmin(admin.ModelAdmin):
list_display = ('pk', 'common_name', 'scientific_name', 'code', 'bird_class')
+ list_filter = ('bird_class',)
class BirdSightingAdmin(OSMGeoAdmin):
@@ -17,7 +20,7 @@ class BirdSightingAdmin(OSMGeoAdmin):
# options for OSM map Using custom ESRI topo map
default_lon = -9285175
default_lat = 4025046
- default_zoom = 6
+ default_zoom = 10
units = True
scrollable = False
map_width = 700
@@ -27,4 +30,5 @@ class BirdSightingAdmin(OSMGeoAdmin):
admin.site.register(BirdSighting, BirdSightingAdmin)
admin.site.register(BirdClass, BirdClassAdmin)
+admin.site.register(BirdAudio, BirdAudioAdmin)
admin.site.register(Bird, BirdAdmin)
diff --git a/app/birds/models.py b/app/birds/models.py
index 3fb0c0f..2ba9451 100644
--- a/app/birds/models.py
+++ b/app/birds/models.py
@@ -1,11 +1,15 @@
import datetime
+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
def get_upload_path(self, filename):
return "images/bird-images/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename)
+
# from http://aba.org/checklist/codes.html
ABA_CODES = (
(1, 'regular occurring - common'),
@@ -23,6 +27,7 @@ class BirdClass(models.Model):
class Meta:
verbose_name_plural = 'Bird Class'
+ ordering = ["common_name",]
def __str__(self):
return self.common_name
@@ -30,17 +35,72 @@ class BirdClass(models.Model):
class Bird(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)
bird_class = models.ForeignKey(BirdClass)
+ image = models.FileField(upload_to=get_upload_path, null=True, blank=True)
def __str__(self):
return self.common_name
+ def get_absolute_url(self):
+ return "/birds/%s" %(self.slug)
+
+ class Meta:
+ ordering = ["common_name",]
+
+ def save(self):
+ self.slug = slugify(self.common_name[:50])
+ super(Bird, self).save()
+
+class BirdAudio(models.Model):
+ bird = models.ForeignKey(Bird, related_name='recordings')
+ audio = models.FileField(upload_to='audio/birds/')
+ recorder = models.CharField(max_length=200, null=True, blank=True)
+ pub_date = models.DateTimeField()
+ location = models.CharField(max_length=200, null=True, blank=True)
+ link = models.CharField(max_length=450, null=True, blank=True)
+ copyright = models.CharField(max_length=250, null=True, blank=True)
+
+ class Meta:
+ verbose_name_plural = 'Bird Audio'
+ ordering = ["bird",]
+
+ def __str__(self):
+ return self.bird.common_name
class BirdSighting(models.Model):
bird = models.ForeignKey(Bird)
point = models.PointField()
location = models.ForeignKey(Location)
- date = models.DateTimeField('Date')
+ date = models.DateTimeField('Date', default=timezone.now)
image = models.FileField(upload_to=get_upload_path, null=True, blank=True)
+ seen_by = models.ManyToManyField(User)
+
+ class Meta:
+ verbose_name_plural = 'Bird Sighting'
+
+ @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 __str__(self):
+ return self.bird.common_name
diff --git a/app/birds/urls.py b/app/birds/urls.py
new file mode 100644
index 0000000..6a219f1
--- /dev/null
+++ b/app/birds/urls.py
@@ -0,0 +1,8 @@
+from django.conf.urls import *
+from django.views.generic.base import RedirectView
+from django.views.generic.detail import DetailView
+
+urlpatterns = patterns('',
+ url(r'^list/(?P<user>[\w]+)', 'birds.views.bird_list'),
+ url(r'^(?P<slug>[-_\w]+)$', 'birds.views.bird_detail'),
+)
diff --git a/app/birds/views.py b/app/birds/views.py
new file mode 100644
index 0000000..bd2f79e
--- /dev/null
+++ b/app/birds/views.py
@@ -0,0 +1,27 @@
+from django.shortcuts import render_to_response, get_object_or_404
+from django.template import RequestContext
+from django.http import Http404
+from django.conf import settings
+# from django.views.generic import ListView
+
+from birds.models import Bird, BirdAudio, BirdSighting
+from locations.models import Region, Country
+
+
+def bird_list(request, user):
+ #request.page_url = '/birds/seen/%d/'
+ #request.page = int(page)
+ context = {
+ 'object_list': BirdSighting.objects.filter(seen_by__username=user),
+ 'user': user,
+ }
+ return render_to_response("archives/birds.html", context, context_instance=RequestContext(request))
+
+
+def bird_detail(request, slug):
+ context = {
+ 'object': Bird.objects.get(slug=slug),
+ 'recording': BirdAudio.objects.get(bird__slug=slug)
+ }
+ return render_to_response("details/bird.html", context, context_instance=RequestContext(request))
+
diff --git a/config/base_urls.py b/config/base_urls.py
index c606171..4585d21 100644
--- a/config/base_urls.py
+++ b/config/base_urls.py
@@ -56,6 +56,7 @@ urlpatterns += patterns('',
(r'^photos/', include('photos.urls')),
(r'^field-notes/', include('notes.urls')),
(r'^photo/', include('photos.detail_urls')),
+ (r'^birds/', include('birds.urls')),
(r'^travel-guide/', include('guide.urls')),
# map
(r'^map/', include('locations.urls')),
diff --git a/design/sass/_birds.scss b/design/sass/_birds.scss
index 7c841c4..8e82411 100644
--- a/design/sass/_birds.scss
+++ b/design/sass/_birds.scss
@@ -1,3 +1,21 @@
-.taxon, .left2:nth-child(2) {
- display: none;
+.iframe-wrapper {
+ @include constrain_wide()
+}
+
+.taxon {
+ display: none !important;
+}
+
+#birds {
+ .post--title {
+ @include fontsize(18);
+ a {
+ font-style: normal;
+ @include fontsize(22);
+ }
+ }
+ .sci {
+ font-style: italic;
+ }
+ .audio small { display: block;}
}
diff --git a/design/templates/archives/birds.html b/design/templates/archives/birds.html
new file mode 100644
index 0000000..17cdb79
--- /dev/null
+++ b/design/templates/archives/birds.html
@@ -0,0 +1,48 @@
+{% extends 'base.html' %}
+{% load typogrify_tags %}
+{% load pagination_tags %}
+
+{% block pagetitle %}Luxagraf | Birds seen {% if region %}in {{region.name|title|smartypants|safe}}{%else%}by {{user}}{%endif%}{% if page != "1" %} -- Page {{page}}{%endif%}{% endblock %}
+{% block metadescription %}Birds 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> &rarr; </li>
+ {% if region %}{%if region.name == 'United States'%} <li><a href="/birds/" title="See all Journal Entries" itemprop="url"><span itemprop="title">Birds</span></a> &rarr;</li>
+ <li itemprop="title">the United States</li>{%else%}<li><a href="/jrnl/" title="See all Journal Entries" itemprop="url"><span>Birds</span></a> &rarr;</li>
+ <li>{{region.name|title|smartypants|safe}}</li>{%endif%}{%else%}<li>Journal</li>{%endif%}
+ </ul>
+ <main role="main" class="archive">
+ <h1 class="hide">Birds 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">
+ <a href="{{object.bird.get_absolute_url}}" title="{{object.bird}}"><img src="{{object.get_image_url}}" alt="{{ object.title }}" class="post-image" /></a>
+ </div>
+ <h3 class="post--title"><a href="{{object.bird.get_absolute_url}}" title="{%if object.title_keywords%}{{object.title_keywords}}{%else%}{{object.bird}}{%endif%}">{{object.bird|safe|smartypants|widont}}</a> (<span class="sci">{{object.bird.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>
+ <p class="audio">{% for recording in object.bird.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}}">&copy; {{recording.copyright}}</a></small>
+ </p>{% 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/bird.html b/design/templates/details/bird.html
new file mode 100644
index 0000000..0a91317
--- /dev/null
+++ b/design/templates/details/bird.html
@@ -0,0 +1,13 @@
+{% extends 'base.html' %}
+{% block extrahead %}
+{% endblock %}
+{% block primary %}
+<main>
+{{object.common_name}}
+<audio autoplay="autoplay" controls="controls">
+ <source src="/media/{{recording.audio}}" />
+</audio>
+
+</main>
+{% endblock %}
+