diff options
author | luxagraf <sng@luxagraf.net> | 2023-07-28 13:39:02 -0500 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2023-07-28 13:39:02 -0500 |
commit | 9a620cf42bf1fe6977e378bd834b41ff4a593dde (patch) | |
tree | cf41a0582681cecaf88a30bfe409f9c2be57972a /bak/unused_apps | |
parent | 6e5897117124cd60ae81efb1574c6347f48e60e5 (diff) |
main: removed some apps I wasn't using and added bak to git to preserve
a copy of old apps
Diffstat (limited to 'bak/unused_apps')
393 files changed, 15758 insertions, 0 deletions
diff --git a/bak/unused_apps/bird.html b/bak/unused_apps/bird.html new file mode 100644 index 0000000..43877fe --- /dev/null +++ b/bak/unused_apps/bird.html @@ -0,0 +1,47 @@ +{% 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 %} +<main> +<article> +<h1>{{object.common_name}}</h1> +<h2 class="sci">{{object.scientific_name}}</h2> +<h3 class="sci">Family {{object.bird_class.scientific_name}} ({{object.bird_class}})</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 {{sighting.location}}, {{sighting.location.comma_name}} in {{sighting.date|date:"M Y"}} by {% for person in sighting.seen_by.all %}<a href="/birds/{{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%}</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.min.js"></script> +<script type="text/javascript"> +window.onload = function() { + createBirdMap({{sighting.latitude}},{{sighting.longitude}}, 12, { type:'point', lat:'{{sighting.latitude}}', lon:'{{sighting.longitude}}'}); return false; +} +</script> +{% endblock %} diff --git a/bak/unused_apps/birds/__init__.py b/bak/unused_apps/birds/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/birds/__init__.py diff --git a/bak/unused_apps/birds/aba_importer.py b/bak/unused_apps/birds/aba_importer.py new file mode 100644 index 0000000..93f5bef --- /dev/null +++ b/bak/unused_apps/birds/aba_importer.py @@ -0,0 +1,16 @@ +for row_index in range(sheet.nrows): + if sheet.cell(row_index, 0).value != '': + sci = sheet.cell(row_index, 0).value.split("(")[1].split(")")[0] + bc = BirdClass.objects.get(scientific_name__exact=sci) + common_name = sheet.cell(row_index, 1).value + sci_name = sheet.cell(row_index, 2).value + code = int(sheet.cell(row_index, 3).value) + bclass = bc + # create bird here + b, created = Bird.objects.get_or_create( + common_name=common_name, + scientific_name=sci_name, + code=code, + bird_class=bc + ) + print(b) diff --git a/bak/unused_apps/birds/admin.py b/bak/unused_apps/birds/admin.py new file mode 100644 index 0000000..df0951a --- /dev/null +++ b/bak/unused_apps/birds/admin.py @@ -0,0 +1,101 @@ +from django.contrib import admin +from django.contrib.gis.admin import OSMGeoAdmin +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() + 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',) + + +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): + form = GalleryFormPlus + list_filter = ( + ) + list_display = ('bird', 'location') + list_filter = ('seen_by',('location', admin.RelatedOnlyFieldListFilter),) + fieldsets = ( + ('Sighting', { + 'fields': ( + 'bird', + 'point', + 'date', + 'seen_by', + 'images', + 'audio', + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + # options for OSM map Using custom ESRI topo map + lat, lon = get_latlon() + 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' + +admin.site.register(BirdSighting, BirdSightingAdmin) +admin.site.register(BirdClass, BirdClassAdmin) +admin.site.register(BirdAudio, BirdAudioAdmin) +admin.site.register(Bird, BirdAdmin) diff --git a/bak/unused_apps/birds/autocomplete_light_registry.py b/bak/unused_apps/birds/autocomplete_light_registry.py new file mode 100644 index 0000000..1cfa881 --- /dev/null +++ b/bak/unused_apps/birds/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/bak/unused_apps/birds/birds.json b/bak/unused_apps/birds/birds.json new file mode 100644 index 0000000..f140300 --- /dev/null +++ b/bak/unused_apps/birds/birds.json @@ -0,0 +1 @@ +[{"fields": {"scientific_name": "Anatidae", "common_name": "Ducks, Geese, and Swans "}, "pk": 1, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Cracidae", "common_name": "Curassows and Guans "}, "pk": 2, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Odontophoridae", "common_name": "New World Quail "}, "pk": 3, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Phasianidae", "common_name": "Partridges, Grouse, Turkeys, and Old World Quail "}, "pk": 4, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Gaviidae", "common_name": "Loons "}, "pk": 5, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Podicipedidae", "common_name": "Grebes "}, "pk": 6, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Phoenicopteridae", "common_name": "Flamingos "}, "pk": 7, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Diomedeidae", "common_name": "Albatrosses "}, "pk": 8, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Procellariidae", "common_name": "Shearwaters and Petrels "}, "pk": 9, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Hydrobatidae", "common_name": "Storm-Petrels "}, "pk": 10, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Phaethontidae", "common_name": "Tropicbirds "}, "pk": 11, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Ciconiidae", "common_name": "Storks "}, "pk": 12, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Fregatidae", "common_name": "Frigatebirds "}, "pk": 13, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Sulidae", "common_name": "Boobies and Gannets "}, "pk": 14, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Phalacrocoracidae", "common_name": "Cormorants "}, "pk": 15, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Anhingidae", "common_name": "Darters "}, "pk": 16, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Pelecanidae", "common_name": "Pelicans "}, "pk": 17, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Ardeidae", "common_name": "Bitterns, Herons, and Allies "}, "pk": 18, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Threskiornithidae", "common_name": "Ibises and Spoonbills "}, "pk": 19, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Cathartidae", "common_name": "New World Vultures "}, "pk": 20, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Pandionidae", "common_name": "Ospreys "}, "pk": 21, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Accipitridae", "common_name": "Hawks, Kites, Eagles, and Allies "}, "pk": 22, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Rallidae", "common_name": "Rails, Gallinules, and Coots "}, "pk": 23, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Heliornithidae", "common_name": "Sungrebes "}, "pk": 24, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Aramidae", "common_name": "Limpkins "}, "pk": 25, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Gruidae", "common_name": "Cranes "}, "pk": 26, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Burhinidae", "common_name": "Thick-knees "}, "pk": 27, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Charadriidae", "common_name": "Lapwings and Plovers "}, "pk": 28, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Haematopodidae", "common_name": "Oystercatchers "}, "pk": 29, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Recurvirostridae", "common_name": "Stilts and Avocets "}, "pk": 30, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Jacanidae", "common_name": "Jacanas "}, "pk": 31, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Scolopacidae", "common_name": "Sandpipers, Phalaropes, and Allies "}, "pk": 32, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Glareolidae", "common_name": "Pratincoles "}, "pk": 33, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Laridae", "common_name": "Gulls, Terns, and Skimmers "}, "pk": 34, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Stercorariidae", "common_name": "Skuas and Jaegers "}, "pk": 35, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Alcidae", "common_name": "Auks, Murres, and Puffins "}, "pk": 36, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Columbidae", "common_name": "Pigeons and Doves "}, "pk": 37, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Cuculidae", "common_name": "Cuckoos, Roadrunners, and Anis "}, "pk": 38, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Tytonidae", "common_name": "Barn Owls "}, "pk": 39, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Strigidae", "common_name": "Typical Owls "}, "pk": 40, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Caprimulgidae", "common_name": "Goatsuckers "}, "pk": 41, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Apodidae", "common_name": "Swifts "}, "pk": 42, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Trochilidae", "common_name": "Hummingbirds "}, "pk": 43, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Trogonidae", "common_name": "Trogons "}, "pk": 44, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Upupidae", "common_name": "Hoopoes "}, "pk": 45, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Alcedinidae", "common_name": "Kingfishers "}, "pk": 46, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Picidae", "common_name": "Woodpeckers and Allies "}, "pk": 47, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Falconidae", "common_name": "Caracaras and Falcons "}, "pk": 48, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Psittacidae", "common_name": "Lories, Parakeets, Macaws, and Parrots "}, "pk": 49, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Tyrannidae", "common_name": "Tyrant Flycatchers "}, "pk": 50, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Tityridae", "common_name": "Becards, Tityras, and Allies "}, "pk": 51, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Laniidae", "common_name": "Shrikes "}, "pk": 52, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Vireonidae", "common_name": "Vireos "}, "pk": 53, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Corvidae", "common_name": "Jays and Crows "}, "pk": 54, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Alaudidae", "common_name": "Larks "}, "pk": 55, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Hirundinidae", "common_name": "Swallows "}, "pk": 56, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Paridae", "common_name": "Chickadees and Titmice "}, "pk": 57, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Remizidae", "common_name": "Verdin "}, "pk": 58, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Aegithalidae", "common_name": "Bushtits "}, "pk": 59, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Sittidae", "common_name": "Nuthatches "}, "pk": 60, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Certhiidae", "common_name": "Creepers "}, "pk": 61, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Troglodytidae", "common_name": "Wrens "}, "pk": 62, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Polioptilidae", "common_name": "Gnatcatchers and Gnatwrens "}, "pk": 63, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Cinclidae", "common_name": "Dippers "}, "pk": 64, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Pycnonotidae", "common_name": "Bulbuls "}, "pk": 65, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Regulidae", "common_name": "Kinglets "}, "pk": 66, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Phylloscopidae", "common_name": "Leaf Warblers "}, "pk": 67, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Sylviidae", "common_name": "Sylviid Warblers "}, "pk": 68, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Acrocephalidae", "common_name": "Reed Warblers "}, "pk": 69, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Megaluridae", "common_name": "Grassbirds "}, "pk": 70, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Muscicapidae", "common_name": "Old World Flycatchers "}, "pk": 71, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Turdidae", "common_name": "Thrushes "}, "pk": 72, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Mimidae", "common_name": "Mockingbirds and Thrashers "}, "pk": 73, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Sturnidae", "common_name": "Starlings "}, "pk": 74, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Prunellidae", "common_name": "Accentors "}, "pk": 75, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Motacillidae", "common_name": "Wagtails and Pipits "}, "pk": 76, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Bombycillidae", "common_name": "Waxwings "}, "pk": 77, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Ptilogonatidae", "common_name": "Silky-flycatchers "}, "pk": 78, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Peucedramidae", "common_name": "Olive Warblers "}, "pk": 79, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Calcariidae", "common_name": "Longspurs and Snow Buntings "}, "pk": 80, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Parulidae", "common_name": "Wood-Warblers "}, "pk": 81, "model": "birds.birdclass"}, {"fields": {"scientific_name": "incertae sedis", "common_name": "Bananaquits "}, "pk": 82, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Thraupidae", "common_name": "Tanagers "}, "pk": 83, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Emberizidae", "common_name": "Emberizids "}, "pk": 84, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Cardinalidae", "common_name": "Cardinals, Piranga Tanagers and Allies "}, "pk": 85, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Icteridae", "common_name": "Blackbirds "}, "pk": 86, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Fringillidae", "common_name": "Fringilline and Cardueline Finches and Allies "}, "pk": 87, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Passeridae", "common_name": "Old World Sparrows "}, "pk": 88, "model": "birds.birdclass"}, {"fields": {"scientific_name": "Dendrocygna autumnalis", "bird_class": 1, "common_name": "Black-bellied Whistling-Duck", "code": 1}, "pk": 1, "model": "birds.bird"}, {"fields": {"scientific_name": "Dendrocygna bicolor", "bird_class": 1, "common_name": "Fulvous Whistling-Duck", "code": 1}, "pk": 2, "model": "birds.bird"}, {"fields": {"scientific_name": "Anser fabalis", "bird_class": 1, "common_name": "Taiga Bean-Goose", "code": 3}, "pk": 3, "model": "birds.bird"}, {"fields": {"scientific_name": "Anser serrirostris", "bird_class": 1, "common_name": "Tundra Bean-Goose", "code": 3}, "pk": 4, "model": "birds.bird"}, {"fields": {"scientific_name": "Anser brachyrhynchus", "bird_class": 1, "common_name": "Pink-footed Goose", "code": 4}, "pk": 5, "model": "birds.bird"}, {"fields": {"scientific_name": "Anser albifrons", "bird_class": 1, "common_name": "Greater White-fronted Goose", "code": 1}, "pk": 6, "model": "birds.bird"}, {"fields": {"scientific_name": "Anser erythropus", "bird_class": 1, "common_name": "Lesser White-fronted Goose", "code": 5}, "pk": 7, "model": "birds.bird"}, {"fields": {"scientific_name": "Anser anser", "bird_class": 1, "common_name": "Graylag Goose", "code": 5}, "pk": 8, "model": "birds.bird"}, {"fields": {"scientific_name": "Chen canagica", "bird_class": 1, "common_name": "Emperor Goose", "code": 2}, "pk": 9, "model": "birds.bird"}, {"fields": {"scientific_name": "Chen caerulescens", "bird_class": 1, "common_name": "Snow Goose", "code": 1}, "pk": 10, "model": "birds.bird"}, {"fields": {"scientific_name": "Chen rossii", "bird_class": 1, "common_name": "Ross's Goose", "code": 1}, "pk": 11, "model": "birds.bird"}, {"fields": {"scientific_name": "Branta bernicla", "bird_class": 1, "common_name": "Brant", "code": 1}, "pk": 12, "model": "birds.bird"}, {"fields": {"scientific_name": "Branta leucopsis", "bird_class": 1, "common_name": "Barnacle Goose", "code": 4}, "pk": 13, "model": "birds.bird"}, {"fields": {"scientific_name": "Branta hutchinsii", "bird_class": 1, "common_name": "Cackling Goose", "code": 1}, "pk": 14, "model": "birds.bird"}, {"fields": {"scientific_name": "Branta canadensis", "bird_class": 1, "common_name": "Canada Goose", "code": 1}, "pk": 15, "model": "birds.bird"}, {"fields": {"scientific_name": "Cygnus olor", "bird_class": 1, "common_name": "Mute Swan", "code": 1}, "pk": 16, "model": "birds.bird"}, {"fields": {"scientific_name": "Cygnus buccinator", "bird_class": 1, "common_name": "Trumpeter Swan", "code": 1}, "pk": 17, "model": "birds.bird"}, {"fields": {"scientific_name": "Cygnus columbianus", "bird_class": 1, "common_name": "Tundra Swan", "code": 1}, "pk": 18, "model": "birds.bird"}, {"fields": {"scientific_name": "Cygnus cygnus", "bird_class": 1, "common_name": "Whooper Swan", "code": 3}, "pk": 19, "model": "birds.bird"}, {"fields": {"scientific_name": "Cairina moschata", "bird_class": 1, "common_name": "Muscovy Duck", "code": 2}, "pk": 20, "model": "birds.bird"}, {"fields": {"scientific_name": "Aix sponsa", "bird_class": 1, "common_name": "Wood Duck", "code": 1}, "pk": 21, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas strepera", "bird_class": 1, "common_name": "Gadwall", "code": 1}, "pk": 22, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas falcata", "bird_class": 1, "common_name": "Falcated Duck", "code": 4}, "pk": 23, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas penelope", "bird_class": 1, "common_name": "Eurasian Wigeon", "code": 2}, "pk": 24, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas americana", "bird_class": 1, "common_name": "American Wigeon", "code": 1}, "pk": 25, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas rubripes", "bird_class": 1, "common_name": "American Black Duck", "code": 1}, "pk": 26, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas platyrhynchos", "bird_class": 1, "common_name": "Mallard", "code": 1}, "pk": 27, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas fulvigula", "bird_class": 1, "common_name": "Mottled Duck", "code": 1}, "pk": 28, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas zonorhyncha", "bird_class": 1, "common_name": "Eastern Spot-billed Duck", "code": 4}, "pk": 29, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas discors", "bird_class": 1, "common_name": "Blue-winged Teal", "code": 1}, "pk": 30, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas cyanoptera", "bird_class": 1, "common_name": "Cinnamon Teal", "code": 1}, "pk": 31, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas clypeata", "bird_class": 1, "common_name": "Northern Shoveler", "code": 1}, "pk": 32, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas bahamensis", "bird_class": 1, "common_name": "White-cheeked Pintail", "code": 4}, "pk": 33, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas acuta", "bird_class": 1, "common_name": "Northern Pintail", "code": 1}, "pk": 34, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas querquedula", "bird_class": 1, "common_name": "Garganey", "code": 4}, "pk": 35, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas formosa", "bird_class": 1, "common_name": "Baikal Teal", "code": 4}, "pk": 36, "model": "birds.bird"}, {"fields": {"scientific_name": "Anas crecca", "bird_class": 1, "common_name": "Green-winged Teal", "code": 1}, "pk": 37, "model": "birds.bird"}, {"fields": {"scientific_name": "Aythya valisineria", "bird_class": 1, "common_name": "Canvasback", "code": 1}, "pk": 38, "model": "birds.bird"}, {"fields": {"scientific_name": "Aythya americana", "bird_class": 1, "common_name": "Redhead", "code": 1}, "pk": 39, "model": "birds.bird"}, {"fields": {"scientific_name": "Aythya ferina", "bird_class": 1, "common_name": "Common Pochard", "code": 3}, "pk": 40, "model": "birds.bird"}, {"fields": {"scientific_name": "Aythya collaris", "bird_class": 1, "common_name": "Ring-necked Duck", "code": 1}, "pk": 41, "model": "birds.bird"}, {"fields": {"scientific_name": "Aythya fuligula", "bird_class": 1, "common_name": "Tufted Duck", "code": 3}, "pk": 42, "model": "birds.bird"}, {"fields": {"scientific_name": "Aythya marila", "bird_class": 1, "common_name": "Greater Scaup", "code": 1}, "pk": 43, "model": "birds.bird"}, {"fields": {"scientific_name": "Aythya affinis", "bird_class": 1, "common_name": "Lesser Scaup", "code": 1}, "pk": 44, "model": "birds.bird"}, {"fields": {"scientific_name": "Polysticta stelleri", "bird_class": 1, "common_name": "Steller's Eider", "code": 3}, "pk": 45, "model": "birds.bird"}, {"fields": {"scientific_name": "Somateria fischeri", "bird_class": 1, "common_name": "Spectacled Eider", "code": 3}, "pk": 46, "model": "birds.bird"}, {"fields": {"scientific_name": "Somateria spectabilis", "bird_class": 1, "common_name": "King Eider", "code": 2}, "pk": 47, "model": "birds.bird"}, {"fields": {"scientific_name": "Somateria mollissima", "bird_class": 1, "common_name": "Common Eider", "code": 1}, "pk": 48, "model": "birds.bird"}, {"fields": {"scientific_name": "Histrionicus histrionicus", "bird_class": 1, "common_name": "Harlequin Duck", "code": 1}, "pk": 49, "model": "birds.bird"}, {"fields": {"scientific_name": "Camptorhynchus labradorius", "bird_class": 1, "common_name": "Labrador Duck", "code": 6}, "pk": 50, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanitta perspicillata", "bird_class": 1, "common_name": "Surf Scoter", "code": 1}, "pk": 51, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanitta fusca", "bird_class": 1, "common_name": "White-winged Scoter", "code": 1}, "pk": 52, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanitta americana", "bird_class": 1, "common_name": "Black Scoter", "code": 1}, "pk": 53, "model": "birds.bird"}, {"fields": {"scientific_name": "Clangula hyemalis", "bird_class": 1, "common_name": "Long-tailed Duck", "code": 1}, "pk": 54, "model": "birds.bird"}, {"fields": {"scientific_name": "Bucephala albeola", "bird_class": 1, "common_name": "Bufflehead", "code": 1}, "pk": 55, "model": "birds.bird"}, {"fields": {"scientific_name": "Bucephala clangula", "bird_class": 1, "common_name": "Common Goldeneye", "code": 1}, "pk": 56, "model": "birds.bird"}, {"fields": {"scientific_name": "Bucephala islandica", "bird_class": 1, "common_name": "Barrow's Goldeneye", "code": 1}, "pk": 57, "model": "birds.bird"}, {"fields": {"scientific_name": "Mergellus albellus", "bird_class": 1, "common_name": "Smew", "code": 3}, "pk": 58, "model": "birds.bird"}, {"fields": {"scientific_name": "Lophodytes cucullatus", "bird_class": 1, "common_name": "Hooded Merganser", "code": 1}, "pk": 59, "model": "birds.bird"}, {"fields": {"scientific_name": "Mergus merganser", "bird_class": 1, "common_name": "Common Merganser", "code": 1}, "pk": 60, "model": "birds.bird"}, {"fields": {"scientific_name": "Mergus serrator", "bird_class": 1, "common_name": "Red-breasted Merganser", "code": 1}, "pk": 61, "model": "birds.bird"}, {"fields": {"scientific_name": "Nomonyx dominicus", "bird_class": 1, "common_name": "Masked Duck", "code": 3}, "pk": 62, "model": "birds.bird"}, {"fields": {"scientific_name": "Oxyura jamaicensis", "bird_class": 1, "common_name": "Ruddy Duck", "code": 1}, "pk": 63, "model": "birds.bird"}, {"fields": {"scientific_name": "Ortalis vetula", "bird_class": 2, "common_name": "Plain Chachalaca", "code": 2}, "pk": 64, "model": "birds.bird"}, {"fields": {"scientific_name": "Oreortyx pictus", "bird_class": 3, "common_name": "Mountain Quail", "code": 1}, "pk": 65, "model": "birds.bird"}, {"fields": {"scientific_name": "Callipepla squamata", "bird_class": 3, "common_name": "Scaled Quail", "code": 1}, "pk": 66, "model": "birds.bird"}, {"fields": {"scientific_name": "Callipepla californica", "bird_class": 3, "common_name": "California Quail", "code": 1}, "pk": 67, "model": "birds.bird"}, {"fields": {"scientific_name": "Callipepla gambelii", "bird_class": 3, "common_name": "Gambel's Quail", "code": 1}, "pk": 68, "model": "birds.bird"}, {"fields": {"scientific_name": "Colinus virginianus", "bird_class": 3, "common_name": "Northern Bobwhite", "code": 1}, "pk": 69, "model": "birds.bird"}, {"fields": {"scientific_name": "Cyrtonyx montezumae", "bird_class": 3, "common_name": "Montezuma Quail", "code": 2}, "pk": 70, "model": "birds.bird"}, {"fields": {"scientific_name": "Alectoris chukar", "bird_class": 4, "common_name": "Chukar", "code": 2}, "pk": 71, "model": "birds.bird"}, {"fields": {"scientific_name": "Tetraogallus himalayensis", "bird_class": 4, "common_name": "Himalayan Snowcock", "code": 2}, "pk": 72, "model": "birds.bird"}, {"fields": {"scientific_name": "Perdix perdix", "bird_class": 4, "common_name": "Gray Partridge", "code": 2}, "pk": 73, "model": "birds.bird"}, {"fields": {"scientific_name": "Phasianus colchicus", "bird_class": 4, "common_name": "Ring-necked Pheasant", "code": 1}, "pk": 74, "model": "birds.bird"}, {"fields": {"scientific_name": "Bonasa umbellus", "bird_class": 4, "common_name": "Ruffed Grouse", "code": 1}, "pk": 75, "model": "birds.bird"}, {"fields": {"scientific_name": "Centrocercus urophasianus", "bird_class": 4, "common_name": "Greater Sage-Grouse", "code": 1}, "pk": 76, "model": "birds.bird"}, {"fields": {"scientific_name": "Centrocercus minimus", "bird_class": 4, "common_name": "Gunnison Sage-Grouse", "code": 2}, "pk": 77, "model": "birds.bird"}, {"fields": {"scientific_name": "Falcipennis canadensis", "bird_class": 4, "common_name": "Spruce Grouse", "code": 2}, "pk": 78, "model": "birds.bird"}, {"fields": {"scientific_name": "Lagopus lagopus", "bird_class": 4, "common_name": "Willow Ptarmigan", "code": 1}, "pk": 79, "model": "birds.bird"}, {"fields": {"scientific_name": "Lagopus muta", "bird_class": 4, "common_name": "Rock Ptarmigan", "code": 1}, "pk": 80, "model": "birds.bird"}, {"fields": {"scientific_name": "Lagopus leucura", "bird_class": 4, "common_name": "White-tailed Ptarmigan", "code": 2}, "pk": 81, "model": "birds.bird"}, {"fields": {"scientific_name": "Dendragapus obscurus", "bird_class": 4, "common_name": "Dusky Grouse", "code": 2}, "pk": 82, "model": "birds.bird"}, {"fields": {"scientific_name": "Dendragapus fuliginosus", "bird_class": 4, "common_name": "Sooty Grouse", "code": 2}, "pk": 83, "model": "birds.bird"}, {"fields": {"scientific_name": "Tympanuchus phasianellus", "bird_class": 4, "common_name": "Sharp-tailed Grouse", "code": 2}, "pk": 84, "model": "birds.bird"}, {"fields": {"scientific_name": "Tympanuchus cupido", "bird_class": 4, "common_name": "Greater Prairie-Chicken", "code": 2}, "pk": 85, "model": "birds.bird"}, {"fields": {"scientific_name": "Tympanuchus pallidicinctus", "bird_class": 4, "common_name": "Lesser Prairie-Chicken", "code": 2}, "pk": 86, "model": "birds.bird"}, {"fields": {"scientific_name": "Meleagris gallopavo", "bird_class": 4, "common_name": "Wild Turkey", "code": 1}, "pk": 87, "model": "birds.bird"}, {"fields": {"scientific_name": "Gavia stellata", "bird_class": 5, "common_name": "Red-throated Loon", "code": 1}, "pk": 88, "model": "birds.bird"}, {"fields": {"scientific_name": "Gavia arctica", "bird_class": 5, "common_name": "Arctic Loon", "code": 2}, "pk": 89, "model": "birds.bird"}, {"fields": {"scientific_name": "Gavia pacifica", "bird_class": 5, "common_name": "Pacific Loon", "code": 1}, "pk": 90, "model": "birds.bird"}, {"fields": {"scientific_name": "Gavia immer", "bird_class": 5, "common_name": "Common Loon", "code": 1}, "pk": 91, "model": "birds.bird"}, {"fields": {"scientific_name": "Gavia adamsii", "bird_class": 5, "common_name": "Yellow-billed Loon", "code": 2}, "pk": 92, "model": "birds.bird"}, {"fields": {"scientific_name": "Tachybaptus dominicus", "bird_class": 6, "common_name": "Least Grebe", "code": 2}, "pk": 93, "model": "birds.bird"}, {"fields": {"scientific_name": "Podilymbus podiceps", "bird_class": 6, "common_name": "Pied-billed Grebe", "code": 1}, "pk": 94, "model": "birds.bird"}, {"fields": {"scientific_name": "Podiceps auritus", "bird_class": 6, "common_name": "Horned Grebe", "code": 1}, "pk": 95, "model": "birds.bird"}, {"fields": {"scientific_name": "Podiceps grisegena", "bird_class": 6, "common_name": "Red-necked Grebe", "code": 1}, "pk": 96, "model": "birds.bird"}, {"fields": {"scientific_name": "Podiceps nigricollis", "bird_class": 6, "common_name": "Eared Grebe", "code": 1}, "pk": 97, "model": "birds.bird"}, {"fields": {"scientific_name": "Aechmophorus occidentalis", "bird_class": 6, "common_name": "Western Grebe", "code": 1}, "pk": 98, "model": "birds.bird"}, {"fields": {"scientific_name": "Aechmophorus clarkii", "bird_class": 6, "common_name": "Clark's Grebe", "code": 1}, "pk": 99, "model": "birds.bird"}, {"fields": {"scientific_name": "Phoenicopterus ruber", "bird_class": 7, "common_name": "American Flamingo", "code": 3}, "pk": 100, "model": "birds.bird"}, {"fields": {"scientific_name": "Thalassarche chlororhynchos", "bird_class": 8, "common_name": "Yellow-nosed Albatross", "code": 4}, "pk": 101, "model": "birds.bird"}, {"fields": {"scientific_name": "Thalassarche cauta", "bird_class": 8, "common_name": "Shy Albatross", "code": 4}, "pk": 102, "model": "birds.bird"}, {"fields": {"scientific_name": "Thalassarche melanophris", "bird_class": 8, "common_name": "Black-browed Albatross", "code": 5}, "pk": 103, "model": "birds.bird"}, {"fields": {"scientific_name": "Phoebetria palpebrata", "bird_class": 8, "common_name": "Light-mantled Albatross", "code": 5}, "pk": 104, "model": "birds.bird"}, {"fields": {"scientific_name": "Diomedea exulans", "bird_class": 8, "common_name": "Wandering Albatross", "code": 5}, "pk": 105, "model": "birds.bird"}, {"fields": {"scientific_name": "Phoebastria immutabilis", "bird_class": 8, "common_name": "Laysan Albatross", "code": 2}, "pk": 106, "model": "birds.bird"}, {"fields": {"scientific_name": "Phoebastria nigripes", "bird_class": 8, "common_name": "Black-footed Albatross", "code": 1}, "pk": 107, "model": "birds.bird"}, {"fields": {"scientific_name": "Phoebastria albatrus", "bird_class": 8, "common_name": "Short-tailed Albatross", "code": 3}, "pk": 108, "model": "birds.bird"}, {"fields": {"scientific_name": "Fulmarus glacialis", "bird_class": 9, "common_name": "Northern Fulmar", "code": 1}, "pk": 109, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma macroptera", "bird_class": 9, "common_name": "Great-winged Petrel", "code": 5}, "pk": 110, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma solandri", "bird_class": 9, "common_name": "Providence Petrel", "code": 4}, "pk": 111, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma arminjoniana", "bird_class": 9, "common_name": "Herald Petrel", "code": 3}, "pk": 112, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma ultima", "bird_class": 9, "common_name": "Murphy's Petrel", "code": 3}, "pk": 113, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma inexpectata", "bird_class": 9, "common_name": "Mottled Petrel", "code": 2}, "pk": 114, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma cahow", "bird_class": 9, "common_name": "Bermuda Petrel", "code": 3}, "pk": 115, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma hasitata", "bird_class": 9, "common_name": "Black-capped Petrel", "code": 2}, "pk": 116, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma phaeopygia/sandwichensis", "bird_class": 9, "common_name": "Galapagos/Hawaiian Petrel", "code": 4}, "pk": 117, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma feae", "bird_class": 9, "common_name": "Fea's Petrel", "code": 3}, "pk": 118, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma cookii", "bird_class": 9, "common_name": "Cook's Petrel", "code": 3}, "pk": 119, "model": "birds.bird"}, {"fields": {"scientific_name": "Pterodroma longirostris", "bird_class": 9, "common_name": "Stejneger's Petrel", "code": 4}, "pk": 120, "model": "birds.bird"}, {"fields": {"scientific_name": "Bulweria bulwerii", "bird_class": 9, "common_name": "Bulwer's Petrel", "code": 5}, "pk": 121, "model": "birds.bird"}, {"fields": {"scientific_name": "Procellaria aequinoctialis", "bird_class": 9, "common_name": "White-chinned Petrel", "code": 5}, "pk": 122, "model": "birds.bird"}, {"fields": {"scientific_name": "Procellaria parkinsoni", "bird_class": 9, "common_name": "Parkinson's Petrel", "code": 5}, "pk": 123, "model": "birds.bird"}, {"fields": {"scientific_name": "Calonectris leucomelas", "bird_class": 9, "common_name": "Streaked Shearwater", "code": 4}, "pk": 124, "model": "birds.bird"}, {"fields": {"scientific_name": "Calonectris diomedea", "bird_class": 9, "common_name": "Cory's Shearwater", "code": 1}, "pk": 125, "model": "birds.bird"}, {"fields": {"scientific_name": "Calonectris edwardsii", "bird_class": 9, "common_name": "Cape Verde Shearwater", "code": 5}, "pk": 126, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus creatopus", "bird_class": 9, "common_name": "Pink-footed Shearwater", "code": 1}, "pk": 127, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus carneipes", "bird_class": 9, "common_name": "Flesh-footed Shearwater", "code": 3}, "pk": 128, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus gravis", "bird_class": 9, "common_name": "Great Shearwater", "code": 1}, "pk": 129, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus pacificus", "bird_class": 9, "common_name": "Wedge-tailed Shearwater", "code": 4}, "pk": 130, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus bulleri", "bird_class": 9, "common_name": "Buller's Shearwater", "code": 2}, "pk": 131, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus griseus", "bird_class": 9, "common_name": "Sooty Shearwater", "code": 1}, "pk": 132, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus tenuirostris", "bird_class": 9, "common_name": "Short-tailed Shearwater", "code": 2}, "pk": 133, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus puffinus", "bird_class": 9, "common_name": "Manx Shearwater", "code": 2}, "pk": 134, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus auricularis", "bird_class": 9, "common_name": "Townsend's Shearwater", "code": 5}, "pk": 135, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus opisthomelas", "bird_class": 9, "common_name": "Black-vented Shearwater", "code": 2}, "pk": 136, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus lherminieri", "bird_class": 9, "common_name": "Audubon's Shearwater", "code": 1}, "pk": 137, "model": "birds.bird"}, {"fields": {"scientific_name": "Puffinus assimilis", "bird_class": 9, "common_name": "Little Shearwater", "code": 5}, "pk": 138, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanites oceanicus", "bird_class": 10, "common_name": "Wilson's Storm-Petrel", "code": 1}, "pk": 139, "model": "birds.bird"}, {"fields": {"scientific_name": "Pelagodroma marina", "bird_class": 10, "common_name": "White-faced Storm-Petrel", "code": 3}, "pk": 140, "model": "birds.bird"}, {"fields": {"scientific_name": "Hydrobates pelagicus", "bird_class": 10, "common_name": "European Storm-Petrel", "code": 4}, "pk": 141, "model": "birds.bird"}, {"fields": {"scientific_name": "Fregetta tropica", "bird_class": 10, "common_name": "Black-bellied Storm-Petrel", "code": 5}, "pk": 142, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma furcata", "bird_class": 10, "common_name": "Fork-tailed Storm-Petrel", "code": 2}, "pk": 143, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma hornbyi", "bird_class": 10, "common_name": "Ringed Storm-Petrel", "code": 5}, "pk": 144, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma monorhis", "bird_class": 10, "common_name": "Swinhoe's Storm-Petrel", "code": 5}, "pk": 145, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma leucorhoa", "bird_class": 10, "common_name": "Leach's Storm-Petrel", "code": 1}, "pk": 146, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma homochroa", "bird_class": 10, "common_name": "Ashy Storm-Petrel", "code": 2}, "pk": 147, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma castro", "bird_class": 10, "common_name": "Band-rumped Storm-Petrel", "code": 2}, "pk": 148, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma tethys", "bird_class": 10, "common_name": "Wedge-rumped Storm-Petrel", "code": 4}, "pk": 149, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma melania", "bird_class": 10, "common_name": "Black Storm-Petrel", "code": 2}, "pk": 150, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma tristrami", "bird_class": 10, "common_name": "Tristram's Storm-Petrel", "code": 5}, "pk": 151, "model": "birds.bird"}, {"fields": {"scientific_name": "Oceanodroma microsoma", "bird_class": 10, "common_name": "Least Storm-Petrel", "code": 3}, "pk": 152, "model": "birds.bird"}, {"fields": {"scientific_name": "Phaethon lepturus", "bird_class": 11, "common_name": "White-tailed Tropicbird", "code": 3}, "pk": 153, "model": "birds.bird"}, {"fields": {"scientific_name": "Phaethon aethereus", "bird_class": 11, "common_name": "Red-billed Tropicbird", "code": 3}, "pk": 154, "model": "birds.bird"}, {"fields": {"scientific_name": "Phaethon rubricauda", "bird_class": 11, "common_name": "Red-tailed Tropicbird", "code": 4}, "pk": 155, "model": "birds.bird"}, {"fields": {"scientific_name": "Jabiru mycteria", "bird_class": 12, "common_name": "Jabiru", "code": 4}, "pk": 156, "model": "birds.bird"}, {"fields": {"scientific_name": "Mycteria americana", "bird_class": 12, "common_name": "Wood Stork", "code": 1}, "pk": 157, "model": "birds.bird"}, {"fields": {"scientific_name": "Fregata magnificens", "bird_class": 13, "common_name": "Magnificent Frigatebird", "code": 1}, "pk": 158, "model": "birds.bird"}, {"fields": {"scientific_name": "Fregata minor", "bird_class": 13, "common_name": "Great Frigatebird", "code": 5}, "pk": 159, "model": "birds.bird"}, {"fields": {"scientific_name": "Fregata ariel", "bird_class": 13, "common_name": "Lesser Frigatebird", "code": 5}, "pk": 160, "model": "birds.bird"}, {"fields": {"scientific_name": "Sula dactylatra", "bird_class": 14, "common_name": "Masked Booby", "code": 3}, "pk": 161, "model": "birds.bird"}, {"fields": {"scientific_name": "Sula nebouxii", "bird_class": 14, "common_name": "Blue-footed Booby", "code": 4}, "pk": 162, "model": "birds.bird"}, {"fields": {"scientific_name": "Sula leucogaster", "bird_class": 14, "common_name": "Brown Booby", "code": 3}, "pk": 163, "model": "birds.bird"}, {"fields": {"scientific_name": "Sula sula", "bird_class": 14, "common_name": "Red-footed Booby", "code": 4}, "pk": 164, "model": "birds.bird"}, {"fields": {"scientific_name": "Morus bassanus", "bird_class": 14, "common_name": "Northern Gannet", "code": 1}, "pk": 165, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalacrocorax penicillatus", "bird_class": 15, "common_name": "Brandt's Cormorant", "code": 1}, "pk": 166, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalacrocorax brasilianus", "bird_class": 15, "common_name": "Neotropic Cormorant", "code": 1}, "pk": 167, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalacrocorax auritus", "bird_class": 15, "common_name": "Double-crested Cormorant", "code": 1}, "pk": 168, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalacrocorax carbo", "bird_class": 15, "common_name": "Great Cormorant", "code": 1}, "pk": 169, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalacrocorax urile", "bird_class": 15, "common_name": "Red-faced Cormorant", "code": 2}, "pk": 170, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalacrocorax pelagicus", "bird_class": 15, "common_name": "Pelagic Cormorant", "code": 1}, "pk": 171, "model": "birds.bird"}, {"fields": {"scientific_name": "Anhinga anhinga", "bird_class": 16, "common_name": "Anhinga", "code": 1}, "pk": 172, "model": "birds.bird"}, {"fields": {"scientific_name": "Pelecanus erythrorhynchos", "bird_class": 17, "common_name": "American White Pelican", "code": 1}, "pk": 173, "model": "birds.bird"}, {"fields": {"scientific_name": "Pelecanus occidentalis", "bird_class": 17, "common_name": "Brown Pelican", "code": 1}, "pk": 174, "model": "birds.bird"}, {"fields": {"scientific_name": "Botaurus lentiginosus", "bird_class": 18, "common_name": "American Bittern", "code": 1}, "pk": 175, "model": "birds.bird"}, {"fields": {"scientific_name": "Ixobrychus sinensis", "bird_class": 18, "common_name": "Yellow Bittern", "code": 5}, "pk": 176, "model": "birds.bird"}, {"fields": {"scientific_name": "Ixobrychus exilis", "bird_class": 18, "common_name": "Least Bittern", "code": 1}, "pk": 177, "model": "birds.bird"}, {"fields": {"scientific_name": "Tigrisoma mexicanum", "bird_class": 18, "common_name": "Bare-throated Tiger-Heron", "code": 5}, "pk": 178, "model": "birds.bird"}, {"fields": {"scientific_name": "Ardea herodias", "bird_class": 18, "common_name": "Great Blue Heron", "code": 1}, "pk": 179, "model": "birds.bird"}, {"fields": {"scientific_name": "Ardea cinerea", "bird_class": 18, "common_name": "Gray Heron", "code": 5}, "pk": 180, "model": "birds.bird"}, {"fields": {"scientific_name": "Ardea alba", "bird_class": 18, "common_name": "Great Egret", "code": 1}, "pk": 181, "model": "birds.bird"}, {"fields": {"scientific_name": "Mesophoyx intermedia", "bird_class": 18, "common_name": "Intermediate Egret", "code": 5}, "pk": 182, "model": "birds.bird"}, {"fields": {"scientific_name": "Egretta eulophotes", "bird_class": 18, "common_name": "Chinese Egret", "code": 5}, "pk": 183, "model": "birds.bird"}, {"fields": {"scientific_name": "Egretta garzetta", "bird_class": 18, "common_name": "Little Egret", "code": 4}, "pk": 184, "model": "birds.bird"}, {"fields": {"scientific_name": "Egretta gularis", "bird_class": 18, "common_name": "Western Reef-Heron", "code": 5}, "pk": 185, "model": "birds.bird"}, {"fields": {"scientific_name": "Egretta thula", "bird_class": 18, "common_name": "Snowy Egret", "code": 1}, "pk": 186, "model": "birds.bird"}, {"fields": {"scientific_name": "Egretta caerulea", "bird_class": 18, "common_name": "Little Blue Heron", "code": 1}, "pk": 187, "model": "birds.bird"}, {"fields": {"scientific_name": "Egretta tricolor", "bird_class": 18, "common_name": "Tricolored Heron", "code": 1}, "pk": 188, "model": "birds.bird"}, {"fields": {"scientific_name": "Egretta rufescens", "bird_class": 18, "common_name": "Reddish Egret", "code": 1}, "pk": 189, "model": "birds.bird"}, {"fields": {"scientific_name": "Bubulcus ibis", "bird_class": 18, "common_name": "Cattle Egret", "code": 1}, "pk": 190, "model": "birds.bird"}, {"fields": {"scientific_name": "Ardeola bacchus", "bird_class": 18, "common_name": "Chinese Pond-Heron", "code": 5}, "pk": 191, "model": "birds.bird"}, {"fields": {"scientific_name": "Butorides virescens", "bird_class": 18, "common_name": "Green Heron", "code": 1}, "pk": 192, "model": "birds.bird"}, {"fields": {"scientific_name": "Nycticorax nycticorax", "bird_class": 18, "common_name": "Black-crowned Night-Heron", "code": 1}, "pk": 193, "model": "birds.bird"}, {"fields": {"scientific_name": "Nyctanassa violacea", "bird_class": 18, "common_name": "Yellow-crowned Night-Heron", "code": 1}, "pk": 194, "model": "birds.bird"}, {"fields": {"scientific_name": "Eudocimus albus", "bird_class": 19, "common_name": "White Ibis", "code": 1}, "pk": 195, "model": "birds.bird"}, {"fields": {"scientific_name": "Eudocimus ruber", "bird_class": 19, "common_name": "Scarlet Ibis", "code": 5}, "pk": 196, "model": "birds.bird"}, {"fields": {"scientific_name": "Plegadis falcinellus", "bird_class": 19, "common_name": "Glossy Ibis", "code": 1}, "pk": 197, "model": "birds.bird"}, {"fields": {"scientific_name": "Plegadis chihi", "bird_class": 19, "common_name": "White-faced Ibis", "code": 1}, "pk": 198, "model": "birds.bird"}, {"fields": {"scientific_name": "Platalea ajaja", "bird_class": 19, "common_name": "Roseate Spoonbill", "code": 1}, "pk": 199, "model": "birds.bird"}, {"fields": {"scientific_name": "Coragyps atratus", "bird_class": 20, "common_name": "Black Vulture", "code": 1}, "pk": 200, "model": "birds.bird"}, {"fields": {"scientific_name": "Cathartes aura", "bird_class": 20, "common_name": "Turkey Vulture", "code": 1}, "pk": 201, "model": "birds.bird"}, {"fields": {"scientific_name": "Gymnogyps californianus", "bird_class": 20, "common_name": "California Condor", "code": 6}, "pk": 202, "model": "birds.bird"}, {"fields": {"scientific_name": "Pandion haliaetus", "bird_class": 21, "common_name": "Osprey", "code": 1}, "pk": 203, "model": "birds.bird"}, {"fields": {"scientific_name": "Chondrohierax uncinatus", "bird_class": 22, "common_name": "Hook-billed Kite", "code": 3}, "pk": 204, "model": "birds.bird"}, {"fields": {"scientific_name": "Elanoides forficatus", "bird_class": 22, "common_name": "Swallow-tailed Kite", "code": 1}, "pk": 205, "model": "birds.bird"}, {"fields": {"scientific_name": "Elanus leucurus", "bird_class": 22, "common_name": "White-tailed Kite", "code": 1}, "pk": 206, "model": "birds.bird"}, {"fields": {"scientific_name": "Rostrhamus sociabilis", "bird_class": 22, "common_name": "Snail Kite", "code": 2}, "pk": 207, "model": "birds.bird"}, {"fields": {"scientific_name": "Harpagus bidentatus", "bird_class": 22, "common_name": "Double-toothed Kite", "code": 5}, "pk": 208, "model": "birds.bird"}, {"fields": {"scientific_name": "Ictinia mississippiensis", "bird_class": 22, "common_name": "Mississippi Kite", "code": 1}, "pk": 209, "model": "birds.bird"}, {"fields": {"scientific_name": "Haliaeetus leucocephalus", "bird_class": 22, "common_name": "Bald Eagle", "code": 1}, "pk": 210, "model": "birds.bird"}, {"fields": {"scientific_name": "Haliaeetus albicilla", "bird_class": 22, "common_name": "White-tailed Eagle", "code": 4}, "pk": 211, "model": "birds.bird"}, {"fields": {"scientific_name": "Haliaeetus pelagicus", "bird_class": 22, "common_name": "Steller's Sea-Eagle", "code": 4}, "pk": 212, "model": "birds.bird"}, {"fields": {"scientific_name": "Circus cyaneus", "bird_class": 22, "common_name": "Northern Harrier", "code": 1}, "pk": 213, "model": "birds.bird"}, {"fields": {"scientific_name": "Accipiter striatus", "bird_class": 22, "common_name": "Sharp-shinned Hawk", "code": 1}, "pk": 214, "model": "birds.bird"}, {"fields": {"scientific_name": "Accipiter cooperii", "bird_class": 22, "common_name": "Cooper's Hawk", "code": 1}, "pk": 215, "model": "birds.bird"}, {"fields": {"scientific_name": "Accipiter gentilis", "bird_class": 22, "common_name": "Northern Goshawk", "code": 1}, "pk": 216, "model": "birds.bird"}, {"fields": {"scientific_name": "Geranospiza caerulescens", "bird_class": 22, "common_name": "Crane Hawk", "code": 5}, "pk": 217, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteogallus anthracinus", "bird_class": 22, "common_name": "Common Black-Hawk", "code": 2}, "pk": 218, "model": "birds.bird"}, {"fields": {"scientific_name": "Parabuteo unicinctus", "bird_class": 22, "common_name": "Harris's Hawk", "code": 1}, "pk": 219, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo magnirostris", "bird_class": 22, "common_name": "Roadside Hawk", "code": 4}, "pk": 220, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo lineatus", "bird_class": 22, "common_name": "Red-shouldered Hawk", "code": 1}, "pk": 221, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo platypterus", "bird_class": 22, "common_name": "Broad-winged Hawk", "code": 1}, "pk": 222, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo plagiatus", "bird_class": 22, "common_name": "Gray Hawk", "code": 2}, "pk": 223, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo brachyurus", "bird_class": 22, "common_name": "Short-tailed Hawk", "code": 2}, "pk": 224, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo swainsoni", "bird_class": 22, "common_name": "Swainson's Hawk", "code": 1}, "pk": 225, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo albicaudatus", "bird_class": 22, "common_name": "White-tailed Hawk", "code": 2}, "pk": 226, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo albonotatus", "bird_class": 22, "common_name": "Zone-tailed Hawk", "code": 2}, "pk": 227, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo jamaicensis", "bird_class": 22, "common_name": "Red-tailed Hawk", "code": 1}, "pk": 228, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo regalis", "bird_class": 22, "common_name": "Ferruginous Hawk", "code": 1}, "pk": 229, "model": "birds.bird"}, {"fields": {"scientific_name": "Buteo lagopus", "bird_class": 22, "common_name": "Rough-legged Hawk", "code": 1}, "pk": 230, "model": "birds.bird"}, {"fields": {"scientific_name": "Aquila chrysaetos", "bird_class": 22, "common_name": "Golden Eagle", "code": 1}, "pk": 231, "model": "birds.bird"}, {"fields": {"scientific_name": "Coturnicops noveboracensis", "bird_class": 23, "common_name": "Yellow Rail", "code": 2}, "pk": 232, "model": "birds.bird"}, {"fields": {"scientific_name": "Laterallus jamaicensis", "bird_class": 23, "common_name": "Black Rail", "code": 2}, "pk": 233, "model": "birds.bird"}, {"fields": {"scientific_name": "Crex crex", "bird_class": 23, "common_name": "Corn Crake", "code": 4}, "pk": 234, "model": "birds.bird"}, {"fields": {"scientific_name": "Rallus longirostris", "bird_class": 23, "common_name": "Clapper Rail", "code": 1}, "pk": 235, "model": "birds.bird"}, {"fields": {"scientific_name": "Rallus elegans", "bird_class": 23, "common_name": "King Rail", "code": 1}, "pk": 236, "model": "birds.bird"}, {"fields": {"scientific_name": "Rallus limicola", "bird_class": 23, "common_name": "Virginia Rail", "code": 1}, "pk": 237, "model": "birds.bird"}, {"fields": {"scientific_name": "Porzana carolina", "bird_class": 23, "common_name": "Sora", "code": 1}, "pk": 238, "model": "birds.bird"}, {"fields": {"scientific_name": "Neocrex erythrops", "bird_class": 23, "common_name": "Paint-billed Crake", "code": 5}, "pk": 239, "model": "birds.bird"}, {"fields": {"scientific_name": "Pardirallus maculatus", "bird_class": 23, "common_name": "Spotted Rail", "code": 5}, "pk": 240, "model": "birds.bird"}, {"fields": {"scientific_name": "Porphyrio martinicus", "bird_class": 23, "common_name": "Purple Gallinule", "code": 1}, "pk": 241, "model": "birds.bird"}, {"fields": {"scientific_name": "Gallinula galeata", "bird_class": 23, "common_name": "Common Gallinule", "code": 1}, "pk": 242, "model": "birds.bird"}, {"fields": {"scientific_name": "Fulica atra", "bird_class": 23, "common_name": "Eurasian Coot", "code": 5}, "pk": 243, "model": "birds.bird"}, {"fields": {"scientific_name": "Fulica americana", "bird_class": 23, "common_name": "American Coot", "code": 1}, "pk": 244, "model": "birds.bird"}, {"fields": {"scientific_name": "Heliornis fulica", "bird_class": 24, "common_name": "Sungrebe", "code": 5}, "pk": 245, "model": "birds.bird"}, {"fields": {"scientific_name": "Aramus guarauna", "bird_class": 25, "common_name": "Limpkin", "code": 2}, "pk": 246, "model": "birds.bird"}, {"fields": {"scientific_name": "Grus canadensis", "bird_class": 26, "common_name": "Sandhill Crane", "code": 1}, "pk": 247, "model": "birds.bird"}, {"fields": {"scientific_name": "Grus grus", "bird_class": 26, "common_name": "Common Crane", "code": 4}, "pk": 248, "model": "birds.bird"}, {"fields": {"scientific_name": "Grus americana", "bird_class": 26, "common_name": "Whooping Crane", "code": 2}, "pk": 249, "model": "birds.bird"}, {"fields": {"scientific_name": "Burhinus bistriatus", "bird_class": 27, "common_name": "Double-striped Thick-knee", "code": 5}, "pk": 250, "model": "birds.bird"}, {"fields": {"scientific_name": "Vanellus vanellus", "bird_class": 28, "common_name": "Northern Lapwing", "code": 4}, "pk": 251, "model": "birds.bird"}, {"fields": {"scientific_name": "Pluvialis squatarola", "bird_class": 28, "common_name": "Black-bellied Plover", "code": 1}, "pk": 252, "model": "birds.bird"}, {"fields": {"scientific_name": "Pluvialis apricaria", "bird_class": 28, "common_name": "European Golden-Plover", "code": 4}, "pk": 253, "model": "birds.bird"}, {"fields": {"scientific_name": "Pluvialis dominica", "bird_class": 28, "common_name": "American Golden-Plover", "code": 1}, "pk": 254, "model": "birds.bird"}, {"fields": {"scientific_name": "Pluvialis fulva", "bird_class": 28, "common_name": "Pacific Golden-Plover", "code": 2}, "pk": 255, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius mongolus", "bird_class": 28, "common_name": "Lesser Sand-Plover", "code": 3}, "pk": 256, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius leschenaultii", "bird_class": 28, "common_name": "Greater Sand-Plover", "code": 5}, "pk": 257, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius collaris", "bird_class": 28, "common_name": "Collared Plover", "code": 5}, "pk": 258, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius nivosus", "bird_class": 28, "common_name": "Snowy Plover", "code": 1}, "pk": 259, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius wilsonia", "bird_class": 28, "common_name": "Wilson's Plover", "code": 1}, "pk": 260, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius hiaticula", "bird_class": 28, "common_name": "Common Ringed Plover", "code": 2}, "pk": 261, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius semipalmatus", "bird_class": 28, "common_name": "Semipalmated Plover", "code": 1}, "pk": 262, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius melodus", "bird_class": 28, "common_name": "Piping Plover", "code": 2}, "pk": 263, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius dubius", "bird_class": 28, "common_name": "Little Ringed Plover", "code": 5}, "pk": 264, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius vociferus", "bird_class": 28, "common_name": "Killdeer", "code": 1}, "pk": 265, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius montanus", "bird_class": 28, "common_name": "Mountain Plover", "code": 2}, "pk": 266, "model": "birds.bird"}, {"fields": {"scientific_name": "Charadrius morinellus", "bird_class": 28, "common_name": "Eurasian Dotterel", "code": 4}, "pk": 267, "model": "birds.bird"}, {"fields": {"scientific_name": "Haematopus ostralegus", "bird_class": 29, "common_name": "Eurasian Oystercatcher", "code": 5}, "pk": 268, "model": "birds.bird"}, {"fields": {"scientific_name": "Haematopus palliatus", "bird_class": 29, "common_name": "American Oystercatcher", "code": 1}, "pk": 269, "model": "birds.bird"}, {"fields": {"scientific_name": "Haematopus bachmani", "bird_class": 29, "common_name": "Black Oystercatcher", "code": 1}, "pk": 270, "model": "birds.bird"}, {"fields": {"scientific_name": "Himantopus himantopus", "bird_class": 30, "common_name": "Black-winged Stilt", "code": 5}, "pk": 271, "model": "birds.bird"}, {"fields": {"scientific_name": "Himantopus mexicanus", "bird_class": 30, "common_name": "Black-necked Stilt", "code": 1}, "pk": 272, "model": "birds.bird"}, {"fields": {"scientific_name": "Recurvirostra americana", "bird_class": 30, "common_name": "American Avocet", "code": 1}, "pk": 273, "model": "birds.bird"}, {"fields": {"scientific_name": "Jacana spinosa", "bird_class": 31, "common_name": "Northern Jacana", "code": 4}, "pk": 274, "model": "birds.bird"}, {"fields": {"scientific_name": "Xenus cinereus", "bird_class": 32, "common_name": "Terek Sandpiper", "code": 3}, "pk": 275, "model": "birds.bird"}, {"fields": {"scientific_name": "Actitis hypoleucos", "bird_class": 32, "common_name": "Common Sandpiper", "code": 3}, "pk": 276, "model": "birds.bird"}, {"fields": {"scientific_name": "Actitis macularius", "bird_class": 32, "common_name": "Spotted Sandpiper", "code": 1}, "pk": 277, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa ochropus", "bird_class": 32, "common_name": "Green Sandpiper", "code": 4}, "pk": 278, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa solitaria", "bird_class": 32, "common_name": "Solitary Sandpiper", "code": 1}, "pk": 279, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa brevipes", "bird_class": 32, "common_name": "Gray-tailed Tattler", "code": 3}, "pk": 280, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa incana", "bird_class": 32, "common_name": "Wandering Tattler", "code": 1}, "pk": 281, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa erythropus", "bird_class": 32, "common_name": "Spotted Redshank", "code": 4}, "pk": 282, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa melanoleuca", "bird_class": 32, "common_name": "Greater Yellowlegs", "code": 1}, "pk": 283, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa nebularia", "bird_class": 32, "common_name": "Common Greenshank", "code": 3}, "pk": 284, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa semipalmata", "bird_class": 32, "common_name": "Willet", "code": 1}, "pk": 285, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa flavipes", "bird_class": 32, "common_name": "Lesser Yellowlegs", "code": 1}, "pk": 286, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa stagnatilis", "bird_class": 32, "common_name": "Marsh Sandpiper", "code": 5}, "pk": 287, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa glareola", "bird_class": 32, "common_name": "Wood Sandpiper", "code": 2}, "pk": 288, "model": "birds.bird"}, {"fields": {"scientific_name": "Tringa totanus", "bird_class": 32, "common_name": "Common Redshank", "code": 5}, "pk": 289, "model": "birds.bird"}, {"fields": {"scientific_name": "Bartramia longicauda", "bird_class": 32, "common_name": "Upland Sandpiper", "code": 1}, "pk": 290, "model": "birds.bird"}, {"fields": {"scientific_name": "Numenius minutus", "bird_class": 32, "common_name": "Little Curlew", "code": 5}, "pk": 291, "model": "birds.bird"}, {"fields": {"scientific_name": "Numenius borealis", "bird_class": 32, "common_name": "Eskimo Curlew", "code": 6}, "pk": 292, "model": "birds.bird"}, {"fields": {"scientific_name": "Numenius phaeopus", "bird_class": 32, "common_name": "Whimbrel", "code": 1}, "pk": 293, "model": "birds.bird"}, {"fields": {"scientific_name": "Numenius tahitiensis", "bird_class": 32, "common_name": "Bristle-thighed Curlew", "code": 2}, "pk": 294, "model": "birds.bird"}, {"fields": {"scientific_name": "Numenius madagascariensis", "bird_class": 32, "common_name": "Far Eastern Curlew", "code": 4}, "pk": 295, "model": "birds.bird"}, {"fields": {"scientific_name": "Numenius tenuirostris", "bird_class": 32, "common_name": "Slender-billed Curlew", "code": 6}, "pk": 296, "model": "birds.bird"}, {"fields": {"scientific_name": "Numenius arquata", "bird_class": 32, "common_name": "Eurasian Curlew", "code": 4}, "pk": 297, "model": "birds.bird"}, {"fields": {"scientific_name": "Numenius americanus", "bird_class": 32, "common_name": "Long-billed Curlew", "code": 1}, "pk": 298, "model": "birds.bird"}, {"fields": {"scientific_name": "Limosa limosa", "bird_class": 32, "common_name": "Black-tailed Godwit", "code": 3}, "pk": 299, "model": "birds.bird"}, {"fields": {"scientific_name": "Limosa haemastica", "bird_class": 32, "common_name": "Hudsonian Godwit", "code": 1}, "pk": 300, "model": "birds.bird"}, {"fields": {"scientific_name": "Limosa lapponica", "bird_class": 32, "common_name": "Bar-tailed Godwit", "code": 2}, "pk": 301, "model": "birds.bird"}, {"fields": {"scientific_name": "Limosa fedoa", "bird_class": 32, "common_name": "Marbled Godwit", "code": 1}, "pk": 302, "model": "birds.bird"}, {"fields": {"scientific_name": "Arenaria interpres", "bird_class": 32, "common_name": "Ruddy Turnstone", "code": 1}, "pk": 303, "model": "birds.bird"}, {"fields": {"scientific_name": "Arenaria melanocephala", "bird_class": 32, "common_name": "Black Turnstone", "code": 1}, "pk": 304, "model": "birds.bird"}, {"fields": {"scientific_name": "Aphriza virgata", "bird_class": 32, "common_name": "Surfbird", "code": 1}, "pk": 305, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris tenuirostris", "bird_class": 32, "common_name": "Great Knot", "code": 4}, "pk": 306, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris canutus", "bird_class": 32, "common_name": "Red Knot", "code": 1}, "pk": 307, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris alba", "bird_class": 32, "common_name": "Sanderling", "code": 1}, "pk": 308, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris pusilla", "bird_class": 32, "common_name": "Semipalmated Sandpiper", "code": 1}, "pk": 309, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris mauri", "bird_class": 32, "common_name": "Western Sandpiper", "code": 1}, "pk": 310, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris ruficollis", "bird_class": 32, "common_name": "Red-necked Stint", "code": 3}, "pk": 311, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris minuta", "bird_class": 32, "common_name": "Little Stint", "code": 4}, "pk": 312, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris temminckii", "bird_class": 32, "common_name": "Temminck's Stint", "code": 3}, "pk": 313, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris subminuta", "bird_class": 32, "common_name": "Long-toed Stint", "code": 3}, "pk": 314, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris minutilla", "bird_class": 32, "common_name": "Least Sandpiper", "code": 1}, "pk": 315, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris fuscicollis", "bird_class": 32, "common_name": "White-rumped Sandpiper", "code": 1}, "pk": 316, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris bairdii", "bird_class": 32, "common_name": "Baird's Sandpiper", "code": 1}, "pk": 317, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris melanotos", "bird_class": 32, "common_name": "Pectoral Sandpiper", "code": 1}, "pk": 318, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris acuminata", "bird_class": 32, "common_name": "Sharp-tailed Sandpiper", "code": 3}, "pk": 319, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris maritima", "bird_class": 32, "common_name": "Purple Sandpiper", "code": 1}, "pk": 320, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris ptilocnemis", "bird_class": 32, "common_name": "Rock Sandpiper", "code": 2}, "pk": 321, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris alpina", "bird_class": 32, "common_name": "Dunlin", "code": 1}, "pk": 322, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris ferruginea", "bird_class": 32, "common_name": "Curlew Sandpiper", "code": 3}, "pk": 323, "model": "birds.bird"}, {"fields": {"scientific_name": "Calidris himantopus", "bird_class": 32, "common_name": "Stilt Sandpiper", "code": 1}, "pk": 324, "model": "birds.bird"}, {"fields": {"scientific_name": "Eurynorhynchus pygmeus", "bird_class": 32, "common_name": "Spoon-billed Sandpiper", "code": 4}, "pk": 325, "model": "birds.bird"}, {"fields": {"scientific_name": "Limicola falcinellus", "bird_class": 32, "common_name": "Broad-billed Sandpiper", "code": 4}, "pk": 326, "model": "birds.bird"}, {"fields": {"scientific_name": "Tryngites subruficollis", "bird_class": 32, "common_name": "Buff-breasted Sandpiper", "code": 1}, "pk": 327, "model": "birds.bird"}, {"fields": {"scientific_name": "Philomachus pugnax", "bird_class": 32, "common_name": "Ruff", "code": 3}, "pk": 328, "model": "birds.bird"}, {"fields": {"scientific_name": "Limnodromus griseus", "bird_class": 32, "common_name": "Short-billed Dowitcher", "code": 1}, "pk": 329, "model": "birds.bird"}, {"fields": {"scientific_name": "Limnodromus scolopaceus", "bird_class": 32, "common_name": "Long-billed Dowitcher", "code": 1}, "pk": 330, "model": "birds.bird"}, {"fields": {"scientific_name": "Lymnocryptes minimus", "bird_class": 32, "common_name": "Jack Snipe", "code": 4}, "pk": 331, "model": "birds.bird"}, {"fields": {"scientific_name": "Gallinago delicata", "bird_class": 32, "common_name": "Wilson's Snipe", "code": 1}, "pk": 332, "model": "birds.bird"}, {"fields": {"scientific_name": "Gallinago gallinago", "bird_class": 32, "common_name": "Common Snipe", "code": 3}, "pk": 333, "model": "birds.bird"}, {"fields": {"scientific_name": "Gallinago stenura", "bird_class": 32, "common_name": "Pin-tailed Snipe", "code": 5}, "pk": 334, "model": "birds.bird"}, {"fields": {"scientific_name": "Gallinago solitaria", "bird_class": 32, "common_name": "Solitary Snipe", "code": 5}, "pk": 335, "model": "birds.bird"}, {"fields": {"scientific_name": "Scolopax rusticola", "bird_class": 32, "common_name": "Eurasian Woodcock", "code": 5}, "pk": 336, "model": "birds.bird"}, {"fields": {"scientific_name": "Scolopax minor", "bird_class": 32, "common_name": "American Woodcock", "code": 1}, "pk": 337, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalaropus tricolor", "bird_class": 32, "common_name": "Wilson's Phalarope", "code": 1}, "pk": 338, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalaropus lobatus", "bird_class": 32, "common_name": "Red-necked Phalarope", "code": 1}, "pk": 339, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalaropus fulicarius", "bird_class": 32, "common_name": "Red Phalarope", "code": 1}, "pk": 340, "model": "birds.bird"}, {"fields": {"scientific_name": "Glareola maldivarum", "bird_class": 33, "common_name": "Oriental Pratincole", "code": 5}, "pk": 341, "model": "birds.bird"}, {"fields": {"scientific_name": "Creagrus furcatus", "bird_class": 34, "common_name": "Swallow-tailed Gull", "code": 5}, "pk": 342, "model": "birds.bird"}, {"fields": {"scientific_name": "Rissa tridactyla", "bird_class": 34, "common_name": "Black-legged Kittiwake", "code": 1}, "pk": 343, "model": "birds.bird"}, {"fields": {"scientific_name": "Rissa brevirostris", "bird_class": 34, "common_name": "Red-legged Kittiwake", "code": 2}, "pk": 344, "model": "birds.bird"}, {"fields": {"scientific_name": "Pagophila eburnea", "bird_class": 34, "common_name": "Ivory Gull", "code": 3}, "pk": 345, "model": "birds.bird"}, {"fields": {"scientific_name": "Xema sabini", "bird_class": 34, "common_name": "Sabine's Gull", "code": 1}, "pk": 346, "model": "birds.bird"}, {"fields": {"scientific_name": "Chroicocephalus philadelphia", "bird_class": 34, "common_name": "Bonaparte's Gull", "code": 1}, "pk": 347, "model": "birds.bird"}, {"fields": {"scientific_name": "Chroicocephalus cirrocephalus", "bird_class": 34, "common_name": "Gray-hooded Gull", "code": 5}, "pk": 348, "model": "birds.bird"}, {"fields": {"scientific_name": "Chroicocephalus ridibundus", "bird_class": 34, "common_name": "Black-headed Gull", "code": 3}, "pk": 349, "model": "birds.bird"}, {"fields": {"scientific_name": "Hydrocoloeus minutus", "bird_class": 34, "common_name": "Little Gull", "code": 3}, "pk": 350, "model": "birds.bird"}, {"fields": {"scientific_name": "Rhodostethia rosea", "bird_class": 34, "common_name": "Ross's Gull", "code": 3}, "pk": 351, "model": "birds.bird"}, {"fields": {"scientific_name": "Leucophaeus atricilla", "bird_class": 34, "common_name": "Laughing Gull", "code": 1}, "pk": 352, "model": "birds.bird"}, {"fields": {"scientific_name": "Leucophaeus pipixcan", "bird_class": 34, "common_name": "Franklin's Gull", "code": 1}, "pk": 353, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus belcheri", "bird_class": 34, "common_name": "Belcher's Gull", "code": 5}, "pk": 354, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus crassirostris", "bird_class": 34, "common_name": "Black-tailed Gull", "code": 4}, "pk": 355, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus heermanni", "bird_class": 34, "common_name": "Heermann's Gull", "code": 1}, "pk": 356, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus canus", "bird_class": 34, "common_name": "Mew Gull", "code": 1}, "pk": 357, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus delawarensis", "bird_class": 34, "common_name": "Ring-billed Gull", "code": 1}, "pk": 358, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus occidentalis", "bird_class": 34, "common_name": "Western Gull", "code": 1}, "pk": 359, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus livens", "bird_class": 34, "common_name": "Yellow-footed Gull", "code": 2}, "pk": 360, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus californicus", "bird_class": 34, "common_name": "California Gull", "code": 1}, "pk": 361, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus argentatus", "bird_class": 34, "common_name": "Herring Gull", "code": 1}, "pk": 362, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus michahellis", "bird_class": 34, "common_name": "Yellow-legged Gull", "code": 4}, "pk": 363, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus thayeri", "bird_class": 34, "common_name": "Thayer's Gull", "code": 2}, "pk": 364, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus glaucoides", "bird_class": 34, "common_name": "Iceland Gull", "code": 2}, "pk": 365, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus fuscus", "bird_class": 34, "common_name": "Lesser Black-backed Gull", "code": 2}, "pk": 366, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus schistisagus", "bird_class": 34, "common_name": "Slaty-backed Gull", "code": 3}, "pk": 367, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus glaucescens", "bird_class": 34, "common_name": "Glaucous-winged Gull", "code": 1}, "pk": 368, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus hyperboreus", "bird_class": 34, "common_name": "Glaucous Gull", "code": 1}, "pk": 369, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus marinus", "bird_class": 34, "common_name": "Great Black-backed Gull", "code": 1}, "pk": 370, "model": "birds.bird"}, {"fields": {"scientific_name": "Larus dominicanus", "bird_class": 34, "common_name": "Kelp Gull", "code": 4}, "pk": 371, "model": "birds.bird"}, {"fields": {"scientific_name": "Anous stolidus", "bird_class": 34, "common_name": "Brown Noddy", "code": 2}, "pk": 372, "model": "birds.bird"}, {"fields": {"scientific_name": "Anous minutus", "bird_class": 34, "common_name": "Black Noddy", "code": 3}, "pk": 373, "model": "birds.bird"}, {"fields": {"scientific_name": "Onychoprion fuscatus", "bird_class": 34, "common_name": "Sooty Tern", "code": 2}, "pk": 374, "model": "birds.bird"}, {"fields": {"scientific_name": "Onychoprion anaethetus", "bird_class": 34, "common_name": "Bridled Tern", "code": 2}, "pk": 375, "model": "birds.bird"}, {"fields": {"scientific_name": "Onychoprion aleuticus", "bird_class": 34, "common_name": "Aleutian Tern", "code": 2}, "pk": 376, "model": "birds.bird"}, {"fields": {"scientific_name": "Sternula antillarum", "bird_class": 34, "common_name": "Least Tern", "code": 1}, "pk": 377, "model": "birds.bird"}, {"fields": {"scientific_name": "Phaetusa simplex", "bird_class": 34, "common_name": "Large-billed Tern", "code": 5}, "pk": 378, "model": "birds.bird"}, {"fields": {"scientific_name": "Gelochelidon nilotica", "bird_class": 34, "common_name": "Gull-billed Tern", "code": 1}, "pk": 379, "model": "birds.bird"}, {"fields": {"scientific_name": "Hydroprogne caspia", "bird_class": 34, "common_name": "Caspian Tern", "code": 1}, "pk": 380, "model": "birds.bird"}, {"fields": {"scientific_name": "Chlidonias niger", "bird_class": 34, "common_name": "Black Tern", "code": 1}, "pk": 381, "model": "birds.bird"}, {"fields": {"scientific_name": "Chlidonias leucopterus", "bird_class": 34, "common_name": "White-winged Tern", "code": 4}, "pk": 382, "model": "birds.bird"}, {"fields": {"scientific_name": "Chlidonias hybrida", "bird_class": 34, "common_name": "Whiskered Tern", "code": 5}, "pk": 383, "model": "birds.bird"}, {"fields": {"scientific_name": "Sterna dougallii", "bird_class": 34, "common_name": "Roseate Tern", "code": 2}, "pk": 384, "model": "birds.bird"}, {"fields": {"scientific_name": "Sterna hirundo", "bird_class": 34, "common_name": "Common Tern", "code": 1}, "pk": 385, "model": "birds.bird"}, {"fields": {"scientific_name": "Sterna paradisaea", "bird_class": 34, "common_name": "Arctic Tern", "code": 1}, "pk": 386, "model": "birds.bird"}, {"fields": {"scientific_name": "Sterna forsteri", "bird_class": 34, "common_name": "Forster's Tern", "code": 1}, "pk": 387, "model": "birds.bird"}, {"fields": {"scientific_name": "Thalasseus maximus", "bird_class": 34, "common_name": "Royal Tern", "code": 1}, "pk": 388, "model": "birds.bird"}, {"fields": {"scientific_name": "Thalasseus sandvicensis", "bird_class": 34, "common_name": "Sandwich Tern", "code": 1}, "pk": 389, "model": "birds.bird"}, {"fields": {"scientific_name": "Thalasseus elegans", "bird_class": 34, "common_name": "Elegant Tern", "code": 1}, "pk": 390, "model": "birds.bird"}, {"fields": {"scientific_name": "Rynchops niger", "bird_class": 34, "common_name": "Black Skimmer", "code": 1}, "pk": 391, "model": "birds.bird"}, {"fields": {"scientific_name": "Stercorarius skua", "bird_class": 35, "common_name": "Great Skua", "code": 3}, "pk": 392, "model": "birds.bird"}, {"fields": {"scientific_name": "Stercorarius maccormicki", "bird_class": 35, "common_name": "South Polar Skua", "code": 2}, "pk": 393, "model": "birds.bird"}, {"fields": {"scientific_name": "Stercorarius pomarinus", "bird_class": 35, "common_name": "Pomarine Jaeger", "code": 1}, "pk": 394, "model": "birds.bird"}, {"fields": {"scientific_name": "Stercorarius parasiticus", "bird_class": 35, "common_name": "Parasitic Jaeger", "code": 1}, "pk": 395, "model": "birds.bird"}, {"fields": {"scientific_name": "Stercorarius longicaudus", "bird_class": 35, "common_name": "Long-tailed Jaeger", "code": 1}, "pk": 396, "model": "birds.bird"}, {"fields": {"scientific_name": "Alle alle", "bird_class": 36, "common_name": "Dovekie", "code": 2}, "pk": 397, "model": "birds.bird"}, {"fields": {"scientific_name": "Uria aalge", "bird_class": 36, "common_name": "Common Murre", "code": 1}, "pk": 398, "model": "birds.bird"}, {"fields": {"scientific_name": "Uria lomvia", "bird_class": 36, "common_name": "Thick-billed Murre", "code": 1}, "pk": 399, "model": "birds.bird"}, {"fields": {"scientific_name": "Alca torda", "bird_class": 36, "common_name": "Razorbill", "code": 1}, "pk": 400, "model": "birds.bird"}, {"fields": {"scientific_name": "Pinguinus impennis", "bird_class": 36, "common_name": "Great Auk", "code": 6}, "pk": 401, "model": "birds.bird"}, {"fields": {"scientific_name": "Cepphus grylle", "bird_class": 36, "common_name": "Black Guillemot", "code": 1}, "pk": 402, "model": "birds.bird"}, {"fields": {"scientific_name": "Cepphus columba", "bird_class": 36, "common_name": "Pigeon Guillemot", "code": 1}, "pk": 403, "model": "birds.bird"}, {"fields": {"scientific_name": "Brachyramphus perdix", "bird_class": 36, "common_name": "Long-billed Murrelet", "code": 3}, "pk": 404, "model": "birds.bird"}, {"fields": {"scientific_name": "Brachyramphus marmoratus", "bird_class": 36, "common_name": "Marbled Murrelet", "code": 1}, "pk": 405, "model": "birds.bird"}, {"fields": {"scientific_name": "Brachyramphus brevirostris", "bird_class": 36, "common_name": "Kittlitz's Murrelet", "code": 2}, "pk": 406, "model": "birds.bird"}, {"fields": {"scientific_name": "Synthliboramphus scrippsi", "bird_class": 36, "common_name": "Scripps\u2019s Murrelet", "code": 2}, "pk": 407, "model": "birds.bird"}, {"fields": {"scientific_name": "Synthliboramphus hypoleucus", "bird_class": 36, "common_name": "Guadalupe Murrelet", "code": 3}, "pk": 408, "model": "birds.bird"}, {"fields": {"scientific_name": "Synthliboramphus craveri", "bird_class": 36, "common_name": "Craveri's Murrelet", "code": 3}, "pk": 409, "model": "birds.bird"}, {"fields": {"scientific_name": "Synthliboramphus antiquus", "bird_class": 36, "common_name": "Ancient Murrelet", "code": 2}, "pk": 410, "model": "birds.bird"}, {"fields": {"scientific_name": "Ptychoramphus aleuticus", "bird_class": 36, "common_name": "Cassin's Auklet", "code": 1}, "pk": 411, "model": "birds.bird"}, {"fields": {"scientific_name": "Aethia psittacula", "bird_class": 36, "common_name": "Parakeet Auklet", "code": 2}, "pk": 412, "model": "birds.bird"}, {"fields": {"scientific_name": "Aethia pusilla", "bird_class": 36, "common_name": "Least Auklet", "code": 2}, "pk": 413, "model": "birds.bird"}, {"fields": {"scientific_name": "Aethia pygmaea", "bird_class": 36, "common_name": "Whiskered Auklet", "code": 2}, "pk": 414, "model": "birds.bird"}, {"fields": {"scientific_name": "Aethia cristatella", "bird_class": 36, "common_name": "Crested Auklet", "code": 2}, "pk": 415, "model": "birds.bird"}, {"fields": {"scientific_name": "Cerorhinca monocerata", "bird_class": 36, "common_name": "Rhinoceros Auklet", "code": 1}, "pk": 416, "model": "birds.bird"}, {"fields": {"scientific_name": "Fratercula arctica", "bird_class": 36, "common_name": "Atlantic Puffin", "code": 1}, "pk": 417, "model": "birds.bird"}, {"fields": {"scientific_name": "Fratercula corniculata", "bird_class": 36, "common_name": "Horned Puffin", "code": 1}, "pk": 418, "model": "birds.bird"}, {"fields": {"scientific_name": "Fratercula cirrhata", "bird_class": 36, "common_name": "Tufted Puffin", "code": 1}, "pk": 419, "model": "birds.bird"}, {"fields": {"scientific_name": "Columba livia", "bird_class": 37, "common_name": "Rock Pigeon", "code": 1}, "pk": 420, "model": "birds.bird"}, {"fields": {"scientific_name": "Patagioenas squamosa", "bird_class": 37, "common_name": "Scaly-naped Pigeon", "code": 5}, "pk": 421, "model": "birds.bird"}, {"fields": {"scientific_name": "Patagioenas leucocephala", "bird_class": 37, "common_name": "White-crowned Pigeon", "code": 2}, "pk": 422, "model": "birds.bird"}, {"fields": {"scientific_name": "Patagioenas flavirostris", "bird_class": 37, "common_name": "Red-billed Pigeon", "code": 2}, "pk": 423, "model": "birds.bird"}, {"fields": {"scientific_name": "Patagioenas fasciata", "bird_class": 37, "common_name": "Band-tailed Pigeon", "code": 1}, "pk": 424, "model": "birds.bird"}, {"fields": {"scientific_name": "Streptopelia orientalis", "bird_class": 37, "common_name": "Oriental Turtle-Dove", "code": 4}, "pk": 425, "model": "birds.bird"}, {"fields": {"scientific_name": "Streptopelia turtur", "bird_class": 37, "common_name": "European Turtle-Dove", "code": 5}, "pk": 426, "model": "birds.bird"}, {"fields": {"scientific_name": "Streptopelia decaocto", "bird_class": 37, "common_name": "Eurasian Collared-Dove", "code": 1}, "pk": 427, "model": "birds.bird"}, {"fields": {"scientific_name": "Streptopelia chinensis", "bird_class": 37, "common_name": "Spotted Dove", "code": 2}, "pk": 428, "model": "birds.bird"}, {"fields": {"scientific_name": "Zenaida asiatica", "bird_class": 37, "common_name": "White-winged Dove", "code": 1}, "pk": 429, "model": "birds.bird"}, {"fields": {"scientific_name": "Zenaida aurita", "bird_class": 37, "common_name": "Zenaida Dove", "code": 5}, "pk": 430, "model": "birds.bird"}, {"fields": {"scientific_name": "Zenaida macroura", "bird_class": 37, "common_name": "Mourning Dove", "code": 1}, "pk": 431, "model": "birds.bird"}, {"fields": {"scientific_name": "Ectopistes migratorius", "bird_class": 37, "common_name": "Passenger Pigeon", "code": 6}, "pk": 432, "model": "birds.bird"}, {"fields": {"scientific_name": "Columbina inca", "bird_class": 37, "common_name": "Inca Dove", "code": 1}, "pk": 433, "model": "birds.bird"}, {"fields": {"scientific_name": "Columbina passerina", "bird_class": 37, "common_name": "Common Ground-Dove", "code": 1}, "pk": 434, "model": "birds.bird"}, {"fields": {"scientific_name": "Columbina talpacoti", "bird_class": 37, "common_name": "Ruddy Ground-Dove", "code": 3}, "pk": 435, "model": "birds.bird"}, {"fields": {"scientific_name": "Leptotila verreauxi", "bird_class": 37, "common_name": "White-tipped Dove", "code": 2}, "pk": 436, "model": "birds.bird"}, {"fields": {"scientific_name": "Geotrygon chrysia", "bird_class": 37, "common_name": "Key West Quail-Dove", "code": 4}, "pk": 437, "model": "birds.bird"}, {"fields": {"scientific_name": "Geotrygon montana", "bird_class": 37, "common_name": "Ruddy Quail-Dove", "code": 5}, "pk": 438, "model": "birds.bird"}, {"fields": {"scientific_name": "Cuculus canorus", "bird_class": 38, "common_name": "Common Cuckoo", "code": 3}, "pk": 439, "model": "birds.bird"}, {"fields": {"scientific_name": "Cuculus optatus", "bird_class": 38, "common_name": "Oriental Cuckoo", "code": 4}, "pk": 440, "model": "birds.bird"}, {"fields": {"scientific_name": "Coccyzus americanus", "bird_class": 38, "common_name": "Yellow-billed Cuckoo", "code": 1}, "pk": 441, "model": "birds.bird"}, {"fields": {"scientific_name": "Coccyzus minor", "bird_class": 38, "common_name": "Mangrove Cuckoo", "code": 2}, "pk": 442, "model": "birds.bird"}, {"fields": {"scientific_name": "Coccyzus erythropthalmus", "bird_class": 38, "common_name": "Black-billed Cuckoo", "code": 1}, "pk": 443, "model": "birds.bird"}, {"fields": {"scientific_name": "Geococcyx californianus", "bird_class": 38, "common_name": "Greater Roadrunner", "code": 1}, "pk": 444, "model": "birds.bird"}, {"fields": {"scientific_name": "Crotophaga ani", "bird_class": 38, "common_name": "Smooth-billed Ani", "code": 3}, "pk": 445, "model": "birds.bird"}, {"fields": {"scientific_name": "Crotophaga sulcirostris", "bird_class": 38, "common_name": "Groove-billed Ani", "code": 2}, "pk": 446, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyto alba", "bird_class": 39, "common_name": "Barn Owl", "code": 1}, "pk": 447, "model": "birds.bird"}, {"fields": {"scientific_name": "Otus flammeolus", "bird_class": 40, "common_name": "Flammulated Owl", "code": 2}, "pk": 448, "model": "birds.bird"}, {"fields": {"scientific_name": "Otus sunia", "bird_class": 40, "common_name": "Oriental Scops-Owl", "code": 5}, "pk": 449, "model": "birds.bird"}, {"fields": {"scientific_name": "Megascops kennicottii", "bird_class": 40, "common_name": "Western Screech-Owl", "code": 1}, "pk": 450, "model": "birds.bird"}, {"fields": {"scientific_name": "Megascops asio", "bird_class": 40, "common_name": "Eastern Screech-Owl", "code": 1}, "pk": 451, "model": "birds.bird"}, {"fields": {"scientific_name": "Megascops trichopsis", "bird_class": 40, "common_name": "Whiskered Screech-Owl", "code": 2}, "pk": 452, "model": "birds.bird"}, {"fields": {"scientific_name": "Bubo virginianus", "bird_class": 40, "common_name": "Great Horned Owl", "code": 1}, "pk": 453, "model": "birds.bird"}, {"fields": {"scientific_name": "Bubo scandiacus", "bird_class": 40, "common_name": "Snowy Owl", "code": 2}, "pk": 454, "model": "birds.bird"}, {"fields": {"scientific_name": "Surnia ulula", "bird_class": 40, "common_name": "Northern Hawk Owl", "code": 2}, "pk": 455, "model": "birds.bird"}, {"fields": {"scientific_name": "Glaucidium gnoma", "bird_class": 40, "common_name": "Northern Pygmy-Owl", "code": 2}, "pk": 456, "model": "birds.bird"}, {"fields": {"scientific_name": "Glaucidium brasilianum", "bird_class": 40, "common_name": "Ferruginous Pygmy-Owl", "code": 3}, "pk": 457, "model": "birds.bird"}, {"fields": {"scientific_name": "Micrathene whitneyi", "bird_class": 40, "common_name": "Elf Owl", "code": 2}, "pk": 458, "model": "birds.bird"}, {"fields": {"scientific_name": "Athene cunicularia", "bird_class": 40, "common_name": "Burrowing Owl", "code": 1}, "pk": 459, "model": "birds.bird"}, {"fields": {"scientific_name": "Ciccaba virgata", "bird_class": 40, "common_name": "Mottled Owl", "code": 5}, "pk": 460, "model": "birds.bird"}, {"fields": {"scientific_name": "Strix occidentalis", "bird_class": 40, "common_name": "Spotted Owl", "code": 2}, "pk": 461, "model": "birds.bird"}, {"fields": {"scientific_name": "Strix varia", "bird_class": 40, "common_name": "Barred Owl", "code": 1}, "pk": 462, "model": "birds.bird"}, {"fields": {"scientific_name": "Strix nebulosa", "bird_class": 40, "common_name": "Great Gray Owl", "code": 2}, "pk": 463, "model": "birds.bird"}, {"fields": {"scientific_name": "Asio otus", "bird_class": 40, "common_name": "Long-eared Owl", "code": 2}, "pk": 464, "model": "birds.bird"}, {"fields": {"scientific_name": "Asio stygius", "bird_class": 40, "common_name": "Stygian Owl", "code": 5}, "pk": 465, "model": "birds.bird"}, {"fields": {"scientific_name": "Asio flammeus", "bird_class": 40, "common_name": "Short-eared Owl", "code": 1}, "pk": 466, "model": "birds.bird"}, {"fields": {"scientific_name": "Aegolius funereus", "bird_class": 40, "common_name": "Boreal Owl", "code": 2}, "pk": 467, "model": "birds.bird"}, {"fields": {"scientific_name": "Aegolius acadicus", "bird_class": 40, "common_name": "Northern Saw-whet Owl", "code": 2}, "pk": 468, "model": "birds.bird"}, {"fields": {"scientific_name": "Ninox scutulata", "bird_class": 40, "common_name": "Brown Hawk-Owl", "code": 5}, "pk": 469, "model": "birds.bird"}, {"fields": {"scientific_name": "Chordeiles acutipennis", "bird_class": 41, "common_name": "Lesser Nighthawk", "code": 1}, "pk": 470, "model": "birds.bird"}, {"fields": {"scientific_name": "Chordeiles minor", "bird_class": 41, "common_name": "Common Nighthawk", "code": 1}, "pk": 471, "model": "birds.bird"}, {"fields": {"scientific_name": "Chordeiles gundlachii", "bird_class": 41, "common_name": "Antillean Nighthawk", "code": 2}, "pk": 472, "model": "birds.bird"}, {"fields": {"scientific_name": "Nyctidromus albicollis", "bird_class": 41, "common_name": "Common Pauraque", "code": 2}, "pk": 473, "model": "birds.bird"}, {"fields": {"scientific_name": "Phalaenoptilus nuttallii", "bird_class": 41, "common_name": "Common Poorwill", "code": 1}, "pk": 474, "model": "birds.bird"}, {"fields": {"scientific_name": "Antrostomus carolinensis", "bird_class": 41, "common_name": "Chuck-will's-widow", "code": 1}, "pk": 475, "model": "birds.bird"}, {"fields": {"scientific_name": "Antrostomus ridgwayi", "bird_class": 41, "common_name": "Buff-collared Nightjar", "code": 3}, "pk": 476, "model": "birds.bird"}, {"fields": {"scientific_name": "Antrostomus vociferus", "bird_class": 41, "common_name": "Eastern Whip-poor-will", "code": 1}, "pk": 477, "model": "birds.bird"}, {"fields": {"scientific_name": "Antrostomus arizonae", "bird_class": 41, "common_name": "Mexican Whip-poor-will", "code": 1}, "pk": 478, "model": "birds.bird"}, {"fields": {"scientific_name": "Caprimulgus indicus", "bird_class": 41, "common_name": "Gray Nightjar", "code": 5}, "pk": 479, "model": "birds.bird"}, {"fields": {"scientific_name": "Cypseloides niger", "bird_class": 42, "common_name": "Black Swift", "code": 2}, "pk": 480, "model": "birds.bird"}, {"fields": {"scientific_name": "Streptoprocne zonaris", "bird_class": 42, "common_name": "White-collared Swift", "code": 4}, "pk": 481, "model": "birds.bird"}, {"fields": {"scientific_name": "Chaetura pelagica", "bird_class": 42, "common_name": "Chimney Swift", "code": 1}, "pk": 482, "model": "birds.bird"}, {"fields": {"scientific_name": "Chaetura vauxi", "bird_class": 42, "common_name": "Vaux's Swift", "code": 1}, "pk": 483, "model": "birds.bird"}, {"fields": {"scientific_name": "Hirundapus caudacutus", "bird_class": 42, "common_name": "White-throated Needletail", "code": 5}, "pk": 484, "model": "birds.bird"}, {"fields": {"scientific_name": "Apus apus", "bird_class": 42, "common_name": "Common Swift", "code": 5}, "pk": 485, "model": "birds.bird"}, {"fields": {"scientific_name": "Apus pacificus", "bird_class": 42, "common_name": "Fork-tailed Swift", "code": 4}, "pk": 486, "model": "birds.bird"}, {"fields": {"scientific_name": "Aeronautes saxatalis", "bird_class": 42, "common_name": "White-throated Swift", "code": 1}, "pk": 487, "model": "birds.bird"}, {"fields": {"scientific_name": "Tachornis phoenicobia", "bird_class": 42, "common_name": "Antillean Palm-Swift", "code": 5}, "pk": 488, "model": "birds.bird"}, {"fields": {"scientific_name": "Colibri thalassinus", "bird_class": 43, "common_name": "Green Violetear", "code": 3}, "pk": 489, "model": "birds.bird"}, {"fields": {"scientific_name": "Anthracothorax prevostii", "bird_class": 43, "common_name": "Green-breasted Mango", "code": 4}, "pk": 490, "model": "birds.bird"}, {"fields": {"scientific_name": "Eugenes fulgens", "bird_class": 43, "common_name": "Magnificent Hummingbird", "code": 2}, "pk": 491, "model": "birds.bird"}, {"fields": {"scientific_name": "Heliomaster constantii", "bird_class": 43, "common_name": "Plain-capped Starthroat", "code": 4}, "pk": 492, "model": "birds.bird"}, {"fields": {"scientific_name": "Lampornis clemenciae", "bird_class": 43, "common_name": "Blue-throated Hummingbird", "code": 2}, "pk": 493, "model": "birds.bird"}, {"fields": {"scientific_name": "Calliphlox evelynae", "bird_class": 43, "common_name": "Bahama Woodstar", "code": 5}, "pk": 494, "model": "birds.bird"}, {"fields": {"scientific_name": "Calothorax lucifer", "bird_class": 43, "common_name": "Lucifer Hummingbird", "code": 2}, "pk": 495, "model": "birds.bird"}, {"fields": {"scientific_name": "Archilochus colubris", "bird_class": 43, "common_name": "Ruby-throated Hummingbird", "code": 1}, "pk": 496, "model": "birds.bird"}, {"fields": {"scientific_name": "Archilochus alexandri", "bird_class": 43, "common_name": "Black-chinned Hummingbird", "code": 1}, "pk": 497, "model": "birds.bird"}, {"fields": {"scientific_name": "Calypte anna", "bird_class": 43, "common_name": "Anna's Hummingbird", "code": 1}, "pk": 498, "model": "birds.bird"}, {"fields": {"scientific_name": "Calypte costae", "bird_class": 43, "common_name": "Costa's Hummingbird", "code": 1}, "pk": 499, "model": "birds.bird"}, {"fields": {"scientific_name": "Atthis heloisa", "bird_class": 43, "common_name": "Bumblebee Hummingbird", "code": 5}, "pk": 500, "model": "birds.bird"}, {"fields": {"scientific_name": "Selasphorus platycercus", "bird_class": 43, "common_name": "Broad-tailed Hummingbird", "code": 1}, "pk": 501, "model": "birds.bird"}, {"fields": {"scientific_name": "Selasphorus rufus", "bird_class": 43, "common_name": "Rufous Hummingbird", "code": 1}, "pk": 502, "model": "birds.bird"}, {"fields": {"scientific_name": "Selasphorus sasin", "bird_class": 43, "common_name": "Allen's Hummingbird", "code": 1}, "pk": 503, "model": "birds.bird"}, {"fields": {"scientific_name": "Selasphorus calliope", "bird_class": 43, "common_name": "Calliope Hummingbird", "code": 1}, "pk": 504, "model": "birds.bird"}, {"fields": {"scientific_name": "Cynanthus latirostris", "bird_class": 43, "common_name": "Broad-billed Hummingbird", "code": 2}, "pk": 505, "model": "birds.bird"}, {"fields": {"scientific_name": "Amazilia beryllina", "bird_class": 43, "common_name": "Berylline Hummingbird", "code": 3}, "pk": 506, "model": "birds.bird"}, {"fields": {"scientific_name": "Amazilia yucatanensis", "bird_class": 43, "common_name": "Buff-bellied Hummingbird", "code": 2}, "pk": 507, "model": "birds.bird"}, {"fields": {"scientific_name": "Amazilia rutila", "bird_class": 43, "common_name": "Cinnamon Hummingbird", "code": 5}, "pk": 508, "model": "birds.bird"}, {"fields": {"scientific_name": "Amazilia violiceps", "bird_class": 43, "common_name": "Violet-crowned Hummingbird", "code": 2}, "pk": 509, "model": "birds.bird"}, {"fields": {"scientific_name": "Hylocharis leucotis", "bird_class": 43, "common_name": "White-eared Hummingbird", "code": 3}, "pk": 510, "model": "birds.bird"}, {"fields": {"scientific_name": "Hylocharis xantusii", "bird_class": 43, "common_name": "Xantus's Hummingbird", "code": 5}, "pk": 511, "model": "birds.bird"}, {"fields": {"scientific_name": "Trogon elegans", "bird_class": 44, "common_name": "Elegant Trogon", "code": 2}, "pk": 512, "model": "birds.bird"}, {"fields": {"scientific_name": "Euptilotis neoxenus", "bird_class": 44, "common_name": "Eared Quetzal", "code": 4}, "pk": 513, "model": "birds.bird"}, {"fields": {"scientific_name": "Upupa epops", "bird_class": 45, "common_name": "Eurasian Hoopoe", "code": 5}, "pk": 514, "model": "birds.bird"}, {"fields": {"scientific_name": "Megaceryle torquata", "bird_class": 46, "common_name": "Ringed Kingfisher", "code": 2}, "pk": 515, "model": "birds.bird"}, {"fields": {"scientific_name": "Megaceryle alcyon", "bird_class": 46, "common_name": "Belted Kingfisher", "code": 1}, "pk": 516, "model": "birds.bird"}, {"fields": {"scientific_name": "Chloroceryle amazona", "bird_class": 46, "common_name": "Amazon Kingfisher", "code": 5}, "pk": 517, "model": "birds.bird"}, {"fields": {"scientific_name": "Chloroceryle americana", "bird_class": 46, "common_name": "Green Kingfisher", "code": 2}, "pk": 518, "model": "birds.bird"}, {"fields": {"scientific_name": "Jynx torquilla", "bird_class": 47, "common_name": "Eurasian Wryneck", "code": 5}, "pk": 519, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanerpes lewis", "bird_class": 47, "common_name": "Lewis's Woodpecker", "code": 1}, "pk": 520, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanerpes erythrocephalus", "bird_class": 47, "common_name": "Red-headed Woodpecker", "code": 1}, "pk": 521, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanerpes formicivorus", "bird_class": 47, "common_name": "Acorn Woodpecker", "code": 1}, "pk": 522, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanerpes uropygialis", "bird_class": 47, "common_name": "Gila Woodpecker", "code": 1}, "pk": 523, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanerpes aurifrons", "bird_class": 47, "common_name": "Golden-fronted Woodpecker", "code": 1}, "pk": 524, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanerpes carolinus", "bird_class": 47, "common_name": "Red-bellied Woodpecker", "code": 1}, "pk": 525, "model": "birds.bird"}, {"fields": {"scientific_name": "Sphyrapicus thyroideus", "bird_class": 47, "common_name": "Williamson's Sapsucker", "code": 1}, "pk": 526, "model": "birds.bird"}, {"fields": {"scientific_name": "Sphyrapicus varius", "bird_class": 47, "common_name": "Yellow-bellied Sapsucker", "code": 1}, "pk": 527, "model": "birds.bird"}, {"fields": {"scientific_name": "Sphyrapicus nuchalis", "bird_class": 47, "common_name": "Red-naped Sapsucker", "code": 1}, "pk": 528, "model": "birds.bird"}, {"fields": {"scientific_name": "Sphyrapicus ruber", "bird_class": 47, "common_name": "Red-breasted Sapsucker", "code": 1}, "pk": 529, "model": "birds.bird"}, {"fields": {"scientific_name": "Dendrocopos major", "bird_class": 47, "common_name": "Great Spotted Woodpecker", "code": 4}, "pk": 530, "model": "birds.bird"}, {"fields": {"scientific_name": "Picoides scalaris", "bird_class": 47, "common_name": "Ladder-backed Woodpecker", "code": 1}, "pk": 531, "model": "birds.bird"}, {"fields": {"scientific_name": "Picoides nuttallii", "bird_class": 47, "common_name": "Nuttall's Woodpecker", "code": 1}, "pk": 532, "model": "birds.bird"}, {"fields": {"scientific_name": "Picoides pubescens", "bird_class": 47, "common_name": "Downy Woodpecker", "code": 1}, "pk": 533, "model": "birds.bird"}, {"fields": {"scientific_name": "Picoides villosus", "bird_class": 47, "common_name": "Hairy Woodpecker", "code": 1}, "pk": 534, "model": "birds.bird"}, {"fields": {"scientific_name": "Picoides arizonae", "bird_class": 47, "common_name": "Arizona Woodpecker", "code": 2}, "pk": 535, "model": "birds.bird"}, {"fields": {"scientific_name": "Picoides borealis", "bird_class": 47, "common_name": "Red-cockaded Woodpecker", "code": 2}, "pk": 536, "model": "birds.bird"}, {"fields": {"scientific_name": "Picoides albolarvatus", "bird_class": 47, "common_name": "White-headed Woodpecker", "code": 1}, "pk": 537, "model": "birds.bird"}, {"fields": {"scientific_name": "Picoides dorsalis", "bird_class": 47, "common_name": "American Three-toed Woodpecker", "code": 2}, "pk": 538, "model": "birds.bird"}, {"fields": {"scientific_name": "Picoides arcticus", "bird_class": 47, "common_name": "Black-backed Woodpecker", "code": 2}, "pk": 539, "model": "birds.bird"}, {"fields": {"scientific_name": "Colaptes auratus", "bird_class": 47, "common_name": "Northern Flicker", "code": 1}, "pk": 540, "model": "birds.bird"}, {"fields": {"scientific_name": "Colaptes chrysoides", "bird_class": 47, "common_name": "Gilded Flicker", "code": 2}, "pk": 541, "model": "birds.bird"}, {"fields": {"scientific_name": "Dryocopus pileatus", "bird_class": 47, "common_name": "Pileated Woodpecker", "code": 1}, "pk": 542, "model": "birds.bird"}, {"fields": {"scientific_name": "Campephilus principalis", "bird_class": 47, "common_name": "Ivory-billed Woodpecker", "code": 6}, "pk": 543, "model": "birds.bird"}, {"fields": {"scientific_name": "Micrastur semitorquatus", "bird_class": 48, "common_name": "Collared Forest-Falcon", "code": 5}, "pk": 544, "model": "birds.bird"}, {"fields": {"scientific_name": "Caracara cheriway", "bird_class": 48, "common_name": "Crested Caracara", "code": 1}, "pk": 545, "model": "birds.bird"}, {"fields": {"scientific_name": "Falco tinnunculus", "bird_class": 48, "common_name": "Eurasian Kestrel", "code": 4}, "pk": 546, "model": "birds.bird"}, {"fields": {"scientific_name": "Falco sparverius", "bird_class": 48, "common_name": "American Kestrel", "code": 1}, "pk": 547, "model": "birds.bird"}, {"fields": {"scientific_name": "Falco vespertinus", "bird_class": 48, "common_name": "Red-footed Falcon", "code": 5}, "pk": 548, "model": "birds.bird"}, {"fields": {"scientific_name": "Falco columbarius", "bird_class": 48, "common_name": "Merlin", "code": 1}, "pk": 549, "model": "birds.bird"}, {"fields": {"scientific_name": "Falco subbuteo", "bird_class": 48, "common_name": "Eurasian Hobby", "code": 4}, "pk": 550, "model": "birds.bird"}, {"fields": {"scientific_name": "Falco femoralis", "bird_class": 48, "common_name": "Aplomado Falcon", "code": 3}, "pk": 551, "model": "birds.bird"}, {"fields": {"scientific_name": "Falco rusticolus", "bird_class": 48, "common_name": "Gyrfalcon", "code": 2}, "pk": 552, "model": "birds.bird"}, {"fields": {"scientific_name": "Falco peregrinus", "bird_class": 48, "common_name": "Peregrine Falcon", "code": 1}, "pk": 553, "model": "birds.bird"}, {"fields": {"scientific_name": "Falco mexicanus", "bird_class": 48, "common_name": "Prairie Falcon", "code": 1}, "pk": 554, "model": "birds.bird"}, {"fields": {"scientific_name": "Melopsittacus undulatus", "bird_class": 49, "common_name": "Budgerigar", "code": 3}, "pk": 555, "model": "birds.bird"}, {"fields": {"scientific_name": "Agapornis roseicollis", "bird_class": 49, "common_name": "Rosy-faced Lovebird", "code": 2}, "pk": 556, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiopsitta monachus", "bird_class": 49, "common_name": "Monk Parakeet", "code": 2}, "pk": 557, "model": "birds.bird"}, {"fields": {"scientific_name": "Conuropsis carolinensis", "bird_class": 49, "common_name": "Carolina Parakeet", "code": 6}, "pk": 558, "model": "birds.bird"}, {"fields": {"scientific_name": "Aratinga holochlora", "bird_class": 49, "common_name": "Green Parakeet", "code": 2}, "pk": 559, "model": "birds.bird"}, {"fields": {"scientific_name": "Nandayus nenday", "bird_class": 49, "common_name": "Nanday Parakeet", "code": 2}, "pk": 560, "model": "birds.bird"}, {"fields": {"scientific_name": "Rhynchopsitta pachyrhyncha", "bird_class": 49, "common_name": "Thick-billed Parrot", "code": 6}, "pk": 561, "model": "birds.bird"}, {"fields": {"scientific_name": "Brotogeris versicolurus", "bird_class": 49, "common_name": "White-winged Parakeet", "code": 2}, "pk": 562, "model": "birds.bird"}, {"fields": {"scientific_name": "Amazona viridigenalis", "bird_class": 49, "common_name": "Red-crowned Parrot", "code": 2}, "pk": 563, "model": "birds.bird"}, {"fields": {"scientific_name": "Camptostoma imberbe", "bird_class": 50, "common_name": "Northern Beardless-Tyrannulet", "code": 2}, "pk": 564, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiopagis viridicata", "bird_class": 50, "common_name": "Greenish Elaenia", "code": 5}, "pk": 565, "model": "birds.bird"}, {"fields": {"scientific_name": "Elaenia albiceps", "bird_class": 50, "common_name": "White-crested Elaenia", "code": 5}, "pk": 566, "model": "birds.bird"}, {"fields": {"scientific_name": "Mitrephanes phaeocercus", "bird_class": 50, "common_name": "Tufted Flycatcher", "code": 5}, "pk": 567, "model": "birds.bird"}, {"fields": {"scientific_name": "Contopus cooperi", "bird_class": 50, "common_name": "Olive-sided Flycatcher", "code": 1}, "pk": 568, "model": "birds.bird"}, {"fields": {"scientific_name": "Contopus pertinax", "bird_class": 50, "common_name": "Greater Pewee", "code": 2}, "pk": 569, "model": "birds.bird"}, {"fields": {"scientific_name": "Contopus sordidulus", "bird_class": 50, "common_name": "Western Wood-Pewee", "code": 1}, "pk": 570, "model": "birds.bird"}, {"fields": {"scientific_name": "Contopus virens", "bird_class": 50, "common_name": "Eastern Wood-Pewee", "code": 1}, "pk": 571, "model": "birds.bird"}, {"fields": {"scientific_name": "Contopus caribaeus", "bird_class": 50, "common_name": "Cuban Pewee", "code": 5}, "pk": 572, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax flaviventris", "bird_class": 50, "common_name": "Yellow-bellied Flycatcher", "code": 1}, "pk": 573, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax virescens", "bird_class": 50, "common_name": "Acadian Flycatcher", "code": 1}, "pk": 574, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax alnorum", "bird_class": 50, "common_name": "Alder Flycatcher", "code": 1}, "pk": 575, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax traillii", "bird_class": 50, "common_name": "Willow Flycatcher", "code": 1}, "pk": 576, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax minimus", "bird_class": 50, "common_name": "Least Flycatcher", "code": 1}, "pk": 577, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax hammondii", "bird_class": 50, "common_name": "Hammond's Flycatcher", "code": 1}, "pk": 578, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax wrightii", "bird_class": 50, "common_name": "Gray Flycatcher", "code": 1}, "pk": 579, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax oberholseri", "bird_class": 50, "common_name": "Dusky Flycatcher", "code": 1}, "pk": 580, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax difficilis", "bird_class": 50, "common_name": "Pacific-slope Flycatcher", "code": 1}, "pk": 581, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax occidentalis", "bird_class": 50, "common_name": "Cordilleran Flycatcher", "code": 1}, "pk": 582, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonax fulvifrons", "bird_class": 50, "common_name": "Buff-breasted Flycatcher", "code": 2}, "pk": 583, "model": "birds.bird"}, {"fields": {"scientific_name": "Sayornis nigricans", "bird_class": 50, "common_name": "Black Phoebe", "code": 1}, "pk": 584, "model": "birds.bird"}, {"fields": {"scientific_name": "Sayornis phoebe", "bird_class": 50, "common_name": "Eastern Phoebe", "code": 1}, "pk": 585, "model": "birds.bird"}, {"fields": {"scientific_name": "Sayornis saya", "bird_class": 50, "common_name": "Say's Phoebe", "code": 1}, "pk": 586, "model": "birds.bird"}, {"fields": {"scientific_name": "Pyrocephalus rubinus", "bird_class": 50, "common_name": "Vermilion Flycatcher", "code": 1}, "pk": 587, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiarchus tuberculifer", "bird_class": 50, "common_name": "Dusky-capped Flycatcher", "code": 2}, "pk": 588, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiarchus cinerascens", "bird_class": 50, "common_name": "Ash-throated Flycatcher", "code": 1}, "pk": 589, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiarchus nuttingi", "bird_class": 50, "common_name": "Nutting's Flycatcher", "code": 5}, "pk": 590, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiarchus crinitus", "bird_class": 50, "common_name": "Great Crested Flycatcher", "code": 1}, "pk": 591, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiarchus tyrannulus", "bird_class": 50, "common_name": "Brown-crested Flycatcher", "code": 1}, "pk": 592, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiarchus sagrae", "bird_class": 50, "common_name": "La Sagra's Flycatcher", "code": 3}, "pk": 593, "model": "birds.bird"}, {"fields": {"scientific_name": "Pitangus sulphuratus", "bird_class": 50, "common_name": "Great Kiskadee", "code": 2}, "pk": 594, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiozetetes similis", "bird_class": 50, "common_name": "Social Flycatcher", "code": 5}, "pk": 595, "model": "birds.bird"}, {"fields": {"scientific_name": "Myiodynastes luteiventris", "bird_class": 50, "common_name": "Sulphur-bellied Flycatcher", "code": 2}, "pk": 596, "model": "birds.bird"}, {"fields": {"scientific_name": "Legatus leucophaius", "bird_class": 50, "common_name": "Piratic Flycatcher", "code": 4}, "pk": 597, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonomus varius", "bird_class": 50, "common_name": "Variegated Flycatcher", "code": 5}, "pk": 598, "model": "birds.bird"}, {"fields": {"scientific_name": "Empidonomus aurantioatrocristatus", "bird_class": 50, "common_name": "Crowned Slaty Flycatcher", "code": 5}, "pk": 599, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus melancholicus", "bird_class": 50, "common_name": "Tropical Kingbird", "code": 2}, "pk": 600, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus couchii", "bird_class": 50, "common_name": "Couch's Kingbird", "code": 2}, "pk": 601, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus vociferans", "bird_class": 50, "common_name": "Cassin's Kingbird", "code": 1}, "pk": 602, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus crassirostris", "bird_class": 50, "common_name": "Thick-billed Kingbird", "code": 2}, "pk": 603, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus verticalis", "bird_class": 50, "common_name": "Western Kingbird", "code": 1}, "pk": 604, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus tyrannus", "bird_class": 50, "common_name": "Eastern Kingbird", "code": 1}, "pk": 605, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus dominicensis", "bird_class": 50, "common_name": "Gray Kingbird", "code": 2}, "pk": 606, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus caudifasciatus", "bird_class": 50, "common_name": "Loggerhead Kingbird", "code": 5}, "pk": 607, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus forficatus", "bird_class": 50, "common_name": "Scissor-tailed Flycatcher", "code": 1}, "pk": 608, "model": "birds.bird"}, {"fields": {"scientific_name": "Tyrannus savana", "bird_class": 50, "common_name": "Fork-tailed Flycatcher", "code": 3}, "pk": 609, "model": "birds.bird"}, {"fields": {"scientific_name": "Tityra semifasciata", "bird_class": 51, "common_name": "Masked Tityra", "code": 5}, "pk": 610, "model": "birds.bird"}, {"fields": {"scientific_name": "Pachyramphus major", "bird_class": 51, "common_name": "Gray-collared Becard", "code": 5}, "pk": 611, "model": "birds.bird"}, {"fields": {"scientific_name": "Pachyramphus aglaiae", "bird_class": 51, "common_name": "Rose-throated Becard", "code": 3}, "pk": 612, "model": "birds.bird"}, {"fields": {"scientific_name": "Lanius cristatus", "bird_class": 52, "common_name": "Brown Shrike", "code": 4}, "pk": 613, "model": "birds.bird"}, {"fields": {"scientific_name": "Lanius ludovicianus", "bird_class": 52, "common_name": "Loggerhead Shrike", "code": 1}, "pk": 614, "model": "birds.bird"}, {"fields": {"scientific_name": "Lanius excubitor", "bird_class": 52, "common_name": "Northern Shrike", "code": 1}, "pk": 615, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo griseus", "bird_class": 53, "common_name": "White-eyed Vireo", "code": 1}, "pk": 616, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo crassirostris", "bird_class": 53, "common_name": "Thick-billed Vireo", "code": 4}, "pk": 617, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo bellii", "bird_class": 53, "common_name": "Bell's Vireo", "code": 1}, "pk": 618, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo atricapilla", "bird_class": 53, "common_name": "Black-capped Vireo", "code": 2}, "pk": 619, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo vicinior", "bird_class": 53, "common_name": "Gray Vireo", "code": 2}, "pk": 620, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo flavifrons", "bird_class": 53, "common_name": "Yellow-throated Vireo", "code": 1}, "pk": 621, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo plumbeus", "bird_class": 53, "common_name": "Plumbeous Vireo", "code": 1}, "pk": 622, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo cassinii", "bird_class": 53, "common_name": "Cassin's Vireo", "code": 1}, "pk": 623, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo solitarius", "bird_class": 53, "common_name": "Blue-headed Vireo", "code": 1}, "pk": 624, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo huttoni", "bird_class": 53, "common_name": "Hutton's Vireo", "code": 1}, "pk": 625, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo gilvus", "bird_class": 53, "common_name": "Warbling Vireo", "code": 1}, "pk": 626, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo philadelphicus", "bird_class": 53, "common_name": "Philadelphia Vireo", "code": 1}, "pk": 627, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo olivaceus", "bird_class": 53, "common_name": "Red-eyed Vireo", "code": 1}, "pk": 628, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo flavoviridis", "bird_class": 53, "common_name": "Yellow-green Vireo", "code": 3}, "pk": 629, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo altiloquus", "bird_class": 53, "common_name": "Black-whiskered Vireo", "code": 2}, "pk": 630, "model": "birds.bird"}, {"fields": {"scientific_name": "Vireo magister", "bird_class": 53, "common_name": "Yucatan Vireo", "code": 5}, "pk": 631, "model": "birds.bird"}, {"fields": {"scientific_name": "Perisoreus canadensis", "bird_class": 54, "common_name": "Gray Jay", "code": 1}, "pk": 632, "model": "birds.bird"}, {"fields": {"scientific_name": "Psilorhinus morio", "bird_class": 54, "common_name": "Brown Jay", "code": 3}, "pk": 633, "model": "birds.bird"}, {"fields": {"scientific_name": "Cyanocorax yncas", "bird_class": 54, "common_name": "Green Jay", "code": 2}, "pk": 634, "model": "birds.bird"}, {"fields": {"scientific_name": "Gymnorhinus cyanocephalus", "bird_class": 54, "common_name": "Pinyon Jay", "code": 1}, "pk": 635, "model": "birds.bird"}, {"fields": {"scientific_name": "Cyanocitta stelleri", "bird_class": 54, "common_name": "Steller's Jay", "code": 1}, "pk": 636, "model": "birds.bird"}, {"fields": {"scientific_name": "Cyanocitta cristata", "bird_class": 54, "common_name": "Blue Jay", "code": 1}, "pk": 637, "model": "birds.bird"}, {"fields": {"scientific_name": "Aphelocoma coerulescens", "bird_class": 54, "common_name": "Florida Scrub-Jay", "code": 2}, "pk": 638, "model": "birds.bird"}, {"fields": {"scientific_name": "Aphelocoma insularis", "bird_class": 54, "common_name": "Island Scrub-Jay", "code": 2}, "pk": 639, "model": "birds.bird"}, {"fields": {"scientific_name": "Aphelocoma californica", "bird_class": 54, "common_name": "Western Scrub-Jay", "code": 1}, "pk": 640, "model": "birds.bird"}, {"fields": {"scientific_name": "Aphelocoma wollweberi", "bird_class": 54, "common_name": "Mexican Jay", "code": 2}, "pk": 641, "model": "birds.bird"}, {"fields": {"scientific_name": "Nucifraga columbiana", "bird_class": 54, "common_name": "Clark's Nutcracker", "code": 1}, "pk": 642, "model": "birds.bird"}, {"fields": {"scientific_name": "Pica hudsonia", "bird_class": 54, "common_name": "Black-billed Magpie", "code": 1}, "pk": 643, "model": "birds.bird"}, {"fields": {"scientific_name": "Pica nuttalli", "bird_class": 54, "common_name": "Yellow-billed Magpie", "code": 2}, "pk": 644, "model": "birds.bird"}, {"fields": {"scientific_name": "Corvus monedula", "bird_class": 54, "common_name": "Eurasian Jackdaw", "code": 4}, "pk": 645, "model": "birds.bird"}, {"fields": {"scientific_name": "Corvus brachyrhynchos", "bird_class": 54, "common_name": "American Crow", "code": 1}, "pk": 646, "model": "birds.bird"}, {"fields": {"scientific_name": "Corvus caurinus", "bird_class": 54, "common_name": "Northwestern Crow", "code": 1}, "pk": 647, "model": "birds.bird"}, {"fields": {"scientific_name": "Corvus imparatus", "bird_class": 54, "common_name": "Tamaulipas Crow", "code": 3}, "pk": 648, "model": "birds.bird"}, {"fields": {"scientific_name": "Corvus ossifragus", "bird_class": 54, "common_name": "Fish Crow", "code": 1}, "pk": 649, "model": "birds.bird"}, {"fields": {"scientific_name": "Corvus cryptoleucus", "bird_class": 54, "common_name": "Chihuahuan Raven", "code": 1}, "pk": 650, "model": "birds.bird"}, {"fields": {"scientific_name": "Corvus corax", "bird_class": 54, "common_name": "Common Raven", "code": 1}, "pk": 651, "model": "birds.bird"}, {"fields": {"scientific_name": "Alauda arvensis", "bird_class": 55, "common_name": "Sky Lark", "code": 3}, "pk": 652, "model": "birds.bird"}, {"fields": {"scientific_name": "Eremophila alpestris", "bird_class": 55, "common_name": "Horned Lark", "code": 1}, "pk": 653, "model": "birds.bird"}, {"fields": {"scientific_name": "Progne subis", "bird_class": 56, "common_name": "Purple Martin", "code": 1}, "pk": 654, "model": "birds.bird"}, {"fields": {"scientific_name": "Progne cryptoleuca", "bird_class": 56, "common_name": "Cuban Martin", "code": 5}, "pk": 655, "model": "birds.bird"}, {"fields": {"scientific_name": "Progne chalybea", "bird_class": 56, "common_name": "Gray-breasted Martin", "code": 5}, "pk": 656, "model": "birds.bird"}, {"fields": {"scientific_name": "Progne elegans", "bird_class": 56, "common_name": "Southern Martin", "code": 5}, "pk": 657, "model": "birds.bird"}, {"fields": {"scientific_name": "Progne tapera", "bird_class": 56, "common_name": "Brown-chested Martin", "code": 5}, "pk": 658, "model": "birds.bird"}, {"fields": {"scientific_name": "Tachycineta bicolor", "bird_class": 56, "common_name": "Tree Swallow", "code": 1}, "pk": 659, "model": "birds.bird"}, {"fields": {"scientific_name": "Tachycineta albilinea", "bird_class": 56, "common_name": "Mangrove Swallow", "code": 5}, "pk": 660, "model": "birds.bird"}, {"fields": {"scientific_name": "Tachycineta thalassina", "bird_class": 56, "common_name": "Violet-green Swallow", "code": 1}, "pk": 661, "model": "birds.bird"}, {"fields": {"scientific_name": "Tachycineta cyaneoviridis", "bird_class": 56, "common_name": "Bahama Swallow", "code": 4}, "pk": 662, "model": "birds.bird"}, {"fields": {"scientific_name": "Stelgidopteryx serripennis", "bird_class": 56, "common_name": "Northern Rough-winged Swallow", "code": 1}, "pk": 663, "model": "birds.bird"}, {"fields": {"scientific_name": "Riparia riparia", "bird_class": 56, "common_name": "Bank Swallow", "code": 1}, "pk": 664, "model": "birds.bird"}, {"fields": {"scientific_name": "Petrochelidon pyrrhonota", "bird_class": 56, "common_name": "Cliff Swallow", "code": 1}, "pk": 665, "model": "birds.bird"}, {"fields": {"scientific_name": "Petrochelidon fulva", "bird_class": 56, "common_name": "Cave Swallow", "code": 1}, "pk": 666, "model": "birds.bird"}, {"fields": {"scientific_name": "Hirundo rustica", "bird_class": 56, "common_name": "Barn Swallow", "code": 1}, "pk": 667, "model": "birds.bird"}, {"fields": {"scientific_name": "Delichon urbicum", "bird_class": 56, "common_name": "Common House-Martin", "code": 4}, "pk": 668, "model": "birds.bird"}, {"fields": {"scientific_name": "Poecile carolinensis", "bird_class": 57, "common_name": "Carolina Chickadee", "code": 1}, "pk": 669, "model": "birds.bird"}, {"fields": {"scientific_name": "Poecile atricapillus", "bird_class": 57, "common_name": "Black-capped Chickadee", "code": 1}, "pk": 670, "model": "birds.bird"}, {"fields": {"scientific_name": "Poecile gambeli", "bird_class": 57, "common_name": "Mountain Chickadee", "code": 1}, "pk": 671, "model": "birds.bird"}, {"fields": {"scientific_name": "Poecile sclateri", "bird_class": 57, "common_name": "Mexican Chickadee", "code": 2}, "pk": 672, "model": "birds.bird"}, {"fields": {"scientific_name": "Poecile rufescens", "bird_class": 57, "common_name": "Chestnut-backed Chickadee", "code": 1}, "pk": 673, "model": "birds.bird"}, {"fields": {"scientific_name": "Poecile hudsonicus", "bird_class": 57, "common_name": "Boreal Chickadee", "code": 1}, "pk": 674, "model": "birds.bird"}, {"fields": {"scientific_name": "Poecile cinctus", "bird_class": 57, "common_name": "Gray-headed Chickadee", "code": 3}, "pk": 675, "model": "birds.bird"}, {"fields": {"scientific_name": "Baeolophus wollweberi", "bird_class": 57, "common_name": "Bridled Titmouse", "code": 2}, "pk": 676, "model": "birds.bird"}, {"fields": {"scientific_name": "Baeolophus inornatus", "bird_class": 57, "common_name": "Oak Titmouse", "code": 1}, "pk": 677, "model": "birds.bird"}, {"fields": {"scientific_name": "Baeolophus ridgwayi", "bird_class": 57, "common_name": "Juniper Titmouse", "code": 1}, "pk": 678, "model": "birds.bird"}, {"fields": {"scientific_name": "Baeolophus bicolor", "bird_class": 57, "common_name": "Tufted Titmouse", "code": 1}, "pk": 679, "model": "birds.bird"}, {"fields": {"scientific_name": "Baeolophus atricristatus", "bird_class": 57, "common_name": "Black-crested Titmouse", "code": 2}, "pk": 680, "model": "birds.bird"}, {"fields": {"scientific_name": "Auriparus flaviceps", "bird_class": 58, "common_name": "Verdin", "code": 1}, "pk": 681, "model": "birds.bird"}, {"fields": {"scientific_name": "Psaltriparus minimus", "bird_class": 59, "common_name": "Bushtit", "code": 1}, "pk": 682, "model": "birds.bird"}, {"fields": {"scientific_name": "Sitta canadensis", "bird_class": 60, "common_name": "Red-breasted Nuthatch", "code": 1}, "pk": 683, "model": "birds.bird"}, {"fields": {"scientific_name": "Sitta carolinensis", "bird_class": 60, "common_name": "White-breasted Nuthatch", "code": 1}, "pk": 684, "model": "birds.bird"}, {"fields": {"scientific_name": "Sitta pygmaea", "bird_class": 60, "common_name": "Pygmy Nuthatch", "code": 1}, "pk": 685, "model": "birds.bird"}, {"fields": {"scientific_name": "Sitta pusilla", "bird_class": 60, "common_name": "Brown-headed Nuthatch", "code": 1}, "pk": 686, "model": "birds.bird"}, {"fields": {"scientific_name": "Certhia americana", "bird_class": 61, "common_name": "Brown Creeper", "code": 1}, "pk": 687, "model": "birds.bird"}, {"fields": {"scientific_name": "Salpinctes obsoletus", "bird_class": 62, "common_name": "Rock Wren", "code": 1}, "pk": 688, "model": "birds.bird"}, {"fields": {"scientific_name": "Catherpes mexicanus", "bird_class": 62, "common_name": "Canyon Wren", "code": 1}, "pk": 689, "model": "birds.bird"}, {"fields": {"scientific_name": "Troglodytes aedon", "bird_class": 62, "common_name": "House Wren", "code": 1}, "pk": 690, "model": "birds.bird"}, {"fields": {"scientific_name": "Troglodytes pacificus", "bird_class": 62, "common_name": "Pacific Wren", "code": 1}, "pk": 691, "model": "birds.bird"}, {"fields": {"scientific_name": "Troglodytes hiemalis", "bird_class": 62, "common_name": "Winter Wren", "code": 1}, "pk": 692, "model": "birds.bird"}, {"fields": {"scientific_name": "Cistothorus platensis", "bird_class": 62, "common_name": "Sedge Wren", "code": 1}, "pk": 693, "model": "birds.bird"}, {"fields": {"scientific_name": "Cistothorus palustris", "bird_class": 62, "common_name": "Marsh Wren", "code": 1}, "pk": 694, "model": "birds.bird"}, {"fields": {"scientific_name": "Thryothorus ludovicianus", "bird_class": 62, "common_name": "Carolina Wren", "code": 1}, "pk": 695, "model": "birds.bird"}, {"fields": {"scientific_name": "Thryomanes bewickii", "bird_class": 62, "common_name": "Bewick's Wren", "code": 1}, "pk": 696, "model": "birds.bird"}, {"fields": {"scientific_name": "Campylorhynchus brunneicapillus", "bird_class": 62, "common_name": "Cactus Wren", "code": 1}, "pk": 697, "model": "birds.bird"}, {"fields": {"scientific_name": "Thryophilus sinaloa", "bird_class": 62, "common_name": "Sinaloa Wren", "code": 5}, "pk": 698, "model": "birds.bird"}, {"fields": {"scientific_name": "Polioptila caerulea", "bird_class": 63, "common_name": "Blue-gray Gnatcatcher", "code": 1}, "pk": 699, "model": "birds.bird"}, {"fields": {"scientific_name": "Polioptila californica", "bird_class": 63, "common_name": "California Gnatcatcher", "code": 2}, "pk": 700, "model": "birds.bird"}, {"fields": {"scientific_name": "Polioptila melanura", "bird_class": 63, "common_name": "Black-tailed Gnatcatcher", "code": 1}, "pk": 701, "model": "birds.bird"}, {"fields": {"scientific_name": "Polioptila nigriceps", "bird_class": 63, "common_name": "Black-capped Gnatcatcher", "code": 3}, "pk": 702, "model": "birds.bird"}, {"fields": {"scientific_name": "Cinclus mexicanus", "bird_class": 64, "common_name": "American Dipper", "code": 1}, "pk": 703, "model": "birds.bird"}, {"fields": {"scientific_name": "Pycnonotus jocosus", "bird_class": 65, "common_name": "Red-whiskered Bulbul", "code": 2}, "pk": 704, "model": "birds.bird"}, {"fields": {"scientific_name": "Regulus satrapa", "bird_class": 66, "common_name": "Golden-crowned Kinglet", "code": 1}, "pk": 705, "model": "birds.bird"}, {"fields": {"scientific_name": "Regulus calendula", "bird_class": 66, "common_name": "Ruby-crowned Kinglet", "code": 1}, "pk": 706, "model": "birds.bird"}, {"fields": {"scientific_name": "Phylloscopus trochilus", "bird_class": 67, "common_name": "Willow Warbler", "code": 5}, "pk": 707, "model": "birds.bird"}, {"fields": {"scientific_name": "Phylloscopus sibilatrix", "bird_class": 67, "common_name": "Wood Warbler", "code": 5}, "pk": 708, "model": "birds.bird"}, {"fields": {"scientific_name": "Phylloscopus fuscatus", "bird_class": 67, "common_name": "Dusky Warbler", "code": 4}, "pk": 709, "model": "birds.bird"}, {"fields": {"scientific_name": "Phylloscopus proregulus", "bird_class": 67, "common_name": "Pallas's Leaf-Warbler", "code": 5}, "pk": 710, "model": "birds.bird"}, {"fields": {"scientific_name": "Phylloscopus inornatus", "bird_class": 67, "common_name": "Yellow-browed Warbler", "code": 4}, "pk": 711, "model": "birds.bird"}, {"fields": {"scientific_name": "Phylloscopus borealis", "bird_class": 67, "common_name": "Arctic Warbler", "code": 2}, "pk": 712, "model": "birds.bird"}, {"fields": {"scientific_name": "Sylvia curruca", "bird_class": 68, "common_name": "Lesser Whitethroat", "code": 5}, "pk": 713, "model": "birds.bird"}, {"fields": {"scientific_name": "Chamaea fasciata", "bird_class": 68, "common_name": "Wrentit", "code": 1}, "pk": 714, "model": "birds.bird"}, {"fields": {"scientific_name": "Acrocephalus schoenobaenus", "bird_class": 69, "common_name": "Sedge Warbler", "code": 5}, "pk": 715, "model": "birds.bird"}, {"fields": {"scientific_name": "Locustella ochotensis", "bird_class": 70, "common_name": "Middendorff's Grasshopper-Warbler", "code": 4}, "pk": 716, "model": "birds.bird"}, {"fields": {"scientific_name": "Locustella lanceolata", "bird_class": 70, "common_name": "Lanceolated Warbler", "code": 5}, "pk": 717, "model": "birds.bird"}, {"fields": {"scientific_name": "Muscicapa griseisticta", "bird_class": 71, "common_name": "Gray-streaked Flycatcher", "code": 4}, "pk": 718, "model": "birds.bird"}, {"fields": {"scientific_name": "Muscicapa dauurica", "bird_class": 71, "common_name": "Asian Brown Flycatcher", "code": 5}, "pk": 719, "model": "birds.bird"}, {"fields": {"scientific_name": "Muscicapa striata", "bird_class": 71, "common_name": "Spotted Flycatcher", "code": 5}, "pk": 720, "model": "birds.bird"}, {"fields": {"scientific_name": "Muscicapa sibirica", "bird_class": 71, "common_name": "Dark-sided Flycatcher", "code": 4}, "pk": 721, "model": "birds.bird"}, {"fields": {"scientific_name": "Luscinia sibilans", "bird_class": 71, "common_name": "Rufous-tailed Robin", "code": 5}, "pk": 722, "model": "birds.bird"}, {"fields": {"scientific_name": "Luscinia calliope", "bird_class": 71, "common_name": "Siberian Rubythroat", "code": 3}, "pk": 723, "model": "birds.bird"}, {"fields": {"scientific_name": "Luscinia svecica", "bird_class": 71, "common_name": "Bluethroat", "code": 2}, "pk": 724, "model": "birds.bird"}, {"fields": {"scientific_name": "Luscinia cyane", "bird_class": 71, "common_name": "Siberian Blue Robin", "code": 5}, "pk": 725, "model": "birds.bird"}, {"fields": {"scientific_name": "Tarsiger cyanurus", "bird_class": 71, "common_name": "Red-flanked Bluetail", "code": 4}, "pk": 726, "model": "birds.bird"}, {"fields": {"scientific_name": "Ficedula narcissina", "bird_class": 71, "common_name": "Narcissus Flycatcher", "code": 5}, "pk": 727, "model": "birds.bird"}, {"fields": {"scientific_name": "Ficedula mugimaki", "bird_class": 71, "common_name": "Mugimaki Flycatcher", "code": 5}, "pk": 728, "model": "birds.bird"}, {"fields": {"scientific_name": "Ficedula albicilla", "bird_class": 71, "common_name": "Taiga Flycatcher", "code": 4}, "pk": 729, "model": "birds.bird"}, {"fields": {"scientific_name": "Oenanthe oenanthe", "bird_class": 71, "common_name": "Northern Wheatear", "code": 2}, "pk": 730, "model": "birds.bird"}, {"fields": {"scientific_name": "Saxicola torquatus", "bird_class": 71, "common_name": "Stonechat", "code": 4}, "pk": 731, "model": "birds.bird"}, {"fields": {"scientific_name": "Sialia sialis", "bird_class": 72, "common_name": "Eastern Bluebird", "code": 1}, "pk": 732, "model": "birds.bird"}, {"fields": {"scientific_name": "Sialia mexicana", "bird_class": 72, "common_name": "Western Bluebird", "code": 1}, "pk": 733, "model": "birds.bird"}, {"fields": {"scientific_name": "Sialia currucoides", "bird_class": 72, "common_name": "Mountain Bluebird", "code": 1}, "pk": 734, "model": "birds.bird"}, {"fields": {"scientific_name": "Myadestes townsendi", "bird_class": 72, "common_name": "Townsend's Solitaire", "code": 1}, "pk": 735, "model": "birds.bird"}, {"fields": {"scientific_name": "Myadestes occidentalis", "bird_class": 72, "common_name": "Brown-backed Solitaire", "code": 5}, "pk": 736, "model": "birds.bird"}, {"fields": {"scientific_name": "Catharus aurantiirostris", "bird_class": 72, "common_name": "Orange-billed Nightingale-Thrush", "code": 5}, "pk": 737, "model": "birds.bird"}, {"fields": {"scientific_name": "Catharus mexicanus", "bird_class": 72, "common_name": "Black-headed Nightingale-Thrush", "code": 5}, "pk": 738, "model": "birds.bird"}, {"fields": {"scientific_name": "Catharus fuscescens", "bird_class": 72, "common_name": "Veery", "code": 1}, "pk": 739, "model": "birds.bird"}, {"fields": {"scientific_name": "Catharus minimus", "bird_class": 72, "common_name": "Gray-cheeked Thrush", "code": 1}, "pk": 740, "model": "birds.bird"}, {"fields": {"scientific_name": "Catharus bicknelli", "bird_class": 72, "common_name": "Bicknell's Thrush", "code": 2}, "pk": 741, "model": "birds.bird"}, {"fields": {"scientific_name": "Catharus ustulatus", "bird_class": 72, "common_name": "Swainson's Thrush", "code": 1}, "pk": 742, "model": "birds.bird"}, {"fields": {"scientific_name": "Catharus guttatus", "bird_class": 72, "common_name": "Hermit Thrush", "code": 1}, "pk": 743, "model": "birds.bird"}, {"fields": {"scientific_name": "Hylocichla mustelina", "bird_class": 72, "common_name": "Wood Thrush", "code": 1}, "pk": 744, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus merula", "bird_class": 72, "common_name": "Eurasian Blackbird", "code": 5}, "pk": 745, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus obscurus", "bird_class": 72, "common_name": "Eyebrowed Thrush", "code": 3}, "pk": 746, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus naumanni", "bird_class": 72, "common_name": "Dusky Thrush", "code": 4}, "pk": 747, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus pilaris", "bird_class": 72, "common_name": "Fieldfare", "code": 4}, "pk": 748, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus iliacus", "bird_class": 72, "common_name": "Redwing", "code": 4}, "pk": 749, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus philomelos", "bird_class": 72, "common_name": "Song Thrush", "code": 5}, "pk": 750, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus grayi", "bird_class": 72, "common_name": "Clay-colored Thrush", "code": 3}, "pk": 751, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus assimilis", "bird_class": 72, "common_name": "White-throated Thrush", "code": 4}, "pk": 752, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus rufopalliatus", "bird_class": 72, "common_name": "Rufous-backed Robin", "code": 3}, "pk": 753, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus migratorius", "bird_class": 72, "common_name": "American Robin", "code": 1}, "pk": 754, "model": "birds.bird"}, {"fields": {"scientific_name": "Turdus plumbeus", "bird_class": 72, "common_name": "Red-legged Thrush", "code": 5}, "pk": 755, "model": "birds.bird"}, {"fields": {"scientific_name": "Ixoreus naevius", "bird_class": 72, "common_name": "Varied Thrush", "code": 1}, "pk": 756, "model": "birds.bird"}, {"fields": {"scientific_name": "Ridgwayia pinicola", "bird_class": 72, "common_name": "Aztec Thrush", "code": 4}, "pk": 757, "model": "birds.bird"}, {"fields": {"scientific_name": "Dumetella carolinensis", "bird_class": 73, "common_name": "Gray Catbird", "code": 1}, "pk": 758, "model": "birds.bird"}, {"fields": {"scientific_name": "Mimus polyglottos", "bird_class": 73, "common_name": "Northern Mockingbird", "code": 1}, "pk": 759, "model": "birds.bird"}, {"fields": {"scientific_name": "Mimus gundlachii", "bird_class": 73, "common_name": "Bahama Mockingbird", "code": 4}, "pk": 760, "model": "birds.bird"}, {"fields": {"scientific_name": "Oreoscoptes montanus", "bird_class": 73, "common_name": "Sage Thrasher", "code": 1}, "pk": 761, "model": "birds.bird"}, {"fields": {"scientific_name": "Toxostoma rufum", "bird_class": 73, "common_name": "Brown Thrasher", "code": 1}, "pk": 762, "model": "birds.bird"}, {"fields": {"scientific_name": "Toxostoma longirostre", "bird_class": 73, "common_name": "Long-billed Thrasher", "code": 2}, "pk": 763, "model": "birds.bird"}, {"fields": {"scientific_name": "Toxostoma bendirei", "bird_class": 73, "common_name": "Bendire's Thrasher", "code": 2}, "pk": 764, "model": "birds.bird"}, {"fields": {"scientific_name": "Toxostoma curvirostre", "bird_class": 73, "common_name": "Curve-billed Thrasher", "code": 1}, "pk": 765, "model": "birds.bird"}, {"fields": {"scientific_name": "Toxostoma redivivum", "bird_class": 73, "common_name": "California Thrasher", "code": 2}, "pk": 766, "model": "birds.bird"}, {"fields": {"scientific_name": "Toxostoma crissale", "bird_class": 73, "common_name": "Crissal Thrasher", "code": 2}, "pk": 767, "model": "birds.bird"}, {"fields": {"scientific_name": "Toxostoma lecontei", "bird_class": 73, "common_name": "Le Conte's Thrasher", "code": 2}, "pk": 768, "model": "birds.bird"}, {"fields": {"scientific_name": "Melanotis caerulescens", "bird_class": 73, "common_name": "Blue Mockingbird", "code": 5}, "pk": 769, "model": "birds.bird"}, {"fields": {"scientific_name": "Sturnus vulgaris", "bird_class": 74, "common_name": "European Starling", "code": 1}, "pk": 770, "model": "birds.bird"}, {"fields": {"scientific_name": "Acridotheres tristis", "bird_class": 74, "common_name": "Common Myna", "code": 2}, "pk": 771, "model": "birds.bird"}, {"fields": {"scientific_name": "Prunella montanella", "bird_class": 75, "common_name": "Siberian Accentor", "code": 4}, "pk": 772, "model": "birds.bird"}, {"fields": {"scientific_name": "Motacilla tschutschensis", "bird_class": 76, "common_name": "Eastern Yellow Wagtail", "code": 2}, "pk": 773, "model": "birds.bird"}, {"fields": {"scientific_name": "Motacilla citreola", "bird_class": 76, "common_name": "Citrine Wagtail", "code": 5}, "pk": 774, "model": "birds.bird"}, {"fields": {"scientific_name": "Motacilla cinerea", "bird_class": 76, "common_name": "Gray Wagtail", "code": 4}, "pk": 775, "model": "birds.bird"}, {"fields": {"scientific_name": "Motacilla alba", "bird_class": 76, "common_name": "White Wagtail", "code": 3}, "pk": 776, "model": "birds.bird"}, {"fields": {"scientific_name": "Anthus trivialis", "bird_class": 76, "common_name": "Tree Pipit", "code": 5}, "pk": 777, "model": "birds.bird"}, {"fields": {"scientific_name": "Anthus hodgsoni", "bird_class": 76, "common_name": "Olive-backed Pipit", "code": 3}, "pk": 778, "model": "birds.bird"}, {"fields": {"scientific_name": "Anthus gustavi", "bird_class": 76, "common_name": "Pechora Pipit", "code": 4}, "pk": 779, "model": "birds.bird"}, {"fields": {"scientific_name": "Anthus cervinus", "bird_class": 76, "common_name": "Red-throated Pipit", "code": 3}, "pk": 780, "model": "birds.bird"}, {"fields": {"scientific_name": "Anthus rubescens", "bird_class": 76, "common_name": "American Pipit", "code": 1}, "pk": 781, "model": "birds.bird"}, {"fields": {"scientific_name": "Anthus spragueii", "bird_class": 76, "common_name": "Sprague's Pipit", "code": 2}, "pk": 782, "model": "birds.bird"}, {"fields": {"scientific_name": "Bombycilla garrulus", "bird_class": 77, "common_name": "Bohemian Waxwing", "code": 2}, "pk": 783, "model": "birds.bird"}, {"fields": {"scientific_name": "Bombycilla cedrorum", "bird_class": 77, "common_name": "Cedar Waxwing", "code": 1}, "pk": 784, "model": "birds.bird"}, {"fields": {"scientific_name": "Ptilogonys cinereus", "bird_class": 78, "common_name": "Gray Silky-flycatcher", "code": 5}, "pk": 785, "model": "birds.bird"}, {"fields": {"scientific_name": "Phainopepla nitens", "bird_class": 78, "common_name": "Phainopepla", "code": 1}, "pk": 786, "model": "birds.bird"}, {"fields": {"scientific_name": "Peucedramus taeniatus", "bird_class": 79, "common_name": "Olive Warbler", "code": 2}, "pk": 787, "model": "birds.bird"}, {"fields": {"scientific_name": "Calcarius lapponicus", "bird_class": 80, "common_name": "Lapland Longspur", "code": 1}, "pk": 788, "model": "birds.bird"}, {"fields": {"scientific_name": "Calcarius ornatus", "bird_class": 80, "common_name": "Chestnut-collared Longspur", "code": 1}, "pk": 789, "model": "birds.bird"}, {"fields": {"scientific_name": "Calcarius pictus", "bird_class": 80, "common_name": "Smith's Longspur", "code": 2}, "pk": 790, "model": "birds.bird"}, {"fields": {"scientific_name": "Rhynchophanes mccownii", "bird_class": 80, "common_name": "McCown's Longspur", "code": 2}, "pk": 791, "model": "birds.bird"}, {"fields": {"scientific_name": "Plectrophenax nivalis", "bird_class": 80, "common_name": "Snow Bunting", "code": 1}, "pk": 792, "model": "birds.bird"}, {"fields": {"scientific_name": "Plectrophenax hyperboreus", "bird_class": 80, "common_name": "McKay's Bunting", "code": 2}, "pk": 793, "model": "birds.bird"}, {"fields": {"scientific_name": "Seiurus aurocapilla", "bird_class": 81, "common_name": "Ovenbird", "code": 1}, "pk": 794, "model": "birds.bird"}, {"fields": {"scientific_name": "Helmitheros vermivorum", "bird_class": 81, "common_name": "Worm-eating Warbler", "code": 1}, "pk": 795, "model": "birds.bird"}, {"fields": {"scientific_name": "Parkesia motacilla", "bird_class": 81, "common_name": "Louisiana Waterthrush", "code": 1}, "pk": 796, "model": "birds.bird"}, {"fields": {"scientific_name": "Parkesia noveboracensis", "bird_class": 81, "common_name": "Northern Waterthrush", "code": 1}, "pk": 797, "model": "birds.bird"}, {"fields": {"scientific_name": "Vermivora bachmanii", "bird_class": 81, "common_name": "Bachman's Warbler", "code": 6}, "pk": 798, "model": "birds.bird"}, {"fields": {"scientific_name": "Vermivora chrysoptera", "bird_class": 81, "common_name": "Golden-winged Warbler", "code": 2}, "pk": 799, "model": "birds.bird"}, {"fields": {"scientific_name": "Vermivora cyanoptera", "bird_class": 81, "common_name": "Blue-winged Warbler", "code": 1}, "pk": 800, "model": "birds.bird"}, {"fields": {"scientific_name": "Mniotilta varia", "bird_class": 81, "common_name": "Black-and-white Warbler", "code": 1}, "pk": 801, "model": "birds.bird"}, {"fields": {"scientific_name": "Protonotaria citrea", "bird_class": 81, "common_name": "Prothonotary Warbler", "code": 1}, "pk": 802, "model": "birds.bird"}, {"fields": {"scientific_name": "Limnothlypis swainsonii", "bird_class": 81, "common_name": "Swainson's Warbler", "code": 2}, "pk": 803, "model": "birds.bird"}, {"fields": {"scientific_name": "Oreothlypis superciliosa", "bird_class": 81, "common_name": "Crescent-chested Warbler", "code": 4}, "pk": 804, "model": "birds.bird"}, {"fields": {"scientific_name": "Oreothlypis peregrina", "bird_class": 81, "common_name": "Tennessee Warbler", "code": 1}, "pk": 805, "model": "birds.bird"}, {"fields": {"scientific_name": "Oreothlypis celata", "bird_class": 81, "common_name": "Orange-crowned Warbler", "code": 1}, "pk": 806, "model": "birds.bird"}, {"fields": {"scientific_name": "Oreothlypis crissalis", "bird_class": 81, "common_name": "Colima Warbler", "code": 1}, "pk": 807, "model": "birds.bird"}, {"fields": {"scientific_name": "Oreothlypis luciae", "bird_class": 81, "common_name": "Lucy's Warbler", "code": 1}, "pk": 808, "model": "birds.bird"}, {"fields": {"scientific_name": "Oreothlypis ruficapilla", "bird_class": 81, "common_name": "Nashville Warbler", "code": 1}, "pk": 809, "model": "birds.bird"}, {"fields": {"scientific_name": "Oreothlypis virginiae", "bird_class": 81, "common_name": "Virginia\u2019s Warbler", "code": 1}, "pk": 810, "model": "birds.bird"}, {"fields": {"scientific_name": "Oporornis agilis", "bird_class": 81, "common_name": "Connecticut Warbler", "code": 2}, "pk": 811, "model": "birds.bird"}, {"fields": {"scientific_name": "Geothlypis poliocephala", "bird_class": 81, "common_name": "Gray-crowned Yellowthroat", "code": 4}, "pk": 812, "model": "birds.bird"}, {"fields": {"scientific_name": "Geothlypis tolmiei", "bird_class": 81, "common_name": "MacGillivray's Warbler", "code": 1}, "pk": 813, "model": "birds.bird"}, {"fields": {"scientific_name": "Geothlypis philadelphia", "bird_class": 81, "common_name": "Mourning Warbler", "code": 1}, "pk": 814, "model": "birds.bird"}, {"fields": {"scientific_name": "Geothlypis formosa", "bird_class": 81, "common_name": "Kentucky Warbler", "code": 1}, "pk": 815, "model": "birds.bird"}, {"fields": {"scientific_name": "Geothlypis trichas", "bird_class": 81, "common_name": "Common Yellowthroat", "code": 1}, "pk": 816, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga citrina", "bird_class": 81, "common_name": "Hooded Warbler", "code": 1}, "pk": 817, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga ruticilla", "bird_class": 81, "common_name": "American Redstart", "code": 1}, "pk": 818, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga kirtlandii", "bird_class": 81, "common_name": "Kirtland's Warbler", "code": 2}, "pk": 819, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga tigrina", "bird_class": 81, "common_name": "Cape May Warbler", "code": 1}, "pk": 820, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga cerulea", "bird_class": 81, "common_name": "Cerulean Warbler", "code": 2}, "pk": 821, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga americana", "bird_class": 81, "common_name": "Northern Parula", "code": 1}, "pk": 822, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga pitiayumi", "bird_class": 81, "common_name": "Tropical Parula", "code": 3}, "pk": 823, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga magnolia", "bird_class": 81, "common_name": "Magnolia Warbler", "code": 1}, "pk": 824, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga castanea", "bird_class": 81, "common_name": "Bay-breasted Warbler", "code": 1}, "pk": 825, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga fusca", "bird_class": 81, "common_name": "Blackburnian Warbler", "code": 1}, "pk": 826, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga petechia", "bird_class": 81, "common_name": "Yellow Warbler", "code": 1}, "pk": 827, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga pensylvanica", "bird_class": 81, "common_name": "Chestnut-sided Warbler", "code": 1}, "pk": 828, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga striata", "bird_class": 81, "common_name": "Blackpoll Warbler", "code": 1}, "pk": 829, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga caerulescens", "bird_class": 81, "common_name": "Black-throated Blue Warbler", "code": 1}, "pk": 830, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga palmarum", "bird_class": 81, "common_name": "Palm Warbler", "code": 1}, "pk": 831, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga pinus", "bird_class": 81, "common_name": "Pine Warbler", "code": 1}, "pk": 832, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga coronata", "bird_class": 81, "common_name": "Yellow-rumped Warbler", "code": 1}, "pk": 833, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga dominica", "bird_class": 81, "common_name": "Yellow-throated Warbler", "code": 1}, "pk": 834, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga discolor", "bird_class": 81, "common_name": "Prairie Warbler", "code": 1}, "pk": 835, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga graciae", "bird_class": 81, "common_name": "Grace's Warbler", "code": 1}, "pk": 836, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga nigrescens", "bird_class": 81, "common_name": "Black-throated Gray Warbler", "code": 1}, "pk": 837, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga townsendi", "bird_class": 81, "common_name": "Townsend's Warbler", "code": 1}, "pk": 838, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga occidentalis", "bird_class": 81, "common_name": "Hermit Warbler", "code": 1}, "pk": 839, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga chrysoparia", "bird_class": 81, "common_name": "Golden-cheeked Warbler", "code": 2}, "pk": 840, "model": "birds.bird"}, {"fields": {"scientific_name": "Setophaga virens", "bird_class": 81, "common_name": "Black-throated Green Warbler", "code": 1}, "pk": 841, "model": "birds.bird"}, {"fields": {"scientific_name": "Basileuterus lachrymosus", "bird_class": 81, "common_name": "Fan-tailed Warbler", "code": 4}, "pk": 842, "model": "birds.bird"}, {"fields": {"scientific_name": "Basileuterus rufifrons", "bird_class": 81, "common_name": "Rufous-capped Warbler", "code": 3}, "pk": 843, "model": "birds.bird"}, {"fields": {"scientific_name": "Basileuterus culicivorus", "bird_class": 81, "common_name": "Golden-crowned Warbler", "code": 4}, "pk": 844, "model": "birds.bird"}, {"fields": {"scientific_name": "Cardellina canadensis", "bird_class": 81, "common_name": "Canada Warbler", "code": 1}, "pk": 845, "model": "birds.bird"}, {"fields": {"scientific_name": "Cardellina pusilla", "bird_class": 81, "common_name": "Wilson's Warbler", "code": 1}, "pk": 846, "model": "birds.bird"}, {"fields": {"scientific_name": "Cardellina rubrifrons", "bird_class": 81, "common_name": "Red-faced Warbler", "code": 2}, "pk": 847, "model": "birds.bird"}, {"fields": {"scientific_name": "Myioborus pictus", "bird_class": 81, "common_name": "Painted Redstart", "code": 2}, "pk": 848, "model": "birds.bird"}, {"fields": {"scientific_name": "Myioborus miniatus", "bird_class": 81, "common_name": "Slate-throated Redstart", "code": 4}, "pk": 849, "model": "birds.bird"}, {"fields": {"scientific_name": "Icteria virens", "bird_class": 81, "common_name": "Yellow-breasted Chat", "code": 1}, "pk": 850, "model": "birds.bird"}, {"fields": {"scientific_name": "Coereba flaveola", "bird_class": 82, "common_name": "Bananaquit", "code": 4}, "pk": 851, "model": "birds.bird"}, {"fields": {"scientific_name": "Spindalis zena", "bird_class": 83, "common_name": "Western Spindalis", "code": 3}, "pk": 852, "model": "birds.bird"}, {"fields": {"scientific_name": "Sporophila torqueola", "bird_class": 84, "common_name": "White-collared Seedeater", "code": 3}, "pk": 853, "model": "birds.bird"}, {"fields": {"scientific_name": "Tiaris olivaceus", "bird_class": 84, "common_name": "Yellow-faced Grassquit", "code": 4}, "pk": 854, "model": "birds.bird"}, {"fields": {"scientific_name": "Tiaris bicolor", "bird_class": 84, "common_name": "Black-faced Grassquit", "code": 4}, "pk": 855, "model": "birds.bird"}, {"fields": {"scientific_name": "Arremonops rufivirgatus", "bird_class": 84, "common_name": "Olive Sparrow", "code": 2}, "pk": 856, "model": "birds.bird"}, {"fields": {"scientific_name": "Pipilo chlorurus", "bird_class": 84, "common_name": "Green-tailed Towhee", "code": 1}, "pk": 857, "model": "birds.bird"}, {"fields": {"scientific_name": "Pipilo maculatus", "bird_class": 84, "common_name": "Spotted Towhee", "code": 1}, "pk": 858, "model": "birds.bird"}, {"fields": {"scientific_name": "Pipilo erythrophthalmus", "bird_class": 84, "common_name": "Eastern Towhee", "code": 1}, "pk": 859, "model": "birds.bird"}, {"fields": {"scientific_name": "Aimophila ruficeps", "bird_class": 84, "common_name": "Rufous-crowned Sparrow", "code": 1}, "pk": 860, "model": "birds.bird"}, {"fields": {"scientific_name": "Melozone fusca", "bird_class": 84, "common_name": "Canyon Towhee", "code": 1}, "pk": 861, "model": "birds.bird"}, {"fields": {"scientific_name": "Melozone crissalis", "bird_class": 84, "common_name": "California Towhee", "code": 1}, "pk": 862, "model": "birds.bird"}, {"fields": {"scientific_name": "Melozone aberti", "bird_class": 84, "common_name": "Abert's Towhee", "code": 1}, "pk": 863, "model": "birds.bird"}, {"fields": {"scientific_name": "Peucaea carpalis", "bird_class": 84, "common_name": "Rufous-winged Sparrow", "code": 2}, "pk": 864, "model": "birds.bird"}, {"fields": {"scientific_name": "Peucaea botterii", "bird_class": 84, "common_name": "Botteri's Sparrow", "code": 2}, "pk": 865, "model": "birds.bird"}, {"fields": {"scientific_name": "Peucaea cassinii", "bird_class": 84, "common_name": "Cassin's Sparrow", "code": 1}, "pk": 866, "model": "birds.bird"}, {"fields": {"scientific_name": "Peucaea aestivalis", "bird_class": 84, "common_name": "Bachman's Sparrow", "code": 2}, "pk": 867, "model": "birds.bird"}, {"fields": {"scientific_name": "Spizella arborea", "bird_class": 84, "common_name": "American Tree Sparrow", "code": 1}, "pk": 868, "model": "birds.bird"}, {"fields": {"scientific_name": "Spizella passerina", "bird_class": 84, "common_name": "Chipping Sparrow", "code": 1}, "pk": 869, "model": "birds.bird"}, {"fields": {"scientific_name": "Spizella pallida", "bird_class": 84, "common_name": "Clay-colored Sparrow", "code": 1}, "pk": 870, "model": "birds.bird"}, {"fields": {"scientific_name": "Spizella breweri", "bird_class": 84, "common_name": "Brewer's Sparrow", "code": 1}, "pk": 871, "model": "birds.bird"}, {"fields": {"scientific_name": "Spizella pusilla", "bird_class": 84, "common_name": "Field Sparrow", "code": 1}, "pk": 872, "model": "birds.bird"}, {"fields": {"scientific_name": "Spizella wortheni", "bird_class": 84, "common_name": "Worthen's Sparrow", "code": 5}, "pk": 873, "model": "birds.bird"}, {"fields": {"scientific_name": "Spizella atrogularis", "bird_class": 84, "common_name": "Black-chinned Sparrow", "code": 1}, "pk": 874, "model": "birds.bird"}, {"fields": {"scientific_name": "Pooecetes gramineus", "bird_class": 84, "common_name": "Vesper Sparrow", "code": 1}, "pk": 875, "model": "birds.bird"}, {"fields": {"scientific_name": "Chondestes grammacus", "bird_class": 84, "common_name": "Lark Sparrow", "code": 1}, "pk": 876, "model": "birds.bird"}, {"fields": {"scientific_name": "Amphispiza quinquestriata", "bird_class": 84, "common_name": "Five-striped Sparrow", "code": 3}, "pk": 877, "model": "birds.bird"}, {"fields": {"scientific_name": "Amphispiza bilineata", "bird_class": 84, "common_name": "Black-throated Sparrow", "code": 1}, "pk": 878, "model": "birds.bird"}, {"fields": {"scientific_name": "Artemisiospiza belli", "bird_class": 84, "common_name": "Sage Sparrow", "code": 1}, "pk": 879, "model": "birds.bird"}, {"fields": {"scientific_name": "Calamospiza melanocorys", "bird_class": 84, "common_name": "Lark Bunting", "code": 1}, "pk": 880, "model": "birds.bird"}, {"fields": {"scientific_name": "Passerculus sandwichensis", "bird_class": 84, "common_name": "Savannah Sparrow", "code": 1}, "pk": 881, "model": "birds.bird"}, {"fields": {"scientific_name": "Ammodramus savannarum", "bird_class": 84, "common_name": "Grasshopper Sparrow", "code": 1}, "pk": 882, "model": "birds.bird"}, {"fields": {"scientific_name": "Ammodramus bairdii", "bird_class": 84, "common_name": "Baird's Sparrow", "code": 2}, "pk": 883, "model": "birds.bird"}, {"fields": {"scientific_name": "Ammodramus henslowii", "bird_class": 84, "common_name": "Henslow's Sparrow", "code": 2}, "pk": 884, "model": "birds.bird"}, {"fields": {"scientific_name": "Ammodramus leconteii", "bird_class": 84, "common_name": "Le Conte's Sparrow", "code": 1}, "pk": 885, "model": "birds.bird"}, {"fields": {"scientific_name": "Ammodramus nelsoni", "bird_class": 84, "common_name": "Nelson's Sparrow", "code": 1}, "pk": 886, "model": "birds.bird"}, {"fields": {"scientific_name": "Ammodramus caudacutus", "bird_class": 84, "common_name": "Saltmarsh Sparrow", "code": 1}, "pk": 887, "model": "birds.bird"}, {"fields": {"scientific_name": "Ammodramus maritimus", "bird_class": 84, "common_name": "Seaside Sparrow", "code": 1}, "pk": 888, "model": "birds.bird"}, {"fields": {"scientific_name": "Passerella iliaca", "bird_class": 84, "common_name": "Fox Sparrow", "code": 1}, "pk": 889, "model": "birds.bird"}, {"fields": {"scientific_name": "Melospiza melodia", "bird_class": 84, "common_name": "Song Sparrow", "code": 1}, "pk": 890, "model": "birds.bird"}, {"fields": {"scientific_name": "Melospiza lincolnii", "bird_class": 84, "common_name": "Lincoln's Sparrow", "code": 1}, "pk": 891, "model": "birds.bird"}, {"fields": {"scientific_name": "Melospiza georgiana", "bird_class": 84, "common_name": "Swamp Sparrow", "code": 1}, "pk": 892, "model": "birds.bird"}, {"fields": {"scientific_name": "Zonotrichia albicollis", "bird_class": 84, "common_name": "White-throated Sparrow", "code": 1}, "pk": 893, "model": "birds.bird"}, {"fields": {"scientific_name": "Zonotrichia querula", "bird_class": 84, "common_name": "Harris's Sparrow", "code": 1}, "pk": 894, "model": "birds.bird"}, {"fields": {"scientific_name": "Zonotrichia leucophrys", "bird_class": 84, "common_name": "White-crowned Sparrow", "code": 1}, "pk": 895, "model": "birds.bird"}, {"fields": {"scientific_name": "Zonotrichia atricapilla", "bird_class": 84, "common_name": "Golden-crowned Sparrow", "code": 1}, "pk": 896, "model": "birds.bird"}, {"fields": {"scientific_name": "Junco hyemalis", "bird_class": 84, "common_name": "Dark-eyed Junco", "code": 1}, "pk": 897, "model": "birds.bird"}, {"fields": {"scientific_name": "Junco phaeonotus", "bird_class": 84, "common_name": "Yellow-eyed Junco", "code": 2}, "pk": 898, "model": "birds.bird"}, {"fields": {"scientific_name": "Emberiza leucocephalos", "bird_class": 84, "common_name": "Pine Bunting", "code": 5}, "pk": 899, "model": "birds.bird"}, {"fields": {"scientific_name": "Emberiza chrysophrys", "bird_class": 84, "common_name": "Yellow-browed Bunting", "code": 5}, "pk": 900, "model": "birds.bird"}, {"fields": {"scientific_name": "Emberiza pusilla", "bird_class": 84, "common_name": "Little Bunting", "code": 4}, "pk": 901, "model": "birds.bird"}, {"fields": {"scientific_name": "Emberiza rustica", "bird_class": 84, "common_name": "Rustic Bunting", "code": 3}, "pk": 902, "model": "birds.bird"}, {"fields": {"scientific_name": "Emberiza elegans", "bird_class": 84, "common_name": "Yellow-throated Bunting", "code": 5}, "pk": 903, "model": "birds.bird"}, {"fields": {"scientific_name": "Emberiza aureola", "bird_class": 84, "common_name": "Yellow-breasted Bunting", "code": 5}, "pk": 904, "model": "birds.bird"}, {"fields": {"scientific_name": "Emberiza variabilis", "bird_class": 84, "common_name": "Gray Bunting", "code": 5}, "pk": 905, "model": "birds.bird"}, {"fields": {"scientific_name": "Emberiza pallasi", "bird_class": 84, "common_name": "Pallas's Bunting", "code": 5}, "pk": 906, "model": "birds.bird"}, {"fields": {"scientific_name": "Emberiza schoeniclus", "bird_class": 84, "common_name": "Reed Bunting", "code": 4}, "pk": 907, "model": "birds.bird"}, {"fields": {"scientific_name": "Piranga flava", "bird_class": 85, "common_name": "Hepatic Tanager", "code": 2}, "pk": 908, "model": "birds.bird"}, {"fields": {"scientific_name": "Piranga rubra", "bird_class": 85, "common_name": "Summer Tanager", "code": 1}, "pk": 909, "model": "birds.bird"}, {"fields": {"scientific_name": "Piranga olivacea", "bird_class": 85, "common_name": "Scarlet Tanager", "code": 1}, "pk": 910, "model": "birds.bird"}, {"fields": {"scientific_name": "Piranga ludoviciana", "bird_class": 85, "common_name": "Western Tanager", "code": 1}, "pk": 911, "model": "birds.bird"}, {"fields": {"scientific_name": "Piranga bidentata", "bird_class": 85, "common_name": "Flame-colored Tanager", "code": 3}, "pk": 912, "model": "birds.bird"}, {"fields": {"scientific_name": "Rhodothraupis celaeno", "bird_class": 85, "common_name": "Crimson-collared Grosbeak", "code": 4}, "pk": 913, "model": "birds.bird"}, {"fields": {"scientific_name": "Cardinalis cardinalis", "bird_class": 85, "common_name": "Northern Cardinal", "code": 1}, "pk": 914, "model": "birds.bird"}, {"fields": {"scientific_name": "Cardinalis sinuatus", "bird_class": 85, "common_name": "Pyrrhuloxia", "code": 1}, "pk": 915, "model": "birds.bird"}, {"fields": {"scientific_name": "Pheucticus chrysopeplus", "bird_class": 85, "common_name": "Yellow Grosbeak", "code": 4}, "pk": 916, "model": "birds.bird"}, {"fields": {"scientific_name": "Pheucticus ludovicianus", "bird_class": 85, "common_name": "Rose-breasted Grosbeak", "code": 1}, "pk": 917, "model": "birds.bird"}, {"fields": {"scientific_name": "Pheucticus melanocephalus", "bird_class": 85, "common_name": "Black-headed Grosbeak", "code": 1}, "pk": 918, "model": "birds.bird"}, {"fields": {"scientific_name": "Cyanocompsa parellina", "bird_class": 85, "common_name": "Blue Bunting", "code": 4}, "pk": 919, "model": "birds.bird"}, {"fields": {"scientific_name": "Passerina caerulea", "bird_class": 85, "common_name": "Blue Grosbeak", "code": 1}, "pk": 920, "model": "birds.bird"}, {"fields": {"scientific_name": "Passerina amoena", "bird_class": 85, "common_name": "Lazuli Bunting", "code": 1}, "pk": 921, "model": "birds.bird"}, {"fields": {"scientific_name": "Passerina cyanea", "bird_class": 85, "common_name": "Indigo Bunting", "code": 1}, "pk": 922, "model": "birds.bird"}, {"fields": {"scientific_name": "Passerina versicolor", "bird_class": 85, "common_name": "Varied Bunting", "code": 2}, "pk": 923, "model": "birds.bird"}, {"fields": {"scientific_name": "Passerina ciris", "bird_class": 85, "common_name": "Painted Bunting", "code": 1}, "pk": 924, "model": "birds.bird"}, {"fields": {"scientific_name": "Spiza americana", "bird_class": 85, "common_name": "Dickcissel", "code": 1}, "pk": 925, "model": "birds.bird"}, {"fields": {"scientific_name": "Dolichonyx oryzivorus", "bird_class": 86, "common_name": "Bobolink", "code": 1}, "pk": 926, "model": "birds.bird"}, {"fields": {"scientific_name": "Agelaius phoeniceus", "bird_class": 86, "common_name": "Red-winged Blackbird", "code": 1}, "pk": 927, "model": "birds.bird"}, {"fields": {"scientific_name": "Agelaius tricolor", "bird_class": 86, "common_name": "Tricolored Blackbird", "code": 2}, "pk": 928, "model": "birds.bird"}, {"fields": {"scientific_name": "Agelaius humeralis", "bird_class": 86, "common_name": "Tawny-shouldered Blackbird", "code": 5}, "pk": 929, "model": "birds.bird"}, {"fields": {"scientific_name": "Sturnella magna", "bird_class": 86, "common_name": "Eastern Meadowlark", "code": 1}, "pk": 930, "model": "birds.bird"}, {"fields": {"scientific_name": "Sturnella neglecta", "bird_class": 86, "common_name": "Western Meadowlark", "code": 1}, "pk": 931, "model": "birds.bird"}, {"fields": {"scientific_name": "Xanthocephalus xanthocephalus", "bird_class": 86, "common_name": "Yellow-headed Blackbird", "code": 1}, "pk": 932, "model": "birds.bird"}, {"fields": {"scientific_name": "Euphagus carolinus", "bird_class": 86, "common_name": "Rusty Blackbird", "code": 1}, "pk": 933, "model": "birds.bird"}, {"fields": {"scientific_name": "Euphagus cyanocephalus", "bird_class": 86, "common_name": "Brewer's Blackbird", "code": 1}, "pk": 934, "model": "birds.bird"}, {"fields": {"scientific_name": "Quiscalus quiscula", "bird_class": 86, "common_name": "Common Grackle", "code": 1}, "pk": 935, "model": "birds.bird"}, {"fields": {"scientific_name": "Quiscalus major", "bird_class": 86, "common_name": "Boat-tailed Grackle", "code": 1}, "pk": 936, "model": "birds.bird"}, {"fields": {"scientific_name": "Quiscalus mexicanus", "bird_class": 86, "common_name": "Great-tailed Grackle", "code": 1}, "pk": 937, "model": "birds.bird"}, {"fields": {"scientific_name": "Molothrus bonariensis", "bird_class": 86, "common_name": "Shiny Cowbird", "code": 3}, "pk": 938, "model": "birds.bird"}, {"fields": {"scientific_name": "Molothrus aeneus", "bird_class": 86, "common_name": "Bronzed Cowbird", "code": 1}, "pk": 939, "model": "birds.bird"}, {"fields": {"scientific_name": "Molothrus ater", "bird_class": 86, "common_name": "Brown-headed Cowbird", "code": 1}, "pk": 940, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus wagleri", "bird_class": 86, "common_name": "Black-vented Oriole", "code": 5}, "pk": 941, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus spurius", "bird_class": 86, "common_name": "Orchard Oriole", "code": 1}, "pk": 942, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus cucullatus", "bird_class": 86, "common_name": "Hooded Oriole", "code": 1}, "pk": 943, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus pustulatus", "bird_class": 86, "common_name": "Streak-backed Oriole", "code": 4}, "pk": 944, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus bullockii", "bird_class": 86, "common_name": "Bullock's Oriole", "code": 1}, "pk": 945, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus pectoralis", "bird_class": 86, "common_name": "Spot-breasted Oriole", "code": 2}, "pk": 946, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus gularis", "bird_class": 86, "common_name": "Altamira Oriole", "code": 2}, "pk": 947, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus graduacauda", "bird_class": 86, "common_name": "Audubon's Oriole", "code": 2}, "pk": 948, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus galbula", "bird_class": 86, "common_name": "Baltimore Oriole", "code": 1}, "pk": 949, "model": "birds.bird"}, {"fields": {"scientific_name": "Icterus parisorum", "bird_class": 86, "common_name": "Scott's Oriole", "code": 1}, "pk": 950, "model": "birds.bird"}, {"fields": {"scientific_name": "Fringilla coelebs", "bird_class": 87, "common_name": "Common Chaffinch", "code": 4}, "pk": 951, "model": "birds.bird"}, {"fields": {"scientific_name": "Fringilla montifringilla", "bird_class": 87, "common_name": "Brambling", "code": 3}, "pk": 952, "model": "birds.bird"}, {"fields": {"scientific_name": "Leucosticte arctoa", "bird_class": 87, "common_name": "Asian Rosy-Finch", "code": 5}, "pk": 953, "model": "birds.bird"}, {"fields": {"scientific_name": "Leucosticte tephrocotis", "bird_class": 87, "common_name": "Gray-crowned Rosy-Finch", "code": 1}, "pk": 954, "model": "birds.bird"}, {"fields": {"scientific_name": "Leucosticte atrata", "bird_class": 87, "common_name": "Black Rosy-Finch", "code": 2}, "pk": 955, "model": "birds.bird"}, {"fields": {"scientific_name": "Leucosticte australis", "bird_class": 87, "common_name": "Brown-capped Rosy-Finch", "code": 2}, "pk": 956, "model": "birds.bird"}, {"fields": {"scientific_name": "Pinicola enucleator", "bird_class": 87, "common_name": "Pine Grosbeak", "code": 1}, "pk": 957, "model": "birds.bird"}, {"fields": {"scientific_name": "Pyrrhula pyrrhula", "bird_class": 87, "common_name": "Eurasian Bullfinch", "code": 4}, "pk": 958, "model": "birds.bird"}, {"fields": {"scientific_name": "Carpodacus erythrinus", "bird_class": 87, "common_name": "Common Rosefinch", "code": 4}, "pk": 959, "model": "birds.bird"}, {"fields": {"scientific_name": "Haemorhous purpureus", "bird_class": 87, "common_name": "Purple Finch", "code": 1}, "pk": 960, "model": "birds.bird"}, {"fields": {"scientific_name": "Haemorhous cassinii", "bird_class": 87, "common_name": "Cassin's Finch", "code": 1}, "pk": 961, "model": "birds.bird"}, {"fields": {"scientific_name": "Haemorhous mexicanus", "bird_class": 87, "common_name": "House Finch", "code": 1}, "pk": 962, "model": "birds.bird"}, {"fields": {"scientific_name": "Loxia curvirostra", "bird_class": 87, "common_name": "Red Crossbill", "code": 1}, "pk": 963, "model": "birds.bird"}, {"fields": {"scientific_name": "Loxia leucoptera", "bird_class": 87, "common_name": "White-winged Crossbill", "code": 2}, "pk": 964, "model": "birds.bird"}, {"fields": {"scientific_name": "Acanthis flammea", "bird_class": 87, "common_name": "Common Redpoll", "code": 1}, "pk": 965, "model": "birds.bird"}, {"fields": {"scientific_name": "Acanthis hornemanni", "bird_class": 87, "common_name": "Hoary Redpoll", "code": 2}, "pk": 966, "model": "birds.bird"}, {"fields": {"scientific_name": "Spinus spinus", "bird_class": 87, "common_name": "Eurasian Siskin", "code": 5}, "pk": 967, "model": "birds.bird"}, {"fields": {"scientific_name": "Spinus pinus", "bird_class": 87, "common_name": "Pine Siskin", "code": 1}, "pk": 968, "model": "birds.bird"}, {"fields": {"scientific_name": "Spinus psaltria", "bird_class": 87, "common_name": "Lesser Goldfinch", "code": 1}, "pk": 969, "model": "birds.bird"}, {"fields": {"scientific_name": "Spinus lawrencei", "bird_class": 87, "common_name": "Lawrence's Goldfinch", "code": 2}, "pk": 970, "model": "birds.bird"}, {"fields": {"scientific_name": "Spinus tristis", "bird_class": 87, "common_name": "American Goldfinch", "code": 1}, "pk": 971, "model": "birds.bird"}, {"fields": {"scientific_name": "Chloris sinica", "bird_class": 87, "common_name": "Oriental Greenfinch", "code": 4}, "pk": 972, "model": "birds.bird"}, {"fields": {"scientific_name": "Coccothraustes vespertinus", "bird_class": 87, "common_name": "Evening Grosbeak", "code": 1}, "pk": 973, "model": "birds.bird"}, {"fields": {"scientific_name": "Coccothraustes coccothraustes", "bird_class": 87, "common_name": "Hawfinch", "code": 4}, "pk": 974, "model": "birds.bird"}, {"fields": {"scientific_name": "Passer domesticus", "bird_class": 88, "common_name": "House Sparrow", "code": 1}, "pk": 975, "model": "birds.bird"}, {"fields": {"scientific_name": "Passer montanus", "bird_class": 88, "common_name": "Eurasian Tree Sparrow", "code": 2}, "pk": 976, "model": "birds.bird"}]
\ No newline at end of file diff --git a/bak/unused_apps/birds/build.py b/bak/unused_apps/birds/build.py new file mode 100644 index 0000000..565b675 --- /dev/null +++ b/bak/unused_apps/birds/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/bak/unused_apps/birds/migrations/0001_initial.py b/bak/unused_apps/birds/migrations/0001_initial.py new file mode 100644 index 0000000..b20387e --- /dev/null +++ b/bak/unused_apps/birds/migrations/0001_initial.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-13 09:49 +from __future__ import unicode_literals + +import birds.models +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 + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + #('locations', '__first__'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Bird', + 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=[(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, null=True, upload_to=birds.models.get_upload_path)), + ], + options={ + 'ordering': ['common_name'], + }, + ), + migrations.CreateModel( + name='BirdAudio', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('audio', models.FileField(upload_to='audio/birds/')), + ('recorder', models.CharField(blank=True, max_length=200, null=True)), + ('pub_date', models.DateTimeField()), + ('location', models.CharField(blank=True, max_length=200, null=True)), + ('link', models.CharField(blank=True, max_length=450, null=True)), + ('copyright', models.CharField(blank=True, max_length=250, null=True)), + ('bird', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recordings', to='birds.Bird')), + ], + options={ + 'ordering': ['bird'], + 'verbose_name_plural': 'Bird Audio', + }, + ), + migrations.CreateModel( + name='BirdClass', + 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)), + ], + options={ + 'ordering': ['common_name'], + 'verbose_name_plural': 'Bird Class', + }, + ), + migrations.CreateModel( + name='BirdSighting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('point', django.contrib.gis.db.models.fields.PointField(srid=4326)), + ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date')), + ('image', models.FileField(blank=True, null=True, upload_to=birds.models.get_upload_path)), + ('bird', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='birds.Bird')), + ('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={ + 'verbose_name_plural': 'Bird Sighting', + }, + ), + migrations.AddField( + model_name='bird', + name='bird_class', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='birds.BirdClass'), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0002_auto_20160313_0953.py b/bak/unused_apps/birds/migrations/0002_auto_20160313_0953.py new file mode 100644 index 0000000..a2085ea --- /dev/null +++ b/bak/unused_apps/birds/migrations/0002_auto_20160313_0953.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-13 09:53 +from __future__ import unicode_literals + +import birds.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('birds', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='bird', + name='image_credit', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AlterField( + model_name='bird', + name='image', + field=models.FileField(blank=True, help_text='width of high res is 1360px', null=True, upload_to=birds.models.get_upload_path), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0003_birdsighting_images.py b/bak/unused_apps/birds/migrations/0003_birdsighting_images.py new file mode 100644 index 0000000..d20e8b7 --- /dev/null +++ b/bak/unused_apps/birds/migrations/0003_birdsighting_images.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-20 08:02 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0007_auto_20160320_0802'), + ('birds', '0002_auto_20160313_0953'), + ] + + operations = [ + migrations.AddField( + model_name='birdsighting', + name='images', + field=models.ManyToManyField(to='photos.LuxImage'), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0004_auto_20160321_1123.py b/bak/unused_apps/birds/migrations/0004_auto_20160321_1123.py new file mode 100644 index 0000000..7672a18 --- /dev/null +++ b/bak/unused_apps/birds/migrations/0004_auto_20160321_1123.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-21 11:23 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('birds', '0003_birdsighting_images'), + ] + + operations = [ + migrations.RemoveField( + model_name='birdsighting', + name='image', + ), + migrations.AddField( + model_name='birdsighting', + name='audio', + field=models.ManyToManyField(to='birds.BirdAudio'), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0005_auto_20170714_2222.py b/bak/unused_apps/birds/migrations/0005_auto_20170714_2222.py new file mode 100644 index 0000000..8bdfb4b --- /dev/null +++ b/bak/unused_apps/birds/migrations/0005_auto_20170714_2222.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2017-07-14 22:22 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('birds', '0004_auto_20160321_1123'), + ] + + operations = [ + migrations.AlterField( + model_name='birdsighting', + name='audio', + field=models.ManyToManyField(blank=True, null=True, to='birds.BirdAudio'), + ), + migrations.AlterField( + model_name='birdsighting', + name='images', + field=models.ManyToManyField(blank=True, null=True, to='photos.LuxImage'), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0006_auto_20170714_2224.py b/bak/unused_apps/birds/migrations/0006_auto_20170714_2224.py new file mode 100644 index 0000000..bd4db82 --- /dev/null +++ b/bak/unused_apps/birds/migrations/0006_auto_20170714_2224.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2017-07-14 22:24 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('birds', '0005_auto_20170714_2222'), + ] + + operations = [ + migrations.AlterField( + model_name='birdsighting', + name='audio', + field=models.ManyToManyField(blank=True, to='birds.BirdAudio'), + ), + migrations.AlterField( + model_name='birdsighting', + name='images', + field=models.ManyToManyField(blank=True, to='photos.LuxImage'), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0007_auto_20170821_1415.py b/bak/unused_apps/birds/migrations/0007_auto_20170821_1415.py new file mode 100644 index 0000000..a51776c --- /dev/null +++ b/bak/unused_apps/birds/migrations/0007_auto_20170821_1415.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2017-08-21 14:15 +from __future__ import unicode_literals + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0018_auto_20161130_1218'), + ('birds', '0006_auto_20170714_2224'), + ] + + operations = [ + migrations.AlterModelOptions( + name='birdsighting', + options={'get_latest_by': 'date', 'verbose_name_plural': 'Bird Sighting'}, + ), + migrations.RemoveField( + model_name='birdsighting', + name='images', + ), + migrations.AddField( + model_name='birdsighting', + name='image', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photos.LuxImage'), + ), + migrations.AlterField( + model_name='birdsighting', + name='point', + field=django.contrib.gis.db.models.fields.PointField(blank=True, srid=4326), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0008_auto_20170821_1418.py b/bak/unused_apps/birds/migrations/0008_auto_20170821_1418.py new file mode 100644 index 0000000..e5c80a4 --- /dev/null +++ b/bak/unused_apps/birds/migrations/0008_auto_20170821_1418.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2017-08-21 14:18 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('birds', '0007_auto_20170821_1415'), + ] + + operations = [ + migrations.RenameField( + model_name='birdsighting', + old_name='image', + new_name='images', + ), + ] diff --git a/bak/unused_apps/birds/migrations/0009_auto_20170821_1429.py b/bak/unused_apps/birds/migrations/0009_auto_20170821_1429.py new file mode 100644 index 0000000..840d7d3 --- /dev/null +++ b/bak/unused_apps/birds/migrations/0009_auto_20170821_1429.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2017-08-21 14:29 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('birds', '0008_auto_20170821_1418'), + ] + + operations = [ + migrations.RenameField( + model_name='birdsighting', + old_name='images', + new_name='image', + ), + ] diff --git a/bak/unused_apps/birds/migrations/0010_auto_20170906_2100.py b/bak/unused_apps/birds/migrations/0010_auto_20170906_2100.py new file mode 100644 index 0000000..aada189 --- /dev/null +++ b/bak/unused_apps/birds/migrations/0010_auto_20170906_2100.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2017-09-06 21:00 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0018_auto_20161130_1218'), + ('birds', '0009_auto_20170821_1429'), + ] + + operations = [ + migrations.RemoveField( + model_name='birdsighting', + name='image', + ), + migrations.AddField( + model_name='birdsighting', + name='images', + field=models.ManyToManyField(blank=True, to='photos.LuxImage'), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0011_auto_20180126_1456.py b/bak/unused_apps/birds/migrations/0011_auto_20180126_1456.py new file mode 100644 index 0000000..d63cf10 --- /dev/null +++ b/bak/unused_apps/birds/migrations/0011_auto_20180126_1456.py @@ -0,0 +1,48 @@ +# Generated by Django 2.0.1 on 2018-01-26 14:56 + +import birds.models +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('birds', '0010_auto_20170906_2100'), + ] + + 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=[(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=birds.models.get_upload_path)), + ('image_credit', models.CharField(blank=True, max_length=200, null=True)), + ], + options={ + '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.AddField( + model_name='ap', + name='apclass', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='birds.APClass'), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0012_auto_20180126_1739.py b/bak/unused_apps/birds/migrations/0012_auto_20180126_1739.py new file mode 100644 index 0000000..307aa42 --- /dev/null +++ b/bak/unused_apps/birds/migrations/0012_auto_20180126_1739.py @@ -0,0 +1,44 @@ +# Generated by Django 2.0.1 on 2018-01-26 17:39 + +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 + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0002_checkin'), + ('photos', '0018_auto_20161130_1218'), + ('contenttypes', '0002_remove_content_type_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('birds', '0011_auto_20180126_1456'), + ] + + operations = [ + migrations.CreateModel( + name='Sighting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, srid=4326)), + ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date')), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ('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)), + ], + ), + migrations.AlterField( + model_name='ap', + name='code', + field=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), + ), + migrations.AlterField( + model_name='bird', + name='code', + field=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), + ), + ] diff --git a/bak/unused_apps/birds/migrations/0013_auto_20180126_2010.py b/bak/unused_apps/birds/migrations/0013_auto_20180126_2010.py new file mode 100644 index 0000000..613597a --- /dev/null +++ b/bak/unused_apps/birds/migrations/0013_auto_20180126_2010.py @@ -0,0 +1,28 @@ +# Generated by Django 2.0.1 on 2018-01-26 20:10 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('birds', '0012_auto_20180126_1739'), + ] + + operations = [ + migrations.RemoveField( + model_name='sighting', + name='content_type', + ), + migrations.RemoveField( + model_name='sighting', + name='object_id', + ), + migrations.AddField( + model_name='sighting', + name='ap', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='birds.AP'), + preserve_default=False, + ), + ] diff --git a/bak/unused_apps/birds/migrations/0014_auto_20180128_0902.py b/bak/unused_apps/birds/migrations/0014_auto_20180128_0902.py new file mode 100644 index 0000000..97791ef --- /dev/null +++ b/bak/unused_apps/birds/migrations/0014_auto_20180128_0902.py @@ -0,0 +1,21 @@ +# Generated by Django 2.0.1 on 2018-01-28 09:02 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('birds', '0013_auto_20180126_2010'), + ] + + operations = [ + migrations.AlterModelOptions( + name='ap', + options={'ordering': ['common_name'], 'verbose_name': 'Animal/Plant', 'verbose_name_plural': 'Animal/Plant'}, + ), + migrations.AlterModelOptions( + name='sighting', + options={'ordering': ['date']}, + ), + ] diff --git a/bak/unused_apps/birds/migrations/__init__.py b/bak/unused_apps/birds/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/birds/migrations/__init__.py diff --git a/bak/unused_apps/birds/models.py b/bak/unused_apps/birds/models.py new file mode 100644 index 0000000..5a14786 --- /dev/null +++ b/bak/unused_apps/birds/models.py @@ -0,0 +1,295 @@ +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/bird-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, 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) + scientific_name = models.CharField(max_length=200) + + class Meta: + verbose_name_plural = 'Bird Class' + ordering = ["common_name", ] + + def __str__(self): + return self.common_name + + +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, 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 + + # function to resize large image to 680px wide and use as normal image + # the question is, should this happen here, or with some universale image + # model that can be attached to other models, loaded in entries and + # displayed in galleries. I suppose the answer is yes then isn't it? + # the problem is that I still can't see exactly what that looks like... + + 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("birds:detail", kwargs={"slug": 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, on_delete=models.CASCADE, 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, 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: + verbose_name_plural = 'Bird Sighting' + get_latest_by = '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.bird.common_name + + def save(self): + if not self.point: + self.point = BirdSighting.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(BirdSighting, self).save() diff --git a/bak/unused_apps/birds/urls.py b/bak/unused_apps/birds/urls.py new file mode 100644 index 0000000..8781433 --- /dev/null +++ b/bak/unused_apps/birds/urls.py @@ -0,0 +1,34 @@ +from django.conf.urls import url +from django.views.generic.base import RedirectView +from . import views + +app_name = "birds" + +urlpatterns = [ + url( + r'(?P<user>[\w]+)/(?P<page>\d+)/', + views.BirdListUserView.as_view(), + name='list_by_person' + ), + url( + r'(?P<page>\d+)/$', + views.BirdListView.as_view(), + name="list" + ), + url( + r'^(?P<slug>[-_\w]+)$', + views.BirdDetailView.as_view(), + name='detail' + ), + # redirect /slug/ to /slug/1/ for live server + url( + r'(?P<user>[-\w]+)/$', + RedirectView.as_view(url="/birds/%(user)s/1/", permanent=False), + name="list_person_redirect" + ), + url( + r'', + RedirectView.as_view(url="/birds/1/", permanent=False), + name="list_redirect" + ), +] diff --git a/bak/unused_apps/birds/views.py b/bak/unused_apps/birds/views.py new file mode 100644 index 0000000..4d7f59e --- /dev/null +++ b/bak/unused_apps/birds/views.py @@ -0,0 +1,49 @@ +from django.views.generic.detail import DetailView +from django.contrib.auth.models import User +from utils.views import PaginatedListView +from birds.models import Bird, BirdAudio, BirdSighting + + +class BirdListView(PaginatedListView): + template_name = 'archives/birds.html' + + def get_queryset(self): + return BirdSighting.objects.all() + + +class BirdListUserView(PaginatedListView): + template_name = 'archives/birds.html' + + def get_queryset(self): + return BirdSighting.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(BirdListUserView, self).get_context_data(**kwargs) + context['user'] = User.objects.get(username=self.kwargs['user']) + return context + + +class BirdDetailView(DetailView): + model = Bird + template_name = "details/bird.html" + slug_field = "slug" + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(BirdDetailView, self).get_context_data(**kwargs) + try: + context['recording'] = BirdAudio.objects.get( + bird__slug=self.kwargs['slug'] + ) + except BirdAudio.DoesNotExist: + pass + try: + context['sighting'] = BirdSighting.objects.get( + bird__slug=self.kwargs['slug'] + ) + except BirdSighting.DoesNotExist: + pass + return context diff --git a/bak/unused_apps/ccg_notes/__init__.py b/bak/unused_apps/ccg_notes/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/ccg_notes/__init__.py diff --git a/bak/unused_apps/ccg_notes/admin.py b/bak/unused_apps/ccg_notes/admin.py new file mode 100644 index 0000000..5aec3ae --- /dev/null +++ b/bak/unused_apps/ccg_notes/admin.py @@ -0,0 +1,32 @@ +from django.contrib import admin + +from utils.widgets import OLAdminBase +from utils.widgets import TagListFilter + +from .models import CcgNote +from .forms import CcgNoteForm + +class CcgNoteAdmin(OLAdminBase): + form = CcgNoteForm + prepopulated_fields = {"slug": ('title',)} + list_display = ('slug', 'pub_date',) + list_filter = ['status', TagListFilter] + fieldsets = ( + ('Note', { + 'fields': ( + ('title', 'slug'), + 'body_markdown', + 'tags', + ('pub_date', 'status'), + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + + +admin.site.register(CcgNote, CcgNoteAdmin) diff --git a/bak/unused_apps/ccg_notes/autocomplete_light_registry.py b/bak/unused_apps/ccg_notes/autocomplete_light_registry.py new file mode 100644 index 0000000..0781848 --- /dev/null +++ b/bak/unused_apps/ccg_notes/autocomplete_light_registry.py @@ -0,0 +1,24 @@ +import autocomplete_light.shortcuts as al +from taggit.models import Tag + +# This will generate a PersonAutocomplete class +al.register(Tag, + # Just like in ModelAdmin.search_fields + search_fields=['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/bak/unused_apps/ccg_notes/build.py b/bak/unused_apps/ccg_notes/build.py new file mode 100644 index 0000000..499adc6 --- /dev/null +++ b/bak/unused_apps/ccg_notes/build.py @@ -0,0 +1,36 @@ +import os +from django.core.urlresolvers import reverse +from builder.base import BuildNew + + +class BuildNotes(BuildNew): + + def build(self): + self.build_detail_view() + self.build_list_view( + base_path=reverse("notes:live_redirect"), + paginate_by=24 + ) + self.build_year_view("notes:list_year") + self.build_month_view("notes:list_month") + + def get_model_queryset(self): + return self.model.objects.all() + + def build_detail_view(self): + ''' + write out all the expenses for each trip + ''' + for obj in self.get_model_queryset(): + url = obj.get_absolute_url() + path, slug = os.path.split(url) + path = '%s/' % path + # write html + response = self.client.get(url) + print(path, slug) + self.write_file(path, response.content, filename=slug) + + +def builder(): + j = BuildNotes("notes", "luxnote") + j.build() diff --git a/bak/unused_apps/ccg_notes/forms.py b/bak/unused_apps/ccg_notes/forms.py new file mode 100644 index 0000000..0934306 --- /dev/null +++ b/bak/unused_apps/ccg_notes/forms.py @@ -0,0 +1,17 @@ +from django import forms +import dal +from dal_select2_taggit.widgets import TaggitSelect2 +from .models import CcgNote +from dal import autocomplete + + +class CcgNoteForm(autocomplete.FutureModelForm): + class Meta: + model = CcgNote + fields = ('tags',) + widgets = { + 'tags': autocomplete.TaggitSelect2( + 'tag-autocomplete' + ), + 'body_markdown': forms.Textarea(attrs={'rows': 18, 'cols': 60}), + } diff --git a/bak/unused_apps/ccg_notes/mdx_urlize.py b/bak/unused_apps/ccg_notes/mdx_urlize.py new file mode 100644 index 0000000..dc8d1d7 --- /dev/null +++ b/bak/unused_apps/ccg_notes/mdx_urlize.py @@ -0,0 +1,81 @@ +"""A more liberal autolinker + +Inspired by Django's urlize function. + +Positive examples: + +>>> import markdown +>>> md = markdown.Markdown(extensions=['urlize']) + +>>> md.convert('http://example.com/') +u'<p><a href="http://example.com/">http://example.com/</a></p>' + +>>> md.convert('go to http://example.com') +u'<p>go to <a href="http://example.com">http://example.com</a></p>' + +>>> md.convert('example.com') +u'<p><a href="http://example.com">example.com</a></p>' + +>>> md.convert('example.net') +u'<p><a href="http://example.net">example.net</a></p>' + +>>> md.convert('www.example.us') +u'<p><a href="http://www.example.us">www.example.us</a></p>' + +>>> md.convert('(www.example.us/path/?name=val)') +u'<p>(<a href="http://www.example.us/path/?name=val">www.example.us/path/?name=val</a>)</p>' + +>>> md.convert('go to <http://example.com> now!') +u'<p>go to <a href="http://example.com">http://example.com</a> now!</p>' + +Negative examples: + +>>> md.convert('del.icio.us') +u'<p>del.icio.us</p>' + +""" + +import markdown + +# Global Vars +URLIZE_RE = '(%s)' % '|'.join([ + r'<(?:f|ht)tps?://[^>]*>', + r'\b(?:f|ht)tps?://[^)<>\s]+[^.,)<>\s]', + r'\bwww\.[^)<>\s]+[^.,)<>\s]', + r'[^(<\s]+\.(?:com|net|org)\b', +]) + +class UrlizePattern(markdown.inlinepatterns.Pattern): + """ Return a link Element given an autolink (`http://example/com`). """ + def handleMatch(self, m): + url = m.group(2) + + if url.startswith('<'): + url = url[1:-1] + + text = url + + if not url.split('://')[0] in ('http','https','ftp'): + if '@' in url and not '/' in url: + url = 'mailto:' + url + else: + url = 'http://' + url + + el = markdown.util.etree.Element("a") + el.set('href', url) + el.text = markdown.util.AtomicString(text) + return el + +class UrlizeExtension(markdown.Extension): + """ Urlize Extension for Python-Markdown. """ + + def extendMarkdown(self, md, md_globals): + """ Replace autolink with UrlizePattern """ + md.inlinePatterns['autolink'] = UrlizePattern(URLIZE_RE, md) + +def makeExtension(configs=None): + return UrlizeExtension(configs=configs) + +if __name__ == "__main__": + import doctest + doctest.testmod() diff --git a/bak/unused_apps/ccg_notes/migrations/0001_initial.py b/bak/unused_apps/ccg_notes/migrations/0001_initial.py new file mode 100644 index 0000000..1877e91 --- /dev/null +++ b/bak/unused_apps/ccg_notes/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-07-22 19:54 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('taggit', '0002_auto_20150616_2121'), + ] + + operations = [ + migrations.CreateModel( + name='CcgNote', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=250, null=True)), + ('slug', models.SlugField(blank=True, unique_for_date='pub_date')), + ('pub_date', models.DateTimeField(default=django.utils.timezone.now)), + ('date_last_updated', models.DateTimeField(blank=True, verbose_name='Date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField(verbose_name='Note')), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=1)), + ('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')), + ], + ), + ] diff --git a/bak/unused_apps/ccg_notes/migrations/__init__.py b/bak/unused_apps/ccg_notes/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/ccg_notes/migrations/__init__.py diff --git a/bak/unused_apps/ccg_notes/models.py b/bak/unused_apps/ccg_notes/models.py new file mode 100644 index 0000000..b235d36 --- /dev/null +++ b/bak/unused_apps/ccg_notes/models.py @@ -0,0 +1,36 @@ +from django.contrib.gis.db import models +from django.utils import timezone +from django.core.urlresolvers import reverse + +from taggit.managers import TaggableManager +from utils.widgets import markdown_to_html +from jrnl.models import render_images + + +class CcgNote(models.Model): + title = models.CharField(max_length=250, null=True, blank=True) + slug = models.SlugField(unique_for_date='pub_date', blank=True) + pub_date = models.DateTimeField(default=timezone.now) + date_last_updated = models.DateTimeField('Date', blank=True) + body_html = models.TextField(blank=True) + body_markdown = models.TextField('Note') + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=1) + tags = TaggableManager(blank=True) + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("ccg_notes:detail", kwargs={"year": self.pub_date.year, "month": self.pub_date.strftime("%m"), "slug": self.slug}) + + def save(self, *args, **kwargs): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + if not self.id: + self.pub_date = timezone.now() + self.date_last_updated = timezone.now() + super(CcgNote, self).save() diff --git a/bak/unused_apps/ccg_notes/urls.py b/bak/unused_apps/ccg_notes/urls.py new file mode 100644 index 0000000..0f9fad7 --- /dev/null +++ b/bak/unused_apps/ccg_notes/urls.py @@ -0,0 +1,62 @@ +from django.conf.urls import url +from django.views.generic.base import RedirectView + +from . import views + +app_name = "notes" + +urlpatterns = [ + url( + r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+).txt$', + views.NoteDetailViewTXT.as_view(), + name="detail-txt" + ), + url( + r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+).amp$', + views.NoteDetailViewAMP.as_view(), + name="detail-amp" + ), + url( + r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+)$', + views.NoteDetailView.as_view(), + name="detail" + ), + url( + r'^(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', + views.NoteMonthArchiveView.as_view(month_format='%m'), + name="list_month" + ), + url( + r'(?P<year>\d{4})/$', + views.NoteYearArchiveView.as_view(), + name="list_year" + ), + + + url( + r'(?P<year>\d{4})/(?P<month>\d{2})/$', + views.date_list, + name="notes_by_month" + ), + url( + r'(?P<year>\d{4})/$', + views.date_list, + name="notes_by_year" + ), + url( + r'(?P<page>\d+)/$', + views.NoteList.as_view(), + name="list" + ), + # redirect / to /1/ for live server + url( + r'', + RedirectView.as_view(url="/field-notes/1/", permanent=False), + name="live_redirect" + ), + url( + r'^$', + views.entry_list, + name="notes_archive" + ), +] diff --git a/bak/unused_apps/ccg_notes/views.py b/bak/unused_apps/ccg_notes/views.py new file mode 100644 index 0000000..1fbe6f4 --- /dev/null +++ b/bak/unused_apps/ccg_notes/views.py @@ -0,0 +1,85 @@ +from django.shortcuts import render_to_response, get_object_or_404 +from django.template import RequestContext +from django.views.generic.dates import YearArchiveView, MonthArchiveView +from django.views.generic.detail import DetailView + +from utils.views import PaginatedListView + +from notes.models import LuxNote, Note + + +class NoteList(PaginatedListView): + """ + Return a list of Notes in reverse chronological order + """ + queryset = LuxNote.objects.all().order_by('-pub_date') + template_name = "archives/notes.html" + + +class NoteDetailView(DetailView): + model = LuxNote + template_name = "details/note.html" + slug_field = "slug" + + +class NoteDetailViewTXT(NoteDetailView): + template_name = "details/entry.txt" + + +class NoteDetailViewAMP(NoteDetailView): + template_name = "details/entry.amp" + + +class NoteYearArchiveView(YearArchiveView): + queryset = LuxNote.objects.all() + date_field = "pub_date" + make_object_list = True + allow_future = True + template_name = "archives/notes_date.html" + + +class NoteMonthArchiveView(MonthArchiveView): + queryset = LuxNote.objects.all() + date_field = "pub_date" + allow_future = True + template_name = "archives/notes_date.html" + + +""" +Legacy Notes views +""" + + +def entry_detail(request, year, month, slug): + context = { + 'object': get_object_or_404(Note, slug__exact=slug), + } + return render_to_response( + 'details/note.html', + context, + context_instance=RequestContext(request) + ) + + +def date_list(request, year, month=None): + if month: + qs = Note.objects.filter(date_created__year=year, date_created__month=month).order_by('-date_created') + else: + qs = Note.objects.filter(date_created__year=year).order_by('-date_created') + context = { + 'year': year, + 'month': month, + 'object_list': qs, + } + return render_to_response( + "archives/notes_date.html", + context, + context_instance=RequestContext(request) + ) + + +def entry_list(request): + context = { + 'object_list': Note.objects.all().order_by('-date_created').select_related(), + } + return render_to_response("archives/notes.html", context, context_instance=RequestContext(request)) diff --git a/bak/unused_apps/daily/admin.py b/bak/unused_apps/daily/admin.py new file mode 100644 index 0000000..32a5747 --- /dev/null +++ b/bak/unused_apps/daily/admin.py @@ -0,0 +1,55 @@ +from django.contrib import admin + +from utils.widgets import LGEntryForm, OLAdminBase +from .models import CheckIn, Daily, Weather + + +class WeatherAdmin(OLAdminBase): + pass + + +class CheckInAdmin(OLAdminBase): + list_display = ('date', 'location') + pass + + +class DailyAdmin(admin.ModelAdmin): + form = LGEntryForm + list_display = ('date', 'user', 'location') + list_filter = ( + 'date', + ('location', admin.RelatedOnlyFieldListFilter), + ) + fieldsets = ( + (None, { + 'fields': ( + 'user', + 'body_markdown', + 'weather_human', + ) + }), + ('Details', { + 'fields': ( + 'location', + 'weather', + 'date' + ), + 'classes': ('collapse',), + }), + ) + + def get_form(self, request, obj=None, **kwargs): + form = super(DailyAdmin, self).get_form(request, **kwargs) + form.current_user = request.user + return form + + def get_queryset(self, request): + qs = super(DailyAdmin, self).get_queryset(request) + if request.user.is_superuser: + return qs + return qs.filter(user=request.user) + + +admin.site.register(CheckIn, CheckInAdmin) +admin.site.register(Weather, WeatherAdmin) +admin.site.register(Daily, DailyAdmin) diff --git a/bak/unused_apps/daily/create_daily.py b/bak/unused_apps/daily/create_daily.py new file mode 100644 index 0000000..5441b77 --- /dev/null +++ b/bak/unused_apps/daily/create_daily.py @@ -0,0 +1,14 @@ +import sys +import os +from os.path import dirname, abspath +import django +PROJECT_ROOT = abspath(dirname(dirname(dirname(__file__)))) + '/' +sys.path.append(PROJECT_ROOT) +sys.path.append(PROJECT_ROOT + '/app') +sys.path.append(PROJECT_ROOT + '/app/lib') +sys.path.append(PROJECT_ROOT + '/config') +sys.path.append(PROJECT_ROOT + '/venv/bin/python3') +os.environ['DJANGO_SETTINGS_MODULE'] = 'config.settings' +django.setup() +from daily import retriever +retriever.create_daily() diff --git a/bak/unused_apps/daily/get_weather.py b/bak/unused_apps/daily/get_weather.py new file mode 100644 index 0000000..6cd56af --- /dev/null +++ b/bak/unused_apps/daily/get_weather.py @@ -0,0 +1,14 @@ +import sys +import os +from os.path import dirname, abspath +import django +PROJECT_ROOT = abspath(dirname(dirname(dirname(__file__)))) + '/' +sys.path.append(PROJECT_ROOT) +sys.path.append(PROJECT_ROOT + '/app') +sys.path.append(PROJECT_ROOT + '/app/lib') +sys.path.append(PROJECT_ROOT + '/config') +sys.path.append(PROJECT_ROOT + '/venv/bin/python3') +os.environ['DJANGO_SETTINGS_MODULE'] = 'config.settings' +django.setup() +from daily import retriever +retriever.get_yesterday_weather() diff --git a/bak/unused_apps/daily/migrations/0001_initial.py b/bak/unused_apps/daily/migrations/0001_initial.py new file mode 100644 index 0000000..70a9755 --- /dev/null +++ b/bak/unused_apps/daily/migrations/0001_initial.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-01-18 20:38 +from __future__ import unicode_literals + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + #('locations', '__first__'), + ] + + operations = [ + migrations.CreateModel( + name='CheckIn', + 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.DateField(default=django.utils.timezone.now)), + ('location', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ], + ), + migrations.CreateModel( + name='Daily', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('slug', models.SlugField()), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField()), + ('date', models.DateField(verbose_name='Date published')), + ('location', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ], + options={ + 'get_latest_by': 'date', + 'ordering': ('date',), + }, + ), + migrations.CreateModel( + name='Weather', + 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, null=True, srid=4326)), + ('date', models.DateField()), + ('temp_max', models.CharField(max_length=4)), + ('temp_min', models.CharField(max_length=4)), + ('temp_mean', models.CharField(max_length=4)), + ('wind_mean', models.CharField(max_length=10)), + ('wind_max', models.CharField(max_length=10)), + ('humidity', models.CharField(max_length=10)), + ('snow_amount', models.CharField(max_length=10)), + ('rain_amount', models.CharField(max_length=10)), + ('fog', models.NullBooleanField()), + ('rain', models.NullBooleanField()), + ('snow', models.NullBooleanField()), + ('hail', models.NullBooleanField()), + ('thunder', models.NullBooleanField()), + ], + ), + migrations.AddField( + model_name='daily', + name='weather', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='daily.Weather'), + ), + ] diff --git a/bak/unused_apps/daily/migrations/0002_auto_20160118_2144.py b/bak/unused_apps/daily/migrations/0002_auto_20160118_2144.py new file mode 100644 index 0000000..4769c21 --- /dev/null +++ b/bak/unused_apps/daily/migrations/0002_auto_20160118_2144.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-01-18 21:44 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='checkin', + name='location', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location'), + ), + ] diff --git a/bak/unused_apps/daily/migrations/0003_auto_20160119_1206.py b/bak/unused_apps/daily/migrations/0003_auto_20160119_1206.py new file mode 100644 index 0000000..d28c9ff --- /dev/null +++ b/bak/unused_apps/daily/migrations/0003_auto_20160119_1206.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-01-19 12:06 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily', '0002_auto_20160118_2144'), + ] + + operations = [ + migrations.AlterModelOptions( + name='checkin', + options={'get_latest_by': 'date', 'ordering': ('-date',)}, + ), + migrations.AlterModelOptions( + name='weather', + options={'get_latest_by': 'date', 'ordering': ('-date',), 'verbose_name_plural': 'Weather'}, + ), + migrations.AlterField( + model_name='daily', + name='body_html', + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name='daily', + name='body_markdown', + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name='daily', + name='slug', + field=models.SlugField(blank=True, null=True), + ), + migrations.AlterField( + model_name='daily', + name='title', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AlterField( + model_name='daily', + name='weather', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='daily.Weather'), + ), + ] diff --git a/bak/unused_apps/daily/migrations/0004_auto_20160119_1223.py b/bak/unused_apps/daily/migrations/0004_auto_20160119_1223.py new file mode 100644 index 0000000..3c77deb --- /dev/null +++ b/bak/unused_apps/daily/migrations/0004_auto_20160119_1223.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-01-19 12:23 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily', '0003_auto_20160119_1206'), + ] + + operations = [ + migrations.AlterField( + model_name='weather', + name='temp_max', + field=models.CharField(max_length=8), + ), + migrations.AlterField( + model_name='weather', + name='temp_mean', + field=models.CharField(max_length=8), + ), + migrations.AlterField( + model_name='weather', + name='temp_min', + field=models.CharField(max_length=8), + ), + ] diff --git a/bak/unused_apps/daily/migrations/0005_remove_daily_slug.py b/bak/unused_apps/daily/migrations/0005_remove_daily_slug.py new file mode 100644 index 0000000..8a896ba --- /dev/null +++ b/bak/unused_apps/daily/migrations/0005_remove_daily_slug.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-01-19 13:38 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily', '0004_auto_20160119_1223'), + ] + + operations = [ + migrations.RemoveField( + model_name='daily', + name='slug', + ), + ] diff --git a/bak/unused_apps/daily/migrations/0006_daily_weather_human.py b/bak/unused_apps/daily/migrations/0006_daily_weather_human.py new file mode 100644 index 0000000..7f701b4 --- /dev/null +++ b/bak/unused_apps/daily/migrations/0006_daily_weather_human.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-01-19 13:45 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily', '0005_remove_daily_slug'), + ] + + operations = [ + migrations.AddField( + model_name='daily', + name='weather_human', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/bak/unused_apps/daily/migrations/0007_auto_20160119_1604.py b/bak/unused_apps/daily/migrations/0007_auto_20160119_1604.py new file mode 100644 index 0000000..1acd5de --- /dev/null +++ b/bak/unused_apps/daily/migrations/0007_auto_20160119_1604.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-01-19 16:04 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily', '0006_daily_weather_human'), + ] + + operations = [ + migrations.AlterField( + model_name='daily', + name='location', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location'), + ), + ] diff --git a/bak/unused_apps/daily/migrations/0008_auto_20160124_0914.py b/bak/unused_apps/daily/migrations/0008_auto_20160124_0914.py new file mode 100644 index 0000000..eff3604 --- /dev/null +++ b/bak/unused_apps/daily/migrations/0008_auto_20160124_0914.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-01-24 09:14 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +from django.contrib.auth.models import User +lux = User.objects.get(username="luxagraf") + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('daily', '0007_auto_20160119_1604'), + ] + + operations = [ + migrations.AddField( + model_name='daily', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='daily', + name='date', + field=models.DateField(), + ), + ] diff --git a/bak/unused_apps/daily/migrations/0009_remove_daily_title.py b/bak/unused_apps/daily/migrations/0009_remove_daily_title.py new file mode 100644 index 0000000..085adf3 --- /dev/null +++ b/bak/unused_apps/daily/migrations/0009_remove_daily_title.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-01-24 16:27 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily', '0008_auto_20160124_0914'), + ] + + operations = [ + migrations.RemoveField( + model_name='daily', + name='title', + ), + ] diff --git a/bak/unused_apps/daily/migrations/0010_auto_20161023_2014.py b/bak/unused_apps/daily/migrations/0010_auto_20161023_2014.py new file mode 100644 index 0000000..72a68cf --- /dev/null +++ b/bak/unused_apps/daily/migrations/0010_auto_20161023_2014.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-10-23 20:14 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily', '0009_remove_daily_title'), + ] + + operations = [ + migrations.AlterModelOptions( + name='daily', + options={'get_latest_by': 'date', 'ordering': ('-date',)}, + ), + ] diff --git a/bak/unused_apps/daily/migrations/0011_auto_20171214_2239.py b/bak/unused_apps/daily/migrations/0011_auto_20171214_2239.py new file mode 100644 index 0000000..2cf9207 --- /dev/null +++ b/bak/unused_apps/daily/migrations/0011_auto_20171214_2239.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-12-14 22:39 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('daily', '0010_auto_20161023_2014'), + ] + + operations = [ + migrations.AlterField( + model_name='checkin', + name='location', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='adsjf', to='locations.Location'), + ), + ] diff --git a/bak/unused_apps/daily/migrations/__init__.py b/bak/unused_apps/daily/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/daily/migrations/__init__.py diff --git a/bak/unused_apps/daily/models.py b/bak/unused_apps/daily/models.py new file mode 100644 index 0000000..bd5f018 --- /dev/null +++ b/bak/unused_apps/daily/models.py @@ -0,0 +1,105 @@ +from django.contrib.gis.db import models +from django.conf import settings +from django import forms +from django.utils import timezone + +from locations.models import Location + +from utils.widgets import markdown_to_html + + +class CheckIn(models.Model): + point = models.PointField(blank=True) + location = models.ForeignKey(Location, blank=True, null=True, related_name='adsjf') + date = models.DateField(default=timezone.now) + + class Meta: + ordering = ('-date',) + get_latest_by = 'date' + + def __str__(self): + return str(self.date) + + @property + def lon(self): + '''Get the site's longitude.''' + return self.point.x + + @property + def lat(self): + '''Get the site's latitude.''' + return self.point.y + + def save(self): + 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(CheckIn, self).save() + + +class Weather(models.Model): + point = models.PointField(null=True, blank=True) + date = models.DateField() + temp_max = models.CharField(max_length=8) + temp_min = models.CharField(max_length=8) + temp_mean = models.CharField(max_length=8) + wind_mean = models.CharField(max_length=10) + wind_max = models.CharField(max_length=10) + humidity = models.CharField(max_length=10) + snow_amount = models.CharField(max_length=10) + rain_amount = models.CharField(max_length=10) + fog = models.NullBooleanField() + rain = models.NullBooleanField() + snow = models.NullBooleanField() + hail = models.NullBooleanField() + thunder = models.NullBooleanField() + + class Meta: + ordering = ('-date',) + get_latest_by = 'date' + verbose_name_plural = "Weather" + + def __str__(self): + return str(self.date) + + +class Daily(models.Model): + user = models.ForeignKey(settings.AUTH_USER_MODEL) + location = models.ForeignKey(Location, blank=True, null=True) + weather = models.ForeignKey(Weather, blank=True, null=True) + weather_human = models.TextField(blank=True, null=True) + body_html = models.TextField(blank=True, null=True) + body_markdown = models.TextField(blank=True, null=True) + date = models.DateField() + + class Meta: + ordering = ('-date',) + get_latest_by = 'date' + + def __str__(self): + return str(self.date) + + @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 + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + @property + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + def save(self, **kwargs): + if self.body_markdown: + self.body_html = markdown_to_html(self.body_markdown) + self.location = CheckIn.objects.latest().location + super(Daily, self).save() diff --git a/bak/unused_apps/daily/retriever.py b/bak/unused_apps/daily/retriever.py new file mode 100644 index 0000000..3b7da87 --- /dev/null +++ b/bak/unused_apps/daily/retriever.py @@ -0,0 +1,53 @@ +from datetime import datetime +import requests +import json + +from django.utils import timezone +from django.template.loader import render_to_string +from django.contrib.auth.models import User +from django.db.models import Q + +from .models import CheckIn, Daily, Weather + + +def create_daily(): + users = User.objects.filter(Q(username="luxagraf") | Q(username="corrinne")) + for user in users: + d, created = Daily.objects.get_or_create( + user=user, + date=timezone.now() + ) + if not created: + print("already existed") + + +def get_yesterday_weather(): + current = CheckIn.objects.latest() + PB_URL = "http://api.wunderground.com/api/39c3ce6a12b14e75/yesterday/q/%s,%s.json" % (current.lat, current.lon) + r = requests.get(PB_URL) + weather = json.loads(r.text) + data = weather['history']['dailysummary'][0] + date = "%s %s %s" % (data['date']['year'], data['date']['mon'], data['date']['mday']) + dt = datetime.strptime(date, "%Y %m %d") + w, created = Weather.objects.get_or_create( + point=current.point, + date=dt, + temp_max=data['maxtempi'], + temp_min=data['mintempi'], + temp_mean=data['meantempi'], + wind_mean=data['meanwindspdi'], + wind_max=data['maxwspdi'], + humidity=data['humidity'], + snow_amount=data['snowfalli'], + rain_amount=data['precipi'], + fog=int(data['fog']), + rain=int(data['rain']), + snow=int(data['snow']), + hail=int(data['hail']), + thunder=int(data['thunder']), + ) + dailies = Daily.objects.filter(date=dt) + for d in dailies: + d.weather = w + d.weather_human = render_to_string('human_weather.txt', {'object': w}).strip() + d.save() diff --git a/bak/unused_apps/ebay/__init__.py b/bak/unused_apps/ebay/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/ebay/__init__.py diff --git a/bak/unused_apps/ebay/admin.py b/bak/unused_apps/ebay/admin.py new file mode 100644 index 0000000..93bf87f --- /dev/null +++ b/bak/unused_apps/ebay/admin.py @@ -0,0 +1,15 @@ +from django.contrib import admin + +from .models import Item, Price, TrackedItem + + +@admin.register(Item) +class ItemAdmin(admin.ModelAdmin): + list_display = ('name', 'currently_own', 'purchase_price') + + +@admin.register(TrackedItem) +class TrackedItemAdmin(admin.ModelAdmin): + list_display = ('title', 'admin_link', 'date_ending', 'get_latest_price', 'amount_desired' ) + list_filter = ('amount_desired',) + diff --git a/bak/unused_apps/ebay/migrations/0001_initial.py b/bak/unused_apps/ebay/migrations/0001_initial.py new file mode 100644 index 0000000..3d2d34e --- /dev/null +++ b/bak/unused_apps/ebay/migrations/0001_initial.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-12-31 11:43 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Item', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.CharField(max_length=200)), + ('title', models.CharField(max_length=200)), + ('purchase_price', models.IntegerField()), + ('currently_own', models.BooleanField(default=False)), + ('sold', models.BooleanField(default=False)), + ('sale_price', models.IntegerField()), + ], + ), + migrations.CreateModel( + name='Price', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField()), + ('price', models.IntegerField()), + ], + ), + migrations.CreateModel( + name='TrackedItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.CharField(max_length=200)), + ('title', models.CharField(max_length=200)), + ], + ), + migrations.AddField( + model_name='price', + name='item', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ebay.TrackedItem'), + ), + ] diff --git a/bak/unused_apps/ebay/migrations/0002_auto_20161231_1401.py b/bak/unused_apps/ebay/migrations/0002_auto_20161231_1401.py new file mode 100644 index 0000000..46f5a12 --- /dev/null +++ b/bak/unused_apps/ebay/migrations/0002_auto_20161231_1401.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-12-31 14:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ebay', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='price', + name='date', + field=models.DateTimeField(blank=True), + ), + migrations.AlterField( + model_name='price', + name='price', + field=models.DecimalField(decimal_places=2, max_digits=6), + ), + ] diff --git a/bak/unused_apps/ebay/migrations/0003_auto_20161231_1419.py b/bak/unused_apps/ebay/migrations/0003_auto_20161231_1419.py new file mode 100644 index 0000000..1f05859 --- /dev/null +++ b/bak/unused_apps/ebay/migrations/0003_auto_20161231_1419.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-12-31 14:19 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ebay', '0002_auto_20161231_1401'), + ] + + operations = [ + migrations.AlterModelOptions( + name='price', + options={'get_latest_by': 'date', 'ordering': ('-date',)}, + ), + migrations.RenameField( + model_name='item', + old_name='title', + new_name='name', + ), + ] diff --git a/bak/unused_apps/ebay/migrations/0004_trackeditem_date_ending.py b/bak/unused_apps/ebay/migrations/0004_trackeditem_date_ending.py new file mode 100644 index 0000000..15f41e7 --- /dev/null +++ b/bak/unused_apps/ebay/migrations/0004_trackeditem_date_ending.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-12-31 14:26 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('ebay', '0003_auto_20161231_1419'), + ] + + operations = [ + migrations.AddField( + model_name='trackeditem', + name='date_ending', + field=models.DateTimeField(default=django.utils.timezone.now), + ), + ] diff --git a/bak/unused_apps/ebay/migrations/0005_auto_20161231_1444.py b/bak/unused_apps/ebay/migrations/0005_auto_20161231_1444.py new file mode 100644 index 0000000..a7a48ac --- /dev/null +++ b/bak/unused_apps/ebay/migrations/0005_auto_20161231_1444.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-12-31 14:44 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ebay', '0004_trackeditem_date_ending'), + ] + + operations = [ + migrations.AlterModelOptions( + name='trackeditem', + options={'get_latest_by': 'date_ending', 'ordering': ('-date_ending', 'amount_desired')}, + ), + migrations.AddField( + model_name='trackeditem', + name='amount_desired', + field=models.IntegerField(choices=[(0, '1'), (1, '2'), (2, '3'), (3, '4'), (4, '5')], default=0), + ), + ] diff --git a/bak/unused_apps/ebay/migrations/__init__.py b/bak/unused_apps/ebay/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/ebay/migrations/__init__.py diff --git a/bak/unused_apps/ebay/models.py b/bak/unused_apps/ebay/models.py new file mode 100644 index 0000000..089e0e4 --- /dev/null +++ b/bak/unused_apps/ebay/models.py @@ -0,0 +1,84 @@ +import datetime +import re +from django.utils.encoding import force_text +from django.contrib.gis.db import models +from django.utils import timezone +from bs4 import BeautifulSoup +import requests + +class Item(models.Model): + name = models.CharField(max_length=200) + purchase_price = models.IntegerField() + url = models.CharField(max_length=200) + currently_own = models.BooleanField(blank=True, default=False) + sold = models.BooleanField(blank=True, default=False) + sale_price = models.IntegerField() + + def save(self, **kwargs): + super(Item, self).save() + + def __str__(self): + return self.name + + +class TrackedItem(models.Model): + title = models.CharField(max_length=200) + url = models.CharField(max_length=200) + DESIRE = ( + (0, '1'), + (1, '2'), + (2, '3'), + (3, '4'), + (4, '5'), + ) + amount_desired = models.IntegerField(choices=DESIRE, default=0) + date_ending = models.DateTimeField(default=timezone.now) + + class Meta: + ordering = ('date_ending', 'amount_desired') + get_latest_by = 'date_ending' + + @property + def get_latest_price(self): + return self.price_set.latest().price + + def admin_link(self): + return force_text('<a href="%s">View Auction page</a>' % (self.url)) + admin_link.allow_tags = True + admin_link.short_description = 'Link' + + def __str__(self): + return self.title + + def save(self, **kwargs): + super(TrackedItem, self).save() + + +class Price(models.Model): + date = models.DateTimeField(blank=True) + price = models.DecimalField(max_digits=6, decimal_places=2) + item = models.ForeignKey(TrackedItem) + + class Meta: + ordering = ('-date',) + get_latest_by = 'date' + + def __str__(self): + return str(self.date) + + def save(self, **kwargs): + if not self.pk: + self.date = datetime.datetime.now() + super(Price, self).save() + + +def update_tracked_item_price(item): + r = requests.get(item.url) + soup = BeautifulSoup(r.text, "lxml") + numbers = re.compile('\d+(?:\.\d+)?') + price = soup.find(itemprop="price").get_text() + price = numbers.findall(price)[0] + l, created = Price.objects.get_or_create( + price=price, + item=item + ) diff --git a/bak/unused_apps/ebay/update_price.py b/bak/unused_apps/ebay/update_price.py new file mode 100755 index 0000000..b8b4c8c --- /dev/null +++ b/bak/unused_apps/ebay/update_price.py @@ -0,0 +1,17 @@ +import sys +import os +from os.path import dirname, abspath +import django +PROJECT_ROOT = abspath(dirname(dirname(dirname(__file__)))) + '/' +sys.path.append(PROJECT_ROOT) +sys.path.append(PROJECT_ROOT + '/app') +sys.path.append(PROJECT_ROOT + '/app/lib') +sys.path.append(PROJECT_ROOT + '/config') +sys.path.append(PROJECT_ROOT + '/venv/bin/python3') +os.environ['DJANGO_SETTINGS_MODULE'] = 'config.settings' +django.setup() +import datetime +from ebay.models import TrackedItem, update_tracked_item_price +items = TrackedItem.objects.filter(date_ending__gte=datetime.datetime.now()) +for item in items: + update_tracked_item_price(item) diff --git a/bak/unused_apps/essays/__init__.py b/bak/unused_apps/essays/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/essays/__init__.py diff --git a/bak/unused_apps/essays/admin.py b/bak/unused_apps/essays/admin.py new file mode 100644 index 0000000..ed39ca3 --- /dev/null +++ b/bak/unused_apps/essays/admin.py @@ -0,0 +1,47 @@ +from django.contrib import admin + +from utils.widgets import LGEntryForm + +from .models import Essay + + +@admin.register(Essay) +class EssayAdmin(admin.ModelAdmin): + form = LGEntryForm + list_display = ('title', 'pub_date', 'enable_comments', 'status') + list_filter = ('pub_date', 'enable_comments', 'status') + prepopulated_fields = {"slug": ('title',)} + fieldsets = ( + ('Entry', { + 'fields': ( + 'title', + 'sub_title', + 'body_markdown', + ('pub_date', 'status'), + 'meta_description', + ('slug', 'enable_comments', 'has_code'), + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + }), + ('meta', { + 'fields': ( + 'originally_published_by', + 'originally_published_by_url', + 'afterword', + 'preamble', + ('field_notes', 'books'), + ), + 'classes': ( + 'hide', + 'extrapretty', + 'wide' + ) + }), + ) + + class Media: + js = ('image-loader.js', 'next-prev-links.js') diff --git a/bak/unused_apps/essays/build.py b/bak/unused_apps/essays/build.py new file mode 100644 index 0000000..392e991 --- /dev/null +++ b/bak/unused_apps/essays/build.py @@ -0,0 +1,22 @@ +import os +from builder.base import BuildNew +from django.urls import reverse +from . import models + + +class BuildEssays(BuildNew): + + def build(self): + self.build_list_view() + self.build_detail_view() + # These are the unique classes for this model: + #self.build_feed("src:feed") + + def build_list_view(self): + response = self.client.get('/essays/') + self.write_file('essays/', response.content) + + +def essaybuilder(): + j = BuildEssays("essays", "essay") + j.build() diff --git a/bak/unused_apps/essays/migrations/0001_initial.py b/bak/unused_apps/essays/migrations/0001_initial.py new file mode 100644 index 0000000..7b7ea62 --- /dev/null +++ b/bak/unused_apps/essays/migrations/0001_initial.py @@ -0,0 +1,49 @@ +# Generated by Django 2.1.5 on 2019-02-04 14:08 + +from django.db import migrations, models +import django.db.models.deletion +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('photos', '0018_auto_20161130_1218'), + ('books', '0007_auto_20190131_2351'), + ('taxonomy', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Essay', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('sub_title', models.CharField(blank=True, max_length=200)), + ('dek', models.TextField(blank=True)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField()), + ('pub_date', models.DateTimeField(verbose_name='Date published')), + ('last_updated', models.DateTimeField(auto_now=True)), + ('enable_comments', models.BooleanField(default=False)), + ('has_code', models.BooleanField(default=False)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=0)), + ('meta_description', models.CharField(blank=True, max_length=256, null=True)), + ('post_type', models.IntegerField(choices=[(0, 'essay'), (1, 'tools'), (2, 'figment')], default=0)), + ('elsewhere', models.CharField(blank=True, max_length=400)), + ('has_video', models.BooleanField(blank=True, default=False)), + ('afterword', models.TextField(blank=True)), + ('books', models.ManyToManyField(blank=True, to='books.Book')), + ('featured_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photos.LuxImage')), + ('tags', taggit.managers.TaggableManager(blank=True, help_text='Topics Covered', through='taxonomy.TaggedItems', to='taxonomy.LuxTag', verbose_name='Tags')), + ], + options={ + 'verbose_name_plural': 'Essays', + 'ordering': ('-pub_date',), + 'get_latest_by': 'pub_date', + }, + ), + ] diff --git a/bak/unused_apps/essays/migrations/0002_auto_20190204_1541.py b/bak/unused_apps/essays/migrations/0002_auto_20190204_1541.py new file mode 100644 index 0000000..f4e6744 --- /dev/null +++ b/bak/unused_apps/essays/migrations/0002_auto_20190204_1541.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.5 on 2019-02-04 15:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='essay', + old_name='elsewhere', + new_name='originally_published_by', + ), + migrations.AddField( + model_name='essay', + name='originally_published_by_url', + field=models.CharField(blank=True, max_length=400), + ), + ] diff --git a/bak/unused_apps/essays/migrations/0003_essay_afterword_html.py b/bak/unused_apps/essays/migrations/0003_essay_afterword_html.py new file mode 100644 index 0000000..5f8301b --- /dev/null +++ b/bak/unused_apps/essays/migrations/0003_essay_afterword_html.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.5 on 2019-02-04 16:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0002_auto_20190204_1541'), + ] + + operations = [ + migrations.AddField( + model_name='essay', + name='afterword_html', + field=models.TextField(blank=True), + ), + ] diff --git a/bak/unused_apps/essays/migrations/0004_auto_20190205_0830.py b/bak/unused_apps/essays/migrations/0004_auto_20190205_0830.py new file mode 100644 index 0000000..65e2e5d --- /dev/null +++ b/bak/unused_apps/essays/migrations/0004_auto_20190205_0830.py @@ -0,0 +1,27 @@ +# Generated by Django 2.1.5 on 2019-02-05 08:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0003_essay_afterword_html'), + ] + + operations = [ + migrations.CreateModel( + name='PostType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('dek', models.TextField(blank=True)), + ('slug', models.SlugField()), + ], + ), + migrations.AlterField( + model_name='essay', + name='post_type', + field=models.IntegerField(choices=[(0, 'essays'), (1, 'tools'), (2, 'figments')], default=0), + ), + ] diff --git a/bak/unused_apps/essays/migrations/0005_auto_20190208_0946.py b/bak/unused_apps/essays/migrations/0005_auto_20190208_0946.py new file mode 100644 index 0000000..5b68bb4 --- /dev/null +++ b/bak/unused_apps/essays/migrations/0005_auto_20190208_0946.py @@ -0,0 +1,25 @@ +# Generated by Django 2.1.5 on 2019-02-08 09:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0004_auto_20190205_0830'), + ] + + operations = [ + migrations.DeleteModel( + name='PostType', + ), + migrations.RemoveField( + model_name='essay', + name='post_type', + ), + migrations.AddField( + model_name='essay', + name='preamble', + field=models.TextField(blank=True), + ), + ] diff --git a/bak/unused_apps/essays/migrations/0006_auto_20190303_1625.py b/bak/unused_apps/essays/migrations/0006_auto_20190303_1625.py new file mode 100644 index 0000000..dde70fd --- /dev/null +++ b/bak/unused_apps/essays/migrations/0006_auto_20190303_1625.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-03-03 16:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0005_auto_20190208_0946'), + ] + + operations = [ + migrations.AlterField( + model_name='essay', + name='field_notes', + field=models.ManyToManyField(blank=True, to='fieldnotes.FieldNote'), + ), + ] diff --git a/bak/unused_apps/essays/migrations/0006_remove_essay_has_video.py b/bak/unused_apps/essays/migrations/0006_remove_essay_has_video.py new file mode 100644 index 0000000..0842d8b --- /dev/null +++ b/bak/unused_apps/essays/migrations/0006_remove_essay_has_video.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.2 on 2019-02-27 21:22 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0005_auto_20190208_0946'), + ] + + operations = [ + migrations.RemoveField( + model_name='essay', + name='has_video', + ), + ] diff --git a/bak/unused_apps/essays/migrations/0007_auto_20190414_1455.py b/bak/unused_apps/essays/migrations/0007_auto_20190414_1455.py new file mode 100644 index 0000000..a5242cb --- /dev/null +++ b/bak/unused_apps/essays/migrations/0007_auto_20190414_1455.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-04-14 14:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0006_auto_20190303_1625'), + ] + + operations = [ + migrations.AddField( + model_name='essay', + name='preamble_html', + field=models.TextField(blank=True), + ), + ] diff --git a/bak/unused_apps/essays/migrations/0007_essay_has_video.py b/bak/unused_apps/essays/migrations/0007_essay_has_video.py new file mode 100644 index 0000000..0057e95 --- /dev/null +++ b/bak/unused_apps/essays/migrations/0007_essay_has_video.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2019-02-27 21:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0006_remove_essay_has_video'), + ] + + operations = [ + migrations.AddField( + model_name='essay', + name='has_video', + field=models.BooleanField(blank=True, default=False), + ), + ] diff --git a/bak/unused_apps/essays/migrations/0008_merge_20190303_1638.py b/bak/unused_apps/essays/migrations/0008_merge_20190303_1638.py new file mode 100644 index 0000000..7c155d8 --- /dev/null +++ b/bak/unused_apps/essays/migrations/0008_merge_20190303_1638.py @@ -0,0 +1,14 @@ +# Generated by Django 2.1.2 on 2019-03-03 16:38 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0007_essay_has_video'), + ('essays', '0006_auto_20190303_1625'), + ] + + operations = [ + ] diff --git a/bak/unused_apps/essays/migrations/0009_merge_20190414_1500.py b/bak/unused_apps/essays/migrations/0009_merge_20190414_1500.py new file mode 100644 index 0000000..83a8323 --- /dev/null +++ b/bak/unused_apps/essays/migrations/0009_merge_20190414_1500.py @@ -0,0 +1,14 @@ +# Generated by Django 2.1.2 on 2019-04-14 15:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('essays', '0008_merge_20190303_1638'), + ('essays', '0007_auto_20190414_1455'), + ] + + operations = [ + ] diff --git a/bak/unused_apps/essays/migrations/0010_essay_field_notes.py b/bak/unused_apps/essays/migrations/0010_essay_field_notes.py new file mode 100644 index 0000000..ca15b38 --- /dev/null +++ b/bak/unused_apps/essays/migrations/0010_essay_field_notes.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.7 on 2019-07-04 09:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fieldnotes', '0002_auto_20190303_1222'), + ('essays', '0009_merge_20190414_1500'), + ] + + operations = [ + migrations.AddField( + model_name='essay', + name='field_notes', + field=models.ManyToManyField(blank=True, to='fieldnotes.FieldNote'), + ), + ] diff --git a/bak/unused_apps/essays/migrations/__init__.py b/bak/unused_apps/essays/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/essays/migrations/__init__.py diff --git a/bak/unused_apps/essays/models.py b/bak/unused_apps/essays/models.py new file mode 100644 index 0000000..c75f72d --- /dev/null +++ b/bak/unused_apps/essays/models.py @@ -0,0 +1,91 @@ +from django.db import models +from django.urls import reverse +from django.contrib.sitemaps import Sitemap +import datetime +from itertools import chain + +from taggit.managers import TaggableManager + +from taxonomy.models import TaggedItems +from utils.util import render_images, markdown_to_html +from fieldnotes.models import FieldNote +from books.models import Book +from photos.models import LuxImage + + +POST_TYPE = ( + (0, 'essays'), + (1, 'tools'), + (2, 'figments'), +) + + +class Essay(models.Model): + title = models.CharField(max_length=200) + sub_title = models.CharField(max_length=200, blank=True) + dek = models.TextField(blank=True) + preamble = models.TextField(blank=True) + preamble_html = models.TextField(blank=True) + slug = models.SlugField(unique_for_date='pub_date') + body_html = models.TextField(blank=True) + body_markdown = models.TextField() + pub_date = models.DateTimeField('Date published') + last_updated = models.DateTimeField(auto_now=True) + enable_comments = models.BooleanField(default=False) + has_code = models.BooleanField(default=False) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + meta_description = models.CharField(max_length=256, null=True, blank=True) + tags = TaggableManager(through=TaggedItems, blank=True, help_text='Topics Covered') + originally_published_by = models.CharField(max_length=400, blank=True) + originally_published_by_url = models.CharField(max_length=400, blank=True) + featured_image = models.ForeignKey(LuxImage, on_delete=models.CASCADE, null=True, blank=True) + has_video = models.BooleanField(blank=True, default=False) + field_notes = models.ManyToManyField(FieldNote, blank=True) + books = models.ManyToManyField(Book, blank=True) + afterword = models.TextField(blank=True) + afterword_html = models.TextField(blank=True) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + verbose_name_plural = 'Essays' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse('essays:detail', kwargs={"slug": self.slug}) + + def comment_period_open(self): + return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + @property + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + def save(self): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + self.afterword_html = markdown_to_html(self.afterword) + self.preamble_html = markdown_to_html(self.preamble) + super(Essay, self).save() + + +class EssaySitemap(Sitemap): + changefreq = "never" + priority = 1.0 + protocol = "https" + + def items(self): + return Essay.objects.filter(status=1) + + def lastmod(self, obj): + return obj.pub_date diff --git a/bak/unused_apps/essays/urls.py b/bak/unused_apps/essays/urls.py new file mode 100644 index 0000000..8216f06 --- /dev/null +++ b/bak/unused_apps/essays/urls.py @@ -0,0 +1,28 @@ +from django.urls import path, re_path + +from . import views + +app_name = "essays" + +urlpatterns = [ + #path( + # r'topic/<str:slug>', + # views.TopicListView.as_view(), + # name="list_topics" + #), + path( + r'<str:slug>', + views.EntryDetailView.as_view(), + name="detail" + ), + path( + r'<str:slug>', + views.EntryDetailViewTXT.as_view(), + name="detail-txt" + ), + path( + r'', + views.EssayListView.as_view(), + name="list", + ), +] diff --git a/bak/unused_apps/essays/views.py b/bak/unused_apps/essays/views.py new file mode 100644 index 0000000..f8c68c7 --- /dev/null +++ b/bak/unused_apps/essays/views.py @@ -0,0 +1,47 @@ +from django.views.generic import ListView +from django.views.generic.detail import DetailView +from django.contrib.syndication.views import Feed + + +from .models import Essay + + +class EssayListView(ListView): + model = Essay + + def get_queryset(self, **kwargs): + qs = Essay.objects.filter(status=1) + return qs + + +class EntryDetailView(DetailView): + model = Essay + + +class EntryDetailViewTXT(EntryDetailView): + template_name = "essays/entry_detail.txt" + + +''' +class TopicListView(ListView): + template_name = 'archives/src_home.html' + + def queryset(self): + return Post.objects.filter(topics__slug=self.kwargs['slug']) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(TopicListView, self).get_context_data(**kwargs) + context['topic'] = Topic.objects.get(slug__exact=self.kwargs['slug']) + return context + + +class SrcRSSFeedView(Feed): + title = "luxagraf:src Code and Technology" + link = "/src/" + description = "Latest postings to luxagraf.net/src" + description_template = 'feeds/blog_description.html' + + def items(self): + return Post.objects.filter(status__exact=1).order_by('-pub_date')[:10] +''' diff --git a/bak/unused_apps/expenses/__init__.py b/bak/unused_apps/expenses/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/expenses/__init__.py diff --git a/bak/unused_apps/expenses/admin.py b/bak/unused_apps/expenses/admin.py new file mode 100644 index 0000000..a754883 --- /dev/null +++ b/bak/unused_apps/expenses/admin.py @@ -0,0 +1,25 @@ +from django.contrib import admin + +from .models import LuxExpense, Trip, Expense, Month + + +@admin.register(Trip) +class TripAdmin(admin.ModelAdmin): + list_display = ('name', 'start') + + +@admin.register(Expense) +class ExpenseAdmin(admin.ModelAdmin): + list_display = ('name', 'category', 'amount', 'date_month') + list_filter = ('date', 'category') + + +@admin.register(LuxExpense) +class LuxExpenseAdmin(admin.ModelAdmin): + list_display = ('category', 'amount', 'month') + list_filter = ('month', 'category') + + +@admin.register(Month) +class MonthAdmin(admin.ModelAdmin): + pass diff --git a/bak/unused_apps/expenses/build.py b/bak/unused_apps/expenses/build.py new file mode 100644 index 0000000..7241a70 --- /dev/null +++ b/bak/unused_apps/expenses/build.py @@ -0,0 +1,34 @@ +import os +from django.core.urlresolvers import reverse +from builder.base import BuildNew + + +class BuildExpenses(BuildNew): + + def build(self): + self.build_detail_view() + self.build_list_view( + base_path=reverse("expenses:list_trip"), + paginate_by=24 + ) + + def get_model_queryset(self): + return self.model.objects.all() + + def build_detail_view(self): + ''' + write out all the expenses for each trip + ''' + for obj in self.get_model_queryset(): + url = obj.get_absolute_url() + path, slug = os.path.split(url) + path = '%s/' % path + # write html + response = self.client.get(url) + print(path, slug) + self.write_file(path, response.content, filename=slug) + + +def builder(): + j = BuildExpenses("expenses", "trip") + j.build() diff --git a/bak/unused_apps/expenses/migrations/0001_initial.py b/bak/unused_apps/expenses/migrations/0001_initial.py new file mode 100644 index 0000000..5ff70cd --- /dev/null +++ b/bak/unused_apps/expenses/migrations/0001_initial.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2017-04-28 22:47 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Expense', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('amount', models.DecimalField(decimal_places=2, max_digits=8)), + ('date', models.DateTimeField(default=django.utils.timezone.now)), + ('notes', models.TextField(blank=True, null=True)), + ('category', models.CharField(choices=[('1', 'Groceries'), ('2', 'Restaurants'), ('3', 'Camping'), ('4', 'Petrol'), ('5', 'Bus'), ('6', 'Misc')], default=1, max_length=2)), + ], + options={ + 'ordering': ('-date',), + }, + ), + migrations.CreateModel( + name='Trip', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('slug', models.SlugField()), + ('start', models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True)), + ('end', models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True)), + ('dek', models.TextField(blank=True, null=True)), + ], + ), + migrations.AddField( + model_name='expense', + name='trip', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='expenses.Trip'), + ), + ] diff --git a/bak/unused_apps/expenses/migrations/0002_luxexpense.py b/bak/unused_apps/expenses/migrations/0002_luxexpense.py new file mode 100644 index 0000000..9edfdb4 --- /dev/null +++ b/bak/unused_apps/expenses/migrations/0002_luxexpense.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2017-04-28 22:48 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('expenses', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='LuxExpense', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('category', models.CharField(choices=[('1', 'Groceries'), ('2', 'Restaurants'), ('3', 'Camping'), ('4', 'Petrol'), ('5', 'Bus'), ('6', 'Misc')], default=1, max_length=2)), + ('amount', models.DecimalField(decimal_places=2, max_digits=8)), + ('date', models.DateTimeField(default=django.utils.timezone.now)), + ('notes', models.TextField(blank=True, null=True)), + ], + ), + ] diff --git a/bak/unused_apps/expenses/migrations/0003_auto_20170429_0748.py b/bak/unused_apps/expenses/migrations/0003_auto_20170429_0748.py new file mode 100644 index 0000000..69f14ec --- /dev/null +++ b/bak/unused_apps/expenses/migrations/0003_auto_20170429_0748.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2017-04-29 07:48 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('expenses', '0002_luxexpense'), + ] + + operations = [ + migrations.CreateModel( + name='Month', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('year', models.IntegerField()), + ('date', models.DateTimeField(default=django.utils.timezone.now)), + ('notes', models.TextField(blank=True, null=True)), + ], + ), + migrations.RemoveField( + model_name='luxexpense', + name='date', + ), + migrations.RemoveField( + model_name='luxexpense', + name='notes', + ), + migrations.AddField( + model_name='luxexpense', + name='month', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='expenses.Month'), + ), + ] diff --git a/bak/unused_apps/expenses/migrations/__init__.py b/bak/unused_apps/expenses/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/expenses/migrations/__init__.py diff --git a/bak/unused_apps/expenses/models.py b/bak/unused_apps/expenses/models.py new file mode 100644 index 0000000..fbcdd7a --- /dev/null +++ b/bak/unused_apps/expenses/models.py @@ -0,0 +1,62 @@ +from django.db import models +from django.urls import reverse +from django.utils import timezone + +CATS = ( + ('1', "Groceries"), + ('2', "Restaurants"), + ('3', "Camping"), + ('4', "Petrol"), + ('5', "Bus"), + ('6', "Misc"), +) + + +class Trip(models.Model): + name = models.CharField(max_length=200) + slug = models.SlugField() + start = models.DateTimeField(default=timezone.now, blank=True, null=True) + end = models.DateTimeField(default=timezone.now, blank=True, null=True) + dek = models.TextField(null=True, blank=True) + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("expenses:list_expense", kwargs={"slug": self.slug}) + +class Month(models.Model): + name = models.CharField(max_length=200) + year = models.IntegerField() + date = models.DateTimeField(default=timezone.now) + notes = models.TextField(null=True, blank=True) + + def __str__(self): + return "%s %s" %(self.name, self.year) + +class LuxExpense(models.Model): + category = models.CharField(max_length=2, choices=CATS, default=1) + amount = models.DecimalField(max_digits=8, decimal_places=2) + month = models.ForeignKey(Month, on_delete=models.CASCADE, null=True) + + def __str__(self): + return '%s-%s' %(self.month, self.get_category_display()) + + + +class Expense(models.Model): + name = models.CharField(max_length=200) + amount = models.DecimalField(max_digits=8, decimal_places=2) + date = models.DateTimeField(default=timezone.now) + notes = models.TextField(null=True, blank=True) + category = models.CharField(max_length=2, choices=CATS, default=1) + trip = models.ForeignKey(Trip, on_delete=models.CASCADE, null=True) + + class Meta: + ordering = ('-date',) + + def __str__(self): + return self.name + + def date_month(self): + return self.date.strftime("%b %Y") diff --git a/bak/unused_apps/expenses/urls.py b/bak/unused_apps/expenses/urls.py new file mode 100644 index 0000000..6ad732e --- /dev/null +++ b/bak/unused_apps/expenses/urls.py @@ -0,0 +1,22 @@ +from django.conf.urls import url +from . import views + +app_name = "expenses" + +urlpatterns = [ + #url( + # r'(?P<slug>[-\w]+)$', + # views.ExpenseListView.as_view(), + # name='list_expense' + #), + url( + r'(?P<slug>[-\w]+)$', + views.LuxExpenseListView.as_view(), + name='list_expense' + ), + url( + r'^$', + views.TripListView.as_view(), + name='list_trip' + ) +] diff --git a/bak/unused_apps/expenses/views.py b/bak/unused_apps/expenses/views.py new file mode 100644 index 0000000..98a4c70 --- /dev/null +++ b/bak/unused_apps/expenses/views.py @@ -0,0 +1,38 @@ +from django.views.generic import ListView + +from .models import LuxExpense, Expense, Trip, CATS + +class ExpenseListView(ListView): + model = Expense + context_object_name = 'object_list' + template_name = 'details/expenses.html' + + def get_queryset(self): + return Expense.objects.filter( + trip__slug=self.kwargs['slug'] + ) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(ExpenseListView, self).get_context_data(**kwargs) + context['categories'] = CATS + return context + +class LuxExpenseListView(ListView): + model = LuxExpense + context_object_name = 'object_list' + template_name = 'details/expenses.html' + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(LuxExpenseListView, self).get_context_data(**kwargs) + context['categories'] = CATS + return context + +class TripListView(ListView): + model = Trip + context_object_name = 'object_list' + template_name = 'archives/expenses.html' + + def get_queryset(self): + return Trip.objects.all() diff --git a/bak/unused_apps/fieldnotes/__init__.py b/bak/unused_apps/fieldnotes/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/fieldnotes/__init__.py diff --git a/bak/unused_apps/fieldnotes/admin.py b/bak/unused_apps/fieldnotes/admin.py new file mode 100644 index 0000000..838063d --- /dev/null +++ b/bak/unused_apps/fieldnotes/admin.py @@ -0,0 +1,38 @@ +from django.contrib import admin +from django.contrib.contenttypes.admin import GenericTabularInline + +from .models import FieldNote +from utils.widgets import LGEntryForm, OLAdminBase +from utils.util import get_latlon + + +@admin.register(FieldNote) +class NoteAdmin(OLAdminBase): + form = LGEntryForm + prepopulated_fields = {"slug": ('title',)} + list_display = ('title', 'slug', 'pub_date', 'location') + fieldsets = ( + ('Note', { + 'fields': ( + ('title', 'note_type'), + 'subtitle', + 'body_markdown', + 'slug', + ('pub_date', 'status'), + 'point' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + lat, lon = get_latlon() + default_lon = lon + default_lat = lat + default_zoom = 10 + + class Media: + js = ('image-loader.js', 'next-prev-links.js') diff --git a/bak/unused_apps/fieldnotes/build.py b/bak/unused_apps/fieldnotes/build.py new file mode 100644 index 0000000..4c5c83b --- /dev/null +++ b/bak/unused_apps/fieldnotes/build.py @@ -0,0 +1,36 @@ +import os +from django.urls import reverse +from builder.base import BuildNew + + +class BuildNotes(BuildNew): + + def build(self): + self.build_detail_view() + self.build_list_view( + base_path=reverse("fieldnotes:list"), + paginate_by=24 + ) + self.build_year_view("fieldnotes:list_year") + self.build_month_view("fieldnotes:list_month") + + def get_model_queryset(self): + return self.model.objects.all() + + def build_detail_view(self): + ''' + write out all the expenses for each trip + ''' + for obj in self.get_model_queryset(): + url = obj.get_absolute_url() + path, slug = os.path.split(url) + path = '%s/' % path + # write html + response = self.client.get(url) + print(path, slug) + self.write_file(path, response.content, filename=slug) + + +def builder(): + j = BuildNotes("fieldnotes", "fieldnote") + j.build() diff --git a/bak/unused_apps/fieldnotes/migrations/0001_initial.py b/bak/unused_apps/fieldnotes/migrations/0001_initial.py new file mode 100644 index 0000000..cec67fb --- /dev/null +++ b/bak/unused_apps/fieldnotes/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 2.1.7 on 2019-03-03 12:13 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('locations', '0017_auto_20190217_1849'), + ] + + operations = [ + migrations.CreateModel( + name='FieldNote', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=250)), + ('subtitle', models.CharField(blank=True, max_length=250)), + ('slug', models.SlugField(blank=True, unique_for_date='pub_date')), + ('pub_date', models.DateTimeField(default=django.utils.timezone.now)), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField(verbose_name='Note')), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=1)), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ], + options={ + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date',), + }, + ), + ] diff --git a/bak/unused_apps/fieldnotes/migrations/0002_auto_20190303_1222.py b/bak/unused_apps/fieldnotes/migrations/0002_auto_20190303_1222.py new file mode 100644 index 0000000..c0e352e --- /dev/null +++ b/bak/unused_apps/fieldnotes/migrations/0002_auto_20190303_1222.py @@ -0,0 +1,25 @@ +# Generated by Django 2.1.7 on 2019-03-03 12:22 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0018_auto_20161130_1218'), + ('fieldnotes', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='fieldnote', + name='featured_image', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='photos.LuxImage'), + ), + migrations.AddField( + model_name='fieldnote', + name='note_type', + field=models.IntegerField(choices=[(0, 'Note'), (1, 'Photo')], default=0), + ), + ] diff --git a/bak/unused_apps/fieldnotes/migrations/__init__.py b/bak/unused_apps/fieldnotes/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/fieldnotes/migrations/__init__.py diff --git a/bak/unused_apps/fieldnotes/models.py b/bak/unused_apps/fieldnotes/models.py new file mode 100644 index 0000000..c14ba80 --- /dev/null +++ b/bak/unused_apps/fieldnotes/models.py @@ -0,0 +1,113 @@ +import re +from django import forms +from django.contrib.gis.db import models +from django.utils import timezone +from django.urls import reverse +from django.conf import settings +from django.contrib.sitemaps import Sitemap + +from locations.models import Location, CheckIn +from photos.models import LuxImage +from utils.util import render_images, parse_image, markdown_to_html, extract_main_image + + +def render_images(s): + s = re.sub('<img(.*)/>', parse_image, s) + return s + + +class FieldNote(models.Model): + title = models.CharField(max_length=250, blank=True) + subtitle = models.CharField(max_length=250, blank=True) + slug = models.SlugField(unique_for_date='pub_date', blank=True) + pub_date = models.DateTimeField(default=timezone.now) + body_html = models.TextField(blank=True) + body_markdown = models.TextField('Note') + point = models.PointField(blank=True, null=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True, null=True) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=1) + NOTE_TYPE = ( + (0, 'Note'), + (1, 'Photo'), + ) + note_type = models.IntegerField(choices=NOTE_TYPE, default=0) + featured_image = models.ForeignKey(LuxImage, on_delete=models.SET_NULL, blank=True, null=True) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("fieldnotes:detail", kwargs={"year": self.pub_date.year, "month": self.pub_date.strftime("%m"), "slug": self.slug}) + + def get_object_type(self): + return 'fieldnote' + + @property + def region(self): + return self.location.lux_region + + @property + def longitude(self): + '''Get the site's longitude.''' + return round(self.point.x, 2) + + @property + def latitude(self): + '''Get the site's latitude.''' + return round(self.point.y, 2) + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date() + + @property + def get_next_published(self): + return self.get_next_by_pub_date() + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + except model.DoesNotExist: + return '' + + def save(self, *args, **kwargs): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + if not self.point: + self.point = CheckIn.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)) + if not self.id: + self.pub_date = timezone.now() + self.date_last_updated = timezone.now() + self.featured_image = extract_main_image(self.body_markdown) + super(FieldNote, self).save() + + +class FieldNoteSitemap(Sitemap): + changefreq = "never" + priority = 0.7 + protocol = "https" + + def items(self): + return FieldNote.objects.filter(status=1) + + def lastmod(self, obj): + return obj.pub_date diff --git a/bak/unused_apps/fieldnotes/templates/fieldnotes/fieldnote_archive_list_date.html b/bak/unused_apps/fieldnotes/templates/fieldnotes/fieldnote_archive_list_date.html new file mode 100644 index 0000000..5d6865f --- /dev/null +++ b/bak/unused_apps/fieldnotes/templates/fieldnotes/fieldnote_archive_list_date.html @@ -0,0 +1,43 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load html5_datetime %} +{% block pagetitle %} Field Notes | luxagraf {% endblock %} +{% block metadescription %} Rough notes and sketches from the field {% endblock %} +{%block bodyid%}id="field-notes"{%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>{% if month or year %}<a href="{% url 'fieldnotes:list' %}">Field Notes</a> →{%else%}Field Notes{%endif%}</li> + <li>{% if not month %}{{year|date:"Y"}}{%else%}<a href="/field-notes/{{month|date:"Y"}}/">{{month|date:"Y"}}</a> →{%endif%}</li> + {% if month %}<li itemprop="title">{{month|date:"F"}}</li>{% endif %} + </ul> + <main role="main" id="essay-archive" class="essay-archive archive-list"> + <div class="essay-intro"> + <h2>Field Notes {% if month or year %}{% if month %} from {{month|date:"F"}} {{month|date:"Y"}}{%else%} from {{year|date:"Y"}}{%endif%}{%endif%}</h2> + <p>Quick notes, sketches and images from the road. This is the semi-orgnized brain dump that comes before the more organized <a href="/jrnl/" title="read the journal">journal entries</a> and <a href="/essays/" title="read essays">essays</a>. If I used social media this is the stuff I'd probably put there, but I prefer to put it here, even if it means a lot few people read it.</p> + </div> + <ul class="fancy-archive-list">{% for object in object_list %}{% if object.slug != 'about' %} + <li class="h-entry hentry" itemscope itemType="http://schema.org/Article"> + <a href="{{object.get_absolute_url}}"> + {% if object.featured_image %}<div class="circle-img-wrapper"><img src="{{object.featured_image.get_thumbnail_url}}" alt="{{object.featured_image.alt}}" /></div>{%endif%} + <span class="date dt-published">{{object.pub_date|date:"F d, Y"}}</span> + <a href="{{object.get_absolute_url}}"> + <h2>{{object.title|safe|smartypants|widont}}</h2> + {% if object.subtitle %}<h3 class="p-summary">{{object.subtitle|safe|smartypants|widont}}</h3>{%endif%} + </a> + {% if object.location %}<h4 class="p-location h-adr post-location" itemprop="geo" itemscope itemtype="http://data-vocabulary.org/Geo"> + <span class="p-locality">{{object.location.name|smartypants|safe}}</span>, + <span class="p-region">{{object.location.state_name}}</span>, + <span class="p-country-name">{{object.location.country_name}}</span> + <data class="p-latitude" value="{{object.latitude}}"></data> + <data class="p-longitude" value="{{object.longitude}}"></data> + </h4>{% endif %} + </li> + {%endif%}{%endfor%}</ul> + </main> + +{% endblock %} + + + diff --git a/bak/unused_apps/fieldnotes/templates/fieldnotes/fieldnote_detail.html b/bak/unused_apps/fieldnotes/templates/fieldnotes/fieldnote_detail.html new file mode 100644 index 0000000..d1c648b --- /dev/null +++ b/bak/unused_apps/fieldnotes/templates/fieldnotes/fieldnote_detail.html @@ -0,0 +1,95 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load html5_datetime %} +{% load month_number_to_name %} +{% block pagetitle %}{{object.title|title|smartypants|safe}} - Luxagraf, Field Notes{% endblock %} + +{% block metadescription %}{{object.body_html|striptags|safe|truncatewords:30}}{% endblock %} +{%block extrahead%} + <link rel="canonical" href="http://luxagraf.net{{object.get_absolute_url}}" /> + <meta name="ICBM" content="{{object.latitude}}, {{object.longitude}}" /> + <meta name="geo.position" content="{{object.latitude}}; {{object.longitude}}" /> + <meta name="geo.placename" content="{% if object.location.country.name == "United States" %}{{object.location.name|smartypants|safe}}, {{object.state.name}}{%else%}{{object.location.name|smartypants|safe}}, {{object.country.name}}{%endif%}"> + <meta name="geo.region" content="{{object.country.iso2}}{%if object.state.code != '' %}-{{object.state.code}}{%endif%}"> +{%endblock%} +{% block bodyid %}class="notes--permalin detail" id="archive-{% if month %}{{month|month_number_to_name}}{%endif%}{{year}}"{%endblock%} +{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +{% block primary %}<main role="main"> + <article class="h-entry hentry {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %} post--article--double{%endif%}{%endwith%}" itemscope itemType="http://schema.org/BlogPosting"> + <header id="header" class="post-header {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %}post--header--double{%endif%}{%endwith%}"> + <h1 class="p-name entry-title post-title" itemprop="headline">{%if object.template_name == 1 or object.template_name == 3 %}{{object.title|smartypants|safe}}{%else%}{{object.title|smartypants|safe}}{%endif%}</h1> + {% if object.subtitle %}<h2 class="post-subtitle">{{object.subtitle|smartypants|safe}}</h2>{%endif%} + <div class="post-linewrapper"> + {% if object.location %}<div class="p-location h-adr adr post-location" itemprop="contentLocation" itemscope itemtype="http://schema.org/Place"> + <h3 class="h-adr" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">{% if object.location.country_name == "United States" %}<span class="p-locality locality" itemprop="addressLocality">{{object.location.name|smartypants|safe}}</span>, <a class="p-region region" href="/jrnl/united-states/" title="travel writing from the United States">{{object.location.state_name|safe}}</a>, <span class="p-country-name" itemprop="addressCountry">U.S.</span>{%else%}<span class="p-region" itemprop="addressRegion">{{object.location.name|smartypants|safe}}</span>, <a class="p-country-name country-name" href="/jrnl/{{object.location.country_slug}}/" title="travel writing from {{object.location.country_name}}"><span itemprop="addressCountry">{{object.location.country_name|safe}}</span></a>{%endif%}</h3> + – <a href="" onclick="showMap({{object.latitude}}, {{object.longitude}}, { type:'point', lat:'{{object.latitude}}', lon:'{{object.longitude}}'}); return false;" title="see a map">Map</a> + </div>{%endif%} + <time class="dt-published published dt-updated post-date" datetime="{{object.pub_date|date:'c'}}" itemprop="datePublished">{{object.pub_date|date:"F"}} <span>{{object.pub_date|date:"j, Y"}}</span></time> + <span class="hide" itemprop="author" itemscope itemtype="http://schema.org/Person">by <a class="p-author h-card" href="/about"><span itemprop="name">Scott Gilbertson</span></a></span> + </div> + </header> + <div class="e-content"> + {{object.body_html|safe|smartypants}} + </div> + <span class="p-author h-card"> + <data class="p-name" value="Scott Gilbertson"></data> + <data class="u-url" value="https://luxagraf.net/"></data> + </span> + <footer> + {%comment%}<p class="note--date"> + <a class="u-url" href="{{object.get_absolute_url}}" rel="bookmark"><time class="dt-published" datetime="{{object.pub_date|html5_datetime}}">{{object.pub_date|date:"F j, Y"}}</time></a> + </p>{%endcomment%} + {% comment %} {% if object.twitter_id %} + <ul class="note--actions"> + <li><a rel="syndication" class="u-syndication" href="https://twitter.com/luxagraf/status/{{object.twitter_id}}">View on Twitter</a></li> + <li> + <indie-action do="reply" with="{{SITE_URL}}{{object.get_absolute_url}}"><a href="https://twitter.com/intent/tweet?in_reply_to={{object.twitter_id}}">Reply</a></indie-action> + </li> + <li> + <indie-action do="post" with="{{SITE_URL}}{{object.get_absolute_url}}"> + <a href="https://twitter.com/intent/retweet?tweet_id={{object.twitter_id}}">Retweet</a> + </indie-action> + </li> + <li> + <indie-action do="bookmark" with="{{SITE_URL}}{{object.get_absolute_url}}"> + <a href="https://twitter.com/intent/favorite?tweet_id={{object.twitter_id}}">Favourite</a> + </indie-action> + </li> + </ul>{% endif %}{% endcomment %} + </footer> + + + {% with object.get_next_published as next %} + {% with object.get_previous_published as prev %} + <nav id="page-navigation"> + <ul>{% if prev%} + <li rel="previous" id="next"><span class="bl">Previous:</span> + <a href="{{ prev.get_absolute_url }}" rel="prev" title=" {{prev.title}}">{{prev.title|safe}}</a> + </li>{%endif%}{% if next%} + <li rel="next" id="prev"><span class="bl">Next:</span> + <a href="{{ next.get_absolute_url }}" rel="next" title=" {{next.title}}">{{next.title|safe}}</a> + </li>{%endif%} + </ul> + </nav>{%endwith%}{%endwith%} + </article> +</main> +{% endblock %} + +{% block js %} +<script> +document.addEventListener("DOMContentLoaded", function(event) { + var leaflet = document.createElement('script'); + leaflet.src = "/media/js/leaflet-master/leaflet-mod.js"; + document.body.appendChild(leaflet); + leaflet.onload = function(){ + var detail = document.createElement('script'); + detail.src = "/media/js/detail.min.js"; + document.body.appendChild(detail); + detail.onload = function(){ + createMap(); + var open = false; + } + } +}); +</script> +{%endblock%} diff --git a/bak/unused_apps/fieldnotes/templates/fieldnotes/fieldnote_list.html b/bak/unused_apps/fieldnotes/templates/fieldnotes/fieldnote_list.html new file mode 100644 index 0000000..37cd0ca --- /dev/null +++ b/bak/unused_apps/fieldnotes/templates/fieldnotes/fieldnote_list.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load get_next %} +{% load html5_datetime %} +{% load pagination_tags %} +{% block pagetitle %} Field Notes | luxagraf {% endblock %} +{% block metadescription %}Rough notes and sketches from the field {% endblock %} +{%block bodyid%}id="field-notes"{%endblock%} +{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +{% block primary %}<main role="main" id="essay-archive" class="essay-archive archive-list"> + <div class="essay-intro"> + <h2>Field Notes</h2> + <p>Quick notes, sketches, and images from the road. This is the semi-organized brain dump that comes before the more organized <a href="/jrnl/" title="read the journal">journal entries</a>. If I used social media this is the stuff I'd probably put there, but I prefer to put it here, even if it means a lot fewer people read it.</p> + </div> + {% autopaginate object_list 30 %} + <ul class="fancy-archive-list">{% for object in object_list %}{% if object.get_object_type == "fieldnote" %} + {% with next_element=object_list|next:forloop.counter0 %} + {% with prev_element=object_list|previous:forloop.counter0 %} + <li class="h-entry hentry {% if not next_element.get_object_type == "fieldnote"%}note-pad-bottom{%endif%} {% if not prev_element.get_object_type == "fieldnote"%}note-pad-top{%endif%}" itemscope itemType="http://schema.org/Article"> + <a href="{{object.get_absolute_url}}" class="u-url"> + {% if object.featured_image %}<div class="circle-img-wrapper"><img src="{{object.featured_image.get_thumbnail_url}}" alt="{{object.featured_image.alt}}" class="u-photo" /></div>{%endif%} + <span class="datei">Field Note: </span><span class="date dt-published">{{object.pub_date|date:"F d, Y"}}</span> + <a href="{{object.get_absolute_url}}"> + <h2>{{object.title|safe|smartypants|widont}}</h2> + {% if object.subtitle %}<h3 class="p-summary">{{object.subtitle|safe|smartypants|widont}}</h3>{%endif%} + </a> + {% if object.location %}<h4 class="p-location h-adr post-location" itemprop="geo" itemscope itemtype="http://data-vocabulary.org/Geo"> + <span class="p-locality">{{object.location.name|smartypants|safe}}</span>, + <span class="p-region">{{object.location.state_name}}</span>, + <span class="p-country-name">{{object.location.country_name}}</span> + <data class="p-latitude" value="{{object.latitude}}"></data> + <data class="p-longitude" value="{{object.longitude}}"></data> + </h4>{% endif %} + </li>{%endwith%}{%endwith%}{%else%} + <li class="field-photo-item"> + <figure class="daily-figure"> + {% include 'lib/img_picwide.html' with image=object caption=False exif=False is_cluster=False cluster_class='' extra='' %} + <figcaption class="picwide">{{object.location}}, {{object.location.state.country}} – {{object.pub_date|date:"M m, Y"}}</figcaption> + </figure> + </li>{%endif%} + {%endfor%}</ul> + </main> + <nav aria-label="page navigation" class="pagination"> + {% paginate %} + </nav> +{% endblock %} + + + diff --git a/bak/unused_apps/fieldnotes/urls.py b/bak/unused_apps/fieldnotes/urls.py new file mode 100644 index 0000000..85ee710 --- /dev/null +++ b/bak/unused_apps/fieldnotes/urls.py @@ -0,0 +1,39 @@ +from django.urls import path, re_path + +from . import views + +app_name = "field notes" + +urlpatterns = [ + re_path( + r'(?P<year>[0-9]{4})/$', + views.FieldNoteYearArchiveView.as_view(), + name="list_year" + ), + path( + r'', + views.FieldNoteListView.as_view(), + {'page': 1}, + name="list" + ), + path( + r'<int:page>/', + views.FieldNoteListView.as_view(), + name="list" + ), + path( + r'<int:year>/<int:month>/<str:slug>.txt', + views.FieldNoteDetailViewTXT.as_view(), + name="detail-txt" + ), + path( + r'<int:year>/<int:month>/<str:slug>', + views.FieldNoteDetailView.as_view(), + name="detail" + ), + path( + r'<int:year>/<int:month>/', + views.FieldNoteMonthArchiveView.as_view(month_format='%m'), + name="list_month" + ), +] diff --git a/bak/unused_apps/fieldnotes/views.py b/bak/unused_apps/fieldnotes/views.py new file mode 100644 index 0000000..9b49cc0 --- /dev/null +++ b/bak/unused_apps/fieldnotes/views.py @@ -0,0 +1,37 @@ +from django.views.generic.dates import YearArchiveView, MonthArchiveView +from django.views.generic.detail import DetailView + +from utils.views import PaginatedListView, LuxDetailView + +from .models import FieldNote + + +class FieldNoteListView(PaginatedListView): + model = FieldNote + """ + Return a list of Notes in reverse chronological order + """ + queryset = FieldNote.objects.filter(status=1).order_by('-pub_date') + + +class FieldNoteDetailView(LuxDetailView): + model = FieldNote + slug_field = "slug" + + +class FieldNoteDetailViewTXT(FieldNoteDetailView): + template_name = "jrnl/entry.txt" + + +class FieldNoteYearArchiveView(YearArchiveView): + queryset = FieldNote.objects.filter(status=1) + date_field = "pub_date" + template_name = "fieldnotes/fieldnote_archive_list_date.html" + make_object_list = True + + +class FieldNoteMonthArchiveView(MonthArchiveView): + queryset = FieldNote.objects.filter(status=1) + date_field = "pub_date" + make_object_list = True + template_name = "fieldnotes/fieldnote_archive_list_date.html" diff --git a/bak/unused_apps/fieldnotes_/__init__.py b/bak/unused_apps/fieldnotes_/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/fieldnotes_/__init__.py diff --git a/bak/unused_apps/fieldnotes_/admin.py b/bak/unused_apps/fieldnotes_/admin.py new file mode 100644 index 0000000..838063d --- /dev/null +++ b/bak/unused_apps/fieldnotes_/admin.py @@ -0,0 +1,38 @@ +from django.contrib import admin +from django.contrib.contenttypes.admin import GenericTabularInline + +from .models import FieldNote +from utils.widgets import LGEntryForm, OLAdminBase +from utils.util import get_latlon + + +@admin.register(FieldNote) +class NoteAdmin(OLAdminBase): + form = LGEntryForm + prepopulated_fields = {"slug": ('title',)} + list_display = ('title', 'slug', 'pub_date', 'location') + fieldsets = ( + ('Note', { + 'fields': ( + ('title', 'note_type'), + 'subtitle', + 'body_markdown', + 'slug', + ('pub_date', 'status'), + 'point' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + lat, lon = get_latlon() + default_lon = lon + default_lat = lat + default_zoom = 10 + + class Media: + js = ('image-loader.js', 'next-prev-links.js') diff --git a/bak/unused_apps/fieldnotes_/build.py b/bak/unused_apps/fieldnotes_/build.py new file mode 100644 index 0000000..4c5c83b --- /dev/null +++ b/bak/unused_apps/fieldnotes_/build.py @@ -0,0 +1,36 @@ +import os +from django.urls import reverse +from builder.base import BuildNew + + +class BuildNotes(BuildNew): + + def build(self): + self.build_detail_view() + self.build_list_view( + base_path=reverse("fieldnotes:list"), + paginate_by=24 + ) + self.build_year_view("fieldnotes:list_year") + self.build_month_view("fieldnotes:list_month") + + def get_model_queryset(self): + return self.model.objects.all() + + def build_detail_view(self): + ''' + write out all the expenses for each trip + ''' + for obj in self.get_model_queryset(): + url = obj.get_absolute_url() + path, slug = os.path.split(url) + path = '%s/' % path + # write html + response = self.client.get(url) + print(path, slug) + self.write_file(path, response.content, filename=slug) + + +def builder(): + j = BuildNotes("fieldnotes", "fieldnote") + j.build() diff --git a/bak/unused_apps/fieldnotes_/migrations/0001_initial.py b/bak/unused_apps/fieldnotes_/migrations/0001_initial.py new file mode 100644 index 0000000..cec67fb --- /dev/null +++ b/bak/unused_apps/fieldnotes_/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 2.1.7 on 2019-03-03 12:13 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('locations', '0017_auto_20190217_1849'), + ] + + operations = [ + migrations.CreateModel( + name='FieldNote', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=250)), + ('subtitle', models.CharField(blank=True, max_length=250)), + ('slug', models.SlugField(blank=True, unique_for_date='pub_date')), + ('pub_date', models.DateTimeField(default=django.utils.timezone.now)), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField(verbose_name='Note')), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=1)), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ], + options={ + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date',), + }, + ), + ] diff --git a/bak/unused_apps/fieldnotes_/migrations/0002_auto_20190303_1222.py b/bak/unused_apps/fieldnotes_/migrations/0002_auto_20190303_1222.py new file mode 100644 index 0000000..c0e352e --- /dev/null +++ b/bak/unused_apps/fieldnotes_/migrations/0002_auto_20190303_1222.py @@ -0,0 +1,25 @@ +# Generated by Django 2.1.7 on 2019-03-03 12:22 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0018_auto_20161130_1218'), + ('fieldnotes', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='fieldnote', + name='featured_image', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='photos.LuxImage'), + ), + migrations.AddField( + model_name='fieldnote', + name='note_type', + field=models.IntegerField(choices=[(0, 'Note'), (1, 'Photo')], default=0), + ), + ] diff --git a/bak/unused_apps/fieldnotes_/migrations/__init__.py b/bak/unused_apps/fieldnotes_/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/fieldnotes_/migrations/__init__.py diff --git a/bak/unused_apps/fieldnotes_/models.py b/bak/unused_apps/fieldnotes_/models.py new file mode 100644 index 0000000..c14ba80 --- /dev/null +++ b/bak/unused_apps/fieldnotes_/models.py @@ -0,0 +1,113 @@ +import re +from django import forms +from django.contrib.gis.db import models +from django.utils import timezone +from django.urls import reverse +from django.conf import settings +from django.contrib.sitemaps import Sitemap + +from locations.models import Location, CheckIn +from photos.models import LuxImage +from utils.util import render_images, parse_image, markdown_to_html, extract_main_image + + +def render_images(s): + s = re.sub('<img(.*)/>', parse_image, s) + return s + + +class FieldNote(models.Model): + title = models.CharField(max_length=250, blank=True) + subtitle = models.CharField(max_length=250, blank=True) + slug = models.SlugField(unique_for_date='pub_date', blank=True) + pub_date = models.DateTimeField(default=timezone.now) + body_html = models.TextField(blank=True) + body_markdown = models.TextField('Note') + point = models.PointField(blank=True, null=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True, null=True) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=1) + NOTE_TYPE = ( + (0, 'Note'), + (1, 'Photo'), + ) + note_type = models.IntegerField(choices=NOTE_TYPE, default=0) + featured_image = models.ForeignKey(LuxImage, on_delete=models.SET_NULL, blank=True, null=True) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("fieldnotes:detail", kwargs={"year": self.pub_date.year, "month": self.pub_date.strftime("%m"), "slug": self.slug}) + + def get_object_type(self): + return 'fieldnote' + + @property + def region(self): + return self.location.lux_region + + @property + def longitude(self): + '''Get the site's longitude.''' + return round(self.point.x, 2) + + @property + def latitude(self): + '''Get the site's latitude.''' + return round(self.point.y, 2) + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date() + + @property + def get_next_published(self): + return self.get_next_by_pub_date() + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + except model.DoesNotExist: + return '' + + def save(self, *args, **kwargs): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + if not self.point: + self.point = CheckIn.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)) + if not self.id: + self.pub_date = timezone.now() + self.date_last_updated = timezone.now() + self.featured_image = extract_main_image(self.body_markdown) + super(FieldNote, self).save() + + +class FieldNoteSitemap(Sitemap): + changefreq = "never" + priority = 0.7 + protocol = "https" + + def items(self): + return FieldNote.objects.filter(status=1) + + def lastmod(self, obj): + return obj.pub_date diff --git a/bak/unused_apps/fieldnotes_/templates/fieldnotes/fieldnote_archive_list_date.html b/bak/unused_apps/fieldnotes_/templates/fieldnotes/fieldnote_archive_list_date.html new file mode 100644 index 0000000..5d6865f --- /dev/null +++ b/bak/unused_apps/fieldnotes_/templates/fieldnotes/fieldnote_archive_list_date.html @@ -0,0 +1,43 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load html5_datetime %} +{% block pagetitle %} Field Notes | luxagraf {% endblock %} +{% block metadescription %} Rough notes and sketches from the field {% endblock %} +{%block bodyid%}id="field-notes"{%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>{% if month or year %}<a href="{% url 'fieldnotes:list' %}">Field Notes</a> →{%else%}Field Notes{%endif%}</li> + <li>{% if not month %}{{year|date:"Y"}}{%else%}<a href="/field-notes/{{month|date:"Y"}}/">{{month|date:"Y"}}</a> →{%endif%}</li> + {% if month %}<li itemprop="title">{{month|date:"F"}}</li>{% endif %} + </ul> + <main role="main" id="essay-archive" class="essay-archive archive-list"> + <div class="essay-intro"> + <h2>Field Notes {% if month or year %}{% if month %} from {{month|date:"F"}} {{month|date:"Y"}}{%else%} from {{year|date:"Y"}}{%endif%}{%endif%}</h2> + <p>Quick notes, sketches and images from the road. This is the semi-orgnized brain dump that comes before the more organized <a href="/jrnl/" title="read the journal">journal entries</a> and <a href="/essays/" title="read essays">essays</a>. If I used social media this is the stuff I'd probably put there, but I prefer to put it here, even if it means a lot few people read it.</p> + </div> + <ul class="fancy-archive-list">{% for object in object_list %}{% if object.slug != 'about' %} + <li class="h-entry hentry" itemscope itemType="http://schema.org/Article"> + <a href="{{object.get_absolute_url}}"> + {% if object.featured_image %}<div class="circle-img-wrapper"><img src="{{object.featured_image.get_thumbnail_url}}" alt="{{object.featured_image.alt}}" /></div>{%endif%} + <span class="date dt-published">{{object.pub_date|date:"F d, Y"}}</span> + <a href="{{object.get_absolute_url}}"> + <h2>{{object.title|safe|smartypants|widont}}</h2> + {% if object.subtitle %}<h3 class="p-summary">{{object.subtitle|safe|smartypants|widont}}</h3>{%endif%} + </a> + {% if object.location %}<h4 class="p-location h-adr post-location" itemprop="geo" itemscope itemtype="http://data-vocabulary.org/Geo"> + <span class="p-locality">{{object.location.name|smartypants|safe}}</span>, + <span class="p-region">{{object.location.state_name}}</span>, + <span class="p-country-name">{{object.location.country_name}}</span> + <data class="p-latitude" value="{{object.latitude}}"></data> + <data class="p-longitude" value="{{object.longitude}}"></data> + </h4>{% endif %} + </li> + {%endif%}{%endfor%}</ul> + </main> + +{% endblock %} + + + diff --git a/bak/unused_apps/fieldnotes_/templates/fieldnotes/fieldnote_detail.html b/bak/unused_apps/fieldnotes_/templates/fieldnotes/fieldnote_detail.html new file mode 100644 index 0000000..d1c648b --- /dev/null +++ b/bak/unused_apps/fieldnotes_/templates/fieldnotes/fieldnote_detail.html @@ -0,0 +1,95 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load html5_datetime %} +{% load month_number_to_name %} +{% block pagetitle %}{{object.title|title|smartypants|safe}} - Luxagraf, Field Notes{% endblock %} + +{% block metadescription %}{{object.body_html|striptags|safe|truncatewords:30}}{% endblock %} +{%block extrahead%} + <link rel="canonical" href="http://luxagraf.net{{object.get_absolute_url}}" /> + <meta name="ICBM" content="{{object.latitude}}, {{object.longitude}}" /> + <meta name="geo.position" content="{{object.latitude}}; {{object.longitude}}" /> + <meta name="geo.placename" content="{% if object.location.country.name == "United States" %}{{object.location.name|smartypants|safe}}, {{object.state.name}}{%else%}{{object.location.name|smartypants|safe}}, {{object.country.name}}{%endif%}"> + <meta name="geo.region" content="{{object.country.iso2}}{%if object.state.code != '' %}-{{object.state.code}}{%endif%}"> +{%endblock%} +{% block bodyid %}class="notes--permalin detail" id="archive-{% if month %}{{month|month_number_to_name}}{%endif%}{{year}}"{%endblock%} +{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +{% block primary %}<main role="main"> + <article class="h-entry hentry {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %} post--article--double{%endif%}{%endwith%}" itemscope itemType="http://schema.org/BlogPosting"> + <header id="header" class="post-header {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %}post--header--double{%endif%}{%endwith%}"> + <h1 class="p-name entry-title post-title" itemprop="headline">{%if object.template_name == 1 or object.template_name == 3 %}{{object.title|smartypants|safe}}{%else%}{{object.title|smartypants|safe}}{%endif%}</h1> + {% if object.subtitle %}<h2 class="post-subtitle">{{object.subtitle|smartypants|safe}}</h2>{%endif%} + <div class="post-linewrapper"> + {% if object.location %}<div class="p-location h-adr adr post-location" itemprop="contentLocation" itemscope itemtype="http://schema.org/Place"> + <h3 class="h-adr" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">{% if object.location.country_name == "United States" %}<span class="p-locality locality" itemprop="addressLocality">{{object.location.name|smartypants|safe}}</span>, <a class="p-region region" href="/jrnl/united-states/" title="travel writing from the United States">{{object.location.state_name|safe}}</a>, <span class="p-country-name" itemprop="addressCountry">U.S.</span>{%else%}<span class="p-region" itemprop="addressRegion">{{object.location.name|smartypants|safe}}</span>, <a class="p-country-name country-name" href="/jrnl/{{object.location.country_slug}}/" title="travel writing from {{object.location.country_name}}"><span itemprop="addressCountry">{{object.location.country_name|safe}}</span></a>{%endif%}</h3> + – <a href="" onclick="showMap({{object.latitude}}, {{object.longitude}}, { type:'point', lat:'{{object.latitude}}', lon:'{{object.longitude}}'}); return false;" title="see a map">Map</a> + </div>{%endif%} + <time class="dt-published published dt-updated post-date" datetime="{{object.pub_date|date:'c'}}" itemprop="datePublished">{{object.pub_date|date:"F"}} <span>{{object.pub_date|date:"j, Y"}}</span></time> + <span class="hide" itemprop="author" itemscope itemtype="http://schema.org/Person">by <a class="p-author h-card" href="/about"><span itemprop="name">Scott Gilbertson</span></a></span> + </div> + </header> + <div class="e-content"> + {{object.body_html|safe|smartypants}} + </div> + <span class="p-author h-card"> + <data class="p-name" value="Scott Gilbertson"></data> + <data class="u-url" value="https://luxagraf.net/"></data> + </span> + <footer> + {%comment%}<p class="note--date"> + <a class="u-url" href="{{object.get_absolute_url}}" rel="bookmark"><time class="dt-published" datetime="{{object.pub_date|html5_datetime}}">{{object.pub_date|date:"F j, Y"}}</time></a> + </p>{%endcomment%} + {% comment %} {% if object.twitter_id %} + <ul class="note--actions"> + <li><a rel="syndication" class="u-syndication" href="https://twitter.com/luxagraf/status/{{object.twitter_id}}">View on Twitter</a></li> + <li> + <indie-action do="reply" with="{{SITE_URL}}{{object.get_absolute_url}}"><a href="https://twitter.com/intent/tweet?in_reply_to={{object.twitter_id}}">Reply</a></indie-action> + </li> + <li> + <indie-action do="post" with="{{SITE_URL}}{{object.get_absolute_url}}"> + <a href="https://twitter.com/intent/retweet?tweet_id={{object.twitter_id}}">Retweet</a> + </indie-action> + </li> + <li> + <indie-action do="bookmark" with="{{SITE_URL}}{{object.get_absolute_url}}"> + <a href="https://twitter.com/intent/favorite?tweet_id={{object.twitter_id}}">Favourite</a> + </indie-action> + </li> + </ul>{% endif %}{% endcomment %} + </footer> + + + {% with object.get_next_published as next %} + {% with object.get_previous_published as prev %} + <nav id="page-navigation"> + <ul>{% if prev%} + <li rel="previous" id="next"><span class="bl">Previous:</span> + <a href="{{ prev.get_absolute_url }}" rel="prev" title=" {{prev.title}}">{{prev.title|safe}}</a> + </li>{%endif%}{% if next%} + <li rel="next" id="prev"><span class="bl">Next:</span> + <a href="{{ next.get_absolute_url }}" rel="next" title=" {{next.title}}">{{next.title|safe}}</a> + </li>{%endif%} + </ul> + </nav>{%endwith%}{%endwith%} + </article> +</main> +{% endblock %} + +{% block js %} +<script> +document.addEventListener("DOMContentLoaded", function(event) { + var leaflet = document.createElement('script'); + leaflet.src = "/media/js/leaflet-master/leaflet-mod.js"; + document.body.appendChild(leaflet); + leaflet.onload = function(){ + var detail = document.createElement('script'); + detail.src = "/media/js/detail.min.js"; + document.body.appendChild(detail); + detail.onload = function(){ + createMap(); + var open = false; + } + } +}); +</script> +{%endblock%} diff --git a/bak/unused_apps/fieldnotes_/templates/fieldnotes/fieldnote_list.html b/bak/unused_apps/fieldnotes_/templates/fieldnotes/fieldnote_list.html new file mode 100644 index 0000000..37cd0ca --- /dev/null +++ b/bak/unused_apps/fieldnotes_/templates/fieldnotes/fieldnote_list.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load get_next %} +{% load html5_datetime %} +{% load pagination_tags %} +{% block pagetitle %} Field Notes | luxagraf {% endblock %} +{% block metadescription %}Rough notes and sketches from the field {% endblock %} +{%block bodyid%}id="field-notes"{%endblock%} +{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +{% block primary %}<main role="main" id="essay-archive" class="essay-archive archive-list"> + <div class="essay-intro"> + <h2>Field Notes</h2> + <p>Quick notes, sketches, and images from the road. This is the semi-organized brain dump that comes before the more organized <a href="/jrnl/" title="read the journal">journal entries</a>. If I used social media this is the stuff I'd probably put there, but I prefer to put it here, even if it means a lot fewer people read it.</p> + </div> + {% autopaginate object_list 30 %} + <ul class="fancy-archive-list">{% for object in object_list %}{% if object.get_object_type == "fieldnote" %} + {% with next_element=object_list|next:forloop.counter0 %} + {% with prev_element=object_list|previous:forloop.counter0 %} + <li class="h-entry hentry {% if not next_element.get_object_type == "fieldnote"%}note-pad-bottom{%endif%} {% if not prev_element.get_object_type == "fieldnote"%}note-pad-top{%endif%}" itemscope itemType="http://schema.org/Article"> + <a href="{{object.get_absolute_url}}" class="u-url"> + {% if object.featured_image %}<div class="circle-img-wrapper"><img src="{{object.featured_image.get_thumbnail_url}}" alt="{{object.featured_image.alt}}" class="u-photo" /></div>{%endif%} + <span class="datei">Field Note: </span><span class="date dt-published">{{object.pub_date|date:"F d, Y"}}</span> + <a href="{{object.get_absolute_url}}"> + <h2>{{object.title|safe|smartypants|widont}}</h2> + {% if object.subtitle %}<h3 class="p-summary">{{object.subtitle|safe|smartypants|widont}}</h3>{%endif%} + </a> + {% if object.location %}<h4 class="p-location h-adr post-location" itemprop="geo" itemscope itemtype="http://data-vocabulary.org/Geo"> + <span class="p-locality">{{object.location.name|smartypants|safe}}</span>, + <span class="p-region">{{object.location.state_name}}</span>, + <span class="p-country-name">{{object.location.country_name}}</span> + <data class="p-latitude" value="{{object.latitude}}"></data> + <data class="p-longitude" value="{{object.longitude}}"></data> + </h4>{% endif %} + </li>{%endwith%}{%endwith%}{%else%} + <li class="field-photo-item"> + <figure class="daily-figure"> + {% include 'lib/img_picwide.html' with image=object caption=False exif=False is_cluster=False cluster_class='' extra='' %} + <figcaption class="picwide">{{object.location}}, {{object.location.state.country}} – {{object.pub_date|date:"M m, Y"}}</figcaption> + </figure> + </li>{%endif%} + {%endfor%}</ul> + </main> + <nav aria-label="page navigation" class="pagination"> + {% paginate %} + </nav> +{% endblock %} + + + diff --git a/bak/unused_apps/fieldnotes_/urls.py b/bak/unused_apps/fieldnotes_/urls.py new file mode 100644 index 0000000..85ee710 --- /dev/null +++ b/bak/unused_apps/fieldnotes_/urls.py @@ -0,0 +1,39 @@ +from django.urls import path, re_path + +from . import views + +app_name = "field notes" + +urlpatterns = [ + re_path( + r'(?P<year>[0-9]{4})/$', + views.FieldNoteYearArchiveView.as_view(), + name="list_year" + ), + path( + r'', + views.FieldNoteListView.as_view(), + {'page': 1}, + name="list" + ), + path( + r'<int:page>/', + views.FieldNoteListView.as_view(), + name="list" + ), + path( + r'<int:year>/<int:month>/<str:slug>.txt', + views.FieldNoteDetailViewTXT.as_view(), + name="detail-txt" + ), + path( + r'<int:year>/<int:month>/<str:slug>', + views.FieldNoteDetailView.as_view(), + name="detail" + ), + path( + r'<int:year>/<int:month>/', + views.FieldNoteMonthArchiveView.as_view(month_format='%m'), + name="list_month" + ), +] diff --git a/bak/unused_apps/fieldnotes_/views.py b/bak/unused_apps/fieldnotes_/views.py new file mode 100644 index 0000000..9b49cc0 --- /dev/null +++ b/bak/unused_apps/fieldnotes_/views.py @@ -0,0 +1,37 @@ +from django.views.generic.dates import YearArchiveView, MonthArchiveView +from django.views.generic.detail import DetailView + +from utils.views import PaginatedListView, LuxDetailView + +from .models import FieldNote + + +class FieldNoteListView(PaginatedListView): + model = FieldNote + """ + Return a list of Notes in reverse chronological order + """ + queryset = FieldNote.objects.filter(status=1).order_by('-pub_date') + + +class FieldNoteDetailView(LuxDetailView): + model = FieldNote + slug_field = "slug" + + +class FieldNoteDetailViewTXT(FieldNoteDetailView): + template_name = "jrnl/entry.txt" + + +class FieldNoteYearArchiveView(YearArchiveView): + queryset = FieldNote.objects.filter(status=1) + date_field = "pub_date" + template_name = "fieldnotes/fieldnote_archive_list_date.html" + make_object_list = True + + +class FieldNoteMonthArchiveView(MonthArchiveView): + queryset = FieldNote.objects.filter(status=1) + date_field = "pub_date" + make_object_list = True + template_name = "fieldnotes/fieldnote_archive_list_date.html" diff --git a/bak/unused_apps/figments/admin.py b/bak/unused_apps/figments/admin.py new file mode 100644 index 0000000..34452bd --- /dev/null +++ b/bak/unused_apps/figments/admin.py @@ -0,0 +1,46 @@ +from django.contrib import admin +from .models import Figment, Series +from utils.widgets import LGEntryForm + + +class FigmentAdmin(admin.ModelAdmin): + form = LGEntryForm + list_display = ('title', 'pub_date', 'get_series') + prepopulated_fields = {"slug": ('title',)} + fieldsets = ( + ('Figment', { + 'fields': ( + 'title', + 'body_markdown', + ('slug', 'status'), + 'pub_date', + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ('Extra', { + 'fields': ( + 'dek', + 'prologue', + 'series', + 'published_link', + ), + 'classes': ( + 'collapse', + 'extrapretty', + 'wide' + ) + } + ), + ) + + +class SeriesAdmin(admin.ModelAdmin): + pass + +admin.site.register(Figment, FigmentAdmin) +admin.site.register(Series, SeriesAdmin) diff --git a/bak/unused_apps/figments/build.py b/bak/unused_apps/figments/build.py new file mode 100644 index 0000000..b656ab0 --- /dev/null +++ b/bak/unused_apps/figments/build.py @@ -0,0 +1,17 @@ +from django.urls import reverse +from builder.base import BuildNew + + +class BuildFigments(BuildNew): + def build(self): + self.build_list_view( + base_path=reverse('figments:list'), + paginate_by=99999 + ) + self.build_detail_view() + self.build_feed('figments:feed') + + +def builder(): + j = BuildFigments("figments", "figment") + j.build() diff --git a/bak/unused_apps/figments/ebook.py b/bak/unused_apps/figments/ebook.py new file mode 100644 index 0000000..81210a4 --- /dev/null +++ b/bak/unused_apps/figments/ebook.py @@ -0,0 +1,79 @@ +import os +from ebooklib import epub + +from django.conf import settings +from typogrify.filters import typogrify + + +def generate_epub_file(f): + book = epub.EpubBook() + + # add metadata + book.set_identifier('lux23') + book.set_title(f.title) + book.set_language('en') + + book.add_author('Scott Gilbertson') + + # intro chapter + c1 = epub.EpubHtml(title='Introduction', file_name='intro.xhtml', lang='en') + c1.content = u'<html><head></head><body><h1>Forward</h1><p>Thank you for downloading my story <em>%s</em>. I hope you enjoy it and please feel free to email me if you have any questions or comments.</p> <p>If you enjoy this story and would like to read more, head on over to <a href="https://luxagraf.net/figments/">luxagraf.net</a>.</p><p>– Scott Gilbertson <br/> sng@luxagraf.net</p></body></html>' % f.title + + # chapter + c2 = epub.EpubHtml(title=f.title, file_name='story.xhtml') + c2.content = '<h1>%s</h1>' % f.title + c2.content += typogrify(f.body_html) + + # add chapters to the book + book.add_item(c1) + book.add_item(c2) + # create table of contents + # - add section + # - add auto created links to chapters + + book.toc = (epub.Link('intro.xhtml', 'Introduction', 'intro'), + epub.Link('story.xhtml', f.title, 'story'), + ) + + # add navigation files + book.add_item(epub.EpubNcx()) + book.add_item(epub.EpubNav()) + + # define css style + style = ''' +@namespace epub "http://www.idpf.org/2007/ops"; +body { + font-family: Georgia, Times, Times New Roman, serif; +} +h2 { + text-align: left; + text-transform: uppercase; + font-weight: 200; +} +ol { + list-style-type: none; +} +ol > li:first-child { + margin-top: 0.3em; +} +nav[epub|type~='toc'] > ol > li > ol { + list-style-type:square; +} +nav[epub|type~='toc'] > ol > li > ol > li { + margin-top: 0.3em; +} +''' + + # add css file + nav_css = epub.EpubItem(uid="style_nav", file_name="style/nav.css", media_type="text/css", content=style) + book.add_item(nav_css) + + # create spine + book.spine = ['nav', c1, c2] + path, filename = os.path.split(f.get_absolute_url()) + fpath = '%s%s/' % (settings.FLATFILES_ROOT, path) + if not os.path.isdir(fpath): + os.makedirs(fpath) + bk = '%s%s.epub' % (fpath, f.slug) + # create epub file + epub.write_epub(bk, book, {}) diff --git a/bak/unused_apps/figments/models.py b/bak/unused_apps/figments/models.py new file mode 100644 index 0000000..8b22049 --- /dev/null +++ b/bak/unused_apps/figments/models.py @@ -0,0 +1,97 @@ +import datetime +from itertools import chain +from django.db import models +from django.urls import reverse +from django.contrib.sitemaps import Sitemap +from django.contrib.syndication.views import Feed +from django.db.models.signals import post_save +from django.dispatch import receiver + +from utils.util import markdown_to_html + +from .ebook import generate_epub_file + + +class Series(models.Model): + title = models.CharField(max_length=200) + slug = models.CharField(max_length=50) + is_book = models.BooleanField(default=False) + body_markdown = models.TextField(null=True, blank=True) + body_html = models.TextField(null=True, blank=True) + pub_date = models.DateTimeField(auto_now_add=True) + + class Meta: + verbose_name_plural = 'Series' + + def __str__(self): + return self.title + + +class Figment(models.Model): + title = models.CharField(max_length=200) + slug = models.CharField(max_length=50) + pub_date = models.DateTimeField(blank=True) + body_markdown = models.TextField(null=True, blank=True) + prologue = models.TextField(null=True, blank=True) + dek = models.TextField(null=True, blank=True) + body_html = models.TextField(null=True, blank=True) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + series = models.ManyToManyField(Series, related_name="series", blank=True) + TEMPLATES = ( + (0, 'default'), + ) + template_name = models.IntegerField(choices=TEMPLATES, default=0) + published_link = models.CharField(max_length=300, help_text="link to online pub", blank=True, null=True) + + class Meta: + ordering = ('-pub_date',) + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("figments:detail", kwargs={"slug": self.slug}) + + def get_series(self): + return "\n".join([s.title for s in self.series.all()]) + + def save(self, *args, **kwargs): + if not self.id and not self.pub_date: + self.pub_date = datetime.datetime.now() + self.body_html = markdown_to_html(self.body_markdown) + super(Figment, self).save() + + +@receiver(post_save, sender=Figment) +def post_save_events(sender, instance, **kwargs): + if instance.body_markdown != instance._loaded_values['body_markdown']: + print("you updated") + generate_epub_file(instance) + else: + print("no update found, not buidling") + + +class LatestFull(Feed): + title = "luxagraf figments: stories less literally true." + link = "/figments/" + description = "Latest postings to luxagraf.net/figments" + description_template = 'feeds/blog_description.html' + + def items(self): + return Figment.objects.filter(status__exact=1).order_by('-pub_date')[:10] + + +class FigmentSitemap(Sitemap): + changefreq = "never" + priority = 0.7 + protocol = "https" + + def items(self): + return list(chain(Figment.objects.filter(status__exact=1), Series.objects.all())) + + def lastmod(self, obj): + return obj.pub_date diff --git a/bak/unused_apps/figments/urls.py b/bak/unused_apps/figments/urls.py new file mode 100644 index 0000000..4a37cf5 --- /dev/null +++ b/bak/unused_apps/figments/urls.py @@ -0,0 +1,38 @@ +from django.conf.urls import url + +from . import views + +app_name = "figments" + +urlpatterns = [ + url( + r'^feed.xml', + views.FigRSSFeedView(), + name="feed" + ), + url( + r'series/$', + views.SeriesListView.as_view(), + name='list_series' + ), + url( + r'(?P<slug>[-\w]+).txt$', + views.FigmentDetailViewTXT.as_view(), + name="detail-txt" + ), + url( + r'(?P<slug>[-\w]+).amp$', + views.FigmentDetailViewAMP.as_view(), + name="detail-amp" + ), + url( + r'(?P<slug>[-\w]+)$', + views.FigmentDetailView.as_view(), + name='detail' + ), + url( + r'^$', + views.FigmentListView.as_view(), + name='list' + ), +] diff --git a/bak/unused_apps/figments/views.py b/bak/unused_apps/figments/views.py new file mode 100644 index 0000000..6521c29 --- /dev/null +++ b/bak/unused_apps/figments/views.py @@ -0,0 +1,42 @@ +from django.views.generic import ListView, DetailView +from django.contrib.syndication.views import Feed + +from .models import Figment, Series + + +class FigmentListView(ListView): + model = Figment + template_name = "archives/figments.html" + context_object_name = 'object_list' + + def get_queryset(self): + return Figment.objects.filter(status__exact=1).order_by('-pub_date') + + +class SeriesListView(ListView): + model = Series + template_name = "archives/figments_series.html" + context_object_name = 'object_list' + + +class FigmentDetailView(DetailView): + model = Figment + template_name = "details/figments.html" + + +class FigmentDetailViewTXT(FigmentDetailView): + template_name = "details/entry.txt" + + +class FigmentDetailViewAMP(FigmentDetailView): + template_name = "details/entry.amp" + + +class FigRSSFeedView(Feed): + title = "luxagraf figments: stories less literally true." + link = "/figments/" + description = "Latest postings to luxagraf.net/figments" + description_template = 'feeds/blog_description.html' + + def items(self): + return Figment.objects.filter(status__exact=1).order_by('-pub_date')[:10] diff --git a/bak/unused_apps/garden/admin.py b/bak/unused_apps/garden/admin.py new file mode 100644 index 0000000..0f9c8d0 --- /dev/null +++ b/bak/unused_apps/garden/admin.py @@ -0,0 +1,38 @@ +from django.contrib import admin +from django.contrib.contenttypes.admin import GenericStackedInline + +from utils.widgets import AdminImageWidget, LGEntryForm +from .models import Plant, Planting, Seed + + +@admin.register(Plant) +class PlantAdmin(admin.ModelAdmin): + form = LGEntryForm + + list_display = ('name', 'family', 'growth_time') + list_filter = ('family', 'edible', 'conditions', 'heirloom') + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } + + +@admin.register(Planting) +class PlantingAdmin(admin.ModelAdmin): + form = LGEntryForm + + list_display = ('plant', 'family', 'number_of_plants', 'date_seed_started', 'date_seed_sprouted', 'date_hardened_off', 'date_planted', 'seed_start_area','garden_area', 'outcome') + list_filter = ('plant__family', 'date_seed_started', 'garden_area', 'seed_start_area', 'outcome') + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } + + +@admin.register(Seed) +class PlantAdmin(admin.ModelAdmin): + form = LGEntryForm diff --git a/bak/unused_apps/garden/migrations/0001_initial.py b/bak/unused_apps/garden/migrations/0001_initial.py new file mode 100644 index 0000000..d73d80d --- /dev/null +++ b/bak/unused_apps/garden/migrations/0001_initial.py @@ -0,0 +1,91 @@ +# Generated by Django 2.1.2 on 2020-07-05 16:18 + +import datetime +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Guild', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=200)), + ('seperation', models.PositiveIntegerField()), + ('notes', models.TextField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='Plant', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('scientific_name', models.CharField(max_length=200)), + ('edible', models.BooleanField(default=False)), + ('date_created', models.DateTimeField(default=datetime.datetime.now)), + ('growth_time', models.PositiveIntegerField()), + ('germination_time', models.PositiveIntegerField()), + ('seperation', models.PositiveIntegerField()), + ('notes', models.TextField(blank=True, null=True)), + ('conditions', models.IntegerField(choices=[(0, 'Direct Sun'), (1, 'Part Sun'), (2, 'Shade')], default=0)), + ('organic', models.BooleanField(default=True)), + ('heirloom', models.BooleanField(default=True)), + ], + options={ + 'ordering': ('-date_created',), + 'get_latest_by': 'date_created', + }, + ), + migrations.CreateModel( + name='Planting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('seed_start_area', models.IntegerField(choices=[(0, 'Indoors'), (1, 'Direct Sow'), (2, 'Greenhouse')], default=0)), + ('date_seed_started', models.DateField(verbose_name='Date published')), + ('date_seed_sprouted', models.DateField(verbose_name='Date published')), + ('date_hardened_off', models.DateField(verbose_name='Date published')), + ('date_planted', models.DateField(verbose_name='Date published')), + ('garden_area', models.IntegerField(choices=[(0, 'Far Plot Mound'), (1, 'Far Plot ground'), (2, 'Herb Plot half mound')], default=0)), + ('notes', models.TextField(blank=True, null=True)), + ('plant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='garden.Plant')), + ], + options={ + 'ordering': ('-date_seed_started',), + 'get_latest_by': 'date_seed_started', + }, + ), + migrations.CreateModel( + name='Seed', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('brand', models.CharField(max_length=200)), + ('date_created', models.DateTimeField(default=datetime.datetime.now)), + ('estimated_growth_time', models.PositiveIntegerField()), + ('estimated_germination_time', models.PositiveIntegerField()), + ('organic', models.BooleanField(default=True)), + ('notes', models.TextField(blank=True, null=True)), + ('plant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='garden.Plant')), + ], + options={ + 'ordering': ('-date_created',), + 'get_latest_by': 'date_created', + }, + ), + migrations.AddField( + model_name='planting', + name='seed', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='garden.Seed'), + ), + migrations.AddField( + model_name='guild', + name='plant', + field=models.ManyToManyField(to='garden.Plant'), + ), + ] diff --git a/bak/unused_apps/garden/migrations/0002_plant_family.py b/bak/unused_apps/garden/migrations/0002_plant_family.py new file mode 100644 index 0000000..db81791 --- /dev/null +++ b/bak/unused_apps/garden/migrations/0002_plant_family.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2020-07-05 16:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('garden', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='plant', + name='family', + field=models.IntegerField(choices=[(0, 'Tomatoes'), (1, 'Cucumbers'), (2, 'Watermelons'), (3, 'Pumkins'), (4, 'Basil')], default=0), + ), + ] diff --git a/bak/unused_apps/garden/migrations/0003_auto_20200705_1629.py b/bak/unused_apps/garden/migrations/0003_auto_20200705_1629.py new file mode 100644 index 0000000..ed0eb01 --- /dev/null +++ b/bak/unused_apps/garden/migrations/0003_auto_20200705_1629.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2020-07-05 16:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('garden', '0002_plant_family'), + ] + + operations = [ + migrations.AlterField( + model_name='plant', + name='scientific_name', + field=models.CharField(max_length=200, null=True), + ), + ] diff --git a/bak/unused_apps/garden/migrations/0004_auto_20200705_1650.py b/bak/unused_apps/garden/migrations/0004_auto_20200705_1650.py new file mode 100644 index 0000000..6e9ea63 --- /dev/null +++ b/bak/unused_apps/garden/migrations/0004_auto_20200705_1650.py @@ -0,0 +1,59 @@ +# Generated by Django 2.1.2 on 2020-07-05 16:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('garden', '0003_auto_20200705_1629'), + ] + + operations = [ + migrations.AddField( + model_name='planting', + name='number_of_plants', + field=models.PositiveIntegerField(default=1), + preserve_default=False, + ), + migrations.AddField( + model_name='planting', + name='outcome', + field=models.IntegerField(choices=[(0, 'Far Plot Mound'), (1, 'Far Plot ground'), (2, 'Herb Plot ground'), (2, 'Herb Plot bed')], default=0), + ), + migrations.AlterField( + model_name='plant', + name='scientific_name', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AlterField( + model_name='planting', + name='date_hardened_off', + field=models.DateField(blank=True, null=True), + ), + migrations.AlterField( + model_name='planting', + name='date_planted', + field=models.DateField(), + ), + migrations.AlterField( + model_name='planting', + name='date_seed_sprouted', + field=models.DateField(blank=True, null=True), + ), + migrations.AlterField( + model_name='planting', + name='date_seed_started', + field=models.DateField(blank=True, null=True), + ), + migrations.AlterField( + model_name='planting', + name='garden_area', + field=models.IntegerField(choices=[(0, 'Far Plot Mound'), (1, 'Far Plot ground'), (2, 'Herb Plot ground'), (2, 'Herb Plot bed')], default=0), + ), + migrations.AlterField( + model_name='planting', + name='seed_start_area', + field=models.IntegerField(choices=[(0, 'Indoors'), (1, 'Direct Sow'), (2, 'Greenhouse'), (2, 'Purchased Seedling')], default=0), + ), + ] diff --git a/bak/unused_apps/garden/migrations/0005_auto_20200705_1652.py b/bak/unused_apps/garden/migrations/0005_auto_20200705_1652.py new file mode 100644 index 0000000..22bebaf --- /dev/null +++ b/bak/unused_apps/garden/migrations/0005_auto_20200705_1652.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1.2 on 2020-07-05 16:52 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('garden', '0004_auto_20200705_1650'), + ] + + operations = [ + migrations.AlterField( + model_name='planting', + name='outcome', + field=models.IntegerField(choices=[(0, 'Unknown'), (1, 'Success'), (2, 'Moderate Success'), (3, 'Fail')], default=0), + ), + migrations.AlterField( + model_name='planting', + name='seed', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='garden.Seed'), + ), + ] diff --git a/bak/unused_apps/garden/migrations/0006_auto_20200706_0854.py b/bak/unused_apps/garden/migrations/0006_auto_20200706_0854.py new file mode 100644 index 0000000..a036635 --- /dev/null +++ b/bak/unused_apps/garden/migrations/0006_auto_20200706_0854.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.2 on 2020-07-06 08:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('garden', '0005_auto_20200705_1652'), + ] + + operations = [ + migrations.AddField( + model_name='plant', + name='annual', + field=models.BooleanField(default=True), + ), + migrations.AlterField( + model_name='plant', + name='family', + field=models.IntegerField(choices=[(0, 'Tomatoes'), (1, 'Cucumbers'), (2, 'Watermelons'), (3, 'Pumkins'), (4, 'Cantelope'), (5, 'Kale'), (6, 'Okra'), (7, 'Collards'), (8, 'Arugula'), (9, 'Butter Lettuce'), (10, 'Basil'), (11, 'Lemongrass'), (12, 'Thyme'), (13, 'Mint')], default=0), + ), + ] diff --git a/bak/unused_apps/garden/migrations/0007_auto_20200706_0854.py b/bak/unused_apps/garden/migrations/0007_auto_20200706_0854.py new file mode 100644 index 0000000..94f85e7 --- /dev/null +++ b/bak/unused_apps/garden/migrations/0007_auto_20200706_0854.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2020-07-06 08:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('garden', '0006_auto_20200706_0854'), + ] + + operations = [ + migrations.AlterField( + model_name='plant', + name='conditions', + field=models.IntegerField(choices=[(0, 'Vegetable'), (1, 'Herb'), (2, 'Berry'), (3, 'Nut')], default=0), + ), + ] diff --git a/bak/unused_apps/garden/migrations/__init__.py b/bak/unused_apps/garden/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/garden/migrations/__init__.py diff --git a/bak/unused_apps/garden/models.py b/bak/unused_apps/garden/models.py new file mode 100644 index 0000000..56e8c30 --- /dev/null +++ b/bak/unused_apps/garden/models.py @@ -0,0 +1,153 @@ +import datetime + +from django.contrib.gis.db import models + +from utils.util import render_images, markdown_to_html + + +PLANT_FAMILY = ( + (0, 'Tomatoes'), + (1, 'Cucumbers'), + (2, 'Watermelons'), + (3, 'Pumkins'), + (4, 'Cantelope'), + (5, 'Kale'), + (6, 'Okra'), + (7, 'Collards'), + (8, 'Arugula'), + (9, 'Butter Lettuce'), + (10, 'Basil'), + (11, 'Lemongrass'), + (12, 'Thyme'), + (13, 'Mint'), +) + + +class Plant(models.Model): + """ + Model to hold Plant Definitions + """ + name = models.CharField(max_length=200) + family = models.IntegerField(choices=PLANT_FAMILY, default=0) + scientific_name = models.CharField(max_length=200, null=True, blank=True) + edible = models.BooleanField(default=False) + date_created = models.DateTimeField(default=datetime.datetime.now) + growth_time = models.PositiveIntegerField() + germination_time = models.PositiveIntegerField() + seperation = models.PositiveIntegerField() + notes = models.TextField(null=True, blank=True) + CONDITIONS = ( + (0, 'Direct Sun'), + (1, 'Part Sun'), + (2, 'Shade'), + ) + conditions = models.IntegerField(choices=CONDITIONS, default=0) + PLANT_TYPE= ( + (0, 'Vegetable'), + (1, 'Herb'), + (2, 'Berry'), + (3, 'Nut'), + ) + conditions = models.IntegerField(choices=PLANT_TYPE, default=0) + organic = models.BooleanField(default=True) + heirloom = models.BooleanField(default=True) + annual = models.BooleanField(default=True) + + class Meta: + ordering = ('-date_created',) + get_latest_by = 'date_created' + + def __str__(self): + return self.name + + +class Seed(models.Model): + plant = models.ForeignKey(Plant, on_delete=models.CASCADE) + brand = models.CharField(max_length=200) + date_created = models.DateTimeField(default=datetime.datetime.now) + estimated_growth_time = models.PositiveIntegerField() + estimated_germination_time = models.PositiveIntegerField() + organic = models.BooleanField(default=True) + notes = models.TextField(null=True, blank=True) + + class Meta: + ordering = ('-date_created',) + get_latest_by = 'date_created' + + def __str__(self): + return self.plant.name + + +class Planting(models.Model): + plant = models.ForeignKey(Plant, on_delete=models.CASCADE) + number_of_plants = models.PositiveIntegerField() + seed = models.ForeignKey(Seed, on_delete=models.CASCADE, null=True, blank=True) + START_AREA = ( + (0, 'Indoors'), + (1, 'Direct Sow'), + (2, 'Greenhouse'), + (2, 'Purchased Seedling'), + ) + seed_start_area = models.IntegerField(choices=START_AREA, default=0) + date_seed_started = models.DateField(null=True, blank=True) + date_seed_sprouted = models.DateField(null=True, blank=True) + date_hardened_off = models.DateField(null=True, blank=True) + date_planted = models.DateField() + GARDEN_PLOT = ( + (0, 'Far Plot Mound'), + (1, 'Far Plot ground'), + (2, 'Herb Plot ground'), + (2, 'Herb Plot bed'), + ) + garden_area = models.IntegerField(choices=GARDEN_PLOT, default=0) + OUTCOME = ( + (0, 'Unknown'), + (1, 'Success'), + (2, 'Moderate Success'), + (3, 'Fail'), + ) + outcome = models.IntegerField(choices=OUTCOME, default=0) + notes = models.TextField(null=True, blank=True) + + class Meta: + ordering = ('-date_seed_started',) + get_latest_by = 'date_seed_started' + + def __str__(self): + return self.plant.name + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + except model.DoesNotExist: + return '' + + + @property + def family(self): + return self.plant.get_family_display() + + + def save(self, *args, **kwargs): + created = self.pk is None + if not created: + md = render_images(self.notes) + self.body_html = markdown_to_html(md) + super(Planting, self).save(*args, **kwargs) + + +class Guild(models.Model): + name = models.CharField(max_length=200, blank=True) + plant = models.ManyToManyField(Plant) + seperation = models.PositiveIntegerField() + notes = models.TextField(null=True, blank=True) + + def __str__(self): + return self.name diff --git a/bak/unused_apps/guide/__init__.py b/bak/unused_apps/guide/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/guide/__init__.py diff --git a/bak/unused_apps/guide/admin.py b/bak/unused_apps/guide/admin.py new file mode 100644 index 0000000..dfdf45e --- /dev/null +++ b/bak/unused_apps/guide/admin.py @@ -0,0 +1,61 @@ +from django.contrib import admin +from guide.models import Guide +from django.contrib.gis.admin import OSMGeoAdmin + + +class GuideAdmin(OSMGeoAdmin): + list_display = ( + 'title', + 'pub_date', + 'template_name', + 'status', + 'location', + 'photo_gallery' + ) + search_fields = ['title', 'body_markdown'] + prepopulated_fields = {"slug": ('title',)} + list_filter = ('pub_date', 'status', 'location__state__country__lux_region', 'location') + fieldsets = ( + ('Note', { + 'fields': ( + 'title', + 'body_markdown', + 'location', + 'pub_date', + 'status', + 'slug', + 'photo_gallery' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + }), + ('Extra', { + 'fields': ( + 'dek', + 'meta_description', + 'template_name', + 'image', + 'thumbnail' + ), + 'classes': ( + 'collapse', + 'wide' + ) + }), + ) + + # options for OSM map Using custom ESRI topo map + default_lon = -9285175 + default_lat = 4025046 + default_zoom = 6 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + +admin.site.register(Guide, GuideAdmin) diff --git a/bak/unused_apps/guide/models.py b/bak/unused_apps/guide/models.py new file mode 100644 index 0000000..7f0d501 --- /dev/null +++ b/bak/unused_apps/guide/models.py @@ -0,0 +1,129 @@ +import datetime +from django.contrib.gis.db import models +from django.conf import settings +from django.contrib.sitemaps import Sitemap +from django.contrib.syndication.views import Feed +from PIL import Image + +from taggit.managers import TaggableManager +from locations.models import Location +from jrnl.models import Entry +from photos.models import PhotoGallery + +from utils.widgets import markdown_to_html + + +def get_upload_path(self, filename): + return "images/guide-images/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +def get_tn_path(self, filename): + return "images/guide-thumbnail/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +def image_url_replace(str): + str = str.replace('[[base_url]]', settings.IMAGES_URL) + return str + + +def markdown_processor(md): + return markdown_to_html(md) + + +class Guide(models.Model): + title = models.CharField(max_length=200) + slug = models.SlugField(unique_for_date='pub_date') + entry = models.ForeignKey(Entry, null=True, blank=True) + body_html = models.TextField(blank=True) + body_markdown = models.TextField() + dek = models.TextField(null=True, blank=True) + pub_date = models.DateTimeField('Date published') + location = models.ForeignKey(Location, null=True, blank=True) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + photo_gallery = models.ForeignKey(PhotoGallery, blank=True, null=True, verbose_name='photo set') + meta_description = models.CharField(max_length=256, null=True, blank=True) + TEMPLATES = ( + (0, 'single'), + (1, 'double'), + (2, 'single-dark'), + (3, 'double-dark'), + ) + template_name = models.IntegerField(choices=TEMPLATES, default=0) + tags = TaggableManager(blank=True) + image = models.FileField(upload_to=get_upload_path, null=True, blank=True) + thumbnail = models.FileField(upload_to=get_tn_path, null=True, blank=True) + + @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 + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + verbose_name_plural = 'entries' + + def __unicode__(self): + return self.title + + def get_absolute_url(self): + if self.location: + return "/travel-guide/location/%s/%s/" % (self.location.slug, self.slug) + else: + return "/travel-guide/%s/" % (self.slug) + + def get_thumbnail_url(self): + image_dir, img = self.thumbnail.url.split('guide-thumbnail/')[1].split('/') + return '%sguide-thumbnail/%s/%s' % (settings.IMAGES_URL, image_dir, img) + + def get_image_url(self): + image_dir, img = self.image.url.split('guide-images/')[1].split('/') + return '%sguide-images/%s/%s' % (settings.IMAGES_URL, image_dir, img) + + def save(self): + if self.image: + img = Image.open(self.image) + self.image_width, self.image_height = img.size + img = Image.open(self.thumbnail) + self.thumb_width, self.thumb_height = img.size + md = image_url_replace(self.body_markdown) + self.body_html = markdown_to_html(md) + self.dek = markdown_to_html(self.dek) + super(Guide, self).save() + + +class GuideSitemap(Sitemap): + changefreq = "never" + priority = 1.0 + + def items(self): + return Entry.objects.filter(status=1) + + def lastmod(self, obj): + return obj.pub_date + + +class GuideFull(Feed): + title = "Luxagraf: Topographical Writings" + link = "/writing/" + description = "Latest postings to luxagraf.net" + description_template = 'feeds/blog_description.html' + + def items(self): + return Entry.objects.filter(status__exact=1).order_by('-pub_date')[:10] + + +#from django.dispatch import dispatcher +#from django.db.models import signals + +#signals.post_save.connect(update_recent, sender=Entry) + diff --git a/bak/unused_apps/guide/urls.py b/bak/unused_apps/guide/urls.py new file mode 100644 index 0000000..e701d3e --- /dev/null +++ b/bak/unused_apps/guide/urls.py @@ -0,0 +1,13 @@ +from django.conf.urls import * +from django.views.generic.base import RedirectView + +from . import views + +urlpatterns = [ + url(r'location/(?P<location>[-\w]+)/(?P<slug>[-\w]+)/$', views.guide_detail), + url(r'location/(?P<location>[-\w]+)/$', views.guide_list_by_location), + url(r'location/$', views.location_list), + url(r'(?P<page>\d+)/$', views.guide_list), + url(r'(?P<slug>[-\w]+)/$', views.guide_detail), + url(r'', RedirectView.as_view(url="/travel-guide/1/")), +] diff --git a/bak/unused_apps/guide/views.py b/bak/unused_apps/guide/views.py new file mode 100644 index 0000000..c22a6b3 --- /dev/null +++ b/bak/unused_apps/guide/views.py @@ -0,0 +1,35 @@ +from django.shortcuts import render_to_response, get_object_or_404 +from django.template import RequestContext + +from guide.models import Guide + + +def guide_list(request, page): + """List of all guides""" + request.page_url = "/guide/%d/" + request.page = int(page) + context = { + 'object_list': Guide.objects.filter(status__exact=1).order_by('-pub_date').select_related(), + 'page': page, + } + return render_to_response("archives/guide.html", context, context_instance=RequestContext(request)) + + +def guide_list_by_location(request, location): + context = { + "object_list": Guide.objects.filter(location__slug__exact=location), + } + return render_to_response("archives/guide.html", context, context_instance=RequestContext(request)) + + +def location_list(request): + """List of all locations with guides""" + context = { + "object_list": Guide.objects.filter(status__exact=1).order_by('-pub_date').select_related() + } + return render_to_response("archives/guide.html", context, context_instance=RequestContext(request)) + + +def guide_detail(request, slug, location=None): + obj = get_object_or_404(Guide, slug__exact=slug) + return render_to_response('details/guide.html', {'object': obj}, context_instance=RequestContext(request)) diff --git a/bak/unused_apps/guides/__init__.py b/bak/unused_apps/guides/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/guides/__init__.py diff --git a/bak/unused_apps/guides/admin.py b/bak/unused_apps/guides/admin.py new file mode 100644 index 0000000..7895aba --- /dev/null +++ b/bak/unused_apps/guides/admin.py @@ -0,0 +1,67 @@ +from django.contrib import admin +from django.contrib.gis.admin import OSMGeoAdmin + +from utils.widgets import LGEntryForm +from utils.util import get_latlon + +from .models import Guide + + +@admin.register(Guide) +class GuideAdmin(OSMGeoAdmin): + form = LGEntryForm + list_display = ('title', 'pub_date', 'enable_comments', 'status', 'post_type') + list_filter = ('pub_date', 'enable_comments', 'status') + prepopulated_fields = {"slug": ('title',)} + fieldsets = ( + ('Entry', { + 'fields': ( + 'title', + 'sub_title', + 'body_markdown', + ('pub_date', 'status', 'post_type', 'disclaimer'), + 'meta_description', + 'featured_image', + 'dek', + 'tags', + 'prologue_markdown', + 'epilogue_markdown', + 'has_video', + ('slug', 'enable_comments'), + 'point', + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + }), + ('meta', { + 'fields': ( + ('field_notes', 'books','jrnl'), + ), + 'classes': ( + 'hide', + 'extrapretty', + 'wide' + ) + }), + ) + + # options for OSM map Using custom ESRI topo map + lat, lon = get_latlon() + default_lon = lon + default_lat = lat + default_zoom = 10 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } diff --git a/bak/unused_apps/guides/build.py b/bak/unused_apps/guides/build.py new file mode 100644 index 0000000..392e991 --- /dev/null +++ b/bak/unused_apps/guides/build.py @@ -0,0 +1,22 @@ +import os +from builder.base import BuildNew +from django.urls import reverse +from . import models + + +class BuildEssays(BuildNew): + + def build(self): + self.build_list_view() + self.build_detail_view() + # These are the unique classes for this model: + #self.build_feed("src:feed") + + def build_list_view(self): + response = self.client.get('/essays/') + self.write_file('essays/', response.content) + + +def essaybuilder(): + j = BuildEssays("essays", "essay") + j.build() diff --git a/bak/unused_apps/guides/guide_urls.py b/bak/unused_apps/guides/guide_urls.py new file mode 100644 index 0000000..ad67061 --- /dev/null +++ b/bak/unused_apps/guides/guide_urls.py @@ -0,0 +1,18 @@ +from django.urls import path, re_path + +from . import views + +app_name = "guide" + +urlpatterns = [ + path( + r'<str:slug>', + views.GuideDetailView.as_view(), + name="guide-detail" + ), + path( + r'<str:slug>.txt', + views.GuideDetailViewTXT.as_view(), + name="guide-detail-txt" + ), +] diff --git a/bak/unused_apps/guides/migrations/0001_initial.py b/bak/unused_apps/guides/migrations/0001_initial.py new file mode 100644 index 0000000..833dfbe --- /dev/null +++ b/bak/unused_apps/guides/migrations/0001_initial.py @@ -0,0 +1,60 @@ +# Generated by Django 2.1.7 on 2019-07-04 09:03 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('jrnl', '0043_auto_20190704_0903'), + ('books', '0009_book_afflink'), + ('photos', '0019_auto_20190704_0903'), + ('fieldnotes', '0002_auto_20190303_1222'), + ('locations', '0018_auto_20190414_2124'), + ('essays', '0010_essay_field_notes'), + ('taxonomy', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Guide', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('sub_title', models.CharField(blank=True, max_length=200)), + ('dek', models.TextField(blank=True)), + ('preamble_markdown', models.TextField(blank=True)), + ('preamble_html', models.TextField(blank=True)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField()), + ('pub_date', models.DateTimeField(verbose_name='Date published')), + ('last_updated', models.DateTimeField(auto_now=True)), + ('enable_comments', models.BooleanField(default=False)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=0)), + ('meta_description', models.CharField(blank=True, max_length=256, null=True)), + ('has_video', models.BooleanField(blank=True, default=False)), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('afterword', models.TextField(blank=True)), + ('afterword_html', models.TextField(blank=True)), + ('books', models.ManyToManyField(blank=True, to='books.Book')), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='taxonomy.Category')), + ('essays', models.ManyToManyField(blank=True, to='essays.Essay')), + ('featured_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photos.LuxImage')), + ('field_notes', models.ManyToManyField(blank=True, to='fieldnotes.FieldNote')), + ('jrnl', models.ManyToManyField(blank=True, to='jrnl.Entry')), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ('tags', taggit.managers.TaggableManager(blank=True, help_text='Topics Covered', through='taxonomy.TaggedItems', to='taxonomy.LuxTag', verbose_name='Tags')), + ], + options={ + 'verbose_name_plural': 'Guides', + 'ordering': ('-pub_date',), + 'get_latest_by': 'pub_date', + }, + ), + ] diff --git a/bak/unused_apps/guides/migrations/0002_remove_guide_category.py b/bak/unused_apps/guides/migrations/0002_remove_guide_category.py new file mode 100644 index 0000000..76eefa6 --- /dev/null +++ b/bak/unused_apps/guides/migrations/0002_remove_guide_category.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.7 on 2019-07-04 12:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('guides', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='guide', + name='category', + ), + ] diff --git a/bak/unused_apps/guides/migrations/0003_guide_category.py b/bak/unused_apps/guides/migrations/0003_guide_category.py new file mode 100644 index 0000000..82d03ac --- /dev/null +++ b/bak/unused_apps/guides/migrations/0003_guide_category.py @@ -0,0 +1,21 @@ +# Generated by Django 2.1.7 on 2019-09-08 17:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('taxonomy', '0001_initial'), + ('guides', '0002_remove_guide_category'), + ] + + operations = [ + migrations.AddField( + model_name='guide', + name='category', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='taxonomy.Category'), + preserve_default=False, + ), + ] diff --git a/bak/unused_apps/guides/migrations/0004_auto_20190914_0646.py b/bak/unused_apps/guides/migrations/0004_auto_20190914_0646.py new file mode 100644 index 0000000..ccd79a5 --- /dev/null +++ b/bak/unused_apps/guides/migrations/0004_auto_20190914_0646.py @@ -0,0 +1,47 @@ +# Generated by Django 2.1.7 on 2019-09-14 06:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('guides', '0003_guide_category'), + ] + + operations = [ + migrations.RenameField( + model_name='guide', + old_name='afterword', + new_name='epilogue_html', + ), + migrations.RenameField( + model_name='guide', + old_name='afterword_html', + new_name='epilogue_markdown', + ), + migrations.RenameField( + model_name='guide', + old_name='preamble_html', + new_name='prologue_html', + ), + migrations.RenameField( + model_name='guide', + old_name='preamble_markdown', + new_name='prologue_markdown', + ), + migrations.RemoveField( + model_name='guide', + name='essays', + ), + migrations.AddField( + model_name='guide', + name='disclaimer', + field=models.BooleanField(blank=True, default=False), + ), + migrations.AddField( + model_name='guide', + name='post_type', + field=models.IntegerField(choices=[(0, 'guide'), (1, 'review')], default=0), + ), + ] diff --git a/bak/unused_apps/guides/migrations/0005_remove_guide_category.py b/bak/unused_apps/guides/migrations/0005_remove_guide_category.py new file mode 100644 index 0000000..71d0280 --- /dev/null +++ b/bak/unused_apps/guides/migrations/0005_remove_guide_category.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.7 on 2019-09-14 07:08 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('guides', '0004_auto_20190914_0646'), + ] + + operations = [ + migrations.RemoveField( + model_name='guide', + name='category', + ), + ] diff --git a/bak/unused_apps/guides/migrations/__init__.py b/bak/unused_apps/guides/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/guides/migrations/__init__.py diff --git a/bak/unused_apps/guides/models.py b/bak/unused_apps/guides/models.py new file mode 100644 index 0000000..8704887 --- /dev/null +++ b/bak/unused_apps/guides/models.py @@ -0,0 +1,136 @@ +from django.contrib.gis.db import models +from django.urls import reverse +from django.contrib.sitemaps import Sitemap +import datetime +from itertools import chain + +from taggit.managers import TaggableManager + +from taxonomy.models import TaggedItems, Category +from utils.util import render_images, markdown_to_html +from fieldnotes.models import FieldNote +from books.models import Book +from locations.models import Location +from photos.models import LuxImage +from essays.models import Essay +from jrnl.models import Entry + + +class Guide(models.Model): + title = models.CharField(max_length=200) + sub_title = models.CharField(max_length=200, blank=True) + dek = models.TextField(blank=True) + prologue_markdown = models.TextField(blank=True) + prologue_html = models.TextField(blank=True) + body_markdown = models.TextField() + body_html = models.TextField(blank=True) + epilogue_markdown = models.TextField(blank=True) + epilogue_html = models.TextField(blank=True) + slug = models.SlugField(unique_for_date='pub_date') + pub_date = models.DateTimeField('Date published') + last_updated = models.DateTimeField(auto_now=True) + enable_comments = models.BooleanField(default=False) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + POST_TYPE = ( + (0, 'guide'), + (1, 'review'), + ) + post_type = models.IntegerField(choices=POST_TYPE, default=0) + meta_description = models.CharField(max_length=256, null=True, blank=True) + tags = TaggableManager(through=TaggedItems, blank=True, help_text='Topics Covered') + featured_image = models.ForeignKey(LuxImage, on_delete=models.CASCADE, null=True, blank=True) + point = models.PointField(null=True, blank=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, null=True, blank=True) + has_video = models.BooleanField(blank=True, default=False) + disclaimer = models.BooleanField(blank=True, default=False) + field_notes = models.ManyToManyField(FieldNote, blank=True) + books = models.ManyToManyField(Book, blank=True) + jrnl = models.ManyToManyField(Entry, blank=True) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + verbose_name_plural = 'Guides' + + def __str__(self): + return self.title + + def get_absolute_url(self): + if self.post_type == 0: + return reverse('guide:guide-detail', kwargs={"slug": self.slug}) + if self.post_type == 1: + return reverse('review:review-detail', kwargs={"slug": self.slug}) + + def comment_period_open(self): + return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + except model.DoesNotExist: + return '' + + @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 save(self, *args, **kwargs): + created = self.pk is None + if not created: + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + self.prologue_html = markdown_to_html(self.prologue_markdown) + self.epilogue_html = markdown_to_html(self.epilogue_markdown) + self.has_video = parse_video(self.body_html) + if self.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)) + if created and not self.featured_image: + self.featured_image = LuxImage.objects.latest() + old = type(self).objects.get(pk=self.pk) if self.pk else None + if old and old.featured_image != self.featured_image: # Field has changed + s = LuxImageSize.objects.get(name="featured_jrnl") + ss = LuxImageSize.objects.get(name="picwide-med") + self.featured_image.sizes.add(s) + self.featured_image.sizes.add(ss) + self.featured_image.save() + super(Guide, self).save(*args, **kwargs) + + +class GuideSitemap(Sitemap): + changefreq = "never" + priority = 1.0 + protocol = "https" + + def items(self): + return Guide.objects.filter(status=1) + + def lastmod(self, obj): + return obj.pub_date diff --git a/bak/unused_apps/guides/review_urls.py b/bak/unused_apps/guides/review_urls.py new file mode 100644 index 0000000..7048065 --- /dev/null +++ b/bak/unused_apps/guides/review_urls.py @@ -0,0 +1,18 @@ +from django.urls import path, re_path + +from . import views + +app_name = "review" + +urlpatterns = [ + path( + r'<str:slug>', + views.GuideDetailView.as_view(), + name="review-detail" + ), + path( + r'<str:slug>.txt', + views.GuideDetailViewTXT.as_view(), + name="review-detail-txt" + ), +] diff --git a/bak/unused_apps/guides/templates/guides/guide_detail.html b/bak/unused_apps/guides/templates/guides/guide_detail.html new file mode 100644 index 0000000..1ef602a --- /dev/null +++ b/bak/unused_apps/guides/templates/guides/guide_detail.html @@ -0,0 +1,176 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load comments %} +{%block htmlclass%}class="detail single"{%endblock%} +{% block pagetitle %}{{object.title|title|smartypants|safe}} - by Scott Gilbertson{% endblock %} + +{% block metadescription %}{% autoescape on %}{{object.meta_description|striptags|safe}}{% endautoescape %}{% endblock %} +{%block extrahead%} +{% if object.has_code %} + <link rel="stylesheet" href="/media/src/solarized.css" type="text/css" media="screen"/> +{%endif %} + <link rel="canonical" href="https://luxagraf.net{{object.get_absolute_url}}" /> + <meta property="og:type" content="article" /> + <meta property="og:title" content="{{object.title|safe}}" /> + <meta property="og:url" content="https://luxagraf.net{{object.get_absolute_url}}" /> + <meta property="og:description" content="{% if object.meta_description %}{{object.meta_description}}{%else%}{{object.sub_title}}{%endif%}" /> + <meta property="article:published_time" content="{{object.pub_date|date:'c'}}" /> + <meta property="article:author" content="Scott Gilbertson" /> + <meta property="og:site_name" content="Luxagraf" /> + <meta property="og:image" content="{{self.get_featured_image}}" /> + <meta property="og:locale" content="en_US" /> + <meta name="twitter:card" content="summary_large_image"/> + <meta name="twitter:description" content="{% if object.meta_description %}{{object.meta_description}}{%else%}{{object.sub_title}}{%endif%}"/> + <meta name="twitter:title" content="{{object.title|safe}}"/> + <meta name="twitter:site" content="@luxagraf"/> + <meta name="twitter:domain" content="luxagraf"/>{% if object.featured_image %} + <meta name="twitter:image:src" content="{{object.featured_image.get_image_url}}"/>{%endif%} + <meta name="twitter:creator" content="@luxagraf"/> +{%endblock%} + +{%block bodyid %}{% if object.get_post_type_display == 'tools' %}class="src"{% endif %}{%endblock%} + +{% block primary %} + <main> + <article class="h-entry hentry {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %} post--article--double{%endif%}{%endwith%}" itemscope itemType="http://schema.org/Article"> + <header id="header" class="post-header {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %}post--header--double{%endif%}{%endwith%}"> + <h1 class="p-name entry-title post-title" itemprop="headline">{%if object.template_name == 1 or object.template_name == 3 %}{{object.title|smartypants|safe}}{%else%}{{object.title|smartypants|safe}}{%endif%}</h1> + <h2 class="post-subtitle">{{object.sub_title|smartypants|safe}}</h2> + <div class="post-linewrapper"> + {% if object.originally_published_by %}<h4 class="post-source">Originally Published By: <a href="{{object.originally_published_by_url}}" title="View {{object.title}} on {{object.originally_published_by}}">{{object.originally_published_by}}</a></h4>{%endif%} + {% if object.location %}<div class="p-location h-adr adr post-location" itemprop="contentLocation" itemscope itemtype="http://schema.org/Place"> + <h3 class="h-adr" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">{% if object.location.country_name == "United States" %}<span class="p-locality locality" itemprop="addressLocality">{{object.location.name|smartypants|safe}}</span>, <a class="p-region region" href="/jrnl/united-states/" title="travel writing from the United States">{{object.location.state_name|safe}}</a>, <span class="p-country-name" itemprop="addressCountry">U.S.</span>{%else%}<span class="p-region" itemprop="addressRegion">{{object.location.name|smartypants|safe}}</span>, <a class="p-country-name country-name" href="/jrnl/{{object.location.country_slug}}/" title="travel writing from {{object.location.country_name}}"><span itemprop="addressCountry">{{object.location.country_name|safe}}</span></a>{%endif%}</h3> + – <a href="" onclick="showMap({{object.latitude}}, {{object.longitude}}, { type:'point', lat:'{{object.latitude}}', lon:'{{object.longitude}}'}); return false;" title="see a map">Map</a> + </div>{%endif%} + <time class="dt-published published dt-updated post-date" datetime="{{object.pub_date|date:'c'}}" itemprop="datePublished">{{object.pub_date|date:"F"}} <span>{{object.pub_date|date:"j, Y"}}</span></time> + <span class="hide" itemprop="author" itemscope itemtype="http://schema.org/Person">by <a class="p-author h-card" href="/about"><span itemprop="name">Scott Gilbertson</span></a></span> + </div> + </header> + <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%} post-essay" itemprop="articleBody"> + {% if object.preamble %}<div class="afterward"> + {{object.preamble_html|smartypants|safe}} + </div>{%endif%} + {{object.body_html|safe|smartypants}} + </div> + {% if object.afterword_html %}<div class="afterward"> + <h4>Afterward</h4> + {{object.afterword_html|smartypants|safe}} + </div>{%endif%} + {%if wildlife or object.field_notes.all or object.books.all %}<div class="entry-footer">{%if wildlife %} + <aside id="wildlife"> + <h3>Fauna and Flora</h3> + {% regroup wildlife by ap.apclass.get_kind_display as wildlife_list %} + <ul> + {% for object_list in wildlife_list %} + <li class="grouper">{{object_list.grouper}}<ul> + {% for object in object_list.list %} + <li>{%if object.ap.body_markdown%}<a href="{% url 'sightings:detail' object.ap.slug %}">{{object}}</a>{%else%}{{object}}{%endif%} </li> + {% endfor %}</ul> + {% endfor %}</ul> + </aside> + {% endif %}{%if object.field_notes.all %} + <aside {% if wildlife %}class="margin-left-none" {%endif%}id="field_notes"> + <h3>Field Notes</h3> + <ul>{% for obj in object.field_notes.all %} + <li><a href="{% url 'fieldnotes:detail' year=obj.pub_date.year month=obj.pub_date|date:"m" slug=obj.slug %}">{{obj}}</a></li> + {% endfor %}</ul> + </aside>{% endif %} + {%if object.books.all %} + <aside id="recommended-reading" {%if object.field_notes.all and wildlife %}class="rr-clear{%endif%}" > + <h3>Recommended Reading</h3> + <ul>{% for obj in object.books.all %} + <li><a href="{% url 'books:detail' slug=obj.slug %}"><img src="{{obj.get_small_image_url}}" /></a></li> + {% endfor %}</ul> + </aside>{% endif %} + </div>{%endif%} + </article> + + {% comment %} <div class="mailing-list--wrapper"> + <h5>If you enjoyed this, you should join the mailing list…</h5> + {% include 'mailing_list.html' %} + </div> {% endcomment %} + </main> + {% if object.enable_comments %} +{% get_comment_count for object as comment_count %} +{%if comment_count > 0 %} +<p class="comments--header">{{comment_count}} Comment{{ comment_count|pluralize }}</p> +{% render_comment_list for object %} +{%endif%} +{% render_comment_form for object %} +{% else %} +<p class="comments--header" style="text-align: center">Sorry, comments have been disabled for this post.</p> +{%endif%} +{% endblock %} +{% block js %} +<script type="text/javascript"> +document.addEventListener("DOMContentLoaded", function(event) { + var leaflet = document.createElement('script'); + leaflet.src = "/media/js/leaflet-master/leaflet-mod.js"; + document.body.appendChild(leaflet); + var lightbox = document.createElement('script'); + lightbox.src = "/media/js/lightbox.js"; + document.body.appendChild(lightbox); + leaflet.onload = function(){ + var detail = document.createElement('script'); + detail.src = "/media/js/detail.min.js"; + document.body.appendChild(detail); + {% with object.get_template_name_display as t %}{%if t == "single" or t == "single-dark" %} + detail.onload = function(){ + createMap(); + var open = false; + } + {%endif%}{%endwith%} + } + + lightbox.onload = function() { + var opts= { + //nextOnClick: false, + captions: true, + onload: function(){ + var im = document.getElementById("jslghtbx-contentwrapper"); + var link = im.appendChild(document.createElement('a')) + link.href = im.firstChild.src; + link.innerHTML= "open "; + link.target = "_blank"; + link.setAttribute('class', 'p-link'); + im.appendChild(link); + } + }; + var lightbox = new Lightbox(); + lightbox.load(opts); + } + {% if object.enable_comments %} +{% get_comment_count for object as comment_count %} +{%if comment_count > 0 %} + //delay loading of gravatar images using noscript data-hash attribute + dataattr = document.getElementsByClassName("datahashloader"); + for(var i=0; i<dataattr.length; i++) { + var c = dataattr[i].parentNode; + var img = document.createElement("img"); + img.src = 'https://images.luxagraf.net/gravcache/' + dataattr[i].getAttribute('data-hash') + '.jpg'; + img.className += "gravatar"; + c.insertBefore(img, c.childNodes[3]); + } +{%endif%} +{%endif%} +{% if object.has_video %} +var tester = document.getElementsByClassName("vidauto"); +var wrapper = document.getElementById('wrapper'); +var dist = 100; + +window.onscroll = function() { + for (var i=0; i<tester.length; i++) { + checkVisible(tester[i]) ? tester[i].play() : tester[i].pause(); + } +}; + +function checkVisible(elm) { + var rect = elm.getBoundingClientRect(); + var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight); + return !(rect.bottom < 0 || rect.top - viewHeight >= 0); +} +{%endif%} + +}); +</script> +{%endblock%} diff --git a/bak/unused_apps/guides/templates/guides/guide_detail.txt b/bak/unused_apps/guides/templates/guides/guide_detail.txt new file mode 100644 index 0000000..547ce79 --- /dev/null +++ b/bak/unused_apps/guides/templates/guides/guide_detail.txt @@ -0,0 +1,8 @@ +{{object.title|safe}} +{% for letter in object.title %}={%endfor%} + + by Scott Gilbertson + <{{SITE_URL}}{{object.get_absolute_url}}> + {{object.pub_date|date:"l, d F Y"}} + +{{object.body_markdown|safe}} diff --git a/bak/unused_apps/guides/templates/guides/guide_list.html b/bak/unused_apps/guides/templates/guides/guide_list.html new file mode 100644 index 0000000..a264a11 --- /dev/null +++ b/bak/unused_apps/guides/templates/guides/guide_list.html @@ -0,0 +1,40 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load html5_datetime %} +{% load pagination_tags %} +{% block pagetitle %}Guides for the Perplexed{% endblock %} +{% block metadescription %}Guides for fellow travelers: tools, tips, and tricks to make life on the road easier.{% 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>Essays</li> + </ul> + <main role="main" id="essay-archive" class="essay-archive archive-list"> + <div class="essay-intro"> + <h2>Guides for fellow travelers</h2> + <p>The less stuff you travel with the better off you will be, up to a point. But where is that point? What's enough? What's too much? That point is what I'm trying to discover here. </p> + <p>What do you really need? What's worth having? What's not?</p> + <p>Topics include {% for topic in topic_list %}{{topic}}, {% endfor %}travel, cooking, photography, writing, simplicity, and once, coffee.</p> + </div> + <h1 class="topic-hed">Guides</h1> + {% autopaginate object_list 30 %} + <ul class="fancy-archive-list">{% for object in object_list %} + <li class="h-entry hentry" itemscope itemType="http://schema.org/Article"> + <a href="{{object.get_absolute_url}}" class="u-url"> + <div class="circle-img-wrapper"><img src="{{object.featured_image.get_thumbnail_url}}" alt="{{object.featured_image.alt}}" class="u-photo" /></div> + <span class="date dt-published">{{object.pub_date|date:"F d, Y"}}</span> + <a href="{{object.get_absolute_url}}"> + <h2>{{object.title|safe|smartypants|widont}}</h2> + {% if object.subtitle %}<h3 class="p-summary">{{object.subtitle|safe|smartypants|widont}}</h3>{%endif%} + </a> + {% if object.location %}<h4 class="p-location h-adr post-location" itemprop="geo" itemscope itemtype="http://data-vocabulary.org/Geo"> + <span class="p-locality">{{object.location.name|smartypants|safe}}</span>, + <span class="p-region">{{object.location.state_name}}</span>, + <span class="p-country-name">{{object.location.country_name}}</span> + <data class="p-latitude" value="{{object.latitude}}"></data> + <data class="p-longitude" value="{{object.longitude}}"></data> + </h4>{% endif %} + </li> + {%endfor%}</ul> + </main> +{%endblock%} diff --git a/bak/unused_apps/guides/urls.py b/bak/unused_apps/guides/urls.py new file mode 100644 index 0000000..c91a5b5 --- /dev/null +++ b/bak/unused_apps/guides/urls.py @@ -0,0 +1,34 @@ +from django.urls import path, re_path + +from . import views + +app_name = "guides" + +urlpatterns = [ + path( + r'', + views.GuideListView.as_view(), + {'page': 1}, + name="list" + ), + path( + r'<int:page>/', + views.GuideListView.as_view(), + name="list" + ), + path( + r'<str:category>/<str:slug>', + views.GuideDetailView.as_view(), + name="detail" + ), + path( + r'<str:category>/<str:slug>', + views.GuideDetailViewTXT.as_view(), + name="detail-txt" + ), + path( + r'<str:category>', + views.GuideCatListView.as_view(), + name="list-cat" + ), +] diff --git a/bak/unused_apps/guides/views.py b/bak/unused_apps/guides/views.py new file mode 100644 index 0000000..01dc974 --- /dev/null +++ b/bak/unused_apps/guides/views.py @@ -0,0 +1,62 @@ +from django.views.generic import ListView +from django.views.generic.detail import DetailView +from django.contrib.syndication.views import Feed + +from utils.views import PaginatedListView + +from .models import Guide + + +class GuideListView(PaginatedListView): + model = Guide + + def get_queryset(self, **kwargs): + qs = Guide.objects.filter(status=1) + return qs + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(GuideListView, self).get_context_data(**kwargs) + context['topic_list'] = Guide.tags.all() + return context + + +class GuideCatListView(GuideListView): + model = Guide + + def get_queryset(self, **kwargs): + cat = Category.objects.get(slug=self.kwargs['slug']) + qs = Guide.objects.filter(status=1, tags=cat) + return qs + +class GuideDetailView(DetailView): + model = Guide + + +class GuideDetailViewTXT(GuideDetailView): + template_name = "essays/entry_detail.txt" + + +''' +class TopicListView(ListView): + template_name = 'archives/src_home.html' + + def queryset(self): + return Post.objects.filter(topics__slug=self.kwargs['slug']) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(TopicListView, self).get_context_data(**kwargs) + context['topic'] = Topic.objects.get(slug__exact=self.kwargs['slug']) + return context + + +class SrcRSSFeedView(Feed): + title = "luxagraf:src Code and Technology" + link = "/src/" + description = "Latest postings to luxagraf.net/src" + description_template = 'feeds/blog_description.html' + + def items(self): + return Post.objects.filter(status__exact=1).order_by('-pub_date')[:10] +''' diff --git a/bak/unused_apps/income/__init__.py b/bak/unused_apps/income/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/income/__init__.py diff --git a/bak/unused_apps/income/admin.py b/bak/unused_apps/income/admin.py new file mode 100644 index 0000000..b165371 --- /dev/null +++ b/bak/unused_apps/income/admin.py @@ -0,0 +1,88 @@ +import datetime +from django.contrib import admin +from django.db.models import Sum +from django.contrib.gis.admin import OSMGeoAdmin +from django.conf.urls import url +from django.shortcuts import render + + +from .models import Gig, Invoice, InvoiceItem + + +@admin.register(InvoiceItem) +class InvoiceItemAdmin(admin.ModelAdmin): + list_display = ('time_start', 'time_end', 'work_done') + + +@admin.register(Invoice) +class InvoiceAdmin(admin.ModelAdmin): + list_display = ('title', 'admin_link', 'date_start', 'date_end') + + +@admin.register(Gig) +class GigAdmin(OSMGeoAdmin): + list_display = ('title', 'status', 'due_date', 'payment_status', 'payment', 'publisher', 'word_count') + list_filter = ('publisher', 'status', 'payment_status') + fieldsets = ( + ('Gig', { + 'fields': ( + 'title', + 'pitch', + ('created', 'due_date'), + ('payment', 'pay_type', 'payment_status', 'invoice_date'), + ('status', 'pub_date', 'word_count'), + 'publisher', + 'pub_item' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + + def get_urls(self): + urls = super(GigAdmin, self).get_urls() + custom_urls = [ + url( + r'^monthly/$', + self.admin_site.admin_view(self.get_monthly), + name='monthly_admin' + ) + ] + return custom_urls + urls + + def get_monthly(self, request): + context = { + 'title': ("This month's income"), + 'app_label': self.model._meta.app_label, + 'opts': self.model._meta, + 'has_change_permission': self.has_change_permission(request) + } + try: + year = request.GET["m"].split("-")[0] + month = request.GET["m"].split("-")[1] + except: + year = datetime.datetime.now().strftime('%Y') + month = datetime.datetime.now().strftime('%m') + qs = self.model.objects.filter( + created__year=year, + created__month=month, + status__in=[1, 2, 3] + ) + context['pitched'] = self.model.objects.filter( + created__year=year, + created__month=month, + status=0 + ) + context['date'] = datetime.datetime.now() + context['billed'] = qs.filter(payment_status=1) + context['billed_total'] = qs.filter(payment_status=1).aggregate(total_payment=Sum('payment')) + context['unbilled'] = qs.filter(payment_status=0) + context['unbilled_total'] = qs.filter(payment_status=0).aggregate(total_payment=Sum('payment')) + context['total_outstanding'] = qs.aggregate(total_payment=Sum('payment')) + context['months'] = self.model.objects.dates('created', 'month') + return render(request, 'admin/income_month.html', context) + diff --git a/bak/unused_apps/income/migrations/0001_initial.py b/bak/unused_apps/income/migrations/0001_initial.py new file mode 100644 index 0000000..3a1e926 --- /dev/null +++ b/bak/unused_apps/income/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-02-10 08:48 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('resume', '0003_auto_20151211_1925'), + ] + + operations = [ + migrations.CreateModel( + name='Gig', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('pitch', models.TextField(blank=True, null=True)), + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ('pub_date', models.DateTimeField(default=django.utils.timezone.now)), + ('status', models.IntegerField(choices=[(0, 'Pitched'), (1, 'Accepted'), (2, 'Submitted'), (3, 'Published'), (4, 'Rejected'), (5, 'TO PITCH')], default=1)), + ('invoice_date', models.DateTimeField(blank=True, null=True)), + ('payment', models.DecimalField(decimal_places=2, max_digits=10)), + ('payment_status', models.IntegerField(choices=[(0, 'NOT SUBMITTED'), (1, 'Invoiced'), (2, 'Paid')], default=1)), + ('pay_type', models.IntegerField(choices=[(0, 'Flat Rate'), (1, 'Per Word'), (2, 'Hourly')], default=1)), + ('word_count', models.DecimalField(blank=True, decimal_places=0, max_digits=7, null=True)), + ('pub_item', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='resume.PubItem')), + ('publisher', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='resume.Publisher')), + ], + ), + ] diff --git a/bak/unused_apps/income/migrations/0002_gig_due_date.py b/bak/unused_apps/income/migrations/0002_gig_due_date.py new file mode 100644 index 0000000..ccd0f73 --- /dev/null +++ b/bak/unused_apps/income/migrations/0002_gig_due_date.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-02-10 08:49 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='gig', + name='due_date', + field=models.DateField(default=django.utils.timezone.now), + ), + ] diff --git a/bak/unused_apps/income/migrations/0003_auto_20161213_1038.py b/bak/unused_apps/income/migrations/0003_auto_20161213_1038.py new file mode 100644 index 0000000..559cd5a --- /dev/null +++ b/bak/unused_apps/income/migrations/0003_auto_20161213_1038.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-12-13 10:38 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0002_gig_due_date'), + ] + + operations = [ + migrations.AlterField( + model_name='gig', + name='due_date', + field=models.DateField(blank=True, null=True), + ), + migrations.AlterField( + model_name='gig', + name='pub_date', + field=models.DateTimeField(blank=True, null=True), + ), + ] diff --git a/bak/unused_apps/income/migrations/0004_invoice_invoiceitem.py b/bak/unused_apps/income/migrations/0004_invoice_invoiceitem.py new file mode 100644 index 0000000..59f389c --- /dev/null +++ b/bak/unused_apps/income/migrations/0004_invoice_invoiceitem.py @@ -0,0 +1,31 @@ +# Generated by Django 2.1 on 2018-09-03 17:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0003_auto_20161213_1038'), + ] + + operations = [ + migrations.CreateModel( + name='Invoice', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('date_start', models.DateField(blank=True, null=True)), + ('date_end', models.DateField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='InvoiceItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('time_start', models.DateTimeField(blank=True, null=True)), + ('time_end', models.DateTimeField(blank=True, null=True)), + ('work_done', models.TextField(blank=True, null=True)), + ], + ), + ] diff --git a/bak/unused_apps/income/migrations/0005_invoice_slug.py b/bak/unused_apps/income/migrations/0005_invoice_slug.py new file mode 100644 index 0000000..3b6dfb2 --- /dev/null +++ b/bak/unused_apps/income/migrations/0005_invoice_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.1 on 2018-09-03 19:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0004_invoice_invoiceitem'), + ] + + operations = [ + migrations.AddField( + model_name='invoice', + name='slug', + field=models.SlugField(default='slug'), + preserve_default=False, + ), + ] diff --git a/bak/unused_apps/income/migrations/0006_auto_20190131_2351.py b/bak/unused_apps/income/migrations/0006_auto_20190131_2351.py new file mode 100644 index 0000000..1c8f64c --- /dev/null +++ b/bak/unused_apps/income/migrations/0006_auto_20190131_2351.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.1 on 2019-01-31 23:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('income', '0005_invoice_slug'), + ] + + operations = [ + migrations.AlterModelOptions( + name='invoiceitem', + options={'ordering': ('time_start',)}, + ), + ] diff --git a/bak/unused_apps/income/migrations/__init__.py b/bak/unused_apps/income/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/income/migrations/__init__.py diff --git a/bak/unused_apps/income/models.py b/bak/unused_apps/income/models.py new file mode 100644 index 0000000..e5a351b --- /dev/null +++ b/bak/unused_apps/income/models.py @@ -0,0 +1,103 @@ +import datetime +from datetime import timedelta +from django.db import models +from django.utils import timezone +from django.urls import reverse +from django.utils.html import format_html + +from resume.models import PubItem, Publisher + + +class Gig(models.Model): + title = models.CharField(max_length=200) + pitch = models.TextField(null=True, blank=True) + created = models.DateTimeField(default=timezone.now) + pub_date = models.DateTimeField(blank=True, null=True) + due_date = models.DateField(blank=True, null=True) + STATUS = ( + (0, "Pitched"), + (1, "Accepted"), + (2, "Submitted"), + (3, "Published"), + (4, "Rejected"), + (5, "TO PITCH"), + ) + status = models.IntegerField(choices=STATUS, default=1) + invoice_date = models.DateTimeField(null=True, blank=True) + payment = models.DecimalField(max_digits=10, decimal_places=2) + PAY_STATUS = ( + (0, "NOT SUBMITTED"), + (1, "Invoiced"), + (2, "Paid"), + ) + payment_status = models.IntegerField(choices=PAY_STATUS, default=1) + PAY_TYPE = ( + (0, "Flat Rate"), + (1, "Per Word"), + (2, "Hourly"), + ) + pay_type = models.IntegerField(choices=PAY_TYPE, default=1) + word_count = models.DecimalField(max_digits=7, decimal_places=0, blank=True, null=True) + publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, blank=True, null=True) + pub_item = models.ForeignKey(PubItem, on_delete=models.CASCADE, blank=True, null=True) + + def __str__(self): + return self.title + + def get_pay_date(self): + days = self.publisher.payment_time * 7 + if self.invoice_date: + return self.invoice_date + datetime.timedelta(float(days)) + + +class Invoice(models.Model): + title = models.CharField(max_length=200) + slug = models.SlugField() + date_start = models.DateField(null=True, blank=True) + date_end = models.DateField(null=True, blank=True) + + def __str__(self): + return self.title + + def admin_link(self): + return format_html('<a href="/admin/income/invoice/monthlyview/%s/">View Invoice</a>' % (self.slug)) + admin_link.short_description = 'Invoice' + + +class InvoiceItem(models.Model): + time_start = models.DateTimeField(null=True, blank=True) + time_end = models.DateTimeField(null=True, blank=True) + work_done = models.TextField(null=True, blank=True) + + class Meta: + ordering = ('time_start',) + + def __str__(self): + return str(self.time_start) + + @property + def total(self): + return self.time_end - self.time_start + + @property + def rounded_total(self): + """ + Rounds the given timedelta by the given timedelta period + :param td: `timedelta` to round + :param period: `timedelta` period to round by. + """ + period = timedelta(minutes=15) + td = self.total + period_seconds = period.total_seconds() + half_period_seconds = period_seconds / 2 + remainder = td.total_seconds() % period_seconds + if remainder >= half_period_seconds: + tdr = timedelta(seconds=td.total_seconds() + (period_seconds - remainder)) + hours, remainder = divmod(tdr.total_seconds(), 3600) + r = remainder/3600 + return float(hours)+r + else: + tdr = timedelta(seconds=td.total_seconds() - remainder) + hours, remainder = divmod(tdr.total_seconds(), 3600) + r = remainder/3600 + return float(hours)+r diff --git a/bak/unused_apps/income/parser.py b/bak/unused_apps/income/parser.py new file mode 100644 index 0000000..b19d039 --- /dev/null +++ b/bak/unused_apps/income/parser.py @@ -0,0 +1,23 @@ +import csv +import datetime +from .models import InvoiceItem + + +def read_timesheet(): + with open('timesheet.csv', newline='') as csvfile: + reader = csv.reader(csvfile, delimiter=';') + counter = 0 + f = "%Y-%m-%d %H:%M:%S" + for row in reader: + if counter > 0: + print(row[4]) + timer = row[0]+' '+row[1] + timerer = row[0]+' '+row[2] + time_start = datetime.datetime.strptime(timer, f) + time_end = datetime.datetime.strptime(timerer, f) + InvoiceItem.objects.get_or_create( + time_start=time_start, + time_end=time_end, + work_done=row[4] + ) + counter = counter + 1 diff --git a/bak/unused_apps/income/views.py b/bak/unused_apps/income/views.py new file mode 100644 index 0000000..1c34068 --- /dev/null +++ b/bak/unused_apps/income/views.py @@ -0,0 +1,53 @@ +import datetime +from django.views.generic.detail import DetailView +from django.template.loader import render_to_string +from django.http import HttpResponse +from django.conf import settings + +#from weasyprint import HTML, CSS + +from .models import Invoice, InvoiceItem + + +class MonthlyInvoiceView(DetailView): + model = Invoice + template_name = "admin/income/monthly.html" + slug_field = "slug" + + def get_context_data(self, **kwargs): + context = super(MonthlyInvoiceView, self).get_context_data(**kwargs) + context['object_list'] = InvoiceItem.objects.filter(time_start__range=[self.object.date_start, self.object.date_end]) + total_time = [] + for item in context['object_list']: + total_time.append(item.rounded_total) + hours = (sum(total_time)) + context['total_hours'] = hours + context['total_billed'] = int(hours * 100) + context['invoice_number'] = self.object.id+21 + return context + + +class DownloadMonthlyInvoiceView(MonthlyInvoiceView): + model = Invoice + slug_field = "slug" + + def get(self, *args, **kwargs): + import logging + logger = logging.getLogger('weasyprint') + logger.addHandler(logging.FileHandler('weasyprint.log')) + self.object = self.get_object() # assign the object to the view + context = self.get_context_data() + c = { + 'object': self.object, + 'object_list': context['object_list'], + 'total_hours': context['total_hours'], + 'total_billed': context['total_billed'], + 'invoice_number': self.object.id+23 + } + t = render_to_string('details/invoice.html', c).encode('utf-8') + #html = HTML(string=t, base_url=self.request.build_absolute_uri()) + #pdf = html.write_pdf(stylesheets=[CSS(settings.MEDIA_ROOT + '/pdf_gen.css')], presentational_hints=True) + #response = HttpResponse(pdf, content_type='application/pdf') + #response['Content-Disposition'] = 'inline; filename="invoice.pdf"' + response = HttpResponse('', content_type='application/pdf') + return response diff --git a/bak/unused_apps/jrnl/__init__.py b/bak/unused_apps/jrnl/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/jrnl/__init__.py diff --git a/bak/unused_apps/jrnl/admin.py b/bak/unused_apps/jrnl/admin.py new file mode 100644 index 0000000..32ef69b --- /dev/null +++ b/bak/unused_apps/jrnl/admin.py @@ -0,0 +1,129 @@ +from django.contrib import admin +from django import forms +from django.contrib.gis.admin import OSMGeoAdmin +from django.contrib.contenttypes.admin import GenericStackedInline + +from utils.widgets import AdminImageWidget, LGEntryForm +from .models import Entry, HomepageCurrator, Home #, RelatedPost + +from photos.forms import GalleryForm +from photos.models import LuxImage +from utils.util import get_latlon + + +#@admin.register(RelatedPost) +#class RelatedPostAdmin(admin.ModelAdmin): +# pass + + +@admin.register(Entry) +class EntryAdmin(OSMGeoAdmin): + form = LGEntryForm + + def get_queryset(self, request): + test_model_qs = super(EntryAdmin, self).get_queryset(request) + test_model_qs = test_model_qs.prefetch_related('related').prefetch_related('books') + + return test_model_qs + + def render_change_form(self, request, context, *args, **kwargs): + #context['adminform'].form.fields['featured_image'].queryset = LuxImage.objects.all()[:200] + return super(EntryAdmin, self).render_change_form(request, context, *args, **kwargs) + + def formfield_for_dbfield(self, db_field, **kwargs): + if db_field.name == 'thumbnail' or db_field.name == 'image': + field = forms.FileField(widget=AdminImageWidget) + elif db_field.name == 'meta_description': + field = forms.CharField(widget=forms.Textarea(attrs={'rows': 4, 'cols': 75})) + field.required = False + else: + field = super(EntryAdmin, self).formfield_for_dbfield(db_field, **kwargs) + return field + + list_display = ('title', 'pub_date', 'template_name', 'status', 'location', 'photo_gallery') + search_fields = ['title', 'body_markdown'] + prepopulated_fields = {"slug": ('title',)} + list_filter = ('pub_date', 'enable_comments', 'status', 'location__state__country__lux_region') + filter_horizontal = ('field_notes', 'books', 'related') + fieldsets = ( + ('Entry', { + 'fields': ( + 'title', + 'subtitle', + 'body_markdown', + ('pub_date', 'status'), + 'slug', + 'point' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ('Formatting data', { + 'fields': ( + 'dek', + 'meta_description', + 'template_name', + 'enable_comments', + 'featured_image', + ), + }), + ('Extra', { + 'fields': ( + 'field_notes', + #'old_field_notes', + 'books', + 'related', + #'oldrelated', + ), + 'classes': ( + 'collapse', + 'extrapretty', + 'wide' + ) + }), + ) + # options for OSM map Using custom ESRI topo map + lat, lon = get_latlon() + default_lon = lon + default_lat = lat + default_zoom = 10 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } + + +@admin.register(HomepageCurrator) +class HomepageCurratorAdmin(admin.ModelAdmin): + form = GalleryForm + filter_horizontal = ('popular',) + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } + + +@admin.register(Home) +class HomeAdmin(admin.ModelAdmin): + form = LGEntryForm + filter_horizontal = ('popular',) + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } diff --git a/bak/unused_apps/jrnl/build.py b/bak/unused_apps/jrnl/build.py new file mode 100644 index 0000000..dcebace --- /dev/null +++ b/bak/unused_apps/jrnl/build.py @@ -0,0 +1,100 @@ +from django.urls import reverse +from django.apps import apps +from builder.base import BuildNew +from itertools import chain + +from django.conf import settings + + +class BuildJrnl(BuildNew): + + def build(self): + self.build_list_view( + base_path=reverse("jrnl:list"), + paginate_by=24 + ) + self.build_year_view("jrnl:list_year") + self.build_month_view("jrnl:list_month") + self.build_detail_view() + self.build_location_view() + self.build_feed("jrnl:feed") + self.build_latest() + + def build_arc(self): + self.build_list_view( + base_path=reverse("jrnl:list"), + paginate_by=24 + ) + self.build_year_view("jrnl:list_year") + self.build_month_view("jrnl:list_month") + self.build_location_view() + + def build_location_view(self): + c = apps.get_model('locations', 'Country') + r = apps.get_model('locations', 'Region') + countries = c.objects.filter(visited=True) + regions = r.objects.all() + locations = list(chain(countries, regions)) + for c in locations: + try: + qs = self.model.objects.filter( + status__exact=1, + location__state__country=c + ) + except: + qs = self.model.objects.filter( + status__exact=1, + location__state__country__lux_region=c.id + ) + print(c) + pages = self.get_pages(qs, 24) + for page in range(pages): + base_path = reverse("jrnl:list_country", kwargs={'slug': c.slug, 'page': page + 1}) + response = self.client.get(base_path) + print(response.content) + if page == 0: + self.write_file(base_path, response.content) + else: + self.write_file(base_path, response.content) + + def build_homepage(self): + response = self.client.get('/') + self.write_file('', response.content) + + def build_latest(self): + response = self.client.get('/jrnl/latest/') + self.write_file(reverse("jrnl:latest"), response.content) + + def build_map(self): + self.build_list_view( + base_path=reverse("map:maplist"), + paginate_by=1000000 + ) + response = self.client.get(reverse("map:mapdata")) + self.write_file("media/js/mainmap", response.content, 'js', '') + + +def archive_builder(): + j = BuildJrnl("jrnl", "entry") + j.build_arc() + + +def detail_builder(): + j = BuildJrnl("jrnl", "entry") + j.build_latest() + j.build_detail_view() + + +def home_builder(): + j = BuildJrnl("jrnl", "entry") + j.build_homepage() + + +def rss_builder(): + j = BuildJrnl("jrnl", "entry") + j.build_feed("jrnl:feed") + + +def map_builder(): + j = BuildJrnl("jrnl", "entry") + j.build_map() diff --git a/bak/unused_apps/jrnl/export.py b/bak/unused_apps/jrnl/export.py new file mode 100644 index 0000000..6407491 --- /dev/null +++ b/bak/unused_apps/jrnl/export.py @@ -0,0 +1,20 @@ +from django.template.loader import render_to_string +from django.template import Context + + +def write_file(entry): + body = '' + lines = entry.body_markdown.split('\n') + for line in lines: + if line.strip(): + if line.startswith('<break>'): + pass + else: + body += line.strip()+'\n\n' + img = entry.image.name[19:] + c = Context({'object': entry, 'body': body, 'image': img},) + t = render_to_string('details/jrnl-export.html', c).encode('utf-8') + filepath = 'temp/%s-%s.txt' % (entry.pub_date.strftime("%Y-%m-%d").lower(), entry.slug) + f = open(filepath, 'wb') + f.write(t) + f.close() diff --git a/bak/unused_apps/jrnl/fields.py b/bak/unused_apps/jrnl/fields.py new file mode 100644 index 0000000..8df2f5e --- /dev/null +++ b/bak/unused_apps/jrnl/fields.py @@ -0,0 +1,7 @@ +from django import forms + +from .widgets import AdminImageWidget + + +class FileUploadForm(forms.ModelForm): + upload = forms.FileField(widget=AdminImageWidget) diff --git a/bak/unused_apps/jrnl/migrations/0001_initial.py b/bak/unused_apps/jrnl/migrations/0001_initial.py new file mode 100644 index 0000000..a3660ab --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0001_initial.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-09 10:13 +from __future__ import unicode_literals + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import jrnl.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + #('locations', '__first__'), + ('photos', '0003_luxgallery_caption_style'), + ] + + operations = [ + migrations.CreateModel( + name='Entry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField()), + ('dek', models.TextField(blank=True, null=True)), + ('pub_date', models.DateTimeField(verbose_name='Date published')), + ('enable_comments', models.BooleanField(default=False)), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=0)), + ('image', models.FileField(blank=True, help_text='should be 205px high', null=True, upload_to=jrnl.models.get_upload_path)), + ('meta_description', models.CharField(blank=True, max_length=256, null=True)), + ('template_name', models.IntegerField(choices=[(0, 'single'), (1, 'double'), (2, 'single-dark'), (3, 'double-dark'), (4, 'bigimg'), (5, 'bigimg-dark')], default=0)), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ('photo_gallery', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photos.PhotoGallery', verbose_name='photo set')), + ], + options={ + 'verbose_name_plural': 'entries', + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date',), + }, + ), + migrations.CreateModel( + name='EntryAside', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('body', models.TextField(blank=True, null=True)), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='jrnl.Entry')), + ], + ), + migrations.CreateModel( + name='HomepageCurrator', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('alt_text', models.CharField(max_length=200)), + ('image_base_url', models.CharField(max_length=200)), + ('tag_line', models.CharField(max_length=200)), + ('template_name', models.CharField(help_text='full path', max_length=200)), + ('banner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='banner', to='jrnl.Entry')), + ('entry_list', models.ManyToManyField(to='jrnl.Entry')), + ], + ), + migrations.CreateModel( + name='PostImage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=100)), + ('image', models.ImageField(upload_to='/Users/sng/Sites/luxagraf/site/media/images/2016')), + ], + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0001_squashed_0019_remove_entry_thumbnail.py b/bak/unused_apps/jrnl/migrations/0001_squashed_0019_remove_entry_thumbnail.py new file mode 100644 index 0000000..7b1b247 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0001_squashed_0019_remove_entry_thumbnail.py @@ -0,0 +1,81 @@ +# Generated by Django 2.0.1 on 2018-07-07 15:30 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import jrnl.models + + +class Migration(migrations.Migration): + + replaces = [('jrnl', '0001_initial'), ('jrnl', '0002_entrytitlesong'), ('jrnl', '0003_auto_20160309_1018'), ('jrnl', '0004_auto_20160309_1031'), ('jrnl', '0005_auto_20160514_2151'), ('jrnl', '0006_auto_20160715_0703'), ('jrnl', '0007_delete_postimage'), ('jrnl', '0008_auto_20160906_0845'), ('jrnl', '0009_homepagecurrator_image_offset_vertical'), ('jrnl', '0010_auto_20161102_0916'), ('jrnl', '0011_auto_20161102_0925'), ('jrnl', '0012_auto_20161102_0930'), ('jrnl', '0013_entry_featured_image'), ('jrnl', '0014_homepagecurrator'), ('jrnl', '0015_entry_has_video'), ('jrnl', '0016_auto_20161219_1058'), ('jrnl', '0017_entry_field_notes'), ('jrnl', '0018_auto_20180303_1037'), ('jrnl', '0019_remove_entry_thumbnail')] + + initial = True + + dependencies = [ + ('photos', '0016_auto_20161022_1411'), + ('books', '0005_auto_20171214_2239'), + ('photos', '0010_auto_20160517_0906'), + ('photos', '0003_luxgallery_caption_style'), + ] + + operations = [ + migrations.CreateModel( + name='Entry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField()), + ('dek', models.TextField(blank=True, null=True)), + ('pub_date', models.DateTimeField(verbose_name='Date published')), + ('enable_comments', models.BooleanField(default=False)), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=0)), + ('image', models.FileField(blank=True, help_text='should be 205px high by 364px wide', null=True, upload_to=jrnl.models.get_upload_path)), + ('meta_description', models.CharField(blank=True, max_length=256, null=True)), + ('template_name', models.IntegerField(choices=[(0, 'single'), (1, 'double'), (2, 'single-dark'), (3, 'double-dark'), (4, 'single-black'), (5, 'double-black')], default=0)), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ('photo_gallery', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photos.PhotoGallery', verbose_name='photo set')), + ('featured_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photos.LuxImage')), + ], + options={ + 'verbose_name_plural': 'entries', + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date',), + }, + ), + migrations.CreateModel( + name='HomepageCurrator', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image_offset_vertical', models.CharField(help_text='add negative top margin to shift image (include css unit)', max_length=20)), + ('tag_line', models.CharField(max_length=200)), + ('template_name', models.CharField(help_text='full path', max_length=200)), + ('featured', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='banner', to='jrnl.Entry')), + ('images', models.ManyToManyField(to='photos.LuxImage')), + ('popular', models.ManyToManyField(to='jrnl.Entry')), + ], + ), + migrations.AddField( + model_name='entry', + name='has_video', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='entry', + name='image', + field=models.FileField(blank=True, help_text='should be 520 by 290', null=True, upload_to=jrnl.models.get_upload_path), + ), + migrations.AddField( + model_name='entry', + name='field_notes', + field=models.ManyToManyField(blank=True, to='fieldnotes.FieldNote'), + ), + migrations.AddField( + model_name='entry', + name='books', + field=models.ManyToManyField(blank=True, to='books.Book'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0002_entrytitlesong.py b/bak/unused_apps/jrnl/migrations/0002_entrytitlesong.py new file mode 100644 index 0000000..66a75a6 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0002_entrytitlesong.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-09 10:13 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='EntryTitleSong', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('pub_date', models.DateField(verbose_name='Date published')), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='jrnl.Entry')), + ], + options={ + 'ordering': ('-pub_date',), + 'get_latest_by': 'pub_date', + 'verbose_name_plural': 'Entry Title Songs', + }, + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0003_auto_20160309_1018.py b/bak/unused_apps/jrnl/migrations/0003_auto_20160309_1018.py new file mode 100644 index 0000000..49a3f6f --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0003_auto_20160309_1018.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-09 10:18 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0002_entrytitlesong'), + ] + + operations = [ + migrations.AddField( + model_name='entrytitlesong', + name='album', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AddField( + model_name='entrytitlesong', + name='band', + field=models.CharField(default='none', max_length=200), + preserve_default=False, + ), + migrations.AddField( + model_name='entrytitlesong', + name='song', + field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0004_auto_20160309_1031.py b/bak/unused_apps/jrnl/migrations/0004_auto_20160309_1031.py new file mode 100644 index 0000000..33bbe05 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0004_auto_20160309_1031.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-09 10:31 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0003_auto_20160309_1018'), + ] + + operations = [ + migrations.AddField( + model_name='entrytitlesong', + name='body_markdown', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='entrytitlesong', + name='listen_link', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AlterField( + model_name='entrytitlesong', + name='slug', + field=models.SlugField(blank=True, unique_for_date='pub_date'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0005_auto_20160514_2151.py b/bak/unused_apps/jrnl/migrations/0005_auto_20160514_2151.py new file mode 100644 index 0000000..701c8c3 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0005_auto_20160514_2151.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-05-14 21:51 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0004_auto_20160309_1031'), + ] + + operations = [ + migrations.AlterField( + model_name='entrytitlesong', + name='title', + field=models.CharField(blank=True, max_length=200), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0006_auto_20160715_0703.py b/bak/unused_apps/jrnl/migrations/0006_auto_20160715_0703.py new file mode 100644 index 0000000..42efc43 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0006_auto_20160715_0703.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-15 07:03 +from __future__ import unicode_literals + +from django.db import migrations, models +import jrnl.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0010_auto_20160517_0906'), + ('jrnl', '0005_auto_20160514_2151'), + ] + + operations = [ + migrations.AddField( + model_name='homepagecurrator', + name='images', + field=models.ManyToManyField(to='photos.LuxImage'), + ), + migrations.AlterField( + model_name='entry', + name='image', + field=models.FileField(blank=True, help_text='should be 205px high by 364px wide', null=True, upload_to=jrnl.models.get_upload_path), + ), + migrations.AlterField( + model_name='entry', + name='template_name', + field=models.IntegerField(choices=[(0, 'single'), (1, 'double'), (2, 'single-dark'), (3, 'double-dark'), (4, 'single-black'), (5, 'double-black')], default=0), + ), + migrations.AlterField( + model_name='postimage', + name='image', + field=models.ImageField(upload_to='/home/lxf/Sites/luxagraf/site/media/images/2016'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0007_delete_postimage.py b/bak/unused_apps/jrnl/migrations/0007_delete_postimage.py new file mode 100644 index 0000000..cc481e8 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0007_delete_postimage.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-15 07:05 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0006_auto_20160715_0703'), + ] + + operations = [ + migrations.DeleteModel( + name='PostImage', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0008_auto_20160906_0845.py b/bak/unused_apps/jrnl/migrations/0008_auto_20160906_0845.py new file mode 100644 index 0000000..4ac5ebf --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0008_auto_20160906_0845.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-09-06 08:45 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0007_delete_postimage'), + ] + + operations = [ + migrations.RenameField( + model_name='homepagecurrator', + old_name='banner', + new_name='featured', + ), + migrations.RenameField( + model_name='homepagecurrator', + old_name='entry_list', + new_name='popular', + ), + migrations.RemoveField( + model_name='homepagecurrator', + name='alt_text', + ), + migrations.RemoveField( + model_name='homepagecurrator', + name='image_base_url', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0009_homepagecurrator_image_offset_vertical.py b/bak/unused_apps/jrnl/migrations/0009_homepagecurrator_image_offset_vertical.py new file mode 100644 index 0000000..5dfbbad --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0009_homepagecurrator_image_offset_vertical.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-09-06 09:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0008_auto_20160906_0845'), + ] + + operations = [ + migrations.AddField( + model_name='homepagecurrator', + name='image_offset_vertical', + field=models.CharField(default='160px', help_text='add negative top margin to shift image (include css unit)', max_length=20), + preserve_default=False, + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0010_auto_20161102_0916.py b/bak/unused_apps/jrnl/migrations/0010_auto_20161102_0916.py new file mode 100644 index 0000000..a2d9eb8 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0010_auto_20161102_0916.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-11-02 09:16 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0009_homepagecurrator_image_offset_vertical'), + ] + + operations = [ + migrations.RemoveField( + model_name='entryaside', + name='entry', + ), + migrations.DeleteModel( + name='EntryAside', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0011_auto_20161102_0925.py b/bak/unused_apps/jrnl/migrations/0011_auto_20161102_0925.py new file mode 100644 index 0000000..e11c1ba --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0011_auto_20161102_0925.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-11-02 09:25 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0010_auto_20161102_0916'), + ] + + operations = [ + migrations.RemoveField( + model_name='entrytitlesong', + name='entry', + ), + migrations.DeleteModel( + name='EntryTitleSong', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0012_auto_20161102_0930.py b/bak/unused_apps/jrnl/migrations/0012_auto_20161102_0930.py new file mode 100644 index 0000000..d1a65dd --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0012_auto_20161102_0930.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-11-02 09:30 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0011_auto_20161102_0925'), + ] + + operations = [ + migrations.RemoveField( + model_name='homepagecurrator', + name='featured', + ), + migrations.RemoveField( + model_name='homepagecurrator', + name='images', + ), + migrations.RemoveField( + model_name='homepagecurrator', + name='popular', + ), + migrations.DeleteModel( + name='HomepageCurrator', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0013_entry_featured_image.py b/bak/unused_apps/jrnl/migrations/0013_entry_featured_image.py new file mode 100644 index 0000000..c607ca7 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0013_entry_featured_image.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-11-02 09:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0016_auto_20161022_1411'), + ('jrnl', '0012_auto_20161102_0930'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='featured_image', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photos.LuxImage'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0014_homepagecurrator.py b/bak/unused_apps/jrnl/migrations/0014_homepagecurrator.py new file mode 100644 index 0000000..2d34866 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0014_homepagecurrator.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-11-02 09:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0016_auto_20161022_1411'), + ('jrnl', '0013_entry_featured_image'), + ] + + operations = [ + migrations.CreateModel( + name='HomepageCurrator', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image_offset_vertical', models.CharField(help_text='add negative top margin to shift image (include css unit)', max_length=20)), + ('tag_line', models.CharField(max_length=200)), + ('template_name', models.CharField(help_text='full path', max_length=200)), + ('featured', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='banner', to='jrnl.Entry')), + ('images', models.ManyToManyField(to='photos.LuxImage')), + ('popular', models.ManyToManyField(to='jrnl.Entry')), + ], + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0015_entry_has_video.py b/bak/unused_apps/jrnl/migrations/0015_entry_has_video.py new file mode 100644 index 0000000..b643342 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0015_entry_has_video.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-11-02 09:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ('photos', '0016_auto_20161022_1411'), + ('jrnl', '0014_homepagecurrator'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='has_video', + field=models.BooleanField(blank=True, default=False), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0016_auto_20161219_1058.py b/bak/unused_apps/jrnl/migrations/0016_auto_20161219_1058.py new file mode 100644 index 0000000..f9a3fc8 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0016_auto_20161219_1058.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-12-19 10:58 +from __future__ import unicode_literals + +from django.db import migrations, models +import jrnl.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0015_entry_has_video'), + ] + + operations = [ + migrations.AlterField( + model_name='entry', + name='image', + field=models.FileField(blank=True, help_text='should be 520 by 290', null=True, upload_to=jrnl.models.get_upload_path), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0017_entry_field_notes.py b/bak/unused_apps/jrnl/migrations/0017_entry_field_notes.py new file mode 100644 index 0000000..f131183 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0017_entry_field_notes.py @@ -0,0 +1,13 @@ +# Generated by Django 2.0.1 on 2018-02-08 07:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0016_auto_20161219_1058'), + ] + + operations = [ + ] diff --git a/bak/unused_apps/jrnl/migrations/0018_auto_20180303_1037.py b/bak/unused_apps/jrnl/migrations/0018_auto_20180303_1037.py new file mode 100644 index 0000000..2b4baa6 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0018_auto_20180303_1037.py @@ -0,0 +1,24 @@ +# Generated by Django 2.0.1 on 2018-03-03 10:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('books', '0005_auto_20171214_2239'), + ('jrnl', '0017_entry_field_notes'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='books', + field=models.ManyToManyField(blank=True, to='books.Book'), + ), + migrations.AlterField( + model_name='entry', + name='field_notes', + field=models.ManyToManyField(blank=True, to='sketches.Sketch'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0019_remove_entry_thumbnail.py b/bak/unused_apps/jrnl/migrations/0019_remove_entry_thumbnail.py new file mode 100644 index 0000000..971cb03 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0019_remove_entry_thumbnail.py @@ -0,0 +1,13 @@ +# Generated by Django 2.0.1 on 2018-06-06 10:58 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0018_auto_20180303_1037'), + ] + + operations = [ + ] diff --git a/bak/unused_apps/jrnl/migrations/0020_auto_20180606_1058.py b/bak/unused_apps/jrnl/migrations/0020_auto_20180606_1058.py new file mode 100644 index 0000000..7575d4f --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0020_auto_20180606_1058.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.1 on 2018-06-06 10:58 + +from django.db import migrations, models +import jrnl.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0019_remove_entry_thumbnail'), + ] + + operations = [ + migrations.AlterField( + model_name='entry', + name='image', + field=models.FileField(blank=True, help_text='should be 520 by 290', upload_to=jrnl.models.get_upload_path), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0021_auto_20180606_1058.py b/bak/unused_apps/jrnl/migrations/0021_auto_20180606_1058.py new file mode 100644 index 0000000..ab42290 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0021_auto_20180606_1058.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.1 on 2018-06-06 10:58 + +from django.db import migrations, models +import jrnl.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0020_auto_20180606_1058'), + ] + + operations = [ + migrations.AlterField( + model_name='entry', + name='image', + field=models.FileField(blank=True, help_text='should be 520 by 290', null=True, upload_to=jrnl.models.get_upload_path), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0022_auto_20180707_0958.py b/bak/unused_apps/jrnl/migrations/0022_auto_20180707_0958.py new file mode 100644 index 0000000..809f562 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0022_auto_20180707_0958.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.1 on 2018-07-07 09:58 + +from django.db import migrations, models +import jrnl.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0021_auto_20180606_1058'), + ] + + operations = [ + migrations.AlterField( + model_name='entry', + name='image', + field=models.FileField(blank=True, help_text='should be 520 by 290', upload_to=jrnl.models.get_upload_path), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0023_auto_20180902_1210.py b/bak/unused_apps/jrnl/migrations/0023_auto_20180902_1210.py new file mode 100644 index 0000000..0ce3853 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0023_auto_20180902_1210.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1 on 2018-09-02 12:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0022_auto_20180707_0958'), + ] + + operations = [ + migrations.AlterField( + model_name='entry', + name='has_video', + field=models.BooleanField(blank=True, default=False), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0024_auto_20180902_1217.py b/bak/unused_apps/jrnl/migrations/0024_auto_20180902_1217.py new file mode 100644 index 0000000..314c8c6 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0024_auto_20180902_1217.py @@ -0,0 +1,32 @@ +# Generated by Django 2.1 on 2018-09-02 12:17 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0018_auto_20161130_1218'), + ('jrnl', '0023_auto_20180902_1210'), + ] + + operations = [ + migrations.CreateModel( + name='Home', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image_offset_vertical', models.CharField(help_text='add negative top margin to shift image (include css unit)', max_length=20)), + ('tag_line', models.CharField(max_length=200)), + ('template_name', models.CharField(help_text='full path', max_length=200)), + ('featured', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='banner', to='jrnl.Entry')), + ('featured_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='photos.LuxImage')), + ('popular', models.ManyToManyField(to='jrnl.Entry')), + ], + ), + migrations.AlterField( + model_name='homepagecurrator', + name='featured', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='oldbanner', to='jrnl.Entry'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0025_auto_20190131_2335.py b/bak/unused_apps/jrnl/migrations/0025_auto_20190131_2335.py new file mode 100644 index 0000000..60b9a8c --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0025_auto_20190131_2335.py @@ -0,0 +1,28 @@ +# Generated by Django 2.1.1 on 2019-01-31 23:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0024_auto_20180902_1217'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='location_name', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AddField( + model_name='entry', + name='region_name', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AddField( + model_name='entry', + name='state_name', + field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0026_entry_country_name.py b/bak/unused_apps/jrnl/migrations/0026_entry_country_name.py new file mode 100644 index 0000000..22d07f9 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0026_entry_country_name.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.1 on 2019-01-31 23:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0025_auto_20190131_2335'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='country_name', + field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0027_entry_country_slug.py b/bak/unused_apps/jrnl/migrations/0027_entry_country_slug.py new file mode 100644 index 0000000..4560497 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0027_entry_country_slug.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.5 on 2019-02-15 21:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0026_entry_country_name'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='country_slug', + field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0028_auto_20190218_1614.py b/bak/unused_apps/jrnl/migrations/0028_auto_20190218_1614.py new file mode 100644 index 0000000..aff2331 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0028_auto_20190218_1614.py @@ -0,0 +1,33 @@ +# Generated by Django 2.1.5 on 2019-02-18 16:14 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0027_entry_country_slug'), + ] + + operations = [ + migrations.RemoveField( + model_name='entry', + name='country_name', + ), + migrations.RemoveField( + model_name='entry', + name='country_slug', + ), + migrations.RemoveField( + model_name='entry', + name='location_name', + ), + migrations.RemoveField( + model_name='entry', + name='region_name', + ), + migrations.RemoveField( + model_name='entry', + name='state_name', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0029_home_more_popular.py b/bak/unused_apps/jrnl/migrations/0029_home_more_popular.py new file mode 100644 index 0000000..2adc219 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0029_home_more_popular.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2019-02-20 11:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0028_auto_20190218_1614'), + ] + + operations = [ + migrations.AddField( + model_name='home', + name='more_popular', + field=models.ManyToManyField(related_name='morepopular', to='jrnl.Entry'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0030_remove_home_popular.py b/bak/unused_apps/jrnl/migrations/0030_remove_home_popular.py new file mode 100644 index 0000000..8ac7408 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0030_remove_home_popular.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.2 on 2019-02-20 11:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0029_home_more_popular'), + ] + + operations = [ + migrations.RemoveField( + model_name='home', + name='popular', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0031_auto_20190220_1136.py b/bak/unused_apps/jrnl/migrations/0031_auto_20190220_1136.py new file mode 100644 index 0000000..621b692 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0031_auto_20190220_1136.py @@ -0,0 +1,22 @@ +# Generated by Django 2.1.2 on 2019-02-20 11:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0030_remove_home_popular'), + ] + + operations = [ + migrations.RemoveField( + model_name='home', + name='more_popular', + ), + migrations.AddField( + model_name='home', + name='popular', + field=models.ManyToManyField(related_name='popular', to='jrnl.Entry'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0032_entry_subtitle.py b/bak/unused_apps/jrnl/migrations/0032_entry_subtitle.py new file mode 100644 index 0000000..d5ccc80 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0032_entry_subtitle.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-02-28 09:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0031_auto_20190220_1136'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='subtitle', + field=models.CharField(blank=True, max_length=200), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0033_entry_field_notes_two.py b/bak/unused_apps/jrnl/migrations/0033_entry_field_notes_two.py new file mode 100644 index 0000000..b90e86b --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0033_entry_field_notes_two.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.7 on 2019-03-03 15:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fieldnotes', '0002_auto_20190303_1222'), + ('jrnl', '0032_entry_subtitle'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='field_notes_two', + field=models.ManyToManyField(blank=True, to='fieldnotes.FieldNote'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0034_remove_entry_field_notes.py b/bak/unused_apps/jrnl/migrations/0034_remove_entry_field_notes.py new file mode 100644 index 0000000..a0e93b0 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0034_remove_entry_field_notes.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.7 on 2019-03-03 16:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0033_entry_field_notes_two'), + ] + + operations = [ + migrations.RemoveField( + model_name='entry', + name='field_notes', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0035_auto_20190303_1610.py b/bak/unused_apps/jrnl/migrations/0035_auto_20190303_1610.py new file mode 100644 index 0000000..7b51186 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0035_auto_20190303_1610.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-03-03 16:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0034_remove_entry_field_notes'), + ] + + operations = [ + migrations.RenameField( + model_name='entry', + old_name='field_notes_two', + new_name='field_notes', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0036_enjoyitem.py b/bak/unused_apps/jrnl/migrations/0036_enjoyitem.py new file mode 100644 index 0000000..aaa8596 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0036_enjoyitem.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.7 on 2019-03-14 23:42 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('jrnl', '0035_auto_20190303_1610'), + ] + + operations = [ + migrations.CreateModel( + name='EnjoyItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ], + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0037_enjoyitem_entry.py b/bak/unused_apps/jrnl/migrations/0037_enjoyitem_entry.py new file mode 100644 index 0000000..733f94c --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0037_enjoyitem_entry.py @@ -0,0 +1,20 @@ +# Generated by Django 2.1.7 on 2019-03-15 00:13 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0036_enjoyitem'), + ] + + operations = [ + migrations.AddField( + model_name='enjoyitem', + name='entry', + field=models.ForeignKey(default='', on_delete=django.db.models.deletion.CASCADE, to='jrnl.Entry'), + preserve_default=False, + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0038_remove_enjoyitem_entry.py b/bak/unused_apps/jrnl/migrations/0038_remove_enjoyitem_entry.py new file mode 100644 index 0000000..0eec153 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0038_remove_enjoyitem_entry.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.7 on 2019-03-15 00:20 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0037_enjoyitem_entry'), + ] + + operations = [ + migrations.RemoveField( + model_name='enjoyitem', + name='entry', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0039_enjoyitem_order.py b/bak/unused_apps/jrnl/migrations/0039_enjoyitem_order.py new file mode 100644 index 0000000..41c5324 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0039_enjoyitem_order.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-03-15 18:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0038_remove_enjoyitem_entry'), + ] + + operations = [ + migrations.AddField( + model_name='enjoyitem', + name='order', + field=models.IntegerField(default=1), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0040_auto_20190315_1906.py b/bak/unused_apps/jrnl/migrations/0040_auto_20190315_1906.py new file mode 100644 index 0000000..9ce813b --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0040_auto_20190315_1906.py @@ -0,0 +1,20 @@ +# Generated by Django 2.1.7 on 2019-03-15 19:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0039_enjoyitem_order'), + ] + + operations = [ + migrations.RemoveField( + model_name='enjoyitem', + name='content_type', + ), + migrations.DeleteModel( + name='EnjoyItem', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0041_auto_20190315_2240.py b/bak/unused_apps/jrnl/migrations/0041_auto_20190315_2240.py new file mode 100644 index 0000000..06b9697 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0041_auto_20190315_2240.py @@ -0,0 +1,28 @@ +# Generated by Django 2.1.7 on 2019-03-15 22:40 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('jrnl', '0040_auto_20190315_1906'), + ] + + operations = [ + migrations.CreateModel( + name='RelatedPost', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField()), + ('post_model', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='contenttypes.ContentType')), + ], + ), + migrations.AddField( + model_name='entry', + name='related', + field=models.ManyToManyField(blank=True, to='jrnl.RelatedPost'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0042_auto_20190412_1805.py b/bak/unused_apps/jrnl/migrations/0042_auto_20190412_1805.py new file mode 100644 index 0000000..71c0d51 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0042_auto_20190412_1805.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1.7 on 2019-04-12 18:05 + +import django.contrib.postgres.indexes +import django.contrib.postgres.search +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0041_auto_20190315_2240'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='search_document', + field=django.contrib.postgres.search.SearchVectorField(null=True), + ), + migrations.AddIndex( + model_name='entry', + index=django.contrib.postgres.indexes.GinIndex(fields=['search_document'], name='jrnl_entry_search__a7a10f_gin'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0043_auto_20190704_0903.py b/bak/unused_apps/jrnl/migrations/0043_auto_20190704_0903.py new file mode 100644 index 0000000..758499a --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0043_auto_20190704_0903.py @@ -0,0 +1,21 @@ +# Generated by Django 2.1.7 on 2019-07-04 09:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0042_auto_20190412_1805'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='entry', + name='jrnl_entry_search__a7a10f_gin', + ), + migrations.RemoveField( + model_name='entry', + name='search_document', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0044_auto_20190917_1703.py b/bak/unused_apps/jrnl/migrations/0044_auto_20190917_1703.py new file mode 100644 index 0000000..c6e5d6c --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0044_auto_20190917_1703.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-09-17 17:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('jrnl', '0043_auto_20190704_0903'), + ] + + operations = [ + migrations.RenameModel( + old_name='RelatedPost', + new_name='OldRelatedPost', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0045_entry_books_new.py b/bak/unused_apps/jrnl/migrations/0045_entry_books_new.py new file mode 100644 index 0000000..a941088 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0045_entry_books_new.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.6 on 2019-11-10 08:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('books', '0009_book_afflink'), + ('jrnl', '0044_auto_20190917_1703'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='books_new', + field=models.ManyToManyField(blank=True, related_name='luxbooks', to='books.Book'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0046_remove_entry_books.py b/bak/unused_apps/jrnl/migrations/0046_remove_entry_books.py new file mode 100644 index 0000000..bad9813 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0046_remove_entry_books.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.6 on 2019-11-10 08:57 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0045_entry_books_new'), + ] + + operations = [ + migrations.RemoveField( + model_name='entry', + name='books', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0047_auto_20191110_0859.py b/bak/unused_apps/jrnl/migrations/0047_auto_20191110_0859.py new file mode 100644 index 0000000..28d3866 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0047_auto_20191110_0859.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2019-11-10 08:59 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0046_remove_entry_books'), + ] + + operations = [ + migrations.RenameField( + model_name='entry', + old_name='books_new', + new_name='books', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0048_auto_20191205_1720.py b/bak/unused_apps/jrnl/migrations/0048_auto_20191205_1720.py new file mode 100644 index 0000000..51be2f1 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0048_auto_20191205_1720.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2019-12-05 17:20 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0047_auto_20191110_0859'), + ] + + operations = [ + migrations.RenameField( + model_name='entry', + old_name='related', + new_name='oldrelated', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0049_entry_newrelated.py b/bak/unused_apps/jrnl/migrations/0049_entry_newrelated.py new file mode 100644 index 0000000..3bd8dee --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0049_entry_newrelated.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.6 on 2019-12-05 17:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('normalize', '0001_initial'), + ('jrnl', '0048_auto_20191205_1720'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='newrelated', + field=models.ManyToManyField(blank=True, to='normalize.RelatedPost'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0050_auto_20191206_0741.py b/bak/unused_apps/jrnl/migrations/0050_auto_20191206_0741.py new file mode 100644 index 0000000..b732b68 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0050_auto_20191206_0741.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.6 on 2019-12-06 07:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0049_entry_newrelated'), + ] + + operations = [ + migrations.RenameField( + model_name='entry', + old_name='newrelated', + new_name='related', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0051_auto_20201027_2102.py b/bak/unused_apps/jrnl/migrations/0051_auto_20201027_2102.py new file mode 100644 index 0000000..09d0dea --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0051_auto_20201027_2102.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1 on 2020-10-27 21:02 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0050_auto_20191206_0741'), + ] + + operations = [ + migrations.RenameField( + model_name='entry', + old_name='field_notes', + new_name='old_field_notes', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0052_entry_new_field_notes.py b/bak/unused_apps/jrnl/migrations/0052_entry_new_field_notes.py new file mode 100644 index 0000000..3ec3167 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0052_entry_new_field_notes.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1 on 2020-10-27 21:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('posts', '0011_remove_post_jrnl'), + ('jrnl', '0051_auto_20201027_2102'), + ] + + operations = [ + migrations.AddField( + model_name='entry', + name='new_field_notes', + field=models.ManyToManyField(blank=True, to='posts.Post'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0053_auto_20201027_2105.py b/bak/unused_apps/jrnl/migrations/0053_auto_20201027_2105.py new file mode 100644 index 0000000..904b6c2 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0053_auto_20201027_2105.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1 on 2020-10-27 21:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('jrnl', '0052_entry_new_field_notes'), + ] + + operations = [ + migrations.RenameField( + model_name='entry', + old_name='new_field_notes', + new_name='field_notes', + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/0054_auto_20201111_2148.py b/bak/unused_apps/jrnl/migrations/0054_auto_20201111_2148.py new file mode 100644 index 0000000..bdeb63f --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/0054_auto_20201111_2148.py @@ -0,0 +1,23 @@ +# Generated by Django 3.1 on 2020-11-11 21:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('posts', '0012_remove_post_field_notes'), + ('jrnl', '0053_auto_20201027_2105'), + ] + + operations = [ + migrations.RemoveField( + model_name='entry', + name='old_field_notes', + ), + migrations.AlterField( + model_name='entry', + name='field_notes', + field=models.ManyToManyField(blank=True, limit_choices_to={'post_type': 5}, to='posts.Post'), + ), + ] diff --git a/bak/unused_apps/jrnl/migrations/__init__.py b/bak/unused_apps/jrnl/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/jrnl/migrations/__init__.py diff --git a/bak/unused_apps/jrnl/models.py b/bak/unused_apps/jrnl/models.py new file mode 100644 index 0000000..f3c0626 --- /dev/null +++ b/bak/unused_apps/jrnl/models.py @@ -0,0 +1,309 @@ +import datetime +import os + +from django.dispatch import receiver +from django.contrib.gis.db import models +from django.db.models.signals import post_save +from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey +from django.contrib.contenttypes.models import ContentType +from django.urls import reverse +from django.utils.functional import cached_property +from django.apps import apps +from django.conf import settings +from django.contrib.sitemaps import Sitemap +from django import forms + +import urllib.request +import urllib.parse +import urllib.error +from django_gravatar.helpers import get_gravatar_url, has_gravatar, calculate_gravatar_hash +from django_comments.signals import comment_was_posted +from django_comments.models import Comment +from django_comments.moderation import CommentModerator, moderator + +from photos.models import PhotoGallery, LuxImage, LuxImageSize +from locations.models import Location +from books.models import Book +from posts.models import Post +from normalize.models import RelatedPost + +from utils.util import render_images, parse_video, markdown_to_html + + +def get_upload_path(self, filename): + return "images/post-images/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +def image_url_replace(s): + s = s.replace('[[base_url]]', settings.IMAGES_URL) + return s + + +class OldRelatedPost(models.Model): + post_model = models.ForeignKey(ContentType, null=True, on_delete=models.SET_NULL) + slug = models.SlugField() + + def __str__(self): + return self.slug + + +class Entry(models.Model): + title = models.CharField(max_length=200) + subtitle = models.CharField(max_length=200, blank=True) + slug = models.SlugField(unique_for_date='pub_date') + body_html = models.TextField(blank=True) + body_markdown = models.TextField() + dek = models.TextField(null=True, blank=True) + pub_date = models.DateTimeField('Date published') + enable_comments = models.BooleanField(default=False) + point = models.PointField(null=True, blank=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, null=True, blank=True) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + photo_gallery = models.ForeignKey(PhotoGallery, on_delete=models.CASCADE, blank=True, null=True, verbose_name='photo set') + image = models.FileField(upload_to=get_upload_path, blank=True, help_text="should be 520 by 290") + meta_description = models.CharField(max_length=256, null=True, blank=True) + TEMPLATES = ( + (0, 'single'), + (1, 'double'), + (2, 'single-dark'), + (3, 'double-dark'), + (4, 'single-black'), + (5, 'double-black'), + ) + template_name = models.IntegerField(choices=TEMPLATES, default=0) + featured_image = models.ForeignKey(LuxImage, on_delete=models.CASCADE, null=True, blank=True) + has_video = models.BooleanField(blank=True, default=False) + #old_field_notes = models.ManyToManyField(FieldNote, blank=True) + field_notes = models.ManyToManyField(Post, blank=True, limit_choices_to={'post_type': 5}) + books = models.ManyToManyField(Book, related_name="luxbooks", blank=True) + oldrelated = models.ManyToManyField(OldRelatedPost, blank=True) + related = models.ManyToManyField(RelatedPost, blank=True) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + verbose_name_plural = 'entries' + + def __str__(self): + return self.title + + def get_absolute_url(self): + # return "/jrnl/%s/%s" % (self.pub_date.strftime("%Y/%m").lower(), self.slug) + return reverse("jrnl:detail", kwargs={"year": self.pub_date.year, "month": self.pub_date.strftime("%m"), "slug": self.slug}) + + def get_absolute_url_old(self): + return "/%s/%s/" % (self.pub_date.strftime("%Y/%b/%d").lower(), self.slug) + + def comment_period_open(self): + return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date + + def get_thumbnail_url(self): + image_dir, img = self.image.url.split('post-thumbnail/')[1].split('/') + return '%spost-thumbnail/%s/%s' % (settings.IMAGES_URL, image_dir, img) + + def get_image_url(self): + try: + image_dir, img = self.image.url.split('post-images/')[1].split('/') + return '%spost-images/%s/%s' % (settings.IMAGES_URL, image_dir, img) + except ValueError: + pass + + def get_image_wide_url(self): + img = self.image.url.split('post-images/')[1].split('/')[1] + # return '%shome-images/%s' % (settings.IMAGES_URL, img) + return '%shome-images/%s' % (settings.IMAGES_URL, img) + + def get_image_hero_url(self): + img = self.image.url.split('post-images/')[1].split('/')[1] + return '/media/images/home-images/hero%s' % (img) + + def get_image_hero_url_sm(self): + img = self.image.url.split('post-images/')[1].split('/')[1] + img = os.path.splitext(img)[0] + return '/media/images/home-images/hero%s_sm.jpg' % (img) + + def get_featured_image(self): + if self.featured_image: + return "%s%s" % (settings.IMAGES_URL, self.featured_image.image.url[14:]) + else: + return self.image.url + + def get_featured_image_thumb(self): + if self.featured_image: + return self.featured_image.get_image_by_size("tn") + else: + print(self.image.url) + image_dir, img = self.image.url.split('post-images/')[1].split('/') + print(image_dir, img) + return '%spost-images/%s/%s' % (settings.IMAGES_URL, image_dir, img) + + @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 + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + except model.DoesNotExist: + return '' + + def get_image_name(self): + return self.image.url.split("post-images/")[1][5:-4] + + def get_image_ext(self): + return self.image.url[-3:] + + def get_content_type(self): + return ContentType.objects.get(app_label="jrnl", model="entry") + + def save(self, *args, **kwargs): + created = self.pk is None + if not created: + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + self.has_video = parse_video(self.body_html) + 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)) + if created and not self.featured_image: + self.featured_image = LuxImage.objects.latest() + old = type(self).objects.get(pk=self.pk) if self.pk else None + if old and old.featured_image != self.featured_image: # Field has changed + s = LuxImageSize.objects.get(name="featured_jrnl") + ss = LuxImageSize.objects.get(name="picwide-med") + self.featured_image.sizes.add(s) + self.featured_image.sizes.add(ss) + self.featured_image.save() + if old and old.title != self.title or old and old.slug != self.slug: + related, c = RelatedPost.objects.get_or_create(model_name=self.get_content_type(), entry_id = self.id, pub_date=self.pub_date) + related.title = self.title + related.slug = self.slug + related.save() + + super(Entry, self).save(*args, **kwargs) + + +class HomepageCurrator(models.Model): + """ + simple model to control the featured article on the homepage + also allows me to fudge the "popular" section to be what I want + """ + image_offset_vertical = models.CharField(max_length=20, help_text="add negative top margin to shift image (include css unit)") + images = models.ManyToManyField(LuxImage) + #featured_image = models.ForeignKey(LuxImage, on_delete=models.CASCADE, null=True, blank=True) + tag_line = models.CharField(max_length=200) + featured = models.ForeignKey(Entry, on_delete=models.CASCADE, related_name="oldbanner") + popular = models.ManyToManyField(Entry) + template_name = models.CharField(max_length=200, help_text="full path") + + +class Home(models.Model): + """ + simple model to control the featured article on the homepage + also allows me to fudge the "popular" section to be what I want + """ + image_offset_vertical = models.CharField(max_length=20, help_text="add negative top margin to shift image (include css unit)") + featured_image = models.ForeignKey(LuxImage, on_delete=models.CASCADE, null=True, blank=True) + tag_line = models.CharField(max_length=200) + featured = models.ForeignKey(Entry, on_delete=models.CASCADE, related_name="banner") + popular = models.ManyToManyField(Entry, related_name="popular") + template_name = models.CharField(max_length=200, help_text="full path") + + +class BlogSitemap(Sitemap): + changefreq = "never" + priority = 1.0 + protocol = "https" + + def items(self): + return Entry.objects.filter(status=1) + + def lastmod(self, obj): + return obj.pub_date + + +class EntryModerator(CommentModerator): + ''' + Moderate everything except people with multiple approvals + ''' + email_notification = True + + def moderate(self, comment, content_object, request): + previous_approvals = Comment.objects.filter(user_email=comment.email, is_public=True) + for approval in previous_approvals: + if approval.submit_date <= datetime.datetime.today() - datetime.timedelta(21): + approve = True + if previous_approvals.count() > 2 and approve: + return False + # do entry build right here so it goes to live site + return True + + +moderator.register(Entry, EntryModerator) + + +@receiver(comment_was_posted, sender=Comment) +def cache_gravatar(sender, comment, **kwargs): + gravatar_exists = has_gravatar(comment.email) + grav_dir = settings.IMAGES_ROOT + '/gravcache/' + if gravatar_exists: + url = get_gravatar_url(comment.email, size=60) + if not os.path.isdir(grav_dir): + os.makedirs(grav_dir) + local_grav = '%s/%s.jpg' % (grav_dir, calculate_gravatar_hash(comment.email)) + urllib.request.urlretrieve(url, local_grav) + + +@receiver(post_save, sender=Entry) +def post_save_events(sender, update_fields, created, instance, **kwargs): + """ + Creates a generic related entry when a new post is added + """ + if created: + related, c = RelatedPost.objects.get_or_create(model_name=instance.get_content_type(), entry_id = instance.id, pub_date=instance.pub_date, title=instance.title, slug=instance.slug) + +# from django_comments.signals import comment_will_be_posted +# from django_comments import akismet + + +# @receiver(comment_will_be_posted, sender=Comment) +# def spam_check(sender, comment, request, **kwargs): +# akismet.USERAGENT = "David Lynch's Python library/1.0" +# try: +# real_key = akismet.verify_key(settings.AKISMET_KEY, "https://luxagraf.net/") +# if real_key: +# is_spam = akismet.comment_check(settings.AKISMET_KEY, "https://luxagraf.net/", request.META['REMOTE_ADDR'], request.META['HTTP_USER_AGENT'], comment_content=comment.comment) +# if is_spam: +# return False +# else: +# return True +# except akismet.AkismetError as e: +# print('Something went wrong, allowing comment') +# print(e.response, e.statuscode) +# return True diff --git a/bak/unused_apps/jrnl/templates/horizontal_select.html b/bak/unused_apps/jrnl/templates/horizontal_select.html new file mode 100644 index 0000000..61dcfd8 --- /dev/null +++ b/bak/unused_apps/jrnl/templates/horizontal_select.html @@ -0,0 +1,17 @@ +{% with id=widget.attrs.id %} + <ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}> + {% for group, options, index in widget.optgroups %} + {% if group %} + <li>{{ group }} + <ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}> + {% endif %} + {% for option in options %} + <li data-imageid="{{option.value}}" data-loopcounter="{{forloop.parentloop.counter}}">{% include option.template_name with widget=option %}</li> + {% endfor %} + {% if group %} + </ul> + </li> + {% endif %} + {% endfor %} + </ul> +{% endwith %} diff --git a/bak/unused_apps/jrnl/templates/jrnl/entry_detail.html b/bak/unused_apps/jrnl/templates/jrnl/entry_detail.html new file mode 100644 index 0000000..0904178 --- /dev/null +++ b/bak/unused_apps/jrnl/templates/jrnl/entry_detail.html @@ -0,0 +1,240 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load comments %} + +{% block pagetitle %}{{object.title|title|smartypants|safe}} - by Scott Gilbertson{% endblock %} + +{% block metadescription %}{% autoescape on %}{{object.meta_description|striptags|safe}}{% endautoescape %}{% endblock %} +{%block extrahead%} + <link rel="canonical" href="https://luxagraf.net{{object.get_absolute_url}}" /> + <meta name="ICBM" content="{{object.latitude}}, {{object.longitude}}" /> + <meta name="geo.position" content="{{object.latitude}}; {{object.longitude}}" /> + <meta name="geo.placename" content="{% if object.location.country_name == "United States" %}{{object.location.name|smartypants|safe}}, {{object.state.name}}{%else%}{{object.location.name|smartypants|safe}}, {{object.location.country_name}}{%endif%}"> + <meta name="geo.region" content="{{object.country.iso2}}{%if object.state.code != '' %}-{{object.state.code}}{%endif%}"> + <meta property="og:type" content="article" /> + <meta property="og:title" content="{{object.title|safe}}" /> + <meta property="og:url" content="https://luxagraf.net{{object.get_absolute_url}}" /> + <meta property="og:description" content="{{object.meta_description}}" /> + <meta property="article:published_time" content="{{object.pub_date|date:'c'}}" /> + <meta property="article:author" content="Scott Gilbertson" /> + <meta property="og:site_name" content="Luxagraf" /> + <meta property="og:image" content="{{object.get_featured_image}}" /> + <meta property="og:locale" content="en_US" /> + <meta name="twitter:card" content="summary_large_image"/> + <meta name="twitter:description" content="{{object.meta_description}}"/> + <meta name="twitter:title" content="{{object.title|safe}}"/> + <meta name="twitter:site" content="@luxagraf"/> + <meta name="twitter:domain" content="luxagraf"/> + <meta name="twitter:image:src" content="{{object.get_featured_image}}"/> + <meta name="twitter:creator" content="@luxagraf"/> +<script type="application/ld+json"> +{ + "@context": "https://schema.org", + "@type": "Article", + "mainEntityOfPage": { + "@type": "WebPage", + "@id": "https://luxagraf.net{{object.get_absolute_url}}" + }, + "headline": "{{object.title}}", + "datePublished": "{{object.pub_date|date:'c'}}+04:00", + "dateModified": "{{object.pub_date|date:'c'}}+04:00", + "author": { + "@type": "Person", + "name": "Scott Gilbertson" + }, + "publisher": { + "@type": "Organization", + "name": "Luxagraf", + "logo": { + "@type": "ImageObject", + "url": "https://luxagraf.net/media/img/logo-white.jpg" + } + }, + "description": "{{object.meta_description}}" +} +</script> +{%endblock%} +{%block htmlclass%}{% with object.template_name as t %} +class="detail {%if t == 1 or t == 3 or t == 5 %}double{%else%}single{%endif%}{%if t == 2 or t == 3 %} dark{%endif%}{%if t == 4 or t == 5 %} black{%endif%}"{%endwith%}{%endblock%} + + {% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +{% block primary %} + <main> + <article class="h-entry hentry entry-content content{% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %} post--article--double{%endif%}{%endwith%}" itemscope itemType="http://schema.org/BlogPosting"> + <header id="header" class="post-header {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %}post--header--double{%endif%}{%endwith%}"> + <h1 class="p-name entry-title post-title" itemprop="headline">{%if object.template_name == 1 or object.template_name == 3 %}{{object.title|smartypants|safe}}{%else%}{{object.title|smartypants|safe}}{%endif%}</h1> + {% if object.subtitle %}<h2 class="post-subtitle">{{object.subtitle|smartypants|safe}}</h2>{%endif%} + <div class="post-linewrapper"> + {% if object.location %}<div class="p-location h-adr adr post-location" itemprop="contentLocation" itemscope itemtype="http://schema.org/Place"> + <h3 class="h-adr" itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">{% if object.location.country_name == "United States" %}<span class="p-locality locality" itemprop="addressLocality">{{object.location.name|smartypants|safe}}</span>, <a class="p-region region" href="/jrnl/united-states/" title="travel writing from the United States">{{object.location.state_name|safe}}</a>, <span class="p-country-name" itemprop="addressCountry">U.S.</span>{%else%}<span class="p-region" itemprop="addressRegion">{{object.location.name|smartypants|safe}}</span>, <a class="p-country-name country-name" href="/jrnl/{{object.location.country_slug}}/" title="travel writing from {{object.location.country_name}}"><span itemprop="addressCountry">{{object.location.country_name|safe}}</span></a>{%endif%}</h3> + – <a href="" onclick="showMap({{object.latitude}}, {{object.longitude}}, { type:'point', lat:'{{object.latitude}}', lon:'{{object.longitude}}'}); return false;" title="see a map">Map</a> + </div>{%endif%} + <time class="dt-published published dt-updated post-date" datetime="{{object.pub_date|date:'c'}}" itemprop="datePublished">{{object.pub_date|date:"F"}} <span>{{object.pub_date|date:"j, Y"}}</span></time> + <span class="hide" itemprop="author" itemscope itemtype="http://schema.org/Person">by <a class="p-author h-card" href="/about"><span itemprop="name">Scott Gilbertson</span></a></span> + </div> + </header> + <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}} + </div> + {%if wildlife or object.field_notes.all or object.books.all %}<div class="entry-footer">{%if wildlife %} + <aside id="wildlife"> + <h3>Fauna and Flora</h3> + {% regroup wildlife by ap.apclass.get_kind_display as wildlife_list %} + <ul> + {% for object_list in wildlife_list %} + <li class="grouper">{{object_list.grouper}}<ul> + {% for object in object_list.list %} + <li>{%if object.ap.body_markdown%}<a href="{% url 'sightings:detail' object.ap.slug %}">{{object}}</a>{%else%}{{object}}{%endif%} </li> + {% endfor %}</ul> + {% endfor %}</ul> + </aside> + {% endif %}{%if object.field_notes.all %} + <aside {% if wildlife %}class="margin-left-none" {%endif%}id="field_notes"> + <h3>Field Notes</h3> + <ul>{% for obj in object.field_notes.all %} + <li><a href="{% url 'fieldnotes:detail' year=obj.pub_date.year month=obj.pub_date|date:"m" slug=obj.slug %}">{{obj}}</a></li> + {% endfor %}</ul> + </aside>{% endif %} + {%if object.books.all %} + <aside id="recommended-reading" {%if object.field_notes.all and wildlife %}class="rr-clear{%endif%}" > + <h3>Recommended Reading</h3> + <ul>{% for obj in object.books.all %} + <li><a href="{% url 'books:detail' slug=obj.slug %}"><img src="{{obj.get_small_image_url}}" /></a></li> + {% endfor %}</ul> + </aside>{% endif %} + </div>{%endif%} + </article> + {% with object.get_next_published as next %} + {% with object.get_previous_published as prev %} + <div class="nav-wrapper"> + <nav id="page-navigation" {%if wildlife or object.field_notes.all or object.books.all %}{%else%}class="page-border-top"{%endif%}> + <ul>{% if prev%} + <li id="prev"><span class="bl">Previous:</span> + <a href="{{ prev.get_absolute_url }}" rel="prev" title=" {{prev.title}}">{{prev.title|safe}}</a> + </li>{%endif%}{% if next%} + <li id="next"><span class="bl">Next:</span> + <a href="{{ next.get_absolute_url }}" rel="next" title=" {{next.title}}">{{next.title|safe}}</a> + </li>{%endif%} + </ul> + </nav>{%endwith%}{%endwith%} + </div> + {% if object.related.all %}<div class="article-afterward related"> + <div class="related-bottom"> + <h6 class="hedtinycaps">You might also enjoy</h6> + <ul class="article-card-list">{% for object in related %} + <li class="article-card-mini"><a href="{{object.get_absolute_url}}" title="{{object.title}}"> + <div class="post-image post-mini-image"> + {% if object.featured_image %} + {% include "lib/img_archive.html" with image=object.featured_image nolightbox=True %} + {% elif object.image %} + {% include "lib/img_archive.html" with image=object.image nolightbox=True %} + {% else %} + <img src="{{object.get_image_url}}" alt="{{ object.title }}" class="u-photo post-image" itemprop="image" />{%endif%} + </div> + <h4 class="p-name entry-title post-title" itemprop="headline">{% if object.title %}{{object.title|safe|smartypants|widont}}{% else %}{{object.common_name}}{%endif%}</h4> + <p class="p-author author hide" itemprop="author"><span class="byline-author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">Scott Gilbertson</span></span></p> + <p class="post-summary"> + <span class="p-location h-adr adr post-location" itemprop="contentLocation" itemscope itemtype="http://schema.org/Place"> + {% if object.location.country_name == "United States" %}{{object.location.state_name}}{%else%}{{object.location.country_name}}{%endif%} + </span> + – + <time class="dt-published published dt-updated post-date" datetime="{{object.pub_date|date:'c'}}"><span>{{object.pub_date|date:" Y"}}</span></time> + </p> + </a> + </li> + {% endfor %}</ul> + </div> + </div>{%endif%} + + {% comment %} <div class="mailing-list--wrapper"> + <h5>If you enjoyed this, you should join the mailing list…</h5> + {% include 'mailing_list.html' %} + </div> {% endcomment %} + {% if object.enable_comments %} +{% get_comment_count for object as comment_count %} +{%if comment_count > 0 %} +<p class="comments--header">{{comment_count}} Comment{{ comment_count|pluralize }}</p> +{% render_comment_list for object %} +{%endif%} +<div class="comment--form--wrapper {%if comment_count > 0%}comment-form-border{%endif%}"> +{% render_comment_form for object %} +</div> +{% else %} +<p class="comments--header" style="text-align: center">Sorry, comments have been disabled for this post.</p> +{%endif%} +</main> +{% endblock %} +{% block js %} +<script> +document.addEventListener("DOMContentLoaded", function(event) { + var leaflet = document.createElement('script'); + leaflet.src = "/media/js/leaflet-master/leaflet-mod.js"; + document.body.appendChild(leaflet); + var lightbox = document.createElement('script'); + lightbox.src = "/media/js/lightbox.js"; + document.body.appendChild(lightbox); + leaflet.onload = function(){ + var detail = document.createElement('script'); + detail.src = "/media/js/detail.min.js"; + document.body.appendChild(detail); + {% with object.get_template_name_display as t %}{%if t == "single" or t == "single-dark" %} + detail.onload = function(){ + createMap(); + var open = false; + } + {%endif%}{%endwith%} + } + + lightbox.onload = function() { + var opts= { + //nextOnClick: false, + captions: true, + onload: function(){ + var im = document.getElementById("jslghtbx-contentwrapper"); + var link = im.appendChild(document.createElement('a')) + link.href = im.firstChild.src; + link.innerHTML= "open "; + link.target = "_blank"; + link.setAttribute('class', 'p-link'); + im.appendChild(link); + } + }; + var lightbox = new Lightbox(); + lightbox.load(opts); + } + {% if object.enable_comments %} +{% get_comment_count for object as comment_count %} +{%if comment_count > 0 %} + //delay loading of gravatar images using noscript data-hash attribute + dataattr = document.getElementsByClassName("datahashloader"); + for(var i=0; i<dataattr.length; i++) { + var c = dataattr[i].parentNode; + var img = document.createElement("img"); + img.src = 'https://images.luxagraf.net/gravcache/' + dataattr[i].getAttribute('data-hash') + '.jpg'; + img.className += "gravatar"; + img.alt = "gravatar icon"; + c.insertBefore(img, c.childNodes[3]); + } +{%endif%} +{%endif%} +{% if object.has_video %} +var tester = document.getElementsByClassName("vidauto"); +var wrapper = document.getElementById('wrapper'); +var dist = 100; + +window.onscroll = function() { + for (var i=0; i<tester.length; i++) { + checkVisible(tester[i]) ? tester[i].play() : tester[i].pause(); + } +}; + +function checkVisible(elm) { + var rect = elm.getBoundingClientRect(); + var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight); + return !(rect.bottom < 0 || rect.top - viewHeight >= 0); +} +{%endif%} + +}); +</script> +{%endblock%} diff --git a/bak/unused_apps/jrnl/templates/jrnl/entry_detail.txt b/bak/unused_apps/jrnl/templates/jrnl/entry_detail.txt new file mode 100644 index 0000000..547ce79 --- /dev/null +++ b/bak/unused_apps/jrnl/templates/jrnl/entry_detail.txt @@ -0,0 +1,8 @@ +{{object.title|safe}} +{% for letter in object.title %}={%endfor%} + + by Scott Gilbertson + <{{SITE_URL}}{{object.get_absolute_url}}> + {{object.pub_date|date:"l, d F Y"}} + +{{object.body_markdown|safe}} diff --git a/bak/unused_apps/jrnl/templates/jrnl/entry_list.html b/bak/unused_apps/jrnl/templates/jrnl/entry_list.html new file mode 100644 index 0000000..6eefe10 --- /dev/null +++ b/bak/unused_apps/jrnl/templates/jrnl/entry_list.html @@ -0,0 +1,33 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load pagination_tags %} +{% block pagetitle %}Luxagraf | {% if region %}Travel Writing from {{region.name|title|smartypants|safe}}{%else%}Travel Writing from Around the World {%endif%}{% if page != "1" %} -- Page {{page}}{%endif%}{% endblock %} +{% block metadescription %}{% if region %}Travel writing, essays and dispatches from {{region.name|title|smartypants|safe}}{%else%}Travel writing, essays and dispatches from around the world{%endif%} Page {{page}}{% endblock %} +{%block bodyid%}id="writing" class="archive"{%endblock%} +{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +{% block primary %}<main class="archive-grid"> + <h1 class="hide">{% if region %}Journal entries from {%if region.name == 'United States'%}the United States{%else%}{{region.name|title|smartypants|safe}}{%endif%}{%else%}Journal {%endif%}</h1>{% autopaginate object_list 24 %} {% for object in object_list %} + <article class="h-entry hentry archive-card {% cycle 'odd' 'even' %} {% cycle 'first' 'second' 'third' %}" itemscope itemType="http://schema.org/Article"> + <div class="post-image"> + <a href="{{object.get_absolute_url}}" title="{{object.title}}">{% if object.featured_image %} + {% include "lib/img_archive.html" with image=object.featured_image %} + {%else%} + <img src="{{object.get_image_url}}" alt="{{ object.title }}" class="u-photo post-image" itemprop="image" />{%endif%}</a> + </div> + <h2 class="p-name entry-title post-title" itemprop="headline"><a href="{{object.get_absolute_url}}" class="u-url" title="{%if object.title_keywords%}{{object.title_keywords}}{%else%}{{object.title}}{%endif%}">{{object.title|safe|smartypants|widont}}</a></h2> + <p class="p-author author hide" itemprop="author"><span class="byline-author" itemscope itemtype="http://schema.org/Person"><span itemprop="name">Scott Gilbertson</span></span></p> + <time class="dt-published published dt-updated post-date" datetime="{{object.pub_date|date:'c'}}">{{object.pub_date|date:"F"}} <span>{{object.pub_date|date:"j, Y"}}</span></time> + <p class="post-summary"> + <span class="p-location h-adr adr post-location" itemprop="contentLocation" itemscope itemtype="http://schema.org/Place"> + {% if object.location.country_name == "United States" %}<span class="p-locality locality">{{object.location.name|smartypants|safe}}</span>, <a class="p-region region" href="/jrnl/united-states/" title="travel writing from the United States">{{object.location.state_name}}</a>, <span class="p-country-name">U.S.</span>{%else%}<span class="p-region">{{object.location.name|smartypants|safe}}</span>, <a class="p-country-name country-name" href="/jrnl/{{object.location.country_slug}}/" title="travel writing from {{object.location.country_name}}">{{object.location.country_name}}</a>{%endif%} + </span> – + <span class="p-summary" itemprop="description"> + {{object.dek|safe}} + </span> + </p> + </article> {% endfor %} + </main> + <nav aria-label="page navigation" class="pagination"> + {% paginate %} + </nav> +{% endblock %} diff --git a/bak/unused_apps/jrnl/urls.py b/bak/unused_apps/jrnl/urls.py new file mode 100644 index 0000000..caf1935 --- /dev/null +++ b/bak/unused_apps/jrnl/urls.py @@ -0,0 +1,61 @@ +from django.urls import path, re_path +from django.views.generic.base import RedirectView + +from . import views + +app_name = "jrnl" + +urlpatterns = [ + path( + r'feed.xml', + views.JrnlRSSFeedView(), + name="feed" + ), + re_path( + r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+).txt$', + views.EntryDetailViewTXT.as_view(), + name="detail-txt" + ), + re_path( + r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+)$', + views.EntryDetailView.as_view(), + name="detail" + ), + re_path( + r'^(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', + views.EntryMonthArchiveView.as_view(month_format='%m'), + name="list_month" + ), + re_path( + r'(?P<year>\d{4})/$', + views.EntryYearArchiveView.as_view(), + name="list_year" + ), + re_path( + r'^(?P<page>\d+)/$', + views.EntryList.as_view(), + name="list" + ), + path( + r'latest/', + views.JrnlLatestView.as_view(), + name="latest" + ), + re_path( + r'(?P<slug>[-\w]+)/(?P<page>\d+)/$', + views.EntryCountryList.as_view(), + name="list_country" + ), + re_path( + r'^(?P<slug>[-\w]+)/$', + views.EntryCountryList.as_view(), + {'page':1}, + name="list_country" + ), + re_path( + r'', + views.EntryList.as_view(), + {'page':1}, + name="list" + ), +] diff --git a/bak/unused_apps/jrnl/views.py b/bak/unused_apps/jrnl/views.py new file mode 100644 index 0000000..6b7cc8d --- /dev/null +++ b/bak/unused_apps/jrnl/views.py @@ -0,0 +1,165 @@ +from django.views.generic import ListView +from django.views.generic.detail import DetailView +from django.views.generic.dates import DateDetailView +from django.urls import reverse +from django.views.generic.dates import YearArchiveView, MonthArchiveView +from django.contrib.syndication.views import Feed +from django.apps import apps +from django.shortcuts import get_object_or_404 +from django.conf import settings +from django.db.models import Q + +from utils.views import PaginatedListView + +from .models import Entry, HomepageCurrator, Home +from locations.models import LuxCheckIn, Country, Region, Location +from sightings.models import Sighting + + +class EntryList(PaginatedListView): + """ + Return a list of Entries in reverse chronological order + """ + model = Entry + + def get_queryset(self): + queryset = super(EntryList, self).get_queryset() + print(queryset) + return queryset.filter(status__exact=1).order_by('-pub_date').prefetch_related('location').prefetch_related('featured_image') + + +class EntryCountryList(PaginatedListView): + """ + Return a list of Entries by Country in reverse chronological order + """ + model = Entry + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(EntryCountryList, self).get_context_data(**kwargs) + try: + context['region'] = Country.objects.get(slug__exact=self.kwargs['slug']) + except: + context['region'] = Region.objects.get(slug__exact=self.kwargs['slug']) + return context + + def get_queryset(self): + try: + region = Country.objects.get(slug__exact=self.kwargs['slug']) + qs = Entry.objects.filter( + status__exact=1, + location__state__country=region + ).order_by('-pub_date') + except: + region = Region.objects.get(slug__exact=self.kwargs['slug']) + qs = Entry.objects.filter( + status__exact=1, + location__state__country__lux_region=region.id + ).order_by('-pub_date') + return qs + + +class EntryYearArchiveView(YearArchiveView): + queryset = Entry.objects.filter(status__exact=1).select_related() + date_field = "pub_date" + make_object_list = True + allow_future = True + template_name = "archives/writing_date.html" + + +class EntryMonthArchiveView(MonthArchiveView): + queryset = Entry.objects.filter(status__exact=1).select_related() + date_field = "pub_date" + allow_future = True + template_name = "archives/writing_date.html" + + +class EntryDetailView(DateDetailView): + model = Entry + date_field = 'pub_date' + slug_field = "slug" + + def get_queryset(self): + queryset = super(EntryDetailView, self).get_queryset() + return queryset.select_related('location').prefetch_related('field_notes').prefetch_related('books') + + def get_object(self, queryset=None): + obj = get_object_or_404( + self.model, + slug=self.kwargs['slug'], + pub_date__month=self.kwargs['month'], + pub_date__year=self.kwargs['year'] + ) + self.location = obj.location + return obj + + def get_context_data(self, **kwargs): + context = super(EntryDetailView, self).get_context_data(**kwargs) + context['wildlife'] = Sighting.objects.filter( + Q(location=self.location) | + Q(location__in=Location.objects.filter(parent=self.location)) + ).select_related().order_by('ap_id', 'ap__apclass__kind').distinct("ap") + related = [] + for obj in self.object.related.all(): + model = apps.get_model(obj.model_name.app_label, obj.model_name.model) + related.append(model.objects.get(slug=obj.slug, pub_date=obj.pub_date)) + context['related'] = related + context['breadcrumbs'] = ("jrnl",) + context['crumb_url'] = reverse('jrnl:list') + return context + + +class EntryDetailViewTXT(EntryDetailView): + template_name = "jrnl/entry_detail.txt" + + +class HomepageList(ListView): + """ + Return a main entry and list of Entries in reverse chronological order + """ + model = Entry + + def get_home(self): + return Home.objects.filter(pk=1).prefetch_related('featured_image').select_related('featured').select_related('featured__location').get() + + def get_queryset(self): + queryset = super(HomepageList, self).get_queryset() + self.home = self.get_home() + return queryset.filter(status__exact=1).order_by('-pub_date').exclude().select_related('location').select_related('featured_image')[1:9] + + def get_template_names(self): + return ['%s' % self.home.template_name] + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(HomepageList, self).get_context_data(**kwargs) + context['homepage'] = self.home + context['location'] = LuxCheckIn.objects.latest() + context['IMAGES_URL'] = settings.IMAGES_URL + return context + + +class JrnlLatestView(EntryDetailView): + template_name = "details/entry_latest.html" + + def get_object(self, queryset=None): + obj = self.model.objects.filter(status=1).latest() + self.location = obj.location + return obj + + +class JrnlRSSFeedView(Feed): + title = "Luxagraf: Topographical Writings" + link = "/jrnl/" + description = "Latest postings to luxagraf.net" + description_template = 'feeds/blog_description.html' + + def items(self): + return Entry.objects.filter(status__exact=1).order_by('-pub_date')[:10] + + def item_pubdate(self, item): + """ + Takes an item, as returned by items(), and returns the item's + pubdate. + """ + return item.pub_date diff --git a/bak/unused_apps/notes/__init__.py b/bak/unused_apps/notes/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/notes/__init__.py diff --git a/bak/unused_apps/notes/admin.py b/bak/unused_apps/notes/admin.py new file mode 100644 index 0000000..eafd0dc --- /dev/null +++ b/bak/unused_apps/notes/admin.py @@ -0,0 +1,28 @@ +from django.contrib import admin +from notes.models import Note +from utils.widgets import LGEntryForm, OLAdminBase + + +@admin.register(Note) +class NoteAdmin(OLAdminBase): + form = LGEntryForm + prepopulated_fields = {"slug": ('title',)} + list_display = ('slug', 'pub_date') + fieldsets = ( + ('Note', { + 'fields': ( + ('title', 'slug'), + 'body_markdown', + 'pub_date', + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + + class Media: + js = ('image-loader.js', 'next-prev-links.js') diff --git a/bak/unused_apps/notes/build.py b/bak/unused_apps/notes/build.py new file mode 100644 index 0000000..dc0654e --- /dev/null +++ b/bak/unused_apps/notes/build.py @@ -0,0 +1,36 @@ +import os +from django.urls import reverse +from builder.base import BuildNew + + +class BuildNotes(BuildNew): + + def build(self): + self.build_detail_view() + self.build_list_view( + base_path=reverse("notes:live_redirect"), + paginate_by=24 + ) + self.build_year_view("notes:list_year") + self.build_month_view("notes:list_month") + + def get_model_queryset(self): + return self.model.objects.all() + + def build_detail_view(self): + ''' + write out all the expenses for each trip + ''' + for obj in self.get_model_queryset(): + url = obj.get_absolute_url() + path, slug = os.path.split(url) + path = '%s/' % path + # write html + response = self.client.get(url) + print(path, slug) + self.write_file(path, response.content, filename=slug) + + +def builder(): + j = BuildNotes("notes", "luxnote") + j.build() diff --git a/bak/unused_apps/notes/migrations/0001_initial.py b/bak/unused_apps/notes/migrations/0001_initial.py new file mode 100644 index 0000000..ea67ad1 --- /dev/null +++ b/bak/unused_apps/notes/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 2.0.1 on 2018-05-09 09:35 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Note', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=250, null=True)), + ('slug', models.SlugField(blank=True, unique_for_date='pub_date')), + ('pub_date', models.DateTimeField(default=django.utils.timezone.now)), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField(verbose_name='Note')), + ], + ), + ] diff --git a/bak/unused_apps/notes/migrations/__init__.py b/bak/unused_apps/notes/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/notes/migrations/__init__.py diff --git a/bak/unused_apps/notes/models.py b/bak/unused_apps/notes/models.py new file mode 100644 index 0000000..8735056 --- /dev/null +++ b/bak/unused_apps/notes/models.py @@ -0,0 +1,50 @@ +from django import forms +from django.contrib.gis.db import models +from django.utils import timezone +from django.conf import settings +from django.urls import reverse +from locations.models import Location + +from locations.models import CheckIn +from utils.util import markdown_to_html, render_images + + +class Note(models.Model): + title = models.CharField(max_length=250, null=True, blank=True) + slug = models.SlugField(unique_for_date='pub_date', blank=True) + pub_date = models.DateTimeField(default=timezone.now) + body_html = models.TextField(blank=True) + body_markdown = models.TextField('Note') + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("notes:detail", kwargs={"year": self.pub_date.year, "month": self.pub_date.strftime("%m"), "slug": self.slug}) + + @property + def region(self): + return self.location.state.country.lux_region + + @property + def longitude(self): + '''Get the site's longitude.''' + return round(self.point.x, 2) + + @property + def latitude(self): + '''Get the site's latitude.''' + return round(self.point.y, 2) + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date() + + @property + def get_next_published(self): + return self.get_next_by_pub_date() + + def save(self, *args, **kwargs): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + super(Note, self).save() diff --git a/bak/unused_apps/notes/urls.py b/bak/unused_apps/notes/urls.py new file mode 100644 index 0000000..3c47c3d --- /dev/null +++ b/bak/unused_apps/notes/urls.py @@ -0,0 +1,55 @@ +from django.conf.urls import url +from django.views.generic.base import RedirectView + +from . import views + +app_name = "notes" + +urlpatterns = [ + url( + r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+).txt$', + views.NoteDetailViewTXT.as_view(), + name="detail-txt" + ), + url( + r'(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+)$', + views.NoteDetailView.as_view(), + name="detail" + ), + url( + r'^(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', + views.NoteMonthArchiveView.as_view(month_format='%m'), + name="list_month" + ), + url( + r'(?P<year>\d{4})/$', + views.NoteYearArchiveView.as_view(), + name="list_year" + ), + url( + r'(?P<year>\d{4})/(?P<month>\d{2})/$', + views.date_list, + name="notes_by_month" + ), + url( + r'(?P<year>\d{4})/$', + views.date_list, + name="notes_by_year" + ), + url( + r'(?P<page>\d+)/$', + views.NoteList.as_view(), + name="list" + ), + # redirect / to /1/ for live server + url( + r'', + RedirectView.as_view(url="/field-notes/1/", permanent=False), + name="live_redirect" + ), + url( + r'^$', + views.entry_list, + name="notes_archive" + ), +] diff --git a/bak/unused_apps/notes/views.py b/bak/unused_apps/notes/views.py new file mode 100644 index 0000000..05fe18e --- /dev/null +++ b/bak/unused_apps/notes/views.py @@ -0,0 +1,39 @@ +from django.views.generic.dates import YearArchiveView, MonthArchiveView +from django.views.generic.detail import DetailView + +from utils.views import PaginatedListView + +from notes.models import Note + + +class NoteList(PaginatedListView): + """ + Return a list of Notes in reverse chronological order + """ + queryset = Note.objects.all().order_by('-pub_date') + template_name = "archives/notes.html" + + +class NoteDetailView(DetailView): + model = Note + template_name = "details/note.html" + slug_field = "slug" + + +class NoteDetailViewTXT(NoteDetailView): + template_name = "details/entry.txt" + + +class NoteYearArchiveView(YearArchiveView): + queryset = Note.objects.all() + date_field = "pub_date" + make_object_list = True + allow_future = True + template_name = "archives/notes_date.html" + + +class NoteMonthArchiveView(MonthArchiveView): + queryset = Note.objects.all() + date_field = "pub_date" + allow_future = True + template_name = "archives/notes_date.html" diff --git a/bak/unused_apps/people/__init__.py b/bak/unused_apps/people/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/people/__init__.py diff --git a/bak/unused_apps/people/admin.py b/bak/unused_apps/people/admin.py new file mode 100644 index 0000000..ea0b463 --- /dev/null +++ b/bak/unused_apps/people/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin +from .models import Person + + +@admin.register(Person) +class PersonAdmin(admin.ModelAdmin): + list_display = ('last_name', 'first_name', 'location_met', ) + diff --git a/bak/unused_apps/people/build.py b/bak/unused_apps/people/build.py new file mode 100644 index 0000000..35fdb19 --- /dev/null +++ b/bak/unused_apps/people/build.py @@ -0,0 +1,35 @@ +import os +from django.urls import reverse +from builder.base import BuildNew + + +class BuildBooks(BuildNew): + + def build(self): + self.build_detail_view() + self.build_list_view( + base_path=reverse("books:live_redirect"), + paginate_by=24 + ) + print("building books") + + def get_model_queryset(self): + return self.model.objects.all() + + def build_detail_view(self): + ''' + write out all the expenses for each trip + ''' + for obj in self.get_model_queryset(): + url = obj.get_absolute_url() + path, slug = os.path.split(url) + path = '%s/' % path + # write html + response = self.client.get(url) + print(path, slug) + self.write_file(path, response.content, filename=slug) + + +def builder(): + j = BuildBooks("books", "book") + j.build() diff --git a/bak/unused_apps/people/migrations/0001_initial.py b/bak/unused_apps/people/migrations/0001_initial.py new file mode 100644 index 0000000..6f21bea --- /dev/null +++ b/bak/unused_apps/people/migrations/0001_initial.py @@ -0,0 +1,42 @@ +# Generated by Django 2.0.1 on 2018-04-05 09:06 + +from django.db import migrations, models +import django.db.models.deletion +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('locations', '0003_auto_20180307_1027'), + ('taggit', '0002_auto_20150616_2121'), + ] + + operations = [ + migrations.CreateModel( + name='Person', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('first_name', models.CharField(max_length=200)), + ('last_name', models.CharField(max_length=200)), + ('email', models.EmailField(blank=True, max_length=120, null=True)), + ('street', models.CharField(blank=True, max_length=355, null=True)), + ('city', models.CharField(blank=True, max_length=200, null=True)), + ('state', models.CharField(blank=True, max_length=200, null=True)), + ('postal_code', models.CharField(blank=True, max_length=20, null=True)), + ('country', models.CharField(max_length=200, null=True)), + ('phone', models.CharField(blank=True, max_length=22, null=True)), + ('slug', models.CharField(blank=True, max_length=50)), + ('body_markdown', models.TextField(blank=True, null=True)), + ('body_html', models.TextField(blank=True, null=True)), + ('next_contact_date', models.DateField(blank=True, null=True)), + ('location_met', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')), + ], + options={ + 'ordering': ('-last_name',), + }, + ), + ] diff --git a/bak/unused_apps/people/migrations/__init__.py b/bak/unused_apps/people/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/people/migrations/__init__.py diff --git a/bak/unused_apps/people/models.py b/bak/unused_apps/people/models.py new file mode 100644 index 0000000..1a07b16 --- /dev/null +++ b/bak/unused_apps/people/models.py @@ -0,0 +1,54 @@ +from django.db import models +from django.template.defaultfilters import slugify + +from taggit.managers import TaggableManager +from locations.models import Location +from utils.util import markdown_to_html + + +class Person(models.Model): + first_name = models.CharField(max_length=200) + last_name = models.CharField(max_length=200) + email = models.EmailField(max_length=120, null=True, blank=True) + street = models.CharField(max_length=355, null=True, blank=True) + city = models.CharField(max_length=200, null=True, blank=True) + state = models.CharField(max_length=200, null=True, blank=True) + postal_code = models.CharField(max_length=20, null=True, blank=True) + country = models.CharField(max_length=200, null=True) + phone = models.CharField(max_length=22, blank=True, null=True) + slug = models.CharField(max_length=50, blank=True) + body_markdown = models.TextField(null=True, blank=True) + body_html = models.TextField(null=True, blank=True) + tags = TaggableManager(blank=True) + location_met = models.ForeignKey(Location, on_delete=models.CASCADE, null=True, blank=True) + next_contact_date = models.DateField(null=True, blank=True) + + class Meta: + ordering = ('-last_name',) + + def __str__(self): + return "%s %s" %(self.first_name, self.last_name) + + def get_absolute_url(self): + return reverse("people:detail", kwargs={"slug": self.slug}) + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + except model.DoesNotExist: + return '' + + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(("%s-%s")[:50] %(self.last_name, self.first_name)) + if self.body_markdown: + self.body_html = markdown_to_html(self.body_markdown) + super(Person, self).save() + diff --git a/bak/unused_apps/people/urls.py b/bak/unused_apps/people/urls.py new file mode 100644 index 0000000..0ae38eb --- /dev/null +++ b/bak/unused_apps/people/urls.py @@ -0,0 +1,36 @@ +from django.urls import path, re_path +from django.views.generic.base import RedirectView + +from . import views + +app_name = "people" + +urlpatterns = [ + re_path( + r'^tag/(?P<slug>[-\w]+)/$', + views.PersonTagListView.as_view(), + {'page':1}, + name='list-tag' + ), + re_path( + r'tag/(?P<slug>[-\w]+)/(?P<page>\d+)/$', + views.PersonTagListView.as_view(), + name='list-tag' + ), + re_path( + r'^(?P<page>\d+)/$', + views.PersonListView.as_view(), + name="list" + ), + path( + r'', + views.PersonListView.as_view(), + {'page':1}, + name="list" + ), + path( + r'<str:slug>/', + views.PersonDetailView.as_view(), + name='detail', + ), +] diff --git a/bak/unused_apps/people/views.py b/bak/unused_apps/people/views.py new file mode 100644 index 0000000..36c0657 --- /dev/null +++ b/bak/unused_apps/people/views.py @@ -0,0 +1,38 @@ +from django.views.generic.detail import DetailView +from utils.views import PaginatedListView + +from taggit.models import Tag +from .models import Person + + +class PersonListView(PaginatedListView): + model = Person + template_name = 'archives/people.html' + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(PersonListView, self).get_context_data(**kwargs) + context['tags'] = Person.tags.all() + return context + + +class PersonDetailView(DetailView): + model = Person + template_name = "details/person.html" + slug_field = "slug" + + +class PersonTagListView(PaginatedListView): + model = Person + template_name = 'archives/people.html' + + def get_queryset(self): + print(self.kwargs['slug']) + return Person.objects.filter(tags__slug=self.kwargs['slug']) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(PersonTagListView, self).get_context_data(**kwargs) + context['tag'] = Tag.objects.get(slug__exact=self.kwargs['slug']) + context['tags'] = Person.tags.all() + return context diff --git a/bak/unused_apps/photos/__init__.py b/bak/unused_apps/photos/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/photos/__init__.py diff --git a/bak/unused_apps/photos/admin.py b/bak/unused_apps/photos/admin.py new file mode 100644 index 0000000..e77e50b --- /dev/null +++ b/bak/unused_apps/photos/admin.py @@ -0,0 +1,184 @@ +from django.contrib import admin +from django import forms +from django.contrib.gis.admin import OSMGeoAdmin +from django.conf.urls import url +from django.utils.translation import ungettext, ugettext_lazy as _ +from photos.models import Photo, PhotoGallery, LuxImage, LuxGallery, LuxImageSize, LuxVideo +from django.shortcuts import render +from django.contrib.admin import helpers +from django.http import HttpResponseRedirect + + +from .forms import UploadZipForm, GalleryForm + + +class LuxImageSizeAdmin(OSMGeoAdmin): + list_display = ('name', 'width', 'height', 'quality') + pass + + +admin.site.register(LuxImageSize, LuxImageSizeAdmin) + + +@admin.register(LuxVideo) +class LuxVideoAdmin(OSMGeoAdmin): + pass + + +class LuxImageAdmin(OSMGeoAdmin): + list_display = ('pk', 'admin_thumbnail', 'pub_date', 'caption', 'location') + list_filter = ('pub_date', 'location') + search_fields = ['title', 'caption', 'alt'] + list_editable = ('location',) + # Options for OSM map Using custom ESRI topo map + default_lon = -9285175 + default_lat = 4025046 + default_zoom = 6 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + + fieldsets = ( + (None, { + 'fields': ('title', ('image'), 'pub_date', 'sizes', 'alt', 'caption', 'point', ('is_public'), ('photo_credit_source', 'photo_credit_url')) + }), + ('Exif Data', { + 'classes': ('collapse',), + 'fields': ('exif_raw', 'exif_aperture', 'exif_make', 'exif_model', 'exif_exposure', 'exif_iso', 'exif_focal_length', 'exif_lens', 'exif_date', 'height', 'width'), + }), + ) + + class Media: + js = ('image-preview.js', 'next-prev-links.js') + + +admin.site.register(LuxImage, LuxImageAdmin) + + +class LuxGalleryAdmin(OSMGeoAdmin): + form = GalleryForm + list_display = ('title', 'location', 'pub_date') + list_filter = ('location',) + + # Options for OSM map Using custom ESRI topo map + default_lon = -9285175 + default_lat = 4025046 + default_zoom = 6 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + + def get_urls(self): + urls = super(LuxGalleryAdmin, self).get_urls() + custom_urls = [ + url( + r'upload_zip/$', + self.admin_site.admin_view(self.upload_zip), + name='upload_zip' + ) + ] + return custom_urls + urls + + def upload_zip(self, request): + context = { + 'title': _('Upload a zip archive of photos'), + 'app_label': self.model._meta.app_label, + 'opts': self.model._meta, + 'has_change_permission': self.has_change_permission(request) + } + + # Handle form request + if request.method == 'POST': + form = UploadZipForm(request.POST, request.FILES) + if form.is_valid(): + form.save(request=request) + return HttpResponseRedirect('..') + else: + form = UploadZipForm() + context['form'] = form + context['adminform'] = helpers.AdminForm(form, + list([(None, {'fields': form.base_fields})]), + {}) + return render(request, 'admin/upload_zip.html', context) + +admin.site.register(LuxGallery, LuxGalleryAdmin) + + +class PhotoAdmin(OSMGeoAdmin): + list_display = ('title', 'admin_thumbnail', 'flickr_id', 'pub_date',) + list_filter = ('pub_date',) + search_fields = ['title', 'description'] + fieldsets = ( + (None, { + 'fields': ( + ('title', 'description'), + 'pub_date', + ('lat', 'lon') + ) + }), + ('Exif Data', { + 'fields': ( + 'exif_aperture', + 'exif_exposure', + 'exif_iso', + 'exif_focal_length', + 'exif_lens', + 'exif_date', + 'exif_make', + 'exif_model' + ), + 'classes': ('collapse') + }), + ('Flickr Data', { + 'fields': ( + 'flickr_id', + 'flickr_owner', + 'flickr_farm', + 'flickr_server', + 'flickr_secret', + 'flickr_originalsecret' + ), + 'classes': ('collapse') + }), + ) + + # Options for OSM map Using custom ESRI topo map + default_lon = -9285175 + default_lat = 4025046 + default_zoom = 6 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + + +admin.site.register(Photo, PhotoAdmin) + + +class PhotoGalleryAdmin(OSMGeoAdmin): + list_display = ('set_title', 'region', 'location', 'pub_date') + list_filter = ('region', 'location') + fieldsets = ( + (None, { + 'fields': ( + ('set_id', 'set_title', 'set_desc'), + 'set_slug', + 'primary', + 'location', + 'region', + 'photos', + 'pub_date' + ) + }), + ) + + +admin.site.register(PhotoGallery, PhotoGalleryAdmin) diff --git a/bak/unused_apps/photos/build.py b/bak/unused_apps/photos/build.py new file mode 100644 index 0000000..e95cbfc --- /dev/null +++ b/bak/unused_apps/photos/build.py @@ -0,0 +1,48 @@ +import os +from django.urls import reverse +from builder.base import BuildNew + +from .models import LuxImage + + +class BuildLuxPhotos(BuildNew): + + def build(self): + self.build_detail_view() + self.build_daily_photo() + + def get_model_queryset(self): + return self.model.objects.all() + + def build_daily_photo(self): + ''' + build out images that I post daily, found by title prefix daily_ + ''' + self.build_list_view( + base_path=reverse("photos:daily_photo_list"), + qs=LuxImage.objects.filter(is_public=True, title__startswith="daily_"), + paginate_by=10 + ) + + def build_detail_view(self): + ''' + write out all the expenses for each trip + ''' + for obj in self.get_model_queryset(): + url = obj.get_absolute_url() + path, slug = os.path.split(url) + path = '%s/' % path + # write html + response = self.client.get(url) + print(path, slug) + self.write_file(path, response.content, filename=slug) + + +def dailybuilder(): + j = BuildLuxPhotos("photos", "LuxImage") + j.build_daily_photo() + + +def builder(): + j = BuildLuxPhotos("photos", "LuxGallery") + j.build() diff --git a/bak/unused_apps/photos/detail_urls.py b/bak/unused_apps/photos/detail_urls.py new file mode 100644 index 0000000..0ab94f6 --- /dev/null +++ b/bak/unused_apps/photos/detail_urls.py @@ -0,0 +1,10 @@ +from django.conf.urls import url +from django.views.generic.detail import DetailView +from photos.models import Photo + +urlpatterns = [ + url( + r'^(?P<object_id>\d+)/$', + DetailView.as_view(model=Photo, template_name='details/photo.html') + ), +] diff --git a/bak/unused_apps/photos/flickrauth b/bak/unused_apps/photos/flickrauth new file mode 100644 index 0000000..0fe7407 --- /dev/null +++ b/bak/unused_apps/photos/flickrauth @@ -0,0 +1,2 @@ +72157629224829547-04c4f099c2550cd8 +812f6c53e5b122f4
\ No newline at end of file diff --git a/bak/unused_apps/photos/forms.py b/bak/unused_apps/photos/forms.py new file mode 100644 index 0000000..6ee7bde --- /dev/null +++ b/bak/unused_apps/photos/forms.py @@ -0,0 +1,179 @@ +import zipfile +from zipfile import BadZipFile +import logging +import datetime +import os +from io import BytesIO +try: + import Image +except ImportError: + from PIL import Image + +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.utils.safestring import mark_safe +from django.contrib import messages +from django.core.files.base import ContentFile +from django.contrib.admin import widgets +from django.conf import settings + +from photos.models import LuxImage, LuxGallery, LuxImageSize + +from .utils import resize_image +from .readexif import readexif + +logger = logging.getLogger('photos.forms') + + +class GalleryForm(forms.ModelForm): + class Meta: + fields = '__all__' + widgets = { + 'images': forms.SelectMultiple, + } + + def __init__(self, *args, **kwargs): + super(GalleryForm, self).__init__(*args, **kwargs) + self.fields['images'].choices = [(image.id, mark_safe('%sqq%sqq%s' % (image.title, image.get_image_by_size('tn'), image.pk))) for image in LuxImage.objects.all()[:40]] + self.fields['images'].allow_tags = True + + +from django.utils.safestring import mark_safe + +class ImageChoiceField(forms.ModelMultipleChoiceField): + + def label_from_instance(self, obj): + + return mark_safe('%sqq%sqq%s' % (obj.title, obj.get_image_by_size('tn'), obj.pk)) + + + +class FKGalleryForm(forms.ModelForm): + class Meta: + fields = '__all__' + widgets = { + 'image': ImageChoiceField(queryset=LuxImage.objects.all()), + } + + def __init__(self, *args, **kwargs): + super(FKGalleryForm, self).__init__(*args, **kwargs) + self.fields['image'].choices = [(o.id, str(o.image.url)) for o in LuxImage.objects.all()] + self.fields['image'].allow_tags = True + +class UploadZipForm(forms.Form): + """ + Handles the uploading of a gallery of photos packed in a .zip file + Creates Gallery object, adds photos with all metadata that's available + """ + zip_file = forms.FileField() + title = forms.CharField(label=_('Gallery Title'), max_length=250) + slug = forms.SlugField(label=_('Gallery Slug')) + desc = forms.CharField(label=_('Gallery Caption'), widget=forms.Textarea, required=False) + date = forms.SplitDateTimeField(label=_('Date'), widget=widgets.AdminSplitDateTime) + is_public = forms.BooleanField(label=_('Is public'), initial=True, required=False, help_text=_('Show on site')) + + def clean_zip_file(self): + """Open the zip file a first time, to check that it is a valid zip archive. + We'll open it again in a moment, so we have some duplication, but let's focus + on keeping the code easier to read! + """ + zip_file = self.cleaned_data['zip_file'] + try: + zip = zipfile.ZipFile(zip_file) + except BadZipFile as e: + raise forms.ValidationError(str(e)) + bad_file = zip.testzip() + if bad_file: + zip.close() + raise forms.ValidationError('"%s" in the .zip archive is corrupt.' % bad_file) + zip.close() # Close file in all cases. + return zip_file + + def clean_title(self): + title = self.cleaned_data['title'] + if title and LuxGallery.objects.filter(title=title).exists(): + raise forms.ValidationError(_('A gallery with that title already exists.')) + return title + + def clean(self): + cleaned_data = super(UploadZipForm, self).clean() + if not self['title'].errors: + # If there's already an error in the title, no need to add another + # error related to the same field. + if not cleaned_data.get('title', None) and not cleaned_data['gallery']: + raise forms.ValidationError( + _('Select an existing gallery, or enter a title for a new gallery.')) + return cleaned_data + + def save(self, request=None, zip_file=None): + if not zip_file: + zip_file = self.cleaned_data['zip_file'] + + gallery, created = LuxGallery.objects.get_or_create( + title=self.cleaned_data['title'], + description=self.cleaned_data['desc'], + slug=self.cleaned_data['slug'], + pub_date=self.cleaned_data['date'], + is_public=self.cleaned_data['is_public'] + ) + zipper = zipfile.ZipFile(zip_file) + count = 1 + for filename in sorted(zipper.namelist()): + f, file_extension = os.path.splitext(filename) + logger.debug('Reading file "{0}".'.format(filename)) + if filename.startswith('__') or filename.startswith('.'): + logger.debug('Ignoring file "{0}".'.format(filename)) + continue + if os.path.dirname(filename): + logger.warning('Ignoring file "{0}" as it is in a subfolder; all images should be in the top ' + 'folder of the zip.'.format(filename)) + if request: + messages.warning(request, + _('Ignoring file "{filename}" as it is in a subfolder').format(filename=filename), fail_silently=True) + continue + data = zipper.read(filename) + + if not len(data): + logger.debug('File "{0}" is empty.'.format(filename)) + continue + + fn, file_extension = os.path.splitext(filename) + if file_extension != ".mp4": + # Basic check that we have a valid image. + try: + file = BytesIO(data) + opened = Image.open(file) + opened.verify() + except Exception: + # Pillow (or PIL) doesn't recognize it as an image. + # If a "bad" file is found we just skip it. + # But we do flag this both in the logs and to the user. + logger.error('Could not process file "{0}" in the .zip archive.'.format(filename)) + if request: + messages.warning(request, + _('Could not process file "{0}" in the .zip archive.').format( + filename), + fail_silently=True) + continue + image = LuxImage( + pub_date=datetime.datetime.now() + ) + contentfile = ContentFile(data) + image.image.save(filename, contentfile) + if file_extension != ".mp4": + img = Image.open(image.image.path) + if img.size[0] > img.size[1]: + image.sizes.add(LuxImageSize.objects.get(width=2560)) + image.sizes.add(LuxImageSize.objects.get(width=1170)) + image.sizes.add(LuxImageSize.objects.get(width=720)) + if img.size[1] > img.size[0]: + image.sizes.add(LuxImageSize.objects.get(height=1600)) + image.sizes.add(LuxImageSize.objects.get(height=800)) + image.sizes.add(LuxImageSize.objects.get(height=460)) + image.save() + gallery.images.add(image) + + zipper.close() + + if request: + messages.success(request, _('The photos have been uploaded')) diff --git a/bak/unused_apps/photos/migrations/0001_initial.py b/bak/unused_apps/photos/migrations/0001_initial.py new file mode 100644 index 0000000..711af1d --- /dev/null +++ b/bak/unused_apps/photos/migrations/0001_initial.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2015-12-11 19:33 +from __future__ import unicode_literals + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import photos.models +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('taggit', '0002_auto_20150616_2121'), + #('locations', '__first__'), + ] + + operations = [ + migrations.CreateModel( + name='LuxGallery', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=300)), + ('description', models.TextField(blank=True, null=True)), + ('slug', models.CharField(blank=True, max_length=300)), + ('thumb', models.CharField(blank=True, max_length=300)), + ('pub_date', models.DateTimeField(null=True)), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('is_public', models.BooleanField(default=True)), + ], + options={ + 'verbose_name_plural': 'Galleries', + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date', 'id'), + }, + ), + migrations.CreateModel( + name='LuxImage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image', models.FileField(blank=True, null=True, upload_to=photos.models.get_upload_path)), + ('title', models.CharField(blank=True, max_length=300, null=True)), + ('caption', models.TextField(blank=True, null=True)), + ('pub_date', models.DateTimeField()), + ('exif_raw', models.TextField(blank=True, null=True)), + ('exif_aperture', models.CharField(blank=True, max_length=50, null=True)), + ('exif_make', models.CharField(blank=True, max_length=50, null=True)), + ('exif_model', models.CharField(blank=True, max_length=50, null=True)), + ('exif_exposure', models.CharField(blank=True, max_length=50, null=True)), + ('exif_iso', models.CharField(blank=True, max_length=50, null=True)), + ('exif_focal_length', models.CharField(blank=True, max_length=50, null=True)), + ('exif_lens', models.CharField(blank=True, max_length=50, null=True)), + ('exif_date', models.DateTimeField(blank=True, null=True)), + ('height', models.CharField(blank=True, max_length=6, null=True)), + ('width', models.CharField(blank=True, max_length=6, null=True)), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('is_public', models.BooleanField(default=True)), + ('is_video', models.BooleanField(default=False)), + ('flickr_id', models.CharField(blank=True, max_length=80, null=True)), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ], + options={ + 'verbose_name_plural': 'Images', + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date', 'id'), + }, + ), + migrations.CreateModel( + name='Photo', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('description', models.TextField(blank=True, null=True)), + ('title', models.CharField(blank=True, max_length=300)), + ('pub_date', models.DateTimeField()), + ('exif_aperture', models.CharField(blank=True, max_length=50, null=True)), + ('exif_make', models.CharField(blank=True, max_length=50, null=True)), + ('exif_model', models.CharField(blank=True, max_length=50, null=True)), + ('exif_exposure', models.CharField(blank=True, max_length=50, null=True)), + ('exif_iso', models.CharField(blank=True, max_length=50, null=True)), + ('exif_focal_length', models.CharField(blank=True, max_length=50, null=True)), + ('exif_lens', models.CharField(blank=True, max_length=50, null=True)), + ('exif_date', models.DateTimeField()), + ('flickr_id', models.CharField(max_length=300)), + ('flickr_owner', models.CharField(max_length=20)), + ('flickr_server', models.IntegerField()), + ('flickr_farm', models.IntegerField()), + ('flickr_secret', models.CharField(max_length=50)), + ('flickr_originalsecret', models.CharField(max_length=50)), + ('lon', models.FloatField(help_text='Longitude of centerpoint', null=True, verbose_name='Longitude')), + ('lat', models.FloatField(help_text='Latitude of centerpoint', null=True, verbose_name='Latitude')), + ('slideshowimage_width', models.CharField(blank=True, max_length=4, null=True)), + ('slideshowimage_height', models.CharField(blank=True, max_length=4, null=True)), + ('slideshowimage_margintop', models.CharField(blank=True, max_length=4, null=True)), + ('slideshowimage_marginleft', models.CharField(blank=True, max_length=4, null=True)), + ('is_public', models.BooleanField(default=True)), + ('location', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ('region', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Region')), + ('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')), + ], + options={ + 'ordering': ('-pub_date',), + }, + ), + migrations.CreateModel( + name='PhotoGallery', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('set_id', models.CharField(blank=True, max_length=300)), + ('set_title', models.CharField(blank=True, max_length=300)), + ('set_desc', models.TextField(blank=True, null=True)), + ('set_slug', models.CharField(blank=True, max_length=300)), + ('primary', models.CharField(blank=True, max_length=300)), + ('pub_date', models.DateTimeField(null=True)), + ('location', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ('photos', models.ManyToManyField(to='photos.Photo')), + ('region', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Region')), + ], + options={ + 'verbose_name_plural': 'Photo Galleries', + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date', 'id'), + }, + ), + migrations.AddField( + model_name='luxgallery', + name='image', + field=models.ManyToManyField(to='photos.LuxImage'), + ), + migrations.AddField( + model_name='luxgallery', + name='location', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location'), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0002_auto_20151216_1958.py b/bak/unused_apps/photos/migrations/0002_auto_20151216_1958.py new file mode 100644 index 0000000..2a2bffb --- /dev/null +++ b/bak/unused_apps/photos/migrations/0002_auto_20151216_1958.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2015-12-16 19:58 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='luxgallery', + old_name='image', + new_name='images', + ), + migrations.AlterField( + model_name='luxgallery', + name='thumb', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='gallery_thumb', to='photos.LuxImage'), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0003_luxgallery_caption_style.py b/bak/unused_apps/photos/migrations/0003_luxgallery_caption_style.py new file mode 100644 index 0000000..b46f970 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0003_luxgallery_caption_style.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2015-12-17 11:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0002_auto_20151216_1958'), + ] + + operations = [ + migrations.AddField( + model_name='luxgallery', + name='caption_style', + field=models.CharField(blank=True, max_length=400, null=True), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0004_luximage_alt.py b/bak/unused_apps/photos/migrations/0004_luximage_alt.py new file mode 100644 index 0000000..ecbaf56 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0004_luximage_alt.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-16 22:45 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0003_luxgallery_caption_style'), + ] + + operations = [ + migrations.AddField( + model_name='luximage', + name='alt', + field=models.CharField(blank=True, max_length=300, null=True), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0005_auto_20160318_1244.py b/bak/unused_apps/photos/migrations/0005_auto_20160318_1244.py new file mode 100644 index 0000000..584714e --- /dev/null +++ b/bak/unused_apps/photos/migrations/0005_auto_20160318_1244.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-18 12:44 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0004_luximage_alt'), + ] + + operations = [ + migrations.AddField( + model_name='luximage', + name='photo_credit_source', + field=models.CharField(blank=True, max_length=300, null=True), + ), + migrations.AddField( + model_name='luximage', + name='photo_credit_url', + field=models.CharField(blank=True, max_length=300, null=True), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0006_auto_20160318_2047.py b/bak/unused_apps/photos/migrations/0006_auto_20160318_2047.py new file mode 100644 index 0000000..7cf0a4f --- /dev/null +++ b/bak/unused_apps/photos/migrations/0006_auto_20160318_2047.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-18 20:47 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0005_auto_20160318_1244'), + ] + + operations = [ + migrations.CreateModel( + name='LuxImageSize', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('size', models.IntegerField(max_length=5)), + ], + options={ + 'verbose_name_plural': 'Image Sizes', + }, + ), + migrations.AddField( + model_name='luximage', + name='sizes', + field=models.ManyToManyField(to='photos.LuxImageSize'), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0007_auto_20160320_0802.py b/bak/unused_apps/photos/migrations/0007_auto_20160320_0802.py new file mode 100644 index 0000000..474bd42 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0007_auto_20160320_0802.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-20 08:02 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0006_auto_20160318_2047'), + ] + + operations = [ + migrations.AlterField( + model_name='luximagesize', + name='size', + field=models.IntegerField(), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0008_luximagesize_quality.py b/bak/unused_apps/photos/migrations/0008_luximagesize_quality.py new file mode 100644 index 0000000..06dc0cc --- /dev/null +++ b/bak/unused_apps/photos/migrations/0008_luximagesize_quality.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-20 08:47 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0007_auto_20160320_0802'), + ] + + operations = [ + migrations.AddField( + model_name='luximagesize', + name='quality', + field=models.IntegerField(default=65), + preserve_default=False, + ), + ] diff --git a/bak/unused_apps/photos/migrations/0009_auto_20160320_0907.py b/bak/unused_apps/photos/migrations/0009_auto_20160320_0907.py new file mode 100644 index 0000000..ccfebf5 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0009_auto_20160320_0907.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-20 09:07 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0008_luximagesize_quality'), + ] + + operations = [ + migrations.RenameField( + model_name='luximagesize', + old_name='size', + new_name='width', + ), + ] diff --git a/bak/unused_apps/photos/migrations/0010_auto_20160517_0906.py b/bak/unused_apps/photos/migrations/0010_auto_20160517_0906.py new file mode 100644 index 0000000..0adf4ff --- /dev/null +++ b/bak/unused_apps/photos/migrations/0010_auto_20160517_0906.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-05-17 09:06 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0009_auto_20160320_0907'), + ] + + operations = [ + migrations.AddField( + model_name='luximage', + name='facebook_link', + field=models.CharField(blank=True, max_length=300, null=True), + ), + migrations.AddField( + model_name='luximage', + name='twitter_link', + field=models.CharField(blank=True, max_length=300, null=True), + ), + migrations.AlterField( + model_name='luximage', + name='sizes', + field=models.ManyToManyField(blank=True, to='photos.LuxImageSize'), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0011_luximagesize_name.py b/bak/unused_apps/photos/migrations/0011_luximagesize_name.py new file mode 100644 index 0000000..49d046b --- /dev/null +++ b/bak/unused_apps/photos/migrations/0011_luximagesize_name.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-10-21 08:46 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0010_auto_20160517_0906'), + ] + + operations = [ + migrations.AddField( + model_name='luximagesize', + name='name', + field=models.CharField(blank=True, max_length=30, null=True), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0012_luximagesize_is_portrait.py b/bak/unused_apps/photos/migrations/0012_luximagesize_is_portrait.py new file mode 100644 index 0000000..a2c2e3a --- /dev/null +++ b/bak/unused_apps/photos/migrations/0012_luximagesize_is_portrait.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-10-22 13:46 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0011_luximagesize_name'), + ] + + operations = [ + migrations.AddField( + model_name='luximagesize', + name='is_portrait', + field=models.BooleanField(default=False), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0013_auto_20161022_1348.py b/bak/unused_apps/photos/migrations/0013_auto_20161022_1348.py new file mode 100644 index 0000000..8dfbf98 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0013_auto_20161022_1348.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-10-22 13:48 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0012_luximagesize_is_portrait'), + ] + + operations = [ + migrations.RenameField( + model_name='luximagesize', + old_name='width', + new_name='long_edge', + ), + ] diff --git a/bak/unused_apps/photos/migrations/0014_remove_luximagesize_is_portrait.py b/bak/unused_apps/photos/migrations/0014_remove_luximagesize_is_portrait.py new file mode 100644 index 0000000..b8e0aa8 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0014_remove_luximagesize_is_portrait.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-10-22 14:11 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0013_auto_20161022_1348'), + ] + + operations = [ + migrations.RemoveField( + model_name='luximagesize', + name='is_portrait', + ), + ] diff --git a/bak/unused_apps/photos/migrations/0015_auto_20161022_1411.py b/bak/unused_apps/photos/migrations/0015_auto_20161022_1411.py new file mode 100644 index 0000000..d380b52 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0015_auto_20161022_1411.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-10-22 14:11 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0014_remove_luximagesize_is_portrait'), + ] + + operations = [ + migrations.RenameField( + model_name='luximagesize', + old_name='long_edge', + new_name='width', + ), + ] diff --git a/bak/unused_apps/photos/migrations/0016_auto_20161022_1411.py b/bak/unused_apps/photos/migrations/0016_auto_20161022_1411.py new file mode 100644 index 0000000..54b84d7 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0016_auto_20161022_1411.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-10-22 14:11 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0015_auto_20161022_1411'), + ] + + operations = [ + migrations.AddField( + model_name='luximagesize', + name='height', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name='luximagesize', + name='width', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0017_auto_20161130_1218.py b/bak/unused_apps/photos/migrations/0017_auto_20161130_1218.py new file mode 100644 index 0000000..8d5a496 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0017_auto_20161130_1218.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-11-30 12:18 +from __future__ import unicode_literals + +import datetime +from django.db import migrations, models +import photos.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0016_auto_20161022_1411'), + ] + + operations = [ + migrations.CreateModel( + name='LuxVideo', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('video_mp4', models.FileField(blank=True, null=True, upload_to=photos.models.get_vid_upload_path)), + ('video_webm', models.FileField(blank=True, null=True, upload_to=photos.models.get_vid_upload_path)), + ('video_poster', models.FileField(blank=True, null=True, upload_to=photos.models.get_vid_upload_path)), + ('title', models.CharField(blank=True, max_length=300, null=True)), + ('pub_date', models.DateTimeField(default=datetime.datetime.now)), + ], + options={ + 'verbose_name_plural': 'Videos', + 'ordering': ('-pub_date', 'id'), + 'get_latest_by': 'pub_date', + }, + ), + migrations.AlterField( + model_name='luximage', + name='pub_date', + field=models.DateTimeField(default=datetime.datetime.now), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0018_auto_20161130_1218.py b/bak/unused_apps/photos/migrations/0018_auto_20161130_1218.py new file mode 100644 index 0000000..784175a --- /dev/null +++ b/bak/unused_apps/photos/migrations/0018_auto_20161130_1218.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-11-30 12:18 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0017_auto_20161130_1218'), + ] + + operations = [ + migrations.RemoveField( + model_name='luximage', + name='is_video', + ), + migrations.AddField( + model_name='luxvideo', + name='vimeo_url', + field=models.CharField(blank=True, max_length=300, null=True), + ), + migrations.AddField( + model_name='luxvideo', + name='youtube_url', + field=models.CharField(blank=True, max_length=80, null=True), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0019_auto_20190704_0903.py b/bak/unused_apps/photos/migrations/0019_auto_20190704_0903.py new file mode 100644 index 0000000..833ee69 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0019_auto_20190704_0903.py @@ -0,0 +1,17 @@ +# Generated by Django 2.1.7 on 2019-07-04 09:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0018_auto_20161130_1218'), + ] + + operations = [ + migrations.AlterModelOptions( + name='luximagesize', + options={'ordering': ('-name', 'id'), 'verbose_name_plural': 'Image Sizes'}, + ), + ] diff --git a/bak/unused_apps/photos/migrations/0020_auto_20201201_2116.py b/bak/unused_apps/photos/migrations/0020_auto_20201201_2116.py new file mode 100644 index 0000000..39d4fd9 --- /dev/null +++ b/bak/unused_apps/photos/migrations/0020_auto_20201201_2116.py @@ -0,0 +1,20 @@ +# Generated by Django 3.1 on 2020-12-01 21:16 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0028_auto_20200308_1152'), + ('photos', '0019_auto_20190704_0903'), + ] + + operations = [ + migrations.AlterField( + model_name='luximage', + name='location', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='locations.location'), + ), + ] diff --git a/bak/unused_apps/photos/migrations/0021_auto_20201201_2118.py b/bak/unused_apps/photos/migrations/0021_auto_20201201_2118.py new file mode 100644 index 0000000..25e4f8f --- /dev/null +++ b/bak/unused_apps/photos/migrations/0021_auto_20201201_2118.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1 on 2020-12-01 21:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('photos', '0020_auto_20201201_2116'), + ] + + operations = [ + migrations.AlterField( + model_name='luximage', + name='sizes', + field=models.ManyToManyField(blank=True, related_name='_luximage_sizes_+', to='photos.LuxImageSize'), + ), + ] diff --git a/bak/unused_apps/photos/migrations/__init__.py b/bak/unused_apps/photos/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/photos/migrations/__init__.py diff --git a/bak/unused_apps/photos/models.py b/bak/unused_apps/photos/models.py new file mode 100644 index 0000000..24a218f --- /dev/null +++ b/bak/unused_apps/photos/models.py @@ -0,0 +1,555 @@ +import os.path +import io +import datetime +from PIL import Image + +from django.core.exceptions import ValidationError +from django.contrib.gis.db import models +from django.contrib.sitemaps import Sitemap +from django.utils.encoding import force_text +from django.utils.functional import cached_property +from django.urls import reverse +from django.apps import apps +from django.utils.html import format_html +from django.utils.text import slugify +from django.conf import settings +from django import forms + +from taggit.managers import TaggableManager + +from resizeimage.imageexceptions import ImageSizeError + +from .utils import resize_image +from .readexif import readexif +from django.db.models.signals import post_save +from django.dispatch import receiver +from django.db.models.signals import m2m_changed + + +def get_upload_path(self, filename): + return "images/original/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + +def get_vid_upload_path(self, filename): + return "images/videos/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + +class LuxImageSize(models.Model): + name = models.CharField(null=True, blank=True, max_length=30) + width = models.IntegerField(null=True, blank=True) + height = models.IntegerField(null=True, blank=True) + quality = models.IntegerField() + + class Meta: + ordering = ('-name', 'id') + verbose_name_plural = 'Image Sizes' + + def __str__(self): + if self.width: + size = self.width + if self.height: + size = self.height + return "%s - %s" %(self.name, str(size)) + + +class LuxImage(models.Model): + image = models.FileField(blank=True, null=True, upload_to=get_upload_path) + title = models.CharField(null=True, blank=True, max_length=300) + alt = models.CharField(null=True, blank=True, max_length=300) + photo_credit_source = models.CharField(null=True, blank=True, max_length=300) + photo_credit_url = models.CharField(null=True, blank=True, max_length=300) + caption = models.TextField(blank=True, null=True) + pub_date = models.DateTimeField(default=datetime.datetime.now) + exif_raw = models.TextField(blank=True, null=True) + exif_aperture = models.CharField(max_length=50, blank=True, null=True) + exif_make = models.CharField(max_length=50, blank=True, null=True) + exif_model = models.CharField(max_length=50, blank=True, null=True) + exif_exposure = models.CharField(max_length=50, blank=True, null=True) + exif_iso = models.CharField(max_length=50, blank=True, null=True) + exif_focal_length = models.CharField(max_length=50, blank=True, null=True) + exif_lens = models.CharField(max_length=50, blank=True, null=True) + exif_date = models.DateTimeField(blank=True, null=True) + height = models.CharField(max_length=6, blank=True, null=True) + width = models.CharField(max_length=6, blank=True, null=True) + point = models.PointField(null=True, blank=True) + location = models.ForeignKey("locations.Location", on_delete=models.CASCADE, null=True, blank=True, related_name="+") + is_public = models.BooleanField(default=True) + sizes = models.ManyToManyField(LuxImageSize, blank=True, related_name="+") + flickr_id = models.CharField(null=True, blank=True, max_length=80) + twitter_link = models.CharField(null=True, blank=True, max_length=300) + facebook_link = models.CharField(null=True, blank=True, max_length=300) + + class Meta: + ordering = ('-pub_date', 'id') + verbose_name_plural = 'Images' + get_latest_by = 'pub_date' + + def __str__(self): + if self.title: + return "%s" % self.title + else: + return "%s" % self.pk + + def get_type(self): + return str(self.__class__.__name__) + + def get_admin_image(self): + for size in self.sizes.all(): + if size.width and size.width <= 820 or size.height and size.height <= 800: + return self.get_image_by_size(size.name) + + def get_admin_insert(self): + return "/media/images/%s/%s_tn.%s" % (self.pub_date.strftime("%Y"), self.get_image_name(), self.get_image_ext()) + + def get_largest_image(self): + t = [] + for size in self.sizes.all(): + t.append(size.width) + t.sort(key=float) + t.reverse() + return self.get_image_path_by_size(t[0]) + + def get_image_name(self): + return self.image.url.split("original/")[1][5:-4] + + def get_image_name_new(self): + return os.path.basename(self.image.path)[:-4] + + def get_image_ext(self): + return self.image.url[-3:] + + @cached_property + def get_featured_jrnl(self): + ''' cached version of getting the primary image for archive page''' + return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), 'featured_jrnl', self.get_image_ext()) + + @cached_property + def get_picwide_sm(self): + ''' cached version of getting the second image for archive page''' + return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), 'picwide-sm', self.get_image_ext()) + + @cached_property + def get_srcset(self): + srcset = "" + length = len(self.sizes.all()) + print(length) + loopnum = 1 + for size in self.sizes.all(): + srcset += "%s%s/%s_%s.%s %sw" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), size.name, self.get_image_ext(), size.width) + if loopnum < length: + srcset += ", " + loopnum = loopnum+1 + return srcset + + @cached_property + def get_src(self): + src = "" + if self.sizes.all().count() > 1: + src += "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), 'picwide-med', self.get_image_ext()) + else: + src += "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.get_image_name(), [size.name for size in self.sizes.all()], self.get_image_ext()) + return src + + def get_image_by_size(self, size="original"): + base = self.get_image_name() + if size == "admin_insert": + return "images/%s/%s.%s" % (self.pub_date.strftime("%Y"), base, self.get_image_ext()) + if size == "original": + return "%soriginal/%s/%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), base, self.get_image_ext()) + else: + if size != 'tn': + try: + self.sizes.filter(name=size) + except DoesNotExist: + print("new size is "+s.name) + self.sizes.add(s) + return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), base, size, self.get_image_ext()) + + def get_image_path_by_size(self, size="original"): + base = self.get_image_name() + if size == "original": + return "%s/original/%s/%s.%s" % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), base, self.get_image_ext()) + else: + return "%s/%s/%s_%s.%s" % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), base, size, self.get_image_ext()) + + def get_thumbnail_url(self): + return self.get_image_by_size("tn") + + def admin_thumbnail(self): + return format_html('<a href="%s"><img src="%s"></a>' % (self.get_image_by_size(), self.get_image_by_size("tn"))) + admin_thumbnail.short_description = 'Thumbnail' + + def get_sizes(self): + return self.sizes.all() + + @property + def latitude(self): + return self.point.y + + @property + def longitude(self): + return self.point.x + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date() + + @property + def get_next_published(self): + return self.get_next_by_pub_date() + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + except model.DoesNotExist: + return '' + + @property + def is_portait(self): + if int(self.height) > int(self.width): + return True + else: + return False + + def save(self, *args, **kwargs): + if not self.point: + self.point = LuxImage.objects.latest().point + try: + self.location = apps.get_model('locations', 'Location').objects.filter( + geometry__contains=self.point + ).get() + except apps.get_model('locations', 'Location').DoesNotExist: + raise forms.ValidationError("There is no location associated with that point, add it: %sadmin/locations/location/add/" % (settings.BASE_URL)) + super(LuxImage, self).save() + + +@receiver(post_save, sender=LuxImage) +def post_save_events(sender, update_fields, created, instance, **kwargs): + if instance.exif_raw == '': + filename, file_extension = os.path.splitext(instance.image.path) + if file_extension != ".mp4": + img = Image.open(instance.image.path) + instance.height = img.height + instance.width = img.width + instance = readexif(instance) + post_save.disconnect(post_save_events, sender=LuxImage) + instance.save() + post_save.connect(post_save_events, sender=LuxImage) + + +@receiver(m2m_changed, sender=LuxImage.sizes.through) +def update_photo_sizes(sender, instance, **kwargs): + base_path = "%s/%s/" % (settings.IMAGES_ROOT, instance.pub_date.strftime("%Y")) + filename, file_extension = os.path.splitext(instance.image.path) + if file_extension != ".mp4": + img = Image.open(instance.image.path) + resize_image(img, 160, None, 78, base_path, "%s_tn.%s" % (instance.get_image_name(), instance.get_image_ext())) + for size in instance.sizes.all(): + if size.width: + print("Image width is:"+str(img.width)) + try: + if size.width <= img.width: + resize_image(img, size.width, None, size.quality, base_path, "%s_%s.%s" % (instance.get_image_name(), slugify(size.name), instance.get_image_ext())) + else: + raise ValidationError({'items': ["Size is larger than source image"]}) + except ImageSizeError: + m2m_changed.disconnect(update_photo_sizes, sender=LuxImage.sizes.through) + instance.sizes.remove(size) + m2m_changed.connect(update_photo_sizes, sender=LuxImage.sizes.through) + if size.height: + try: + if size.height <= img.height: + resize_image(img, None, size.height, size.quality, base_path, "%s_%s.%s" % (instance.get_image_name(), slugify(size.name), instance.get_image_ext())) + + else: + pass + except ImageSizeError: + m2m_changed.disconnect(update_photo_sizes, sender=LuxImage.sizes.through) + instance.sizes.remove(size) + m2m_changed.connect(update_photo_sizes, sender=LuxImage.sizes.through) + + +class LuxGallery(models.Model): + title = models.CharField(blank=True, max_length=300) + description = models.TextField(blank=True, null=True) + slug = models.CharField(blank=True, max_length=300) + thumb = models.ForeignKey(LuxImage, on_delete=models.CASCADE, related_name="gallery_thumb", null=True, blank=True) + images = models.ManyToManyField(LuxImage) + pub_date = models.DateTimeField(null=True) + point = models.PointField(null=True, blank=True) + location = models.ForeignKey("locations.Location", on_delete=models.CASCADE, null=True, blank=True) + is_public = models.BooleanField(default=True) + caption_style = models.CharField(blank=True, null=True, max_length=400) + + class Meta: + ordering = ('-pub_date', 'id') + verbose_name_plural = 'Galleries' + get_latest_by = 'pub_date' + + def __str__(self): + return self.title + + def get_main_image(self): + return "%sgallery_thumbs/%s.jpg" % (settings.IMAGES_URL, self.id) + + def get_absolute_url(self): + if self.is_public: + return "/photos/galleries/%s" % (self.slug) + else: + return "/photos/galleries/private/%s" % (self.slug) + + def latitude(self): + if self.point: + return self.point.y + + def longitude(self): + if self.point: + return self.point.x + + def thumbs(self): + lst = [x.image.name for x in self.images.all()] + lst = ["<a href='/media/%s'>%s</a>" % (x, x.split('/')[-1]) for x in lst] + return ', '.join(item for item in lst if item) + thumbs.allow_tags = True + + +class LuxVideo(models.Model): + video_mp4 = models.FileField(blank=True, null=True, upload_to=get_vid_upload_path) + video_webm = models.FileField(blank=True, null=True, upload_to=get_vid_upload_path) + video_poster = models.FileField(blank=True, null=True, upload_to=get_vid_upload_path) + title = models.CharField(null=True, blank=True, max_length=300) + pub_date = models.DateTimeField(default=datetime.datetime.now) + youtube_url = models.CharField(null=True, blank=True, max_length=80) + vimeo_url = models.CharField(null=True, blank=True, max_length=300) + + def __str__(self): + if self.title: + return self.title + else: + return str(self.pk) + + def get_type(self): + return str(self.__class__.__name__) + + class Meta: + ordering = ('-pub_date', 'id') + verbose_name_plural = 'Videos' + get_latest_by = 'pub_date' + +class Photo(models.Model): + description = models.TextField(blank=True, null=True) + title = models.CharField(blank=True, max_length=300) + pub_date = models.DateTimeField() + tags = TaggableManager(blank=True) + exif_aperture = models.CharField(max_length=50, blank=True, null=True) + exif_make = models.CharField(max_length=50, blank=True, null=True) + exif_model = models.CharField(max_length=50, blank=True, null=True) + exif_exposure = models.CharField(max_length=50, blank=True, null=True) + exif_iso = models.CharField(max_length=50, blank=True, null=True) + exif_focal_length = models.CharField(max_length=50, blank=True, null=True) + exif_lens = models.CharField(max_length=50, blank=True, null=True) + exif_date = models.DateTimeField() + """Flickr Specific Stuff""" + # Vlickr id is varchar since Flickr ids are larger than than integerfield can handle and BigIntegerField gets weird in Postgres. + flickr_id = models.CharField(max_length=300) + flickr_owner = models.CharField(max_length=20) + flickr_server = models.IntegerField() + flickr_farm = models.IntegerField() + flickr_secret = models.CharField(max_length=50) + flickr_originalsecret = models.CharField(max_length=50) + lon = models.FloatField('Longitude', help_text="Longitude of centerpoint", null=True) + lat = models.FloatField('Latitude', help_text="Latitude of centerpoint", null=True) + location = models.ForeignKey("locations.Location", on_delete=models.CASCADE, null=True) + region = models.ForeignKey("locations.Region", on_delete=models.CASCADE, null=True) + slideshowimage_width = models.CharField(max_length=4, blank=True, null=True) + slideshowimage_height = models.CharField(max_length=4, blank=True, null=True) + slideshowimage_margintop = models.CharField(max_length=4, blank=True, null=True) + slideshowimage_marginleft = models.CharField(max_length=4, blank=True, null=True) + is_public = models.BooleanField(default=True) + + class Meta: + ordering = ('-pub_date',) + + def admin_thumbnail(self): + return force_text('<a href="%s"><img src="%s"></a>' % (self.get_absolute_url(), self.get_small_square_url())) + admin_thumbnail.allow_tags = True + admin_thumbnail.short_description = 'Thumbnail' + + def get_local_medium_url(self): + return '%sflickr/med/%s/%s.jpg' % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.flickr_id) + + def get_local_orig_url(self): + return '%s/flickr/full/%s/%s.jpg' % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), self.flickr_id) + + def get_local_slideshow_url(self): + return '%sslideshow/%s/%s.jpg' % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.flickr_id) + + def __str__(self): + return self.title + + def get_absolute_url(self): + return "/photo/%s/" % (self.id) + + def get_model_name(self): + return 'photo' + + def get_small_square_url(self): + return self.get_pic_url(size="small_square") + + def get_large_url(self): + return self.get_pic_url(size="large") + + def get_small_url(self): + return self.get_pic_url(size="small") + + def get_medium_url(self): + return self.get_pic_url(size="medium") + + def get_original_url(self): + # return self.get_pic_url(size="original") + return "http://farm%s.static.flickr.com/%s/%s_%s_o.jpg" % (self.flickr_farm, self.flickr_server, self.flickr_id, self.flickr_originalsecret) + + def get_retina_slideshow_url(self): + return '%sslideshow/%s/%sx2.jpg' % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.flickr_id) + + def has_retina(self): + return os.path.isfile('%s/slideshow/%s/%sx2.jpg' % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), self.flickr_id)) + + @property + def get_height(self): + im = Image.open('%s/slideshow/%s/%s.jpg' % (settings.IMAGES_ROOT, + self.pub_date.strftime("%Y"), self.flickr_id)) + xsize, ysize = im.size + return ysize + + @property + def get_width(self): + im = Image.open('%s/slideshow/%s/%s.jpg' % (settings.IMAGES_ROOT, + self.pub_date.strftime("%Y"), self.flickr_id)) + xsize, ysize = im.size + return xsize + + @property + def get_margin_top(self): + im = Image.open('%s/slideshow/%s/%s.jpg' % (settings.IMAGES_ROOT, + self.pub_date.strftime("%Y"), self.flickr_id)) + xsize, ysize = im.size + mtop = 340 - (ysize / 2) + return mtop + + @property + def get_margin_left(self): + im = Image.open('%s/slideshow/%s/%s.jpg' % (settings.IMAGES_ROOT, + self.pub_date.strftime("%Y"), self.flickr_id)) + xsize, ysize = im.size + mtop = 500 - (xsize / 2) + return mtop + + @property + def flickr_link(self): + return '%s%s/' % ('http://www.flickr.com/photos/luxagraf/', self.flickr_id) + + @property + def is_portait(self): + if int(self.slideshowimage_height) > int(self.slideshowimage_width): + return True + else: + return False + + def get_pic_url(self, size='small'): + # small_square=75x75 + # thumb=100 on longest side + # small=240 on longest side + # medium=500 on longest side + # large=1024 on longest side + # original=duh + base_url = "http://static.flickr.com" + size_char = 's' # default to small_square + if size == 'small_square': + size_char = '_s' + elif size == 'thumb': + size_char = '_t' + elif size == 'small': + size_char = '_m' + elif size == 'medium': + size_char = '' + elif size == 'large': + size_char = '_b' + elif size == 'original': + size_char = '_o' + + return "http://farm%s.static.flickr.com/%s/%s_%s%s.jpg" % (self.flickr_farm, self.flickr_server, self.flickr_id, self.flickr_secret, size_char) + + def get_tumble_image(self): + return "%s/crops/%s/%s.jpg" % (settings.IMAGES_URL, self.pub_date.strftime("%Y/%b").lower(), self.id) + + def get_previous_published(self): + return self.get_previous_by_pub_date() + + def get_next_published(self): + return self.get_next_by_pub_date() + + def comment_period_open(self): + return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date + + def save(self, *args, **kwargs): + super(Photo, self).save() + + +class PhotoGallery(models.Model): + set_id = models.CharField(blank=True, max_length=300) + set_title = models.CharField(blank=True, max_length=300) + set_desc = models.TextField(blank=True, null=True) + set_slug = models.CharField(blank=True, max_length=300) + primary = models.CharField(blank=True, max_length=300) + photos = models.ManyToManyField(Photo) + location = models.ForeignKey("locations.Location", on_delete=models.CASCADE, null=True) + region = models.ForeignKey("locations.Region", on_delete=models.CASCADE, null=True) + pub_date = models.DateTimeField(null=True) + + class Meta: + ordering = ('-pub_date', 'id') + verbose_name_plural = 'Photo Galleries' + get_latest_by = 'pub_date' + + def __str__(self): + return self.set_title + + def get_main_image(self): + return "%sgallery_thumbs/%s.jpg" % (settings.IMAGES_URL, self.id) + + def get_absolute_url(self): + return "/photos/galleries/%s/" % (self.set_slug) + + +class PhotoGallerySitemap(Sitemap): + changefreq = "never" + priority = 0.7 + protocol = "https" + + def items(self): + return PhotoGallery.objects.all() + + def lastmod(self, obj): + return obj.pub_date + + +def resize_luximage(self, image): + image.save() + img = Image.open(image.image.path) + base_path = "%s/galleries/" % settings.IMAGES_ROOT + if img.size[0] > img.size[1]: + resize_image(img, 2280, None, 65, base_path+'large/', image.get_image_name()) + resize_image(img, 1140, None, 72, base_path+'medium/', image.get_image_name()) + resize_image(img, 720, None, 68, base_path+'small/', image.get_image_name()) + if img.size[1] > img.size[0]: + resize_image(img, None, 1600, 65, base_path+'large/', image.get_image_name()) + resize_image(img, None, 800, 72, base_path+'medium/', image.get_image_name()) + resize_image(img, None, 460, 60, base_path+'small/', image.get_image_name()) + + resize_image(img, 160, None, 68, base_path+'thumb/', image.get_image_name()) diff --git a/bak/unused_apps/photos/photos.js b/bak/unused_apps/photos/photos.js new file mode 100644 index 0000000..b93467a --- /dev/null +++ b/bak/unused_apps/photos/photos.js @@ -0,0 +1,71 @@ +//Utility functions for map info window +function mapit(obj) { + lat = parseFloat(obj.attr('data-latitude')); + lon = parseFloat(obj.attr('data-longitude')); + elid= obj.attr('data-imgid'); + map = L.map(document.getElementById("mw-"+elid)); + centerCoord = new L.LatLng(lat, lon); + zoom = 8; + L.tileLayer.provider('Esri.WorldTopoMap', {maxZoom: 18, attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Tiles © Esri and the GIS User Community'}).addTo(map); + map.setView(centerCoord, zoom); + L.marker([lat, lon]).addTo(map); +} + //########## utility functions to create/remove map container ############ +function create_map(obj) { + //find id of this image caption: + var imgid = obj.attr('data-imgid'); + //create container divs + $('<div class="map-container" id="mc-'+imgid+'">').insertBefore($(obj).parent().parent()); + //$(obj).parent().parent().parent().prepend('<div class="map-container" id="mc-'+imgid+'">'); + $('#mc-'+imgid).append('<div class="map-wrapper" id="mw-'+imgid+'">'); + //deal with the variable height of div.legend + $('#mc-'+imgid).css({ + bottom: function(index, value) { + return parseFloat($(obj).parent().parent().height())+20; + } + }); + + mapit(obj); +} +function remove_map(imgid) { + $('#mc-'+imgid).remove(); +} + +//############ Document.ready events ############## +$(document).ready(function(){ + + //set up click events for map button + $('.map-link').click( function() { + imgid = $(this).attr('data-imgid'); + if ($('#mc-'+imgid).is(":visible")) { + remove_map(imgid); + } else { + create_map($(this)); + } + return false; + + }); + var $ele = $('#slides').children(); + var $curr = 0; + $(document).bind('keydown', function (e) { + var code = e.which; + switch (code) { + case 39: + if ($curr <= $ele.size()) { + $.scrollTo($ele[$curr], 800 ); + $curr++; + } + break; + case 37: + if ($curr > 0) { + $curr--; + var $now = $curr; + $now--; + $.scrollTo($ele[$now], 800 ); + } + break; + } + return; + }); +}); + diff --git a/bak/unused_apps/photos/readexif.py b/bak/unused_apps/photos/readexif.py new file mode 100644 index 0000000..70a6987 --- /dev/null +++ b/bak/unused_apps/photos/readexif.py @@ -0,0 +1,78 @@ +import time +from fractions import Fraction + +from django.contrib.gis.geos import Point + +import exiftool + +from locations.models import Location + + +def readexif(image): + """ + takes an image and fills in all the exif data tracked in the image model + + """ + with exiftool.ExifTool() as et: + meta = et.get_metadata(image.image.path) + et.terminate() + image.exif_raw = meta + try: + image.title = meta["EXIF:ImageDescription"] + except: + try: + image.title = meta["XMP:Title"] + except: + pass + try: + image.caption = meta["EXIF:UserComment"] + except: + pass + try: + image.exif_lens = meta["MakerNotes:LensType"] + except: + try: + image.exif_lens = meta["XMP:Lens"] + except: + pass + try: + image.exif_aperture = meta["EXIF:FNumber"] + except: + pass + try: + image.exif_make = meta["EXIF:Make"] + except: + pass + try: + image.exif_model = meta["EXIF:Model"] + except: + pass + try: + image.exif_exposure = str(Fraction(float(meta["EXIF:ExposureTime"])).limit_denominator()) + except: + pass + try: + image.exif_iso = meta["EXIF:ISO"] + except: + pass + try: + image.exif_focal_length = meta["EXIF:FocalLength"] + except: + pass + try: + fmt_date = time.strptime(meta["EXIF:DateTimeOriginal"], "%Y:%m:%d %H:%M:%S") + except: + pass + try: + image.exif_date = time.strftime("%Y-%m-%d %H:%M:%S", fmt_date) + except: + pass + try: + image.height = meta["File:ImageHeight"] + except: + pass + try: + image.width = meta["File:ImageWidth"] + except: + pass + return image diff --git a/bak/unused_apps/photos/resize.py b/bak/unused_apps/photos/resize.py new file mode 100644 index 0000000..13c0151 --- /dev/null +++ b/bak/unused_apps/photos/resize.py @@ -0,0 +1,53 @@ +import os +import io + +from django.conf import settings + +try: + import Image + import ImageFile +except ImportError: + try: + from PIL import Image + from PIL import ImageFile + except ImportError: + raise ImportError("Could not import the Python Imaging Library.") + +ImageFile.MAXBLOCK = 1000000 + + +def make_local_copies(self,photo): + orig_dir = settings.IMAGES_ROOT + '/flickr/full/' + photo.pub_date.strftime("%Y") + if not os.path.isdir(orig_dir): + os.makedirs(orig_dir) + im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image + img = Image.open(im) + local_full = '%s/%s.jpg' % (orig_dir, photo.flickr_id) + img.save(local_full) + + #calculate crop: + cur_width, cur_height = img.size + new_width, new_height = 291, 350 + ratio = max(float(new_width) / cur_width, float(new_height) / cur_height) + x = (cur_width * ratio) + y = (cur_height * ratio) + xd = abs(new_width - x) + yd = abs(new_height - y) + x_diff = int(xd / 2) + y_diff = int(yd / 2) + box = (int(x_diff), int(y_diff), int(x_diff + new_width), int(y_diff + new_height)) + + # create resized file + resized = img.resize((int(x), int(y)), Image.ANTIALIAS).crop(box) + # save resized file + resized_filename = '%s/%s.jpg' % (crop_dir, set.id) + try: + if img.format == 'JPEG': + resized.save(resized_filename, 'JPEG', quality=95, optimize=True) + else: + resized.save(resized_filename) + except IOError as e: + if os.path.isfile(resized_filename): + os.unlink(resized_filename) + raise e + os.unlink(img) diff --git a/bak/unused_apps/photos/retriever.py b/bak/unused_apps/photos/retriever.py new file mode 100644 index 0000000..f5cae68 --- /dev/null +++ b/bak/unused_apps/photos/retriever.py @@ -0,0 +1,323 @@ +import json +import datetime +import os +import io +import urllib.request +import urllib.parse +import urllib.error + +from django.template.defaultfilters import slugify +from django.core.exceptions import ObjectDoesNotExist +from django.utils.encoding import force_text +from django.conf import settings + +from photos.models import Photo, PhotoGallery + +# from https://github.com/alexis-mignon/python-flickr-api +# terribly documented, but offers a good clean OOP approach if you're willing to figure it out... +import flickr_api +import flickrapi + +# Required PIL classes may or may not be available from the root namespace depending on the installation +try: + import Image + import ImageFile +except ImportError: + try: + from PIL import Image + from PIL import ImageFile + except ImportError: + raise ImportError("Could not import the Python Imaging Library.") + +ImageFile.MAXBLOCK = 1000000 + +EXIF_PARAMS = { + "FNumber": 'f/2.8', + "Make": 'Apple', + "Model": 'iPhone', + "ExposureTime": '', + "ISO": '', + "FocalLength": '', + "LensModel": '', + 'DateTimeOriginal': '2013:09:03 22:44:25' +} + +class SyncFlickr(): + + def __init__(self): + self.flickr = flickrapi.FlickrAPI(settings.FLICKR_API_KEY, settings.FLICKR_API_SECRET,format='parsed-json') + + + def sync_sets(self, *args, **kwargs): + p = self.flickr.photosets.getList(user_id='85322932@N00') + disregard = [ + 'POTD 2008', + 'Snow Day', + 'Wedding', + 'Some random stuff', + 'Lilah & Olivia', + '6 months+', + '6-9 months', + '9-18 months', + ] + for photoset in p['photosets']['photoset']: + if photoset['title']['_content'] in disregard: + pass + else: + try: + row = PhotoGallery.objects.get(set_id__exact=photoset['id']) + print(('%s %s %s' % ('already have', row.set_title, 'moving on...'))) + # okay it already exists, but is it up-to-date? + self.get_photos_in_set(photoset['id'],row) + except ObjectDoesNotExist: + s = PhotoGallery.objects.get_or_create( + set_id=force_text(photoset['id']), + set_title=force_text(photoset['title']['_content']), + set_desc=force_text(photoset['description']['_content']), + set_slug=slugify(force_text(photoset['title']['_content'])[:40]), + primary=force_text(photoset['primary']), + pub_date=datetime.datetime.fromtimestamp(float(photoset['date_create'])) + ) + + #get_photos_in_set(photoset, s) + #create the gallery thumbnail image: + #photo = Photo.objects.get(flickr_id__exact=str(photoset['primary'])) + #make_gallery_thumb(photo, s) + + + + def get_photos_in_set(self, flickr_id, photoset): + photos = self.flickr.photosets.getPhotos(photoset_id=flickr_id) + for photo in photos['photoset']['photo']: + try: + p = Photo.objects.get(flickr_id__exact=str(photo['id'])) + except ObjectDoesNotExist: + p = self.get_photo(photo['id']) + if p.is_public: + pass #photoset.photos.add(p) + #slideshow_image(p, 1000, 800, 95) + print(p) + + def get_photo(self, photo_id): + photo = self.flickr.photos.getInfo(photo_id=photo_id) + info = photo['photo'] + try: + geo = self.flickr.photos.geo.getLocation(photo_id=photo_id) + location, region = self.get_geo(float(geo['photo']['location']['latitude']), float(geo['photo']['location']['longitude'])) + except KeyError: + print("no effing geodata asshat") + exif = self.exif_handler(self.flickr.photos.getExif(photo_id=photo_id)['photo']['exif']) + p, created = Photo.objects.get_or_create( + title=info['title']['_content'], + flickr_id=info['id'], + flickr_owner=info['owner']['nsid'], + flickr_server=info['server'], + flickr_secret=info['secret'], + flickr_originalsecret=info['originalsecret'], + flickr_farm=info['farm'], + pub_date=self.flickr_datetime_to_datetime(exif["DateTimeOriginal"].replace(':', '-', 2)), + description=info['description']['_content'], + exif_aperture=exif['FNumber'], + exif_make=exif['Make'], + exif_model=exif['Model'], + exif_exposure=exif['ExposureTime'], + exif_iso=exif['ISO'], + exif_lens=exif['LensModel'], + exif_focal_length=exif['FocalLength'], + exif_date=self.flickr_datetime_to_datetime(exif["DateTimeOriginal"].replace(':', '-', 2)), + lat=float(geo['photo']['location']['latitude']), + lon=float(geo['photo']['location']['longitude']), + region=region, + location=location, + ) + if created: + for tag in info['tags']['tag']: + p.tags.add(tag['raw']) + p.save() + + local = FlickrImage() + local.make_local_copies(p) + #retina image: + #slideshow_image(p, 2000, 1600, 75) + #normal image + print("grabbing... "+p.title) + return p + + + def sync_flickr_photos(self, *args, **kwargs): + photos = self.flickr.people.getPhotos(user_id="85322932@N00", extras="date_upload,date_taken,geo") + for photo in photos['photos']['photo']: + try: + row = Photo.objects.get(flickr_id=photo['id'], flickr_secret=photo['secret']) + print('already have ' + photo['id'] + ' moving on') + except ObjectDoesNotExist: + p = self.get_photo(photo['id']) + + + + """ + ################################################ + ## Various meta data and geo helper functions ## + ################################################ + """ + + def exif_handler(self, data): + converted = {} + try: + for t in data: + converted[t['tag']] = t['raw']['_content'] + except: + pass + for k, v in list(EXIF_PARAMS.items()): + if k not in converted: + converted[k] = v + return converted + + + def flickr_datetime_to_datetime(self, fdt): + from datetime import datetime + from time import strptime + date_parts = strptime(fdt, '%Y-%m-%d %H:%M:%S') + return datetime(*date_parts[0:6]) + + + def get_geo(self, lat, lon): + from locations.models import Location, Region + from django.contrib.gis.geos import Point + pnt_wkt = Point(lon, lat) + try: + location = Location.objects.get(geometry__contains=pnt_wkt) + except Location.DoesNotExist: + location = None + try: + region = Region.objects.get(geometry__contains=pnt_wkt) + except Region.DoesNotExist: + region = None + return location, region + + + + + + +class FlickrImage(): + """ + ## Photo retrieval functions to pull down images from Flickr servers ## + """ + + def slideshow_image(self, photo, max_width, max_height, quality): + slide_dir = settings.IMAGES_ROOT + '/slideshow/' + photo.pub_date.strftime("%Y") + if not os.path.isdir(slide_dir): + os.makedirs(slide_dir) + + # Is it a retina image or not? + if max_width >= 1001 or max_height >= 801: + filename = '%s/%sx2.jpg' % (slide_dir, photo.flickr_id) + else: + filename = '%s/%s.jpg' % (slide_dir, photo.flickr_id) + + flickr_photo = photo.get_original_url() + fname = urllib.request.urlopen(flickr_photo) + im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image + img = Image.open(im) + cur_width, cur_height = img.size + #if image landscape + if cur_width > cur_height: + new_width = max_width + #check to make sure we aren't upsizing + if cur_width > new_width: + ratio = float(new_width) / cur_width + x = (cur_width * ratio) + y = (cur_height * ratio) + resized = img.resize((int(x), int(y)), Image.ANTIALIAS) + resized.save(filename, 'JPEG', quality=quality, optimize=True) + else: + img.save(filename) + else: + #image portrait + new_height = max_height + #check to make sure we aren't upsizing + if cur_height > new_height: + ratio = float(new_height) / cur_height + x = (cur_width * ratio) + y = (cur_height * ratio) + resized = img.resize((int(x), int(y)), Image.ANTIALIAS) + resized.save(filename, 'JPEG', quality=quality, optimize=True) + else: + img.save(filename) + photo.slideshowimage_width = photo.get_width + photo.slideshowimage_height = photo.get_height + photo.slideshowimage_margintop = photo.get_margin_top + photo.slideshowimage_marginleft = photo.get_margin_left + photo.save() + #now resize the local copy + + + def make_local_copies(self,photo): + orig_dir = settings.IMAGES_ROOT + '/flickr/full/' + photo.pub_date.strftime("%Y") + if not os.path.isdir(orig_dir): + os.makedirs(orig_dir) + full = photo.get_original_url() + fname = urllib.request.urlopen(full) + im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image + img = Image.open(im) + local_full = '%s/%s.jpg' % (orig_dir, photo.flickr_id) + img.save(local_full) + #save large size + large_dir = settings.IMAGES_ROOT + '/flickr/large/' + photo.pub_date.strftime("%Y") + if not os.path.isdir(large_dir): + os.makedirs(large_dir) + large = photo.get_large_url() + fname = urllib.request.urlopen(large) + im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image + img = Image.open(im) + local_large = '%s/%s.jpg' % (large_dir, photo.flickr_id) + if img.format == 'JPEG': + img.save(local_large) + #save medium size + med_dir = settings.IMAGES_ROOT + '/flickr/med/' + photo.pub_date.strftime("%Y") + if not os.path.isdir(med_dir): + os.makedirs(med_dir) + med = photo.get_medium_url() + fname = urllib.request.urlopen(med) + im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image + img = Image.open(im) + local_med = '%s/%s.jpg' % (med_dir, photo.flickr_id) + img.save(local_med) + + + def make_gallery_thumb(self, photo, set): + crop_dir = settings.IMAGES_ROOT + '/gallery_thumbs/' + if not os.path.isdir(crop_dir): + os.makedirs(crop_dir) + remote = photo.get_original_url() + print(remote) + fname = urllib.request.urlopen(remote) + im = io.StringIO(fname.read().decode('UTF-8')) # constructs a StringIO holding the image + img = Image.open(im) + #calculate crop: + cur_width, cur_height = img.size + new_width, new_height = 291, 350 + ratio = max(float(new_width) / cur_width, float(new_height) / cur_height) + x = (cur_width * ratio) + y = (cur_height * ratio) + xd = abs(new_width - x) + yd = abs(new_height - y) + x_diff = int(xd / 2) + y_diff = int(yd / 2) + box = (int(x_diff), int(y_diff), int(x_diff + new_width), int(y_diff + new_height)) + + # create resized file + resized = img.resize((int(x), int(y)), Image.ANTIALIAS).crop(box) + # save resized file + resized_filename = '%s/%s.jpg' % (crop_dir, set.id) + try: + if img.format == 'JPEG': + resized.save(resized_filename, 'JPEG', quality=95, optimize=True) + else: + resized.save(resized_filename) + except IOError as e: + if os.path.isfile(resized_filename): + os.unlink(resized_filename) + raise e + os.unlink(img) diff --git a/bak/unused_apps/photos/retriever.py.bak b/bak/unused_apps/photos/retriever.py.bak new file mode 100644 index 0000000..d3c572a --- /dev/null +++ b/bak/unused_apps/photos/retriever.py.bak @@ -0,0 +1,314 @@ +from __future__ import division +import datetime +import os +import cStringIO +import urllib + +from django.template.defaultfilters import slugify +from django.core.exceptions import ObjectDoesNotExist +from django.utils.encoding import force_unicode +from django.conf import settings + +# Required PIL classes may or may not be available from the root namespace +# depending on the installation +try: + import Image + import ImageFile +except ImportError: + try: + from PIL import Image + from PIL import ImageFile + except ImportError: + raise ImportError("Could not import the Python Imaging Library.") + +ImageFile.MAXBLOCK = 1000000 + +from photos.models import Photo, PhotoGallery + +# from https://github.com/alexis-mignon/python-flickr-api +# terribly documented, but offers a good clean OOP approach if you're willing to figure it out... +import flickr_api + +EXIF_PARAMS = { + "FNumber": 'f/2.8', + "Make": 'Apple', + "Model": 'iPhone', + "ExposureTime": '', + "ISO": '', + "FocalLength": '', + "LensModel": '', + 'DateTimeOriginal': '2013:09:03 22:44:25' +} + + +def sync_flickr_photos(*args, **kwargs): + flickr_api.set_keys(api_key=settings.FLICKR_API_KEY, api_secret=settings.FLICKR_API_SECRET) + flickr_api.set_auth_handler("app/photos/flickrauth") + user = flickr_api.test.login() + photos = user.getPhotos(extras="date_upload,date_taken,geo") + # reverse! reverse! + photos.reverse() + for photo in photos: + info = photo.getInfo() + try: + row = Photo.objects.get(flickr_id=info['id'], flickr_secret=info['secret']) + print('already have ' + info['id'] + ' moving on') + except ObjectDoesNotExist: + get_photo(photo) + + +def get_photo(photo): + info = photo.getInfo() + geo = photo.getLocation() + location, region = get_geo(float(geo['latitude']), float(geo['longitude'])) + exif = exif_handler(photo.getExif()) + p, created = Photo.objects.get_or_create( + title=info['title'], + flickr_id=info['id'], + flickr_owner=info['owner']['id'], + flickr_server=info['server'], + flickr_secret=info['secret'], + flickr_originalsecret=info['originalsecret'], + flickr_farm=info['farm'], + pub_date=flickr_datetime_to_datetime(info['taken']), + description=info['description'], + exif_aperture=exif['FNumber'], + exif_make=exif['Make'], + exif_model=exif['Model'], + exif_exposure=exif['ExposureTime'], + exif_iso=exif['ISO'], + exif_lens=exif['LensModel'], + exif_focal_length=exif['FocalLength'], + exif_date=flickr_datetime_to_datetime(exif["DateTimeOriginal"].replace(':', '-', 2)), + lat=float(geo['latitude']), + lon=float(geo['longitude']), + region=region, + location=location, + ) + if created: + for tag in info['tags']: + p.tags.add(tag['raw']) + p.save() + make_local_copies(p) + #retina image: + #slideshow_image(p, 2000, 1600, 75) + #normal image + print(p.title) + return p + + +def sync_sets(*args, **kwargs): + flickr_api.set_keys(api_key=settings.FLICKR_API_KEY, api_secret=settings.FLICKR_API_SECRET) + flickr_api.set_auth_handler("app/photos/flickrauth") + user = flickr_api.test.login() + photosets = user.getPhotosets() + # reverse! reverse! + photosets.reverse() + disregard = [ + 'POTD 2008', + 'Snow Day', + 'Wedding', + 'Some random stuff', + 'Lilah & Olivia', + '6 months+', + '6-9 months', + '9-18 months', + ] + for photoset in photosets: + if photoset['title'] in disregard: + pass + else: + try: + row = PhotoGallery.objects.get(set_id__exact=photoset['id']) + print('%s %s %s' % ('already have', row.set_title, 'moving on...')) + # okay it already exists, but is it up-to-date? + #get_photos_in_set(row,set.id) + except ObjectDoesNotExist: + s = PhotoGallery.objects.create( + set_id=force_unicode(photoset['id']), + set_title=force_unicode(photoset['title']), + set_desc=force_unicode(photoset['description']), + set_slug=slugify(force_unicode(photoset['title'])), + primary=force_unicode(photoset['primary']), + pub_date=datetime.datetime.fromtimestamp(float(photoset['date_create'])) + ) + + get_photos_in_set(photoset, s) + #create the gallery thumbnail image: + photo = Photo.objects.get(flickr_id__exact=str(photoset['primary'])) + make_gallery_thumb(photo, s) + + +def get_photos_in_set(flickr_photoset, photoset): + for photo in flickr_photoset.getPhotos(): + try: + p = Photo.objects.get(flickr_id__exact=str(photo['id'])) + except ObjectDoesNotExist: + p = get_photo(photo) + if p.is_public: + photoset.photos.add(p) + slideshow_image(p, 1000, 800, 95) + + +################################################ +## Various meta data and geo helper functions ## +################################################ + + +def exif_handler(data): + converted = {} + try: + for t in data: + converted[t['tag']] = t['raw'] + except: + pass + for k, v in EXIF_PARAMS.items(): + if not converted.has_key(k): + converted[k] = v + return converted + + +def flickr_datetime_to_datetime(fdt): + from datetime import datetime + from time import strptime + date_parts = strptime(fdt, '%Y-%m-%d %H:%M:%S') + return datetime(*date_parts[0:6]) + +def get_geo(lat,lon): + from locations.models import Location, Region + from django.contrib.gis.geos import Point + pnt_wkt = Point(lon, lat) + try: + location = Location.objects.get(geometry__contains=pnt_wkt) + except Location.DoesNotExist: + location = None + try: + region = Region.objects.get(geometry__contains=pnt_wkt) + except Region.DoesNotExist: + region = None + return location, region + +####################################################################### +## Photo retrieval functions to pull down images from Flickr servers ## +####################################################################### + +def slideshow_image(photo,max_width, max_height, quality): + slide_dir = settings.IMAGES_ROOT + '/slideshow/'+ photo.pub_date.strftime("%Y") + if not os.path.isdir(slide_dir): + os.makedirs(slide_dir) + + # Is it a retina image or not? + if max_width >= 1001 or max_height >= 801: + filename = '%s/%sx2.jpg' %(slide_dir, photo.flickr_id) + else: + filename = '%s/%s.jpg' %(slide_dir, photo.flickr_id) + + flickr_photo = photo.get_original_url() + fname = urllib.urlopen(flickr_photo) + im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image + img = Image.open(im) + cur_width, cur_height = img.size + #if image landscape + if cur_width > cur_height: + new_width = max_width + #check to make sure we aren't upsizing + if cur_width > new_width: + ratio = float(new_width)/cur_width + x = (cur_width * ratio) + y = (cur_height * ratio) + resized = img.resize((int(x), int(y)), Image.ANTIALIAS) + resized.save(filename, 'JPEG', quality=quality, optimize=True) + else: + img.save(filename) + else: + #image portrait + new_height = max_height + #check to make sure we aren't upsizing + if cur_height > new_height: + ratio = float(new_height)/cur_height + x = (cur_width * ratio) + y = (cur_height * ratio) + resized = img.resize((int(x), int(y)), Image.ANTIALIAS) + resized.save(filename, 'JPEG', quality=quality, optimize=True) + else: + img.save(filename) + photo.slideshowimage_width = photo.get_width + photo.slideshowimage_height = photo.get_height + photo.slideshowimage_margintop = photo.get_margin_top + photo.slideshowimage_marginleft = photo.get_margin_left + photo.save() + #now resize the local copy + + + +def make_local_copies(photo): + orig_dir = settings.IMAGES_ROOT + '/flickr/full/'+ photo.pub_date.strftime("%Y") + if not os.path.isdir(orig_dir): + os.makedirs(orig_dir) + full = photo.get_original_url() + fname = urllib.urlopen(full) + im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image + img = Image.open(im) + local_full = '%s/%s.jpg' %(orig_dir, photo.flickr_id) + img.save(local_full) + #save large size + large_dir = settings.IMAGES_ROOT + '/flickr/large/'+ photo.pub_date.strftime("%Y") + if not os.path.isdir(large_dir): + os.makedirs(large_dir) + large = photo.get_large_url() + fname = urllib.urlopen(large) + im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image + img = Image.open(im) + local_large = '%s/%s.jpg' %(large_dir, photo.flickr_id) + if img.format == 'JPEG': + img.save(local_large) + #save medium size + med_dir = settings.IMAGES_ROOT + '/flickr/med/'+ photo.pub_date.strftime("%Y") + if not os.path.isdir(med_dir): + os.makedirs(med_dir) + med = photo.get_medium_url() + fname = urllib.urlopen(med) + im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image + img = Image.open(im) + local_med = '%s/%s.jpg' %(med_dir, photo.flickr_id) + img.save(local_med) + +def make_gallery_thumb(photo,set): + crop_dir = settings.IMAGES_ROOT + '/gallery_thumbs/' + if not os.path.isdir(crop_dir): + os.makedirs(crop_dir) + remote = photo.get_original_url() + print(remote) + fname = urllib.urlopen(remote) + im = cStringIO.StringIO(fname.read()) # constructs a StringIO holding the image + img = Image.open(im) + + #calculate crop: + cur_width, cur_height = img.size + new_width, new_height = 291, 350 + ratio = max(float(new_width)/cur_width,float(new_height)/cur_height) + x = (cur_width * ratio) + y = (cur_height * ratio) + xd = abs(new_width - x) + yd = abs(new_height - y) + x_diff = int(xd / 2) + y_diff = int(yd / 2) + box = (int(x_diff), int(y_diff), int(x_diff+new_width), int(y_diff+new_height)) + + #create resized file + resized = img.resize((int(x), int(y)), Image.ANTIALIAS).crop(box) + # save resized file + resized_filename = '%s/%s.jpg' %(crop_dir, set.id) + try: + if img.format == 'JPEG': + resized.save(resized_filename, 'JPEG', quality=95, optimize=True) + else: + resized.save(resized_filename) + except IOError, e: + if os.path.isfile(resized_filename): + os.unlink(resized_filename) + raise e + #os.unlink(img) + + + diff --git a/bak/unused_apps/photos/static/image-preview.js b/bak/unused_apps/photos/static/image-preview.js new file mode 100644 index 0000000..b8fead5 --- /dev/null +++ b/bak/unused_apps/photos/static/image-preview.js @@ -0,0 +1,42 @@ +function build_image_preview () { + var url = window.location.href + var cur = url.split('/')[6]; + if (cur) { + var container = document.createElement("div"); + container.className = "form-row field-image"; + var wrapper = document.createElement("div"); + var label = document.createElement("label"); + label.textContent = "Image:"; + var pwrap = document.createElement("p"); + var img = document.createElement("img"); + + var request = new XMLHttpRequest(); + request.open('GET', '/photos/luximage/data/admin/preview/'+cur+'/', true); + request.onload = function() { + if (request.status >= 200 && request.status < 400) { + var data = JSON.parse(request.responseText); + //console.log(data); + img.src = data['url']; + } else { + console.log("server error"); + } + }; + request.onerror = function() { + console.log("error on request"); + }; + request.send(); + pwrap.appendChild(img); + wrapper.appendChild(label); + wrapper.appendChild(pwrap); + container.appendChild(wrapper); + parent = document.getElementById("luximage_form"); + node = parent.children[1].children[0]; + node.parentNode.insertBefore(container, node.previousSibling); + } else { + return; + } +} + +document.addEventListener("DOMContentLoaded", function(event) { + build_image_preview(); +}); diff --git a/bak/unused_apps/photos/static/my_styles.css b/bak/unused_apps/photos/static/my_styles.css new file mode 100644 index 0000000..65005c8 --- /dev/null +++ b/bak/unused_apps/photos/static/my_styles.css @@ -0,0 +1 @@ + #id_featured_image {/*style the "box" in its minimzed state*/ border:1px solid black; width:230px; overflow:hidden; height:300px; overflow-y:scroll; /*animate collapsing the dropdown from open to closed state (v. fast)*/}#id_featured_image input {/*hide the nasty default radio buttons. like, completely!*/ position:absolute;top:0;left:0;opacity:0;}#id_featured_image label {/*style the labels to look like dropdown options, kinda*/ color:#000; display:block; margin:2px 2px 2px 10px; height:102px; opacity:.6; background-repeat:no-repeat;}#id_featured_image:hover label{/*this is how labels render in the "expanded" state. we want to see only the selected radio button in the collapsed menu, and all of them when expanded*/}#id_featured_image label:hover {opacity:.8;}#id_featured_image input:checked + label {/*tricky! labels immediately following a checked radio button (with our markup they are semantically related) should be fully opaque regardless of hover, and they should always be visible (i.e. even in the collapsed menu*/ opacity:1 !important; display:block; background:#333;}#trace {margin:0 0 20px;}#id_featured_image li:first-child {display:none;}
\ No newline at end of file diff --git a/bak/unused_apps/photos/static/my_styles.uncompressed.css b/bak/unused_apps/photos/static/my_styles.uncompressed.css new file mode 100644 index 0000000..d13c8e4 --- /dev/null +++ b/bak/unused_apps/photos/static/my_styles.uncompressed.css @@ -0,0 +1,40 @@ + +/*o.v.*/ + +#id_featured_image { + /*style the "box" in its minimzed state*/ + border:1px solid black; width:230px; overflow:hidden; + height:300px; overflow-y:scroll; + /*animate collapsing the dropdown from open to closed state (v. fast)*/ +} +#id_featured_image input { + /*hide the nasty default radio buttons. like, completely!*/ + position:absolute;top:0;left:0;opacity:0; +} + + +#id_featured_image label { + /*style the labels to look like dropdown options, kinda*/ + color: #000; + display:block; + margin: 2px 2px 2px 10px; + height:102px; + opacity:.6; + background-repeat: no-repeat; +} +#id_featured_image:hover label{ + /*this is how labels render in the "expanded" state. we want to see only the selected radio button in the collapsed menu, and all of them when expanded*/ +} +#id_featured_image label:hover { + opacity:.8; +} +#id_featured_image input:checked + label { + /*tricky! labels immediately following a checked radio button (with our markup they are semantically related) should be fully opaque regardless of hover, and they should always be visible (i.e. even in the collapsed menu*/ + opacity:1 !important; + display:block; + background: #333; +} + +/*pfft, nothing as cool here, just the value trace*/ +#trace {margin:0 0 20px;} +#id_featured_image li:first-child { display: none;} diff --git a/bak/unused_apps/photos/sync_photo_sets.py b/bak/unused_apps/photos/sync_photo_sets.py new file mode 100644 index 0000000..e31d6e4 --- /dev/null +++ b/bak/unused_apps/photos/sync_photo_sets.py @@ -0,0 +1,14 @@ +import sys +import os +from os.path import dirname, abspath +PROJECT_ROOT = abspath(dirname(dirname(dirname(__file__)))) + '/' +# PROJECT_ROOT = abspath(dirname(dirname(__file__))) +print PROJECT_ROOT +sys.path.append(PROJECT_ROOT) +sys.path.append(PROJECT_ROOT + '/app') +sys.path.append(PROJECT_ROOT + '/app/lib') +sys.path.append(PROJECT_ROOT + '/config') +sys.path.append('/home/luxagraf/apps/venv/bin/python2.7/') +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings.settings' +from photos import retriever +retriever.sync_sets() diff --git a/bak/unused_apps/photos/sync_photos.py b/bak/unused_apps/photos/sync_photos.py new file mode 100644 index 0000000..886d221 --- /dev/null +++ b/bak/unused_apps/photos/sync_photos.py @@ -0,0 +1,13 @@ +import sys +import os +from os.path import dirname, abspath +PROJECT_ROOT = abspath(dirname(dirname(dirname(__file__)))) + '/' +# PROJECT_ROOT = abspath(dirname(dirname(__file__))) +sys.path.append(PROJECT_ROOT) +sys.path.append(PROJECT_ROOT + '/app') +sys.path.append(PROJECT_ROOT + '/app/lib') +sys.path.append(PROJECT_ROOT + '/config') +sys.path.append('/home/luxagraf/apps/venv/bin/python2.7/') +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings.settings' +from photos import retriever +retriever.sync_flickr_photos() diff --git a/bak/unused_apps/photos/templatetags/__init__.py b/bak/unused_apps/photos/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/photos/templatetags/__init__.py diff --git a/bak/unused_apps/photos/templatetags/get_image_by_size.py b/bak/unused_apps/photos/templatetags/get_image_by_size.py new file mode 100644 index 0000000..c56c44e --- /dev/null +++ b/bak/unused_apps/photos/templatetags/get_image_by_size.py @@ -0,0 +1,8 @@ +from django import template + +register = template.Library() + +@register.simple_tag +def get_image_by_size(obj, *args): + method = getattr(obj, "get_image_by_size") + return method(*args) diff --git a/bak/unused_apps/photos/templatetags/get_image_width.py b/bak/unused_apps/photos/templatetags/get_image_width.py new file mode 100644 index 0000000..ac39184 --- /dev/null +++ b/bak/unused_apps/photos/templatetags/get_image_width.py @@ -0,0 +1,9 @@ +from math import floor +from django import template + +register = template.Library() + +@register.simple_tag +def get_image_width(obj, size, *args): + ratio = floor(int(size)*100/int(obj.height))/100 + return floor(ratio*int(obj.height)) diff --git a/bak/unused_apps/photos/templatetags/get_size_by_name.py b/bak/unused_apps/photos/templatetags/get_size_by_name.py new file mode 100644 index 0000000..fc64a61 --- /dev/null +++ b/bak/unused_apps/photos/templatetags/get_size_by_name.py @@ -0,0 +1,8 @@ +from django import template + +register = template.Library() + +@register.simple_tag +def get_size_by_name(obj, *args): + method = getattr(obj, "get_size_by_name") + return method(*args) diff --git a/bak/unused_apps/photos/urls.py b/bak/unused_apps/photos/urls.py new file mode 100644 index 0000000..6673135 --- /dev/null +++ b/bak/unused_apps/photos/urls.py @@ -0,0 +1,74 @@ +from django.urls import path, re_path +from django.views.generic.base import RedirectView + +from . import views + +app_name = "photos" + +urlpatterns = [ + path( + r'daily/<int:page>', + views.DailyPhotoList.as_view(), + name="daily_photo_list" + ), + path( + r'daily/', + views.DailyPhotoList.as_view(), + {'page': 1}, + name="daily_photo_list" + ), + path( + r'data/(<str:slug>/', + views.photo_json + ), + re_path( + r'data/admin/preview/(?P<pk>\d+)/$', + views.photo_preview_json, + name="admin_image_preview" + ), + re_path( + r'data/admin/tn/(?P<pk>\d+)/$', + views.thumb_preview_json, + name="admin_thumb_preview" + ), + re_path( + r'galleries/private/(?P<slug>[-\w]+)$', + views.PrivateGallery.as_view(), + name="private" + ), + re_path( + r'galleries/private/(?P<page>\d+)/$', + views.PrivateGalleryList.as_view(), + name="private_list" + ), + re_path( + r'galleries/private/$', + RedirectView.as_view(url="/photos/galleries/private/1/", permanent=False) + ), + re_path( + r'galleries/(?P<slug>[-\w]+)$', + views.Gallery.as_view(), + name="private" + ), + re_path( + r'galleries/(?P<page>\d+)/$', + views.GalleryList.as_view(), + name="private_list" + ), + re_path( + r'galleries/$', + RedirectView.as_view(url="/photos/galleries/1/", permanent=False) + ), + re_path( + r'(?P<page>\d+)/$', + views.gallery_list, + ), + re_path( + r'(?P<slug>[-\w]+)/$', + RedirectView.as_view(url="/photos/%(slug)s/1/", permanent=False) + ), + re_path( + r'', + RedirectView.as_view(url="/photos/1/", permanent=False) + ), +] diff --git a/bak/unused_apps/photos/utils.py b/bak/unused_apps/photos/utils.py new file mode 100644 index 0000000..84e72f5 --- /dev/null +++ b/bak/unused_apps/photos/utils.py @@ -0,0 +1,28 @@ +import os +import re +import subprocess + +from django.apps import apps +from django.conf import settings + +from PIL import ImageFile +from bs4 import BeautifulSoup +# pip install python-resize-image +from resizeimage import resizeimage + + +def resize_image(img, width=None, height=None, quality=72, base_path="", filename=""): + if width and height: + newimg = resizeimage.resize_cover(img, [width, height]) + if width and not height: + newimg = resizeimage.resize_width(img, width) + if height and not width: + newimg = resizeimage.resize_height(img, height) + if not os.path.isdir(base_path): + os.makedirs(base_path) + path = "%s%s" % (base_path, filename) + ImageFile.MAXBLOCK = img.size[0] * img.size[1] * 4 + newimg.save(path, newimg.format, quality=quality) + subprocess.call(["jpegoptim", "%s" % path]) + + diff --git a/bak/unused_apps/photos/views.py b/bak/unused_apps/photos/views.py new file mode 100644 index 0000000..070b40d --- /dev/null +++ b/bak/unused_apps/photos/views.py @@ -0,0 +1,137 @@ +import json +from django.shortcuts import render +from django.template import RequestContext +from django.http import Http404, HttpResponse +from django.core import serializers + +from .models import Photo, PhotoGallery, LuxGallery, LuxImage +from locations.models import Country, Region + +from utils.views import PaginatedListView +from django.views.generic import ListView +from django.views.generic.detail import DetailView + + +class PrivateGallery(DetailView): + model = LuxGallery + slug_field = "slug" + template_name = "details/photo_gallery.html" + + +class PrivateGalleryList(PaginatedListView): + template_name = 'archives/gallery_list.html' + + def get_queryset(self): + return LuxGallery.objects.filter(is_public=False) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(PrivateGalleryList, self).get_context_data(**kwargs) + context['is_private'] = True + return context + + +class Gallery(DetailView): + model = LuxGallery + slug_field = "slug" + template_name = "details/photo_gallery.html" + + +class GalleryList(PaginatedListView): + template_name = 'archives/gallery_list.html' + + def get_queryset(self): + return LuxGallery.objects.filter(is_public=True) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(GalleryList, self).get_context_data(**kwargs) + context['is_private'] = False + return context + + +class OldGalleryList(PaginatedListView): + template_name = 'archives/gallery_list.html' + model = PhotoGallery + + def get_queryset(self): + return PhotoGallery.objects.filter(is_public=True) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(OldGalleryList, self).get_context_data(**kwargs) + return context + + +class DailyPhotoList(PaginatedListView): + model=LuxImage + template_name = 'archives/photo_daily_list.html' + + def get_queryset(self): + return LuxImage.objects.filter(is_public=True, title__startswith="daily_") + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(DailyPhotoList, self).get_context_data(**kwargs) + context['breadcrumbs'] = ['daily',] + return context + + +def gallery_list(request, page): + request.page_url = '/photos/%d/' + request.page = int(page) + context = { + 'object_list': PhotoGallery.objects.all(), + 'page': page, + } + return render(request, "archives/photos.html", context) + + +def gallery(request, slug): + context = { + 'object': PhotoGallery.objects.get(set_slug=slug) + } + return render(request, 'details/photo_galleries.html', context) + + +def photo_json(request, slug): + p = PhotoGallery.objects.filter(set_slug=slug) + return HttpResponse(serializers.serialize('json', p), mimetype='application/json') + + +def photo_preview_json(request, pk): + p = LuxImage.objects.get(pk=pk) + data = {} + data['url'] = p.get_admin_image() + data = json.dumps(data) + return HttpResponse(data) + + +def thumb_preview_json(request, pk): + p = LuxImage.objects.get(pk=pk) + data = {} + data['url'] = p.get_admin_insert() + data = json.dumps(data) + return HttpResponse(data) + + +def gallery_list_by_area(request, slug, page): + """Grabs entries by region or country""" + request.page_url = '/photos/' + slug + '/%d/' + request.page = int(page) + try: + region = Region.objects.get(slug__exact=slug) + qs = PhotoGallery.objects.filter(region=region).order_by('-id') + except: + region = Country.objects.get(slug__exact=slug) + qs = PhotoGallery.objects.filter(location__state__country=region).order_by('-id') + if not region: + raise Http404 + context = { + 'object_list': qs, + 'country_list': Country.objects.filter(visited=True), + 'region_list': Region.objects.all(), + 'region': region, + 'page': page + } + return render(request, "archives/photos.html", context) diff --git a/bak/unused_apps/products/admin.py b/bak/unused_apps/products/admin.py new file mode 100644 index 0000000..3a26358 --- /dev/null +++ b/bak/unused_apps/products/admin.py @@ -0,0 +1,28 @@ +from django.contrib import admin + +from .models import Brand, Product +from utils.widgets import AdminImageWidget, LGEntryForm + + +@admin.register(Product) +class ProductAdmin(admin.ModelAdmin): + form = LGEntryForm + list_display = ('name', 'admin_thumbnail', 'rating', 'pub_date') + search_fields = ['name', 'body_markdown'] + list_filter = ('rating', 'pub_date') + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } + + +@admin.register(Brand) +class BrandAdmin(admin.ModelAdmin): + list_display = ('name', ) + search_fields = ['name',] + list_filter = ('pub_date',) + + class Media: + js = ('next-prev-links.js',) diff --git a/bak/unused_apps/products/migrations/0001_initial.py b/bak/unused_apps/products/migrations/0001_initial.py new file mode 100644 index 0000000..eb2a822 --- /dev/null +++ b/bak/unused_apps/products/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# Generated by Django 2.2.6 on 2019-10-08 07:46 + +from django.db import migrations, models +import products.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Product', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('slug', models.CharField(max_length=50)), + ('pub_date', models.DateTimeField()), + ('body_markdown', models.TextField(blank=True)), + ('body_html', models.TextField(blank=True, null=True)), + ('primary_offer_url', models.CharField(max_length=400)), + ('primary_offer_price', models.IntegerField()), + ('secondary_offer_url', models.CharField(blank=True, max_length=400, null=True)), + ('secondar_offer_price', models.IntegerField(blank=True, null=True)), + ('rating', models.IntegerField()), + ('is_public', models.BooleanField(default=True)), + ('image', models.FileField(blank=True, null=True, upload_to=products.models.get_upload_path)), + ], + options={ + 'ordering': ('-pub_date',), + }, + ), + ] diff --git a/bak/unused_apps/products/migrations/0002_auto_20191008_0841.py b/bak/unused_apps/products/migrations/0002_auto_20191008_0841.py new file mode 100644 index 0000000..11b2d59 --- /dev/null +++ b/bak/unused_apps/products/migrations/0002_auto_20191008_0841.py @@ -0,0 +1,29 @@ +# Generated by Django 2.2.6 on 2019-10-08 08:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('products', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Brand', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('slug', models.CharField(max_length=50)), + ('pub_date', models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.AddField( + model_name='product', + name='brand', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.Brand'), + preserve_default=False, + ), + ] diff --git a/bak/unused_apps/products/migrations/0003_auto_20191008_0941.py b/bak/unused_apps/products/migrations/0003_auto_20191008_0941.py new file mode 100644 index 0000000..085fd32 --- /dev/null +++ b/bak/unused_apps/products/migrations/0003_auto_20191008_0941.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.6 on 2019-10-08 09:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('products', '0002_auto_20191008_0841'), + ] + + operations = [ + migrations.RenameField( + model_name='product', + old_name='secondar_offer_price', + new_name='secondary_offer_price', + ), + migrations.AddField( + model_name='product', + name='primary_offer_retailer', + field=models.CharField(default='Amazon', max_length=400), + preserve_default=False, + ), + migrations.AddField( + model_name='product', + name='secondary_offer_retailer', + field=models.CharField(default='Amazon', max_length=400), + preserve_default=False, + ), + ] diff --git a/bak/unused_apps/products/migrations/0004_auto_20191109_0849.py b/bak/unused_apps/products/migrations/0004_auto_20191109_0849.py new file mode 100644 index 0000000..1666a56 --- /dev/null +++ b/bak/unused_apps/products/migrations/0004_auto_20191109_0849.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.6 on 2019-11-09 08:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('products', '0003_auto_20191008_0941'), + ] + + operations = [ + migrations.AlterField( + model_name='product', + name='primary_offer_retailer', + field=models.IntegerField(choices=[(0, 'Amazon'), (1, 'REI'), (2, 'eBay')], default=0), + ), + migrations.AlterField( + model_name='product', + name='secondary_offer_retailer', + field=models.IntegerField(choices=[(0, 'Amazon'), (1, 'REI'), (2, 'eBay')], default=0), + ), + ] diff --git a/bak/unused_apps/products/migrations/0005_product_featured_image.py b/bak/unused_apps/products/migrations/0005_product_featured_image.py new file mode 100644 index 0000000..032bbfa --- /dev/null +++ b/bak/unused_apps/products/migrations/0005_product_featured_image.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.6 on 2019-11-09 09:35 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('products', '0004_auto_20191109_0849'), + ] + + operations = [ + migrations.AddField( + model_name='product', + name='featured_image', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='media.LuxImage'), + ), + ] diff --git a/bak/unused_apps/products/migrations/0006_remove_product_image.py b/bak/unused_apps/products/migrations/0006_remove_product_image.py new file mode 100644 index 0000000..b05110d --- /dev/null +++ b/bak/unused_apps/products/migrations/0006_remove_product_image.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.6 on 2019-11-09 09:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('products', '0005_product_featured_image'), + ] + + operations = [ + migrations.RemoveField( + model_name='product', + name='image', + ), + ] diff --git a/bak/unused_apps/products/migrations/__init__.py b/bak/unused_apps/products/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/products/migrations/__init__.py diff --git a/bak/unused_apps/products/models.py b/bak/unused_apps/products/models.py new file mode 100644 index 0000000..4b7dbec --- /dev/null +++ b/bak/unused_apps/products/models.py @@ -0,0 +1,92 @@ +import os +from PIL import Image +from django.db import models +from django.db.models.signals import post_save +from django.contrib.sitemaps import Sitemap +from django.dispatch import receiver +from django.urls import reverse +from django.apps import apps +from django.utils.html import format_html +from django.conf import settings +from django.template.defaultfilters import slugify + +from media.models import LuxImage, LuxImageSize +from media.utils import resize_image +from utils.util import render_images, render_products, parse_video, markdown_to_html + + +def get_upload_path(self, filename): + return "images/products/%s" % (filename) + +class Brand(models.Model): + name = models.CharField(max_length=200) + slug = models.CharField(max_length=50) + pub_date = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return self.name + +class Product(models.Model): + name = models.CharField(max_length=200) + brand = models.ForeignKey(Brand, on_delete=models.CASCADE) + slug = models.CharField(max_length=50) + pub_date = models.DateTimeField() + body_markdown = models.TextField(blank=True) + body_html = models.TextField(null=True, blank=True) + RETAILER = ( + (0, 'Amazon'), + (1, 'REI'), + (2, 'eBay'), + ) + primary_offer_retailer = models.IntegerField(choices=RETAILER, default=0) + primary_offer_url = models.CharField(max_length=400) + primary_offer_price = models.IntegerField() + secondary_offer_retailer = models.IntegerField(choices=RETAILER, default=0) + secondary_offer_url = models.CharField(max_length=400, blank=True, null=True) + secondary_offer_price = models.IntegerField(blank=True, null=True) + rating = models.IntegerField() + is_public = models.BooleanField(default=True) + featured_image = models.ForeignKey(LuxImage, on_delete=models.CASCADE, null=True, blank=True) + + class Meta: + ordering = ('-pub_date',) + + def __str__(self): + return self.name + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_read_date() + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[n.id]) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[self.get_next_by_read_date().pk]) + except model.DoesNotExist: + return '' + + def admin_thumbnail(self): + return format_html('<img src="%s" width="100" style="width:100px" />' % (self.featured_image.get_thumbnail_url())) + admin_thumbnail.short_description = 'Thumbnail' + + def get_full_name(self): + return "%s %s" % (self.brand.name, self.name) + + def save(self, *args, **kwargs): + md = render_images(self.body_markdown) + prods = render_products(md) + self.body_html = markdown_to_html(prods) + super(Product, self).save() + + +@receiver(post_save, sender=Product) +def post_save_events(sender, update_fields, created, instance, **kwargs): + #base_path = "%s/%s/" % (settings.MEDIA_ROOT, "/".join(str(i) for i in instance.image.name.split('/')[:-1])) + #filename, file_extension = os.path.splitext(instance.image.path) + #img = Image.open(instance.image.path) + #resize_image(img, None, 160, 78, base_path, "%s_tn%s" % (filename.split('/')[-1], file_extension)) + #resize_image(img, None, 650, 78, base_path, "%s_small%s" % (filename.split('/')[-1], file_extension)) + pass + diff --git a/bak/unused_apps/products/static/product-loader.js b/bak/unused_apps/products/static/product-loader.js new file mode 100644 index 0000000..6d04b61 --- /dev/null +++ b/bak/unused_apps/products/static/product-loader.js @@ -0,0 +1,10 @@ +function add_products(){ + var el = document.getElementById("images_frame"); + if (el){ + var iframe='<iframe frameborder="0" style="border: #dddddd 1px solid;margin-left: 20px;width:330px; height:720px;" src="/luxproduct/insert/?textarea='+el.id+'"></iframe>'; + el.insertAdjacentHTML('afterend', iframe); + } +} +document.addEventListener("DOMContentLoaded", function(event) { + add_products(); +}); diff --git a/bak/unused_apps/products/templates/products/snippet.html b/bak/unused_apps/products/templates/products/snippet.html new file mode 100644 index 0000000..3fc9f6f --- /dev/null +++ b/bak/unused_apps/products/templates/products/snippet.html @@ -0,0 +1,41 @@ +{% load get_image_by_size %} +{% load get_image_width %} +{% with image=object.featured_image %} +<div itemscope itemtype="http://schema.org/Product" class="product-card"> + <meta itemprop="brand" content="{{object.brand.name}}" /> + <figure itemscope itemtype="http://schema.org/ImageObject" class="picfull"> + <a href="{% get_image_by_size image 'original' %}" title="view larger image"> + <img class="u-photo" itemprop="contentUrl" sizes="(max-width: 750px) 100vw, (min-width: 751) 750px" srcset="{% for size in image.sizes.all%}{% get_image_by_size image size.name %} {{size.width}}w{% if forloop.last%}"{%else%}, {%endif%}{%endfor%}{% for size in image.sizes.all%}{%if not forloop.first and not forloop.last%} src="{% get_image_by_size image size.name%}"{%endif%}{%endfor%} alt="{{image.alt}} photographed by {% if image.photo_credit_source %}{{image.photo_credit_source}}{%else%}luxagraf{%endif%}" > + </a> + <figcaption>{% if image.caption %}{{image.caption|safe}}{% endif %}{% if image.photo_credit_source %}{%if image.caption %} | {%endif%}image: {% if image.photo_credit_url %}<a href="{{image.photo_credit_url}}" itemprop="author">{%endif%}{{image.photo_credit_source|lower}}{% if image.photo_credit_url %}</a>{%endif%}{%endif%} + </figcaption> + </figure> + <div class="buy-btn-wrapper"> + <h4 class="product-header" itemprop="name">{{object.get_full_name}}</h4> + <h5 class="product-link" itemprop="offers" itemscope itemtype="http://schema.org/Offer"> + <a href="{{object.primary_offer_url}}" title="buy the {{object.get_full_name}} for ${{object.primary_offer_price}} from {{object.primary_offer_retailer.get_primary_offer_retailer_display}}" itemprop="url" rel="nofollow"> + Buy Now ({{object.get_primary_offer_retailer_display}} + <span itemprop="priceCurrency" content="USD">$</span><span itemprop="price" content="{{object.primary_offer_price}}">{{object.primary_offer_price}}</span>) + </a> + <link itemprop="availability" href="http://schema.org/InStock" /> + </h5>{% if object.secondary_offer_url %} + <h5 class="product-link" itemprop="offers" itemscope itemtype="http://schema.org/Offer"> + <a href="{{object.secondary_offer_url}}" title="buy the {{object.get_full_name}} for ${{object.secondary_offer_price}} from {{object.secondary_offer_retailer.get_secondary_offer_retailer_display}}" itemprop="url"> + Buy Now ({{object.get_secondary_offer_retailer_display}} + <span itemprop="priceCurrency" content="USD">$</span><span itemprop="price" content="{{object.secondary_offer_price}}">{{object.secondary_offer_price}}</span>) + </a> + <link itemprop="availability" href="http://schema.org/InStock" /> + </h5>{% endif %} + </div> + <span itemprop="review" itemscope itemtype="http://schema.org/Review"> + <meta itemprop="name" content="{{object.get_full_name}}" /> + <meta itemprop="author" content="Scott Gilbertson" /> + <meta itemprop="datePublished" content="{{object.pub_date}}" /> + <span itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating"> + <meta itemprop="worstRating" content = "1"/> + <meta itemprop="ratingValue" content="{{object.rating}}" /> + <meta itemprop="bestRating" content="10" /> + </span> + <meta itemprop="description" content="{{object.body_markdown}}" /> +</div> +{% endwith %} diff --git a/bak/unused_apps/products/views.py b/bak/unused_apps/products/views.py new file mode 100644 index 0000000..30be542 --- /dev/null +++ b/bak/unused_apps/products/views.py @@ -0,0 +1,15 @@ +from django.shortcuts import render +from .models import Product + + +def insert_products(request): + """ + The view that handles the admin insert products feature + """ + object_list = Product.objects.all() + #object_list = sorted( + # chain(images, videos, audio), + # key=lambda instance: instance.pub_date, + # reverse=True + #) + return render(request, 'admin/insert_products.html', {'object_list': object_list, 'textarea_id': request.GET['textarea']}) diff --git a/bak/unused_apps/projects/__init__.py b/bak/unused_apps/projects/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/projects/__init__.py diff --git a/bak/unused_apps/projects/admin.py b/bak/unused_apps/projects/admin.py new file mode 100644 index 0000000..040b867 --- /dev/null +++ b/bak/unused_apps/projects/admin.py @@ -0,0 +1,135 @@ +from django.contrib import admin +from django.contrib.gis.admin import OSMGeoAdmin + +from projects.models.base import Project +from projects.models.fiveby import FiveBy +from projects.models.natparks import NationalParks +from projects.models.gifs import AnimatedGif +from projects.models.self_experiments import Experiment + + +class ProjectAdmin(OSMGeoAdmin): + list_display = ('title', 'pub_date', 'status',) + search_fields = ['title', 'body_markdown'] + prepopulated_fields = {"slug": ('title',)} + list_filter = ('pub_date', 'status') + fieldsets = ( + ('Project', { + 'fields': ( + 'title', + 'subtitle', + 'lede', + 'pub_date', + 'model_name', + ('status', 'image'), + 'slug', + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + }), + ) + + class Media: + js = ['/media/admin/custom/model.js'] + + +class FiveByAdmin(OSMGeoAdmin): + list_display = ('title', 'pub_date', 'status',) + search_fields = ['title'] + prepopulated_fields = {"slug": ('title',)} + list_filter = ('pub_date', 'status') + fieldsets = ( + ('Project', {'fields': ('title', 'lede', 'pub_date', 'status', ('image', 'videoh264', 'videoogg'), 'slug', ('vimeo_link', 'youtube_link'), ('point', 'location', 'region')), 'classes': ('show', 'extrapretty', 'wide')}), + ) + + # options for OSM map Using custom ESRI topo map + default_lon = -9285175 + default_lat = 4025046 + default_zoom = 6 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + + +class NationalParksAdmin(OSMGeoAdmin): + list_display = ('unit_name', 'type', 'state', 'name', 'visited', 'size') + list_filter = ('state', 'type') + search_fields = ['name'] + fieldsets = ( + ('Project', { + 'fields': ( + 'name', + 'tag_line', + ('state', 'visited'), + 'dek', + 'date_visited_begin', + 'date_visited_end', + 'mpoly', + 'image', + ('post', 'gallery'), + ('url', 'size', 'fee', 'camping_fee', 'date_park_created'), + 'zoom' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + }), + ) + + # options for OSM map Using custom ESRI topo map + default_lon = -9285175 + default_lat = 4025046 + default_zoom = 6 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + + +class AnimatedGifAdmin(admin.ModelAdmin): + list_display = ('title', 'date_created') + search_fields = ['title'] + fieldsets = ( + (None, { + 'fields': ( + 'title', + 'gif', + 'date_created', + 'slug', + 'music_ogg', + 'music_mp3' + ) + }), + ) + + +class ExperimentAdmin(admin.ModelAdmin): + list_display = ('title', 'date_start', 'date_end_projected', 'days_remaining') + search_fields = ['title'] + fieldsets = ( + (None, { + 'fields': ( + 'title', + 'slug', + 'body_markdown', + ('date_start', 'duration', 'date_end_projected'), + 'date_end_actual', + 'status' + ) + }), + ) + + +admin.site.register(Experiment, ExperimentAdmin) +admin.site.register(AnimatedGif, AnimatedGifAdmin) +admin.site.register(Project, ProjectAdmin) +admin.site.register(FiveBy, FiveByAdmin) +admin.site.register(NationalParks, NationalParksAdmin) diff --git a/bak/unused_apps/projects/models/__init__.py b/bak/unused_apps/projects/models/__init__.py new file mode 100644 index 0000000..3230ff4 --- /dev/null +++ b/bak/unused_apps/projects/models/__init__.py @@ -0,0 +1,5 @@ +from .base import Project +from .fiveby import FiveBy +from .natparks import NationalParks +from .gifs import AnimatedGif +from .self_experiments import Experiment diff --git a/bak/unused_apps/projects/models/base.py b/bak/unused_apps/projects/models/base.py new file mode 100644 index 0000000..aa795d2 --- /dev/null +++ b/bak/unused_apps/projects/models/base.py @@ -0,0 +1,67 @@ +import datetime +from django.contrib.gis.db import models +from django.contrib.sitemaps import Sitemap +from django.conf import settings + + +def get_upload_path(self, filename): + return "images/project-thumbs/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +class Project(models.Model): + title = models.CharField(max_length=200) + subtitle = models.CharField(max_length=200, null=True, blank=True) + slug = models.CharField(max_length=50) + lede = models.TextField(blank=True) + pub_date = models.DateTimeField('Date published') + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + image = models.FileField(upload_to=get_upload_path, null=True, blank=True) + model_name = models.CharField(max_length=200, null=True) + + @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 + + @property + def get_project_image(self): + return "%s%s" % (settings.IMAGES_URL, self.image.name[7:]) + + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + app_label = 'projects' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return "/%s/" % (self.slug) + + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + +class ProjectSitemap(Sitemap): + changefreq = "monthly" + priority = 0.5 + protocol = "https" + + def items(self): + return Project.objects.filter(status=1) + + def lastmod(self, obj): + return obj.pub_date diff --git a/bak/unused_apps/projects/models/fiveby.py b/bak/unused_apps/projects/models/fiveby.py new file mode 100644 index 0000000..473c095 --- /dev/null +++ b/bak/unused_apps/projects/models/fiveby.py @@ -0,0 +1,53 @@ +import datetime +from django.contrib.gis.db import models +from locations.models import Location, Region + + +def get_upload_path(self, filename): + return "images/projects/videos/5x5/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +def get_image_upload_path(self, filename): + return "images/projects/5x5/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +class FiveBy(models.Model): + title = models.CharField(max_length=200) + slug = models.SlugField(unique_for_date='pub_date') + lede = models.TextField(blank=True) + image = models.FileField(upload_to=get_image_upload_path, null=True, blank=True) + videoh264 = models.FileField(upload_to=get_upload_path, null=True, blank=True) + videoogg = models.FileField(upload_to=get_upload_path, null=True, blank=True) + vimeo_link = models.CharField(max_length=200) + youtube_link = models.CharField(max_length=200) + pub_date = models.DateTimeField('Date published') + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + point = models.PointField(null=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, null=True) + region = models.ForeignKey(Region, on_delete=models.CASCADE, null=True) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + app_label = 'projects' + verbose_name_plural = '5x5' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return "/%s/%s/%s/" % ('projects', '5x5', self.slug) + + @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 diff --git a/bak/unused_apps/projects/models/gifs.py b/bak/unused_apps/projects/models/gifs.py new file mode 100644 index 0000000..25b8734 --- /dev/null +++ b/bak/unused_apps/projects/models/gifs.py @@ -0,0 +1,26 @@ +import datetime +from django.db import models + + +def get_upload_path(self, filename): + return "images/projects/gifs/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +class AnimatedGif(models.Model): + title = models.CharField(max_length=254) + gif = models.ImageField(upload_to=get_upload_path) + slug = models.SlugField() + date_created = models.DateField('Date Created') + music_ogg = models.FileField(upload_to=get_upload_path, blank=True, null=True) + music_mp3 = models.FileField(upload_to=get_upload_path, blank=True, null=True) + + class Meta: + verbose_name_plural = "Animated Gifs" + app_label = 'projects' + ordering = ('-date_created',) + + def __str__(self): + return self.slug + + def get_absolute_url(self): + return '/projects/gifs/%s/' % (self.slug) diff --git a/bak/unused_apps/projects/models/natparks.py b/bak/unused_apps/projects/models/natparks.py new file mode 100644 index 0000000..980d9fa --- /dev/null +++ b/bak/unused_apps/projects/models/natparks.py @@ -0,0 +1,55 @@ +import datetime +from PIL import Image +from django.contrib.gis.db import models +from django.conf import settings +from jrnl.models import Entry +from photos.models import PhotoGallery +from locations.models import State + + +def get_upload_path(self, filename): + return "images/projects/np/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +class NationalParks(models.Model): + type = models.CharField(max_length=50) + name = models.CharField(max_length=254) + state = models.ForeignKey(State, on_delete=models.CASCADE, null=True) + size = models.CharField(max_length=10, null=True) + fee = models.CharField(max_length=5, null=True) + camping_fee = models.CharField(max_length=10, null=True) + url = models.CharField(max_length=250, null=True) + code = models.CharField(max_length=16) + unit_name = models.CharField(max_length=254) + date_visited_begin = models.DateField('Date Visited', null=True) + date_visited_end = models.DateField('Date Visited', null=True) + date_park_created = models.DateField('Date Park Created', null=True) + zoom = models.IntegerField(null=True) + mpoly = models.MultiPolygonField(null=True) + visited = models.BooleanField(default=False) + dek = models.TextField(null=True, blank=True) + tag_line = models.CharField(max_length=254, null=True) + post = models.ForeignKey(Entry, on_delete=models.CASCADE, null=True) + gallery = models.ForeignKey(PhotoGallery, on_delete=models.CASCADE, blank=True, null=True, verbose_name='photo set') + image = models.FileField(upload_to=get_upload_path, null=True, blank=True, + help_text="width: 980px, height: > 450px") + image_height = models.CharField(max_length=20, null=True, blank=True) + image_width = models.CharField(max_length=20, null=True, blank=True) + + class Meta: + verbose_name_plural = "National Parks" + app_label = 'projects' + ordering = ('-visited', 'unit_name',) + + def __str__(self): + return self.unit_name + + @property + def get_image_url(self): + return "%s%s" % (settings.IMAGES_URL, self.image.name[7:]) + + def save(self): + #get image dimensions + img = Image.open(self.image) + self.image_width, self.image_height = img.size + super(NationalParks, self).save() diff --git a/bak/unused_apps/projects/models/self_experiments.py b/bak/unused_apps/projects/models/self_experiments.py new file mode 100644 index 0000000..be9d0b7 --- /dev/null +++ b/bak/unused_apps/projects/models/self_experiments.py @@ -0,0 +1,56 @@ +import datetime +from django.db import models + +import markdown + +PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), +) + + +def markdown_processor(md): + return markdown.markdown(md, ['footnotes'], safe_mode=False) + + +class Experiment(models.Model): + title = models.CharField(max_length=254) + slug = models.SlugField() + date_created = models.DateField(auto_now_add=True) + date_start = models.DateField() + date_end_actual = models.DateField(blank=True, null=True) + date_end_projected = models.DateField(blank=True) + duration = models.PositiveSmallIntegerField(default=30) + status = models.IntegerField(choices=PUB_STATUS, default=0) + body_html = models.TextField(blank=True) + body_markdown = models.TextField() + + class Meta: + app_label = 'projects' + ordering = ('-date_start',) + + # Returns the string representation of the model. + def __str__(self): + return self.title + + def get_absolute_url(self): + return "/projects/experiments/%s/%s/" % (self.pub_date.strftime("%Y").lower(), self.slug) + + def days_remaining(self): + return self.date_end_projected - datetime.date.today() + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + @property + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + def comment_period_open(self): + return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date + + def save(self): + self.body_html = markdown_processor(self.body_markdown) + self.date_end_projected = self.date_start + datetime.timedelta(self.duration) + super(Experiment, self).save() diff --git a/bak/unused_apps/projects/natparks.js b/bak/unused_apps/projects/natparks.js new file mode 100644 index 0000000..8481408 --- /dev/null +++ b/bak/unused_apps/projects/natparks.js @@ -0,0 +1,94 @@ +//Utility functions for map info window +function mapit(lat,lon,zoom,id) { + map = L.map(document.getElementById("map-wrapper-"+id)); + centerCoord = new L.LatLng(lat, lon); + zoom = zoom; + L.tileLayer.provider('Esri.WorldTopoMap', {maxZoom: 18, attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Tiles © Esri and the GIS User Community'}).addTo(map); + map.setView(centerCoord, zoom); + ////get the geojson for this map + $.ajax({ + url: "/projects/data/natparks/"+id+".json", + dataType: "json", + success: function(data, text, request) { draw_poly(data, map); } + //complete: function(xhr, status) {console.log(status); return false; }, + }); + //draw the polygon + function draw_poly(data, map) { + var myStyle = { + "color": "#201a11", + "weight": 2, + "opacity": 0.65 + }; + L.geoJson(data, { + style: myStyle + }).addTo(map); + } +} + +// utility functions to create/remove map container +function create_map(obj) { + var lat = parseFloat(obj.attr('data-latitude')); + var lon = parseFloat(obj.attr('data-longitude')); + var zoom= parseInt(obj.attr('data-zoom')); + var id= obj.attr('data-id'); + + //create container divs + $(obj).parents().eq(3).append('<div class="map-container" id="map-container-'+id+'">'); + $('#map-container-'+id).append('<div class="map-wrapper" id="map-wrapper-'+id+'">'); + mapit(lat,lon,zoom,id); +} +function remove_map(id) { + $(id).remove(); +} + +//functions to handle the "more" link +// utility functions to create/remove camera info container +function get_exif(obj,id) { + //$(obj).parents().eq(2).append('<div id="exif-container">'); + $(obj).parents().eq(3).append('<div class="more-container" id="'+id+'">'); $(obj).parents().eq(2).children('.meta').clone().appendTo('#'+id).css('visibility', 'visible'); + + + + //deal with the variable height of div.legend + $('#exif-container').css({ + bottom: function(index, value) { + return parseFloat($(obj).parent().parent().css("height"))-14; + } + }); +} +function remove_exif(id) { + $('#'+id).remove(); +} +$(document).ready(function(){ +//set up click events for map button + $('.map-link').click( function() { + var more_id = 'more-container-'+$(this).parent().next().children('.more-link').attr('id').split('-')[1]; + var id = '#map-container-'+$(this).attr('data-id'); + if ($('#'+more_id).is(":visible")){ + remove_exif(more_id); + } + if ($(id).is(":visible")) { + remove_map(id); + } else { + create_map($(this)); + } + return false; + + }); + + //set up click events for more info button + $('.more-link').click( function() { + var map_id = '#map-container-'+$(this).parent().prev().children('.map-link').attr('data-id'); + var id = 'more-container-'+this.id.split('-')[1]; + if ($(map_id).is(":visible")){ + remove_map(map_id); + } + if ($('#'+id).is(":visible")) { + remove_exif(id); + } else { + get_exif(this, id); + } + return false; + }); + +}); diff --git a/bak/unused_apps/projects/shortcuts.py b/bak/unused_apps/projects/shortcuts.py new file mode 100644 index 0000000..90b1bb6 --- /dev/null +++ b/bak/unused_apps/projects/shortcuts.py @@ -0,0 +1,235 @@ +from django.contrib.gis.db.models.fields import GeometryField
+#from django.contrib.gis.gdal import Envelope
+from django.contrib.gis.geos import Polygon
+import json
+from django.http import HttpResponse
+#from django.db.models.fields.related import ManyRelatedManager
+
+# also need to check out:
+# http://code.google.com/p/dojango/source/browse/trunk/dojango/util/__init__.py#82
+
+
+# example usages:
+
+"""
+
+def a_shapes(request):
+ ids = request.GET.get('ids').split(',')
+ mimetype = 'text/plain' #'application/javascript; charset=utf8'
+ pretty_print = True
+ if ids:
+ qs = WorldBorders.objects.filter(affiliates__in=ids).annotate(num_a=Count('affiliates')).filter(num_a__gt=0)
+ else:
+ qs = WorldBorders.objects.none()
+ return render_to_geojson(qs,
+ extra_attributes=['num_a','affiliates_set'],
+ geom_attribute='point',
+ included_fields=['id','name'],
+ mimetype=mimetype,
+ proj_transform=900913,
+ pretty_print=pretty_print
+ )
+
+def responses(qs,type_='countries',pretty_print=True,mimetype='text/plain'):
+ if type_ == 'locations':
+ qs = qs.geolocations()
+ return render_to_geojson(qs,
+ excluded_fields=['json'],
+ geom_field='point',
+ proj_transform=900913,
+ mimetype=mimetype,
+ pretty_print=pretty_print
+ )
+ elif type_ == 'affiliates':
+ qs = qs.exclude(geokeywords='').attach_locations()
+ return render_to_geojson(qs,
+ included_fields=['id','_geokeywords_cache'],
+ geom_attribute='point',
+ extra_attributes=['name'],
+ proj_transform=900913,
+ mimetype=mimetype,
+ pretty_print=pretty_print
+ )
+ elif type_ == 'countries':
+ qs2 = W.objects.filter(affiliates__in=qs).annotate(num_a=Count('affiliates')).filter(num_a__gt=0)
+ return render_to_geojson(qs2,
+ extra_attributes=['num_a'],
+ #geom_attribute='point',
+ mimetype=mimetype,
+ pretty_print=pretty_print
+ )
+ else:# type_ == 'countries' or type is None:
+ if len(qs) > 10:
+ # this is a limit, weird huh?
+ # requires another all() otherwise it
+ # returns a list!
+ qs = qs.all()[:10]
+ return render_to_geojson(qs,
+ included_fields=['id','_geokeywords_cache'],
+ geom_attribute='countries.unionagg',
+ extra_attributes=['name'],
+ mimetype=mimetype,
+ pretty_print=pretty_print
+ )
+"""
+
+
+
+def render_to_geojson(query_set, geom_field=None, geom_attribute=None, extra_attributes=[],mimetype='text/plain', pretty_print=False, excluded_fields=[],included_fields=[],proj_transform=None):
+ '''
+
+ Shortcut to render a GeoJson FeatureCollection from a Django QuerySet.
+ Currently computes a bbox and adds a crs member as a sr.org link
+
+ '''
+ excluded_fields.append('_state')
+ collection = {}
+ if hasattr(query_set,'_meta'): # its a model instance
+ fields = query_set._meta.fields
+ query_set = [query_set]
+ else:
+ fields = query_set.model._meta.fields
+
+ if geom_attribute:
+ geometry_name = geom_attribute
+ geo_field = None
+ if '.' in geom_attribute:
+ prop, meth = geom_attribute.split('.')
+ if len(query_set):
+ p = getattr(query_set[0],prop)
+ geo_field = getattr(p,meth)
+ if callable(geo_field):
+ geo_field = geo_field()
+ else:
+ if len(query_set):
+ geo_field = getattr(query_set[0],geom_attribute)
+ if callable(geo_field):
+ geo_field = geo_field()
+ if not geo_field:
+ srid = 4326
+ else:
+ srid = geo_field.srid
+
+ else:
+ geo_fields = [f for f in fields if isinstance(f, GeometryField)]
+
+ #attempt to assign geom_field that was passed in
+ if geom_field:
+ #import pdb;pdb.set_trace()
+ geo_fieldnames = [x.name for x in geo_fields]
+ try:
+ geo_field = geo_fields[geo_fieldnames.index(geom_field)]
+ except:
+ raise Exception('%s is not a valid geometry on this model' % geom_field)
+ else:
+ if not len(geo_fields):
+ raise Exception('There appears to be no valid geometry on this model')
+ geo_field = geo_fields[0] # no support yet for multiple geometry fields
+
+
+ #remove other geom fields from showing up in attributes
+ if len(geo_fields) > 1:
+ for field in geo_fields:
+ if field.name not in excluded_fields:
+ excluded_fields.append(field.name)
+
+ geometry_name = geo_field.name
+
+
+ srid = geo_field.srid
+
+ if proj_transform:
+ to_srid = proj_transform
+ else:
+ to_srid = srid
+ # Gather the projection information
+ crs = {}
+ crs['type'] = "link"
+ crs_properties = {}
+ crs_properties['href'] = 'http://spatialreference.org/ref/epsg/%s/' % to_srid
+ crs_properties['type'] = 'proj4'
+ crs['properties'] = crs_properties
+ collection['crs'] = crs
+ collection['srid'] = to_srid
+
+ # Build list of features
+ features = []
+ if query_set.distinct():
+ for item in query_set:
+ feat = {}
+ feat['type'] = 'Feature'
+ if included_fields:
+ d = {}
+ for f in included_fields:
+ if hasattr(item,f):
+ d[f] = getattr(item,f)
+ else:
+ d = item.__dict__.copy()
+ for field in excluded_fields:
+ if field in d.keys():
+ d.pop(field)
+ if geometry_name in d:
+ d.pop(geometry_name)
+
+ for attr in extra_attributes:
+ a = getattr(item,attr)
+ # crappy way of trying to figure out it this is a
+ # m2m, aka 'ManyRelatedManager'
+ if hasattr(a,'values_list'):
+ a = list(a.values_list('id',flat=True))
+ if callable(a):
+ d[attr] = a()
+ else:
+ d[attr] = a
+ if '.' in geometry_name:
+ prop, meth = geometry_name.split('.')
+ a = getattr(item,prop)
+ g = getattr(a,meth)
+ if callable(g):
+ g = g()
+ else:
+ g = getattr(item,geometry_name)
+ if g:
+ if proj_transform:
+ g.transform(proj_transform)
+ feat['geometry'] = json.loads(g.geojson)
+ feat['properties'] = d
+ features.append(feat)
+ else:
+ pass #features.append({'type':'Feature','geometry': {},'properties':{}})
+
+ # Label as FeatureCollection and add Features
+ collection['type'] = "FeatureCollection"
+ collection['features'] = features
+
+ # Attach extent of all features
+ if query_set:
+ ex = None
+ query_set.query.distinct = False
+ if hasattr(query_set,'agg_extent'):
+ ex = [x for x in query_set.agg_extent.tuple]
+ elif '.' in geometry_name:
+ prop, meth = geometry_name.split('.')
+ a = getattr(item,prop)
+ if a:
+ ex = [x for x in a.extent()]
+ else:
+ # make sure qs does not have .distinct() in it...
+ ex = [x for x in query_set.extent()]
+ if ex:
+ if proj_transform:
+ poly = Polygon.from_bbox(ex)
+ poly.srid = srid
+ poly.transform(proj_transform)
+ ex = poly.extent
+ collection['bbox'] = ex
+
+ # Return response
+ response = HttpResponse()
+ if pretty_print:
+ response.write('%s' % json.dumps(collection, indent=1))
+ else:
+ response.write('%s' % json.dumps(collection))
+ response['Content-length'] = str(len(response.content))
+ response['Content-Type'] = mimetype
+ return response
diff --git a/bak/unused_apps/projects/urls.py b/bak/unused_apps/projects/urls.py new file mode 100644 index 0000000..8e56b16 --- /dev/null +++ b/bak/unused_apps/projects/urls.py @@ -0,0 +1,29 @@ +from django.conf.urls import url +from django.views.generic import ListView +from projects.models.base import Project + +from . import views + +app_name = "project" + +urlpatterns = [ + url( + r'data/(?P<id>\d+)/$', + views.data_json + ), + url( + r'gifs/(?P<slug>[-\w]+)/$', + views.gif_detail + ), + url( + r'(?P<slug>[-\w]+)/$', + views.detail + ), + url( + r'^$', + ListView.as_view( + queryset=Project.objects.filter(status__exact=1).order_by('-pub_date'), + template_name="archives/projects.html", + ) + ), +] diff --git a/bak/unused_apps/projects/views.py b/bak/unused_apps/projects/views.py new file mode 100644 index 0000000..ad5f167 --- /dev/null +++ b/bak/unused_apps/projects/views.py @@ -0,0 +1,45 @@ +from django.shortcuts import render, get_object_or_404 +from django.template import RequestContext +from django.apps import apps + +from projects.shortcuts import render_to_geojson +from projects.models.natparks import NationalParks +from projects.models.gifs import AnimatedGif + +projects = { + '5x5': 'FiveBy', + '6x6': 'SixBy', + 'national-parks': 'NationalParks', + 'code': 'Code' +} + + +def detail(request, slug): + """Projects by slug""" + name = projects[slug] + model = apps.get_model('projects', name) + if slug == 'national-parks': + qs = model.objects.filter(visited__exact=True).order_by("-date_visited_begin") + else: + qs = model.objects.filter(status__exact=1) + context = { + "object_list": qs, + } + template = 'details/%s.html' % (slug) + return render(request, template, context) + + +def gif_detail(request, slug): + obj = get_object_or_404(AnimatedGif, slug__exact=slug) + return render(request, 'details/gifs.html', {'object': obj}) + + +def data_json(request, id): + qs = NationalParks.objects.filter(pk=id) + return render_to_geojson( + qs, + included_fields=['id'], + geom_attribute='mpoly', + mimetype='application/json', + pretty_print=True + ) diff --git a/bak/unused_apps/prompts/__init__.py b/bak/unused_apps/prompts/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/prompts/__init__.py diff --git a/bak/unused_apps/prompts/admin.py b/bak/unused_apps/prompts/admin.py new file mode 100644 index 0000000..f12c904 --- /dev/null +++ b/bak/unused_apps/prompts/admin.py @@ -0,0 +1,30 @@ +from django.contrib import admin + +from utils.widgets import LGEntryForm +from .models import Prompt + + +@admin.register(Prompt) +class PromptAdmin(admin.ModelAdmin): + form = LGEntryForm + prepopulated_fields = {"slug": ('title',)} + list_display = ('title', 'source', 'prompt_type') + list_filter = ('source', 'prompt_type') + fieldsets = ( + ('Prompt', { + 'fields': ( + 'title', + 'subtitle', + 'body_markdown', + ('pub_date', 'prompt_type'), + ('slug','source'), + 'meta_description' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) diff --git a/bak/unused_apps/prompts/migrations/0001_initial.py b/bak/unused_apps/prompts/migrations/0001_initial.py new file mode 100644 index 0000000..c5a24ec --- /dev/null +++ b/bak/unused_apps/prompts/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 2.1.5 on 2019-02-20 22:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Prompt', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('subtitle', models.CharField(max_length=200)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField()), + ('pub_date', models.DateTimeField(verbose_name='Date published')), + ('meta_description', models.CharField(blank=True, max_length=256, null=True)), + ('source', models.CharField(max_length=350)), + ], + options={ + 'ordering': ('-pub_date',), + 'get_latest_by': 'pub_date', + }, + ), + ] diff --git a/bak/unused_apps/prompts/migrations/0002_auto_20190228_0941.py b/bak/unused_apps/prompts/migrations/0002_auto_20190228_0941.py new file mode 100644 index 0000000..5b66a99 --- /dev/null +++ b/bak/unused_apps/prompts/migrations/0002_auto_20190228_0941.py @@ -0,0 +1,34 @@ +# Generated by Django 2.1.7 on 2019-02-28 09:41 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('prompts', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='prompt', + name='pub_date', + field=models.DateTimeField(default=datetime.datetime.now), + ), + migrations.AlterField( + model_name='prompt', + name='slug', + field=models.SlugField(blank=True, unique_for_date='pub_date'), + ), + migrations.AlterField( + model_name='prompt', + name='source', + field=models.CharField(blank=True, max_length=350), + ), + migrations.AlterField( + model_name='prompt', + name='subtitle', + field=models.CharField(blank=True, max_length=200), + ), + ] diff --git a/bak/unused_apps/prompts/migrations/0003_prompt_prompt_type.py b/bak/unused_apps/prompts/migrations/0003_prompt_prompt_type.py new file mode 100644 index 0000000..d027c6e --- /dev/null +++ b/bak/unused_apps/prompts/migrations/0003_prompt_prompt_type.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2020-07-05 22:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('prompts', '0002_auto_20190228_0941'), + ] + + operations = [ + migrations.AddField( + model_name='prompt', + name='prompt_type', + field=models.IntegerField(choices=[(0, 'Meditation'), (1, 'Writing'), (2, 'Kids Writing')], default=0), + ), + ] diff --git a/bak/unused_apps/prompts/migrations/__init__.py b/bak/unused_apps/prompts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/prompts/migrations/__init__.py diff --git a/bak/unused_apps/prompts/models.py b/bak/unused_apps/prompts/models.py new file mode 100644 index 0000000..7b5dc71 --- /dev/null +++ b/bak/unused_apps/prompts/models.py @@ -0,0 +1,94 @@ +import datetime +from django.contrib.gis.db import models +from django.urls import reverse +from django.apps import apps + +from utils.util import render_images, markdown_to_html + + +class Source(models.Model): + name = models.CharField(max_length=200) + link = models.CharField(max_length=400) + SOURCE_TYPE = ( + (0, 'Book'), + (1, 'Website'), + (2, 'Other'), + ) + source_type = models.IntegerField(choices=SOURCE_TYPE, default=0) + + class Meta: + ordering = ('name',) + + def __str__(self): + return self.name + + +class Prompt(models.Model): + title = models.CharField(max_length=200) + subtitle = models.CharField(max_length=200, blank=True) + meta_description = models.CharField(max_length=256, null=True, blank=True) + slug = models.SlugField(unique_for_date='pub_date', blank=True) + body_markdown = models.TextField() + body_html = models.TextField(blank=True) + pub_date = models.DateTimeField(default=datetime.datetime.now) + source = models.CharField(max_length=350, blank=True) + PROMPT_TYPE = ( + (0, 'Meditation'), + (1, 'Writing'), + (2, 'Kids Writing'), + ) + prompt_type = models.IntegerField(choices=PROMPT_TYPE, default=0) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("prompt:detail", kwargs={"slug": self.slug}) + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[n.id]) + + @property + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk]) + except model.DoesNotExist: + return '' + + def save(self, *args, **kwargs): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + super(Prompt, self).save(*args, **kwargs) + + + +class Source(models.Model): + name = models.CharField(max_length=200) + link = models.CharField(max_length=400) + SOURCE_TYPE = ( + (0, 'Book'), + (1, 'Website'), + (2, 'Other'), + ) + source_type = models.IntegerField(choices=SOURCE_TYPE, default=0) + + class Meta: + ordering = ('name',) + + def __str__(self): + return self.name diff --git a/bak/unused_apps/prompts/urls.py b/bak/unused_apps/prompts/urls.py new file mode 100644 index 0000000..7cbe09a --- /dev/null +++ b/bak/unused_apps/prompts/urls.py @@ -0,0 +1,13 @@ +from django.urls import path + +from . import views + +app_name = "prompts" + +urlpatterns = [ + path( + r'', + views.RandomList.as_view(), + name="random-prompt" + ), +] diff --git a/bak/unused_apps/prompts/views.py b/bak/unused_apps/prompts/views.py new file mode 100644 index 0000000..4744299 --- /dev/null +++ b/bak/unused_apps/prompts/views.py @@ -0,0 +1,11 @@ +from django.views.generic.detail import DetailView + +from .models import Prompt + + +class PromptList(DetailView): + model = Prompt + + def get_object(self, queryset): + """ This might be faster: random.choice(Prompt.objects.all())""" + return Prompt.objects.order_by('?').first() diff --git a/bak/unused_apps/publications/__init__.py b/bak/unused_apps/publications/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/publications/__init__.py diff --git a/bak/unused_apps/publications/admin.py b/bak/unused_apps/publications/admin.py new file mode 100644 index 0000000..15456d5 --- /dev/null +++ b/bak/unused_apps/publications/admin.py @@ -0,0 +1,85 @@ +from django.contrib import admin +from utils.widgets import LGEntryForm + +from .models import Publication, Editor, Pitch, Section, PitchIdea + + +@admin.register(Publication) +class PublicationAdmin(admin.ModelAdmin): + form = LGEntryForm + list_display = ('name', 'status') + list_filter = ('status',) + search_fields = ['name'] + fieldsets = ( + ('', { + 'fields': ( + 'name', + 'url', + 'notes', + ('section', 'tags'), + ('pays', 'pays_amount'), + ('submission_period_start', 'submission_period_end'), + 'status' + ), + 'classes': ('show', 'extrapretty', 'wide') + }), + ) + + +@admin.register(Editor) +class EditorAdmin(admin.ModelAdmin): + list_display = ('last_name', 'first_name', 'publication', 'sections') + list_filter = ('publication', 'section') + search_fields = ['first_name', 'last_name', 'publication__name', 'section'] + filter_horizontal = ('section',) + + def sections(self, obj): + return [section for section in obj.section.all()] + sections.boolean = False + + fieldsets = ( + ('', { + 'fields': ('first_name', 'last_name', 'email', 'publication', 'section', 'notes', 'phone', 'twitter'), + 'classes': ('show', 'extrapretty', 'wide') + }), + ) + + +@admin.register(Pitch) +class PitchAdmin(admin.ModelAdmin): + list_display = ('title', 'date_sent', 'accepted', 'editor', 'publication') + list_filter = ('editor__publication', 'editor__section') + + def publication(self, obj): + return obj.editor.publication + publication.boolean = False + + fieldsets = ( + ('', { + 'fields': ('title', 'pitch', 'editor', 'accepted', 'date_sent'), + 'classes': ('show', 'extrapretty', 'wide') + }), + ) + + +@admin.register(PitchIdea) +class PitchIdeaAdmin(admin.ModelAdmin): + list_display = ('title', ) + + fieldsets = ( + ('', { + 'fields': ('title', 'pitch', 'publication'), + 'classes': ('show', 'extrapretty', 'wide') + }), + ) + + +@admin.register(Section) +class SectionAdmin(admin.ModelAdmin): + list_display = ('name',) + fieldsets = ( + ('', { + 'fields': ('name',), + 'classes': ('show', 'extrapretty', 'wide') + }), + ) diff --git a/bak/unused_apps/publications/migrations/0001_initial.py b/bak/unused_apps/publications/migrations/0001_initial.py new file mode 100644 index 0000000..9212571 --- /dev/null +++ b/bak/unused_apps/publications/migrations/0001_initial.py @@ -0,0 +1,73 @@ +# Generated by Django 2.1.1 on 2019-01-31 08:56 + +from django.db import migrations, models +import django.db.models.deletion +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('taxonomy', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Editor', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('first_name', models.CharField(max_length=250)), + ('last_name', models.CharField(max_length=250)), + ('email', models.CharField(blank=True, max_length=250)), + ('phone', models.CharField(blank=True, max_length=250)), + ('twitter', models.CharField(blank=True, max_length=250)), + ('slug', models.SlugField(blank=True)), + ('notes', models.TextField(blank=True)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ], + options={ + 'ordering': ('-last_name', '-date_created'), + }, + ), + migrations.CreateModel( + name='Pitch', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=250)), + ('pitch', models.TextField()), + ('slug', models.SlugField(blank=True)), + ('accepted', models.BooleanField(blank=True, null=True)), + ('date_sent', models.DateTimeField(blank=True, null=True)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('editor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='publications.Editor')), + ], + options={ + 'ordering': ('-title', '-date_sent'), + }, + ), + migrations.CreateModel( + name='Publication', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=250)), + ('slug', models.SlugField(blank=True, unique_for_date='pub_date')), + ('notes', models.TextField(blank=True)), + ('status', models.IntegerField(choices=[(0, 'Have Published With'), (1, 'Published')], default=0)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('tags', taggit.managers.TaggableManager(blank=True, help_text='Topics Covered', through='taxonomy.TaggedItems', to='taxonomy.LuxTag', verbose_name='Tags')), + ], + options={ + 'ordering': ('-name', '-date_created'), + }, + ), + migrations.AddField( + model_name='editor', + name='publication', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='publications.Publication'), + ), + ] diff --git a/bak/unused_apps/publications/migrations/0002_editor_section.py b/bak/unused_apps/publications/migrations/0002_editor_section.py new file mode 100644 index 0000000..a4675ae --- /dev/null +++ b/bak/unused_apps/publications/migrations/0002_editor_section.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.1 on 2019-01-31 09:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('publications', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='editor', + name='section', + field=models.CharField(blank=True, max_length=50), + ), + ] diff --git a/bak/unused_apps/publications/migrations/0003_auto_20190131_0923.py b/bak/unused_apps/publications/migrations/0003_auto_20190131_0923.py new file mode 100644 index 0000000..6844c55 --- /dev/null +++ b/bak/unused_apps/publications/migrations/0003_auto_20190131_0923.py @@ -0,0 +1,37 @@ +# Generated by Django 2.1.1 on 2019-01-31 09:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('publications', '0002_editor_section'), + ] + + operations = [ + migrations.CreateModel( + name='Section', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=250)), + ('slug', models.SlugField(blank=True)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ], + ), + migrations.RemoveField( + model_name='editor', + name='section', + ), + migrations.AlterField( + model_name='publication', + name='status', + field=models.IntegerField(choices=[(0, 'Not Published'), (1, 'Published')], default=0), + ), + migrations.AddField( + model_name='editor', + name='section', + field=models.ManyToManyField(blank=True, to='publications.Section'), + ), + ] diff --git a/bak/unused_apps/publications/migrations/0004_pitchidea.py b/bak/unused_apps/publications/migrations/0004_pitchidea.py new file mode 100644 index 0000000..db72d58 --- /dev/null +++ b/bak/unused_apps/publications/migrations/0004_pitchidea.py @@ -0,0 +1,28 @@ +# Generated by Django 2.1.1 on 2019-01-31 10:46 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('publications', '0003_auto_20190131_0923'), + ] + + operations = [ + migrations.CreateModel( + name='PitchIdea', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=250)), + ('pitch', models.TextField()), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('publication', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='publications.Publication')), + ], + options={ + 'ordering': ('-title', '-date_created'), + }, + ), + ] diff --git a/bak/unused_apps/publications/migrations/0005_auto_20190131_1100.py b/bak/unused_apps/publications/migrations/0005_auto_20190131_1100.py new file mode 100644 index 0000000..a800dcf --- /dev/null +++ b/bak/unused_apps/publications/migrations/0005_auto_20190131_1100.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1.1 on 2019-01-31 11:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('publications', '0004_pitchidea'), + ] + + operations = [ + migrations.AddField( + model_name='publication', + name='section', + field=models.ManyToManyField(blank=True, to='publications.Section'), + ), + migrations.AddField( + model_name='publication', + name='url', + field=models.CharField(blank=True, max_length=250), + ), + ] diff --git a/bak/unused_apps/publications/migrations/0006_auto_20190222_1819.py b/bak/unused_apps/publications/migrations/0006_auto_20190222_1819.py new file mode 100644 index 0000000..8c779dd --- /dev/null +++ b/bak/unused_apps/publications/migrations/0006_auto_20190222_1819.py @@ -0,0 +1,33 @@ +# Generated by Django 2.1.5 on 2019-02-22 18:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('publications', '0005_auto_20190131_1100'), + ] + + operations = [ + migrations.AddField( + model_name='publication', + name='pays', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='publication', + name='pays_amount', + field=models.CharField(blank=True, max_length=100), + ), + migrations.AddField( + model_name='publication', + name='submission_period_end', + field=models.DateField(blank=True, null=True), + ), + migrations.AddField( + model_name='publication', + name='submission_period_start', + field=models.DateField(blank=True, null=True), + ), + ] diff --git a/bak/unused_apps/publications/migrations/__init__.py b/bak/unused_apps/publications/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/publications/migrations/__init__.py diff --git a/bak/unused_apps/publications/models.py b/bak/unused_apps/publications/models.py new file mode 100644 index 0000000..05f7267 --- /dev/null +++ b/bak/unused_apps/publications/models.py @@ -0,0 +1,133 @@ +from django.contrib.gis.db import models +from django.utils import timezone +from django.utils.text import slugify +from django.urls import reverse + +from taggit.managers import TaggableManager + +from taxonomy.models import TaggedItems + + +class Section(models.Model): + """ Generic model for Categories """ + name = models.CharField(max_length=250) + slug = models.SlugField(blank=True) + date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False) + date_updated = models.DateTimeField(blank=True, auto_now=True, editable=False) + + def __str__(self): + return self.name + + def save(self, *args, **kwargs): + if self._state.adding: + self.slug = slugify(self.name)[:50] + super(Section, self).save() + + +class Publication(models.Model): + name = models.CharField(max_length=250) + url = models.CharField(max_length=250, blank=True) + section = models.ManyToManyField(Section, blank=True) + slug = models.SlugField(unique_for_date='pub_date', blank=True) + notes = models.TextField(blank=True) + tags = TaggableManager(through=TaggedItems, blank=True, help_text='Topics Covered') + PUB_STATUS = ( + (0, 'Not Published'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False) + date_updated = models.DateTimeField(blank=True, auto_now=True, editable=False) + pays = models.BooleanField(default=True) + pays_amount = models.CharField(max_length=100, blank=True) + submission_period_start = models.DateField(blank=True, null=True) + submission_period_end = models.DateField(blank=True, null=True) + + class Meta: + ordering = ('-name', '-date_created') + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("publications:pub-detail", kwargs={"slug": self.slug}) + + def save(self, *args, **kwargs): + if not self.id: + self.date_created = timezone.now() + self.date_updated = timezone.now() + super(Publication, self).save() + + +class Editor(models.Model): + first_name = models.CharField(max_length=250) + last_name = models.CharField(max_length=250) + email = models.CharField(max_length=250, blank=True) + phone = models.CharField(max_length=250, blank=True) + twitter = models.CharField(max_length=250, blank=True) + slug = models.SlugField(blank=True) + notes = models.TextField(blank=True) + publication = models.ForeignKey(Publication, on_delete=models.SET_NULL, blank=True, null=True) + section = models.ManyToManyField(Section, blank=True) + date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False) + date_updated = models.DateTimeField(blank=True, auto_now=True, editable=False) + + class Meta: + ordering = ('-last_name', '-date_created') + + def __str__(self): + return "{0} {1}".format(self.first_name, self.last_name) + + def get_absolute_url(self): + return reverse("publications:pub-detail", kwargs={"slug": self.slug}) + + def save(self, *args, **kwargs): + if self._state.adding: + self.slug = slugify('%s %s' % (self.first_name, self.last_name))[:50] + self.date_created = timezone.now() + self.date_updated = timezone.now() + super(Editor, self).save() + + +class Pitch(models.Model): + title = models.CharField(max_length=250) + pitch = models.TextField() + slug = models.SlugField(blank=True) + editor = models.ForeignKey(Editor, on_delete=models.SET_NULL, blank=True, null=True) + accepted = models.BooleanField(null=True, blank=True) + date_sent = models.DateTimeField(blank=True, null=True) + date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False) + date_updated = models.DateTimeField(blank=True, auto_now=True, editable=False) + + class Meta: + ordering = ('-title', '-date_sent') + + def __str__(self): + return self.title + + def save(self, *args, **kwargs): + if self._state.adding: + self.slug = slugify(self.title)[:50] + self.date_created = timezone.now() + self.date_updated = timezone.now() + super(Pitch, self).save() + + +class PitchIdea(models.Model): + title = models.CharField(max_length=250) + pitch = models.TextField() + publication = models.ForeignKey(Publication, on_delete=models.SET_NULL, blank=True, null=True) + date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False) + date_updated = models.DateTimeField(blank=True, auto_now=True, editable=False) + + class Meta: + ordering = ('-title', '-date_created') + + def __str__(self): + return self.title + + def save(self, *args, **kwargs): + if self._state.adding: + self.date_created = timezone.now() + self.date_updated = timezone.now() + super(PitchIdea, self).save() diff --git a/bak/unused_apps/publications/urls.py b/bak/unused_apps/publications/urls.py new file mode 100644 index 0000000..3f43b32 --- /dev/null +++ b/bak/unused_apps/publications/urls.py @@ -0,0 +1,24 @@ +from django.urls import path + +from . import views + +app_name = "publications" + +urlpatterns = [ + path( + r'', + views.PubListView.as_view(), + {'page': 1}, + name="list" + ), + path( + r'<int:page>/', + views.PubListView.as_view(), + name="list" + ), + path( + r'<str:slug>', + views.PubsDetailView.as_view(), + name="pub-detail" + ), +] diff --git a/bak/unused_apps/publications/views.py b/bak/unused_apps/publications/views.py new file mode 100644 index 0000000..0398dcb --- /dev/null +++ b/bak/unused_apps/publications/views.py @@ -0,0 +1,20 @@ +from django.views.generic.detail import DetailView + +from utils.views import PaginatedListView + +from .models import Publication + + +class PubListView(PaginatedListView): + """ + Return a list of Publications + """ + queryset = Publication.objects.all() + + +class PubDetailView(DetailView): + """ + Return a detail view of Publication + """ + model = Publication + slug_field = "slug" diff --git a/bak/unused_apps/recordings/__init__.py b/bak/unused_apps/recordings/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/recordings/__init__.py diff --git a/bak/unused_apps/recordings/admin.py b/bak/unused_apps/recordings/admin.py new file mode 100644 index 0000000..d972497 --- /dev/null +++ b/bak/unused_apps/recordings/admin.py @@ -0,0 +1,38 @@ +from django.contrib import admin +from django.contrib.contenttypes.admin import GenericTabularInline + +from .models import Audio +from utils.widgets import LGEntryForm, OLAdminBase +from utils.util import get_latlon + +@admin.register(Audio) +class NoteAdmin(OLAdminBase): + form = LGEntryForm + prepopulated_fields = {"slug": ('title',)} + list_display = ('title', 'pub_date', 'location') + fieldsets = ( + ('Note', { + 'fields': ( + 'title', + 'subtitle', + 'body_markdown', + 'slug', + 'pub_date', + ('mp3', 'ogg'), + 'point' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + lat, lon = get_latlon() + default_lon = lon + default_lat = lat + default_zoom = 10 + + class Media: + js = ('image-loader.js', 'next-prev-links.js') diff --git a/bak/unused_apps/recordings/migrations/0001_initial.py b/bak/unused_apps/recordings/migrations/0001_initial.py new file mode 100644 index 0000000..d70b7dc --- /dev/null +++ b/bak/unused_apps/recordings/migrations/0001_initial.py @@ -0,0 +1,34 @@ +# Generated by Django 2.1.7 on 2019-03-25 14:12 + +import datetime +from django.db import migrations, models +import recordings.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Audio', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('subtitle', models.CharField(blank=True, max_length=200)), + ('slug', models.SlugField(blank=True, unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField(blank=True)), + ('pub_date', models.DateTimeField(default=datetime.datetime.now)), + ('mp3', models.FileField(blank=True, null=True, upload_to=recordings.models.get_upload_path)), + ('ogg', models.FileField(blank=True, null=True, upload_to=recordings.models.get_upload_path)), + ], + options={ + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date',), + }, + ), + ] diff --git a/bak/unused_apps/recordings/migrations/0002_auto_20190325_1417.py b/bak/unused_apps/recordings/migrations/0002_auto_20190325_1417.py new file mode 100644 index 0000000..d5193d3 --- /dev/null +++ b/bak/unused_apps/recordings/migrations/0002_auto_20190325_1417.py @@ -0,0 +1,26 @@ +# Generated by Django 2.1.7 on 2019-03-25 14:17 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('locations', '0017_auto_20190217_1849'), + ('recordings', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='audio', + name='location', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location'), + ), + migrations.AddField( + model_name='audio', + name='point', + field=django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326), + ), + ] diff --git a/bak/unused_apps/recordings/migrations/__init__.py b/bak/unused_apps/recordings/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/recordings/migrations/__init__.py diff --git a/bak/unused_apps/recordings/models.py b/bak/unused_apps/recordings/models.py new file mode 100644 index 0000000..8da5c36 --- /dev/null +++ b/bak/unused_apps/recordings/models.py @@ -0,0 +1,71 @@ +import datetime +from django.contrib.gis.db import models +from django import forms +from django.urls import reverse +from django.apps import apps + +from utils.util import render_images, markdown_to_html + +from locations.models import Location, CheckIn + + +def get_upload_path(self, filename): + return "audio/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +class Audio(models.Model): + title = models.CharField(max_length=200) + subtitle = models.CharField(max_length=200, blank=True) + slug = models.SlugField(unique_for_date='pub_date', blank=True) + body_html = models.TextField(blank=True) + body_markdown = models.TextField(blank=True) + pub_date = models.DateTimeField(default=datetime.datetime.now) + mp3 = models.FileField(blank=True, null=True, upload_to=get_upload_path) + ogg = models.FileField(blank=True, null=True, upload_to=get_upload_path) + point = models.PointField(blank=True, null=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True, null=True) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("prompt:detail", kwargs={"slug": self.slug}) + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[n.id]) + + @property + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk]) + except model.DoesNotExist: + return '' + + def get_type(self): + return str(self.__class__.__name__) + + def save(self, *args, **kwargs): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + if not self.point: + self.point = CheckIn.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(Audio, self).save(*args, **kwargs) diff --git a/bak/unused_apps/sketches/__init__.py b/bak/unused_apps/sketches/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/sketches/__init__.py diff --git a/bak/unused_apps/sketches/admin.py b/bak/unused_apps/sketches/admin.py new file mode 100644 index 0000000..0c59867 --- /dev/null +++ b/bak/unused_apps/sketches/admin.py @@ -0,0 +1,42 @@ +from django.contrib import admin +from django.contrib.gis.admin import OSMGeoAdmin +from django.contrib.contenttypes.admin import GenericTabularInline + +from .models import Sketch +from utils.widgets import LGEntryForm, OLAdminBase +from utils.util import get_latlon + + +@admin.register(Sketch) +class SketchAdmin(OLAdminBase): + form = LGEntryForm + prepopulated_fields = {"slug": ('title',)} + list_display = ('title', 'slug', 'pub_date', 'location') + fieldsets = ( + ('Note', { + 'fields': ( + 'title', + 'subtitle', + 'body_markdown', + 'slug', + ('pub_date', 'status'), + 'point' + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + lat, lon = get_latlon() + default_lon = lon + default_lat = lat + default_zoom = 10 + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + + + diff --git a/bak/unused_apps/sketches/build.py b/bak/unused_apps/sketches/build.py new file mode 100644 index 0000000..e75b6fd --- /dev/null +++ b/bak/unused_apps/sketches/build.py @@ -0,0 +1,36 @@ +import os +from django.urls import reverse +from builder.base import BuildNew + + +class BuildSketches(BuildNew): + + def build(self): + self.build_detail_view() + self.build_list_view( + base_path=reverse("sketches:list"), + paginate_by=24 + ) + self.build_year_view("sketches:list_year") + self.build_month_view("sketches:list_month") + + def get_model_queryset(self): + return self.model.objects.all() + + def build_detail_view(self): + ''' + write out all the expenses for each trip + ''' + for obj in self.get_model_queryset(): + url = obj.get_absolute_url() + path, slug = os.path.split(url) + path = '%s/' % path + # write html + response = self.client.get(url) + print(path, slug) + self.write_file(path, response.content, filename=slug) + + +def builder(): + j = BuildSketches("sketches", "sketch") + j.build() diff --git a/bak/unused_apps/sketches/migrations/0001_initial.py b/bak/unused_apps/sketches/migrations/0001_initial.py new file mode 100644 index 0000000..1e034b4 --- /dev/null +++ b/bak/unused_apps/sketches/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 2.0.1 on 2018-02-01 20:56 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('locations', '0002_checkin'), + ] + + operations = [ + migrations.CreateModel( + name='Sketch', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=250, null=True)), + ('slug', models.SlugField(blank=True, unique_for_date='pub_date')), + ('pub_date', models.DateTimeField(default=django.utils.timezone.now)), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField(verbose_name='Note')), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=1)), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.Location')), + ], + ), + ] diff --git a/bak/unused_apps/sketches/migrations/0002_auto_20180208_0743.py b/bak/unused_apps/sketches/migrations/0002_auto_20180208_0743.py new file mode 100644 index 0000000..664fcbc --- /dev/null +++ b/bak/unused_apps/sketches/migrations/0002_auto_20180208_0743.py @@ -0,0 +1,17 @@ +# Generated by Django 2.0.1 on 2018-02-08 07:43 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sketches', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='sketch', + options={'get_latest_by': 'pub_date', 'ordering': ('-pub_date',), 'verbose_name_plural': 'sketches'}, + ), + ] diff --git a/bak/unused_apps/sketches/migrations/0003_auto_20190303_1057.py b/bak/unused_apps/sketches/migrations/0003_auto_20190303_1057.py new file mode 100644 index 0000000..87cfbf1 --- /dev/null +++ b/bak/unused_apps/sketches/migrations/0003_auto_20190303_1057.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1.7 on 2019-03-03 10:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sketches', '0002_auto_20180208_0743'), + ] + + operations = [ + migrations.AddField( + model_name='sketch', + name='subtitle', + field=models.CharField(blank=True, max_length=250), + ), + migrations.AlterField( + model_name='sketch', + name='title', + field=models.CharField(blank=True, default='', max_length=250), + preserve_default=False, + ), + ] diff --git a/bak/unused_apps/sketches/migrations/__init__.py b/bak/unused_apps/sketches/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/sketches/migrations/__init__.py diff --git a/bak/unused_apps/sketches/models.py b/bak/unused_apps/sketches/models.py new file mode 100644 index 0000000..f6df269 --- /dev/null +++ b/bak/unused_apps/sketches/models.py @@ -0,0 +1,89 @@ +import re +from django.contrib.gis.db import models +from django.utils import timezone +from django.conf import settings +from django import forms +from locations.models import Location +from django.urls import reverse + +from utils.util import render_images, parse_image, markdown_to_html +from locations.models import CheckIn + +def render_images(s): + s = re.sub('<img(.*)/>', parse_image, s) + return s + +class Sketch(models.Model): + title = models.CharField(max_length=250, blank=True) + subtitle = models.CharField(max_length=250,blank=True) + slug = models.SlugField(unique_for_date='pub_date', blank=True) + pub_date = models.DateTimeField(default=timezone.now) + body_html = models.TextField(blank=True) + body_markdown = models.TextField('Note') + point = models.PointField(blank=True, null=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True, null=True) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=1) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + verbose_name_plural = 'sketches' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("sketches:detail", kwargs={"year": self.pub_date.year, "month": self.pub_date.strftime("%m"), "slug": self.slug}) + + @property + def region(self): + return self.location.state.country.lux_region + + @property + def longitude(self): + '''Get the site's longitude.''' + return round(self.point.x, 2) + + @property + def latitude(self): + '''Get the site's latitude.''' + return round(self.point.y, 2) + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date() + + @property + def get_next_published(self): + return self.get_next_by_pub_date() + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + except model.DoesNotExist: + return '' + + def save(self, *args, **kwargs): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + if not self.point: + self.point = CheckIn.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)) + if not self.id: + self.pub_date= timezone.now() + self.date_last_updated = timezone.now() + super(Sketch, self).save() diff --git a/bak/unused_apps/sketches/urls.py b/bak/unused_apps/sketches/urls.py new file mode 100644 index 0000000..465a0d2 --- /dev/null +++ b/bak/unused_apps/sketches/urls.py @@ -0,0 +1,39 @@ +from django.urls import path, re_path + +from . import views + +app_name = "sketches" + +urlpatterns = [ + re_path( + r'(?P<year>[0-9]{4})/$', + views.SketchYearArchiveView.as_view(), + name="list_year" + ), + path( + r'', + views.SketchListView.as_view(), + {'page': 1}, + name="list" + ), + path( + r'<int:page>/', + views.SketchListView.as_view(), + name="list" + ), + path( + r'<int:year>/<int:month>/<str:slug>.txt', + views.SketchDetailViewTXT.as_view(), + name="detail-txt" + ), + path( + r'<int:year>/<int:month>/<str:slug>', + views.SketchDetailView.as_view(), + name="detail" + ), + path( + r'<int:year>/<int:month>/', + views.SketchMonthArchiveView.as_view(month_format='%m'), + name="list_month" + ), +] diff --git a/bak/unused_apps/sketches/views.py b/bak/unused_apps/sketches/views.py new file mode 100644 index 0000000..b696932 --- /dev/null +++ b/bak/unused_apps/sketches/views.py @@ -0,0 +1,40 @@ +from django.views.generic.dates import YearArchiveView, MonthArchiveView +from django.views.generic.detail import DetailView + +from utils.views import PaginatedListView + +from .models import Sketch + + +class SketchListView(PaginatedListView): + """ + Return a list of Notes in reverse chronological order + """ + queryset = Sketch.objects.all().order_by('-pub_date') + template_name = "archives/sketches.html" + + +class SketchDetailView(DetailView): + model = Sketch + template_name = "details/note.html" + slug_field = "slug" + + +class SketchDetailViewTXT(SketchDetailView): + template_name = "details/entry.txt" + + +class SketchYearArchiveView(YearArchiveView): + queryset = Sketch.objects.all() + date_field = "pub_date" + make_object_list = True + allow_future = True + template_name = "archives/notes_date.html" + + +class SketchMonthArchiveView(MonthArchiveView): + queryset = Sketch.objects.all() + date_field = "pub_date" + allow_future = True + template_name = "archives/notes_date.html" + diff --git a/bak/unused_apps/src/__init__.py b/bak/unused_apps/src/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/src/__init__.py diff --git a/bak/unused_apps/src/admin.py b/bak/unused_apps/src/admin.py new file mode 100644 index 0000000..f354b15 --- /dev/null +++ b/bak/unused_apps/src/admin.py @@ -0,0 +1,62 @@ +from django.contrib import admin +from .models import Topic, SrcPost, Book +from utils.widgets import LGEntryForm + + +@admin.register(Topic) +class TopicAdmin(admin.ModelAdmin): + prepopulated_fields = {"slug": ('name',), "pluralized_name": ('name',)} + + +@admin.register(Book) +class BookAdmin(admin.ModelAdmin): + prepopulated_fields = {"slug": ('title', )} + list_display = ('title', 'pub_date', 'status') + fieldsets = ( + ('Entry', { + 'fields': ( + 'title', + 'body_markdown', + 'image', + ('pub_date', 'status'), + ('price', 'price_sale'), + 'meta_description', + ('slug', 'template_name', 'pages'), + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + + +@admin.register(SrcPost) +class PostAdmin(admin.ModelAdmin): + form = LGEntryForm + list_display = ('title', 'pub_date', 'enable_comments', 'status') + list_filter = ('pub_date', 'enable_comments', 'status') + prepopulated_fields = {"slug": ('title',)} + fieldsets = ( + ('Entry', { + 'fields': ( + 'title', + 'body_markdown', + ('pub_date', 'status'), + 'topics', + 'meta_description', + ('slug', 'enable_comments', 'has_code', 'template_name'), + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ) + + class Media: + js = ('image-loader.js', 'next-prev-links.js') diff --git a/bak/unused_apps/src/build.py b/bak/unused_apps/src/build.py new file mode 100644 index 0000000..a16bdaf --- /dev/null +++ b/bak/unused_apps/src/build.py @@ -0,0 +1,62 @@ +import os +from builder.base import BuildNew +from django.urls import reverse +from . import models + + +class BuildSrc(BuildNew): + + def build(self): + self.build_list_view( + base_path=reverse("src:list"), + paginate_by=99999 + ) + self.build_list_view( + base_path=reverse("src:list_books"), + paginate_by=99999 + ) + self.build_detail_view() + # These are the unique classes for this model: + # self.build_books_view() + self.build_topic_view() + self.build_feed("src:feed") + + def build_topic_view(self): + for topic in models.Topic.objects.all(): + ctype = ContentType.objects.get(app_label='posts', model='post') + for cat in Category.objects.all(): + + url = reverse("src:list_topics", kwargs={'slug': topic.slug, }) + path, slug = os.path.split(url) + response = self.client.get(url, HTTP_HOST='127.0.0.1') + self.write_file('%s/' % path, response.content, filename=slug) + + def build_books_view(self): + for obj in models.Book.objects.all(): + url = reverse("src:detail_book", kwargs={'slug': obj.slug, }) + path, slug = os.path.split(url) + response = self.client.get(url, HTTP_HOST='127.0.0.1') + self.write_file('%s/' % path, response.content, filename=slug) + + +def builder(): + j = BuildSrc("src", "srcpost") + j.build() + + +""" + + + + + def build_books(self): + path = 'src/books/' + c = Context({ + 'object_list': Book.objects.filter(status__exact=1), + 'MEDIA_URL': settings.BAKED_MEDIA_URL, + 'IMAGES_URL': settings.BAKED_IMAGES_URL + }) + t = render_to_string('archives/src_books.html', c).encode('utf-8') + self.write_file(path, t) + +""" diff --git a/bak/unused_apps/src/migrations/0001_initial.py b/bak/unused_apps/src/migrations/0001_initial.py new file mode 100644 index 0000000..1f672ee --- /dev/null +++ b/bak/unused_apps/src/migrations/0001_initial.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-29 21:06 +from __future__ import unicode_literals + +from django.db import migrations, models +import src.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Book', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('image', models.FileField(blank=True, null=True, upload_to=src.models.get_upload_path)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField()), + ('pub_date', models.DateTimeField(verbose_name='Date published')), + ('last_updated', models.DateTimeField(auto_now=True)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=0)), + ('price', models.FloatField()), + ('price_sale', models.FloatField()), + ('meta_description', models.CharField(blank=True, max_length=256, null=True)), + ('pages', models.DecimalField(blank=True, decimal_places=0, max_digits=3, null=True)), + ('template_name', models.CharField(choices=[('details/src_book.html', 'Default'), ('details/src_book_2.html', 'Book Two')], default='details/src_book.html', max_length=200)), + ], + options={ + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date',), + }, + ), + migrations.CreateModel( + name='Entry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('body_html', models.TextField(blank=True)), + ('body_markdown', models.TextField()), + ('pub_date', models.DateTimeField(verbose_name='Date published')), + ('last_updated', models.DateTimeField(auto_now=True)), + ('enable_comments', models.BooleanField(default=False)), + ('has_code', models.BooleanField(default=False)), + ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=0)), + ('meta_description', models.CharField(blank=True, max_length=256, null=True)), + ('template_name', models.IntegerField(choices=[(0, 'default')], default=0)), + ], + options={ + 'get_latest_by': 'pub_date', + 'ordering': ('-pub_date',), + 'verbose_name_plural': 'entries', + }, + ), + migrations.CreateModel( + name='Topic', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=60)), + ('slug', models.SlugField()), + ('pluralized_name', models.CharField(max_length=60)), + ], + ), + migrations.AddField( + model_name='entry', + name='topics', + field=models.ManyToManyField(blank=True, to='src.Topic'), + ), + ] diff --git a/bak/unused_apps/src/migrations/0002_auto_20160329_2107.py b/bak/unused_apps/src/migrations/0002_auto_20160329_2107.py new file mode 100644 index 0000000..25f5e4e --- /dev/null +++ b/bak/unused_apps/src/migrations/0002_auto_20160329_2107.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-29 21:07 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('src', '0001_initial'), + ] + + operations = [ + migrations.RenameModel( + old_name='Entry', + new_name='Post', + ), + ] diff --git a/bak/unused_apps/src/migrations/0003_auto_20180707_0958.py b/bak/unused_apps/src/migrations/0003_auto_20180707_0958.py new file mode 100644 index 0000000..f619888 --- /dev/null +++ b/bak/unused_apps/src/migrations/0003_auto_20180707_0958.py @@ -0,0 +1,17 @@ +# Generated by Django 2.0.1 on 2018-07-07 09:58 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('src', '0002_auto_20160329_2107'), + ] + + operations = [ + migrations.AlterModelOptions( + name='post', + options={'get_latest_by': 'pub_date', 'ordering': ('-pub_date',), 'verbose_name_plural': 'posts'}, + ), + ] diff --git a/bak/unused_apps/src/migrations/0004_auto_20191007_0905.py b/bak/unused_apps/src/migrations/0004_auto_20191007_0905.py new file mode 100644 index 0000000..6c223a0 --- /dev/null +++ b/bak/unused_apps/src/migrations/0004_auto_20191007_0905.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.6 on 2019-10-07 09:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('src', '0003_auto_20180707_0958'), + ] + + operations = [ + migrations.RenameModel( + old_name='Post', + new_name='SrcPost', + ), + ] diff --git a/bak/unused_apps/src/migrations/__init__.py b/bak/unused_apps/src/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/src/migrations/__init__.py diff --git a/bak/unused_apps/src/models.py b/bak/unused_apps/src/models.py new file mode 100644 index 0000000..69d85a5 --- /dev/null +++ b/bak/unused_apps/src/models.py @@ -0,0 +1,171 @@ +import re +from django.db import models +from django.urls import reverse +from django.contrib.sitemaps import Sitemap +from django.conf import settings +import datetime +from itertools import chain + +from utils.util import parse_image, markdown_to_html + + +def render_images(s): + s = re.sub('<img(.*)/>', parse_image, s) + return s + + +class Topic(models.Model): + name = models.CharField(max_length=60) + slug = models.SlugField() + pluralized_name = models.CharField(max_length=60) + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse('src:list_topics', kwargs={"slug": self.slug}) + + @property + def pub_date(self): + return datetime.datetime.now() + + +class SrcPost(models.Model): + title = models.CharField(max_length=200) + slug = models.SlugField(unique_for_date='pub_date') + body_html = models.TextField(blank=True) + body_markdown = models.TextField() + pub_date = models.DateTimeField('Date published') + topics = models.ManyToManyField(Topic, blank=True) + last_updated = models.DateTimeField(auto_now=True) + enable_comments = models.BooleanField(default=False) + has_code = models.BooleanField(default=False) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + meta_description = models.CharField(max_length=256, null=True, blank=True) + TEMPLATES = ( + (0, 'default'), + ) + template_name = models.IntegerField(choices=TEMPLATES, default=0) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + verbose_name_plural = 'posts' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse('src:detail', kwargs={"slug": self.slug}) + + def comment_period_open(self): + return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1) + + @property + def get_next_published(self): + return self.get_next_by_pub_date(status__exact=1) + + def save(self): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + super(SrcPost, self).save() + + +def get_upload_path(self, filename): + return "images/src/%s" % filename + + +class Book(models.Model): + title = models.CharField(max_length=200) + image = models.FileField(blank=True, null=True, upload_to=get_upload_path) + slug = models.SlugField(unique_for_date='pub_date') + body_html = models.TextField(blank=True) + body_markdown = models.TextField() + pub_date = models.DateTimeField('Date published') + last_updated = models.DateTimeField(auto_now=True) + PUB_STATUS = ( + (0, 'Draft'), + (1, 'Published'), + ) + status = models.IntegerField(choices=PUB_STATUS, default=0) + price = models.FloatField() + price_sale = models.FloatField() + meta_description = models.CharField(max_length=256, null=True, blank=True) + pages = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True) + DEFAULT = 'details/src_book.html' + BOOK2 = 'details/src_book_2.html' + TEMPLATES = ( + (DEFAULT, 'Default'), + (BOOK2, 'Book Two'), + ) + template_name = models.CharField( + max_length=200, + choices=TEMPLATES, + default=DEFAULT + ) + + class Meta: + ordering = ('-pub_date',) + get_latest_by = 'pub_date' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse('src:detail_book', kwargs={"slug": self.slug}) + + def get_image_url(self): + img = self.image.url.split('src/')[1] + return '%ssrc/%s' % (settings.IMAGES_URL, img) + + def save(self): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + super(Book, self).save() + + +'''class SrcDemo(models.Model): + title = models.CharField(max_length=254) + slug = models.SlugField() + body = models.TextField(blank=True, null=True) + head = models.TextField(blank=True, null=True) + DEMO_TEMPLATES = ( + (0, 'Blank'), + (1, 'Basic_light'), + ) + template = models.IntegerField(choices=DEMO_TEMPLATES, default=0) + pub_date = models.DateTimeField('Date published', blank=True) + + class Meta: + verbose_name_plural = "Demos" + app_label = 'projects' + ordering = ('-pub_date',) + + def save(self): + if not self.id: + self.pub_date = datetime.datetime.now() + super(SrcDemo, self).save() +''' + + +class SrcSitemap(Sitemap): + changefreq = "never" + priority = 0.7 + protocol = "https" + + def items(self): + return list(chain( + SrcPost.objects.filter(status=1), + Topic.objects.all() + )) + + def lastmod(self, obj): + return obj.pub_date diff --git a/bak/unused_apps/src/templates/src/srcpost_detail.html b/bak/unused_apps/src/templates/src/srcpost_detail.html new file mode 100644 index 0000000..733f586 --- /dev/null +++ b/bak/unused_apps/src/templates/src/srcpost_detail.html @@ -0,0 +1,112 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load comments %} +{% block pagetitle %}{{object.title|striptags}} - by Scott Gilbertson{% endblock %} +{% block metadescription %}{% autoescape on %}{{object.meta_description|striptags|safe}}{% endautoescape %}{% endblock %} +{%block extrahead%} + <meta property="og:type" content="article" /> + <meta property="og:site_name" content="luxagraf:src"/> + <meta property="og:title" content="{{object.title|safe}}" /> + <meta property="og:url" content="https://luxagraf.net{{object.get_absolute_url}}" /> + <meta property="og:image" content=""> + <meta property="og:description" content="{{object.meta_description}}" /> + <meta property="article:published_time" content="{{object.pub_date|date:'c'}}" /> + <meta property="article:author" content="Luxagraf" /> + <meta property="og:site_name" content="Luxagraf:src" /> + <meta property="og:locale" content="en_US" /> + <meta name="twitter:card" content="summary_large_image"/> + <meta name="twitter:site" content="@luxagraf"/> + <meta name="twitter:creator" content="@luxagraf"/> + <link rel="stylesheet" href="/media/src/solarized.css" type="text/css" media="screen"/> +{%endblock%} + +{% block bodyid %}class="src detail single"{% endblock %} +{%block sitesubtitle %}Code Slowly{% endblock%} +{% block breadcrumbs %}<ol class="bl" id="breadcrumbs" itemscope itemtype="http://schema.org/BreadcrumbList"> + <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"> + <a itemprop="item" href="/"> + <span itemprop="name">Home</span> + </a> → + <meta itemprop="position" content="1" /> + </li> + <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"> + <a href="/src/" itemprop="item"> + <span itemprop="name">Src</span> + </a> + <meta itemprop="position" content="2" /> + </li> + </ol>{% endblock %} +{% block primary %}<main role="main"> + <article class="hentry post-article{% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %} post--article--double{%endif%}{%endwith%}" itemscope itemType="http://schema.org/Article"> + <header id="header" class="post-header {% with object.get_template_name_display as t %}{%if t == "double" or t == "double-dark" %}post--header--double{%endif%}{%endwith%}"> + <h1 class="p-name entry-title post--title" itemprop="headline">{%if object.template_name == 1 or object.template_name == 3 %}{{object.title|safe|smartypants}}{%else%}{{object.title|safe|smartypants|widont}}{%endif%}</h1> + <h2 class="post-subtitle">{{object.meta_description|smartypants|safe}}</h2> + <div class="post-linewrapper"> + {% if object.originally_published_by %}<h4 class="post-source">Originally Published By: <a href="{{object.originally_published_by_url}}" title="View {{object.title}} on {{object.originally_published_by}}">{{object.originally_published_by}}</a></h4>{%endif%} + {% if object.topics.all %}<h4 class="post-source">Topics: {% for topic in object.topics.all%} <a href="/src/topic/{{topic.slug}}">{{topic.name}}</a>{%if forloop.last%}{%else%}, {%endif%}{%endfor%}</h4>{%endif%} + <time class="dt-published published dt-updated post-date" datetime="{{object.pub_date|date:'c'}}" itemprop="datePublished">{{object.pub_date|date:"F"}} <span>{{object.pub_date|date:"j, Y"}}</span></time> + <span class="hide" itemprop="author" itemscope itemtype="http://schema.org/Person">by <a class="p-author h-card" href="/about"><span itemprop="name">Scott Gilbertson</span></a></span> + </div> + </header> + + + <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> + </article> + {% if object.slug != 'about' %} + {% with object.get_next_published as next %} + {% with object.get_previous_published as prev %} + <nav id="page-navigation"> + <ul>{% if prev%} + <li id="prev"><span class="bl">Previous:</span> + <a href="{{ prev.get_absolute_url }}" rel="prev" title=" {{prev.title}}">{{prev.title|safe}}</a> + </li>{%endif%}{% if next%} + <li id="next"><span class="bl">Next:</span> + <a href="{{ next.get_absolute_url }}" rel="next" title=" {{next.title}}">{{next.title|safe}}</a> + </li>{%endif%} + </ul> + </nav>{%endwith%}{%endwith%} + {%endif%} + </main> + {% if object.slug != 'about' %} + {% if object.enable_comments %} +{% get_comment_count for object as comment_count %} +{%if comment_count > 0 %} +<p class="comments--header">{{comment_count}} Comment{{ comment_count|pluralize }}</p> +{% render_comment_list for object %} +{%endif%} +<div class="comment--form--wrapper {%if comment_count > 0%}comment-form-border{%endif%}"> +{% render_comment_form for object %} +</div> +{% else %} +<p class="comments--header" style="text-align: center">Sorry, comments have been disabled for this post.</p> +{%endif%} +{%endif%} +{% endblock %} +{% block js %} +<script type="text/javascript"> +window.onload = function() { + {% if object.enable_comments %} +{% get_comment_count for object as comment_count %} +{%if comment_count > 0 %} + //delay loading of gravatar images using noscript data-hash attribute + dataattr = document.getElementsByClassName("datahashloader"); + for(var i=0; i<dataattr.length; i++) { + var c = dataattr[i].parentNode; + var img = document.createElement("img"); + img.src = 'https://images.luxagraf.net/gravcache/' + dataattr[i].getAttribute('data-hash') + '.jpg'; + img.className += "gravatar"; + c.insertBefore(img, c.childNodes[3]); + } +{%endif%} +{%endif%} + {% with object.get_template_name_display as t %}{%if t == "single" or t == "single-dark" %} + createMap(); + var open = false; + {%endif%}{%endwith%} +} +</script> +{% if object.has_code %} +{%endif %} +{% endblock %} diff --git a/bak/unused_apps/src/templates/src/srcpost_list.html b/bak/unused_apps/src/templates/src/srcpost_list.html new file mode 100644 index 0000000..dd5d410 --- /dev/null +++ b/bak/unused_apps/src/templates/src/srcpost_list.html @@ -0,0 +1,30 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load comments %} + +{% block pagetitle %}Tutorials and tools for building great things{% endblock %} +{% block metadescription %}Tutorials and tools for building great things on the web - by Scott Gilbertson.{% endblock %} +{%block sitesubtitle %}Code Slowly{% endblock%} +{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +{% block primary %}<main role="main" id="essay-archive" class="essay-archive archive-list"> + <div class="essay-intro"> + <h2>Tutorials and tools for building great things on the web.</h2> + <p>The indie web is an amazing democratic publishing platform unlike anything in history. The catch is, to avoid serving at the pleasure of the corporate king, you need to know <em>how</em> to publish. That's what these articles are here for, to help you learn how to use independent, community supported open source tools. The web won't last forever, let's build something cool while we can.</p> + <p>Topics include HTML, CSS, Django, Linux, Nginx, Python, Postgresql, free software, and, once, the evil that is Google AMP.</p> + <p>A few of the articles below were previously published in: <em><a href="https://arstechnica.com/">Ars Technica</a></em>, <em><a href="https://www.wired.com/author/scott-gilbertson/">Wired</a></em>, and <em><a href="https://www.theregister.co.uk/Author/Scott-Gilbertson/">The Register</a></em></p> + </div> + <h1 class="topic-hed">Articles</h1> + <ul class="fancy-archive-list">{% for object in object_list %}{% if object.slug != 'about' %} + <li class="h-entry hentry" itemscope itemType="http://schema.org/Article"> + <span class="date dt-published">{{object.pub_date|date:"F Y"}}</span> + <a href="{{object.get_absolute_url}}"> + <h2>{{object.title|safe|smartypants|widont}}</h2> + <p class="p-summary">{{object.meta_description|safe|smartypants|widont}}</p> + </a> + </li> + {%endif%}{%endfor%}</ul> + + + + </main> +{%endblock%} diff --git a/bak/unused_apps/src/templates/src/topic_list.html b/bak/unused_apps/src/templates/src/topic_list.html new file mode 100644 index 0000000..7149823 --- /dev/null +++ b/bak/unused_apps/src/templates/src/topic_list.html @@ -0,0 +1,35 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load comments %} + +{% block pagetitle %}Tutorials and tools for building great things{% endblock %} + +{% block metadescription %}Tutorials about {{topic}} - by Scott Gilbertson.{% endblock %} +{%block sitesubtitle %}Code Slowly{% 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 topic %} + <li><a href="/src/" title="luxagraf:src homepage" itemprop="url"><span itemprop="title"><code>src</code></span></a> → </li> + <li>{{topic}}</li>{%else%} + <li><code>src</code></li>{%endif%} + </ul> + <main role="main" id="essay-archive" class="essay-archive archive-list"> + <div class="essay-intro"> + <h2>Tutorials and tools for building great things on the web.</h2> + <p>The indie web is an amazing democratic publishing platform unlike anything in history. The catch is, to avoid serving at the pleasure of the corporate king, you need to know <em>how</em> to publish. That's what these articles are here for, to help you learn how to use independent, community supported open source tools. The web won't last forever, let's build something cool while we can.</p> + <p>A few of the articles below were previously published in: <em><a href="https://arstechnica.com/">Ars Technica</a></em>, <em><a href="https://www.wired.com/author/scott-gilbertson/">Wired</a></em>, and <em><a href="https://www.theregister.co.uk/Author/Scott-Gilbertson/">The Register</a></em></p> + </div> + <h1 class="topic-hed">Tutorials about {{topic}}</h1> + <ul class="fancy-archive-list">{% for object in object_list %}{% if object.slug != 'about' %} + <li class="h-entry hentry" itemscope itemType="http://schema.org/Article"> + <span class="date dt-published">{{object.pub_date|date:"F Y"}}</span> + <a href="{{object.get_absolute_url}}"> + <h2>{{object.title|safe|smartypants|widont}}</h2> + <p class="p-summary">{{object.meta_description|safe|smartypants|widont}}</p> + </a> + </li> + {%endif%}{%endfor%}</ul> + + + + </main> +{%endblock%} diff --git a/bak/unused_apps/src/urls.py b/bak/unused_apps/src/urls.py new file mode 100644 index 0000000..0ac6897 --- /dev/null +++ b/bak/unused_apps/src/urls.py @@ -0,0 +1,49 @@ +from django.urls import path, re_path + +from . import views + +app_name = "src" + +urlpatterns = [ + path( + r'feed.xml', + views.SrcRSSFeedView(), + name="feed" + ), + path( + r'topic/<str:slug>', + views.TopicListView.as_view(), + name="list_topics" + ), + path( + r'books/<str:slug>', + views.BookDetailView.as_view(), + name='detail_book' + ), + path( + r'books/', + views.BookListView.as_view(), + name='list_books' + ), + path( + r'<str:slug>.txt', + views.EntryDetailViewTXT.as_view(), + name="detail-txt" + ), + path( + r'<str:slug>', + views.EntryDetailView.as_view(), + name="detail" + ), + re_path( + r'<int:page>', + views.SrcListView.as_view(), + name="list" + ), + path( + r'', + views.SrcListView.as_view(), + {'page':1}, + name="list" + ), +] diff --git a/bak/unused_apps/src/views.py b/bak/unused_apps/src/views.py new file mode 100644 index 0000000..7540e02 --- /dev/null +++ b/bak/unused_apps/src/views.py @@ -0,0 +1,89 @@ +from django.views.generic import ListView +from django.views.generic.detail import DetailView +from django.contrib.syndication.views import Feed +from django.urls import reverse +from django.conf import settings + +#from paypal.standard.forms import PayPalPaymentsForm +from utils.views import PaginatedListView +from .models import SrcPost, Topic, Book + + +class BookListView(ListView): + template_name = "archives/src_books.html" + + def queryset(self): + return Book.objects.filter(status__exact=1) + + +class BookDetailView(DetailView): + model = Book + + def get_template_names(self): + book = self.get_object() + return [book.template_name] + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(BookDetailView, self).get_context_data(**kwargs) + book = self.get_object() + if book.price_sale < book.price: + price = book.price_sale + else: + price = book.price + #paypal_dict = { + # "business": settings.PAYPAL_RECEIVER_EMAIL, + # "amount": price, + # "item_name": book.title, + # "invoice": "unique-invoice-id", + # "notify_url": "https://luxagraf.net/src/paypal/" + reverse('src:paypal-ipn'), + # "return_url": "https://luxagraf.net/src/thank-you", + # "cancel_return": "https://luxagraf.net/src/books/", + #} + #context['paypal_form'] = PayPalPaymentsForm(initial=paypal_dict) + return context + + +class SrcListView(PaginatedListView): + model = SrcPost + + def get_queryset(self): + return SrcPost.objects.filter(status__exact=1) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(SrcListView, self).get_context_data(**kwargs) + context['topics'] = Topic.objects.all() + return context + + +class EntryDetailView(DetailView): + model = SrcPost + slug_field = "slug" + + +class EntryDetailViewTXT(EntryDetailView): + template_name = "jrnl/entry_detail.txt" + + +class TopicListView(ListView): + template_name = 'src/topic_list.html' + + def get_queryset(self): + return SrcPost.objects.filter(topics__slug=self.kwargs['slug']) + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(TopicListView, self).get_context_data(**kwargs) + context['topic'] = Topic.objects.get(slug__exact=self.kwargs['slug']) + return context + + +class SrcRSSFeedView(Feed): + title = "luxagraf:src Code and Technology" + link = "/src/" + description = "Latest postings to luxagraf.net/src" + description_template = 'feeds/blog_description.html' + + def items(self): + return SrcPost.objects.filter(status__exact=1).order_by('-pub_date')[:10] diff --git a/bak/unused_apps/stuff/__init__.py b/bak/unused_apps/stuff/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/stuff/__init__.py diff --git a/bak/unused_apps/stuff/admin.py b/bak/unused_apps/stuff/admin.py new file mode 100644 index 0000000..135b17b --- /dev/null +++ b/bak/unused_apps/stuff/admin.py @@ -0,0 +1,56 @@ +from django.contrib import admin +from .models import Item + + +@admin.register(Item) +class ItemAdmin(admin.ModelAdmin): + list_display = ( + 'title', + 'admin_thumbnail', + 'item_type', + 'creator', + 'purchase_price', + 'item_format', + 'isbn' + ) + list_filter = ['item_format', 'item_type'] + + fieldsets = ( + ('Item', { + 'fields': ( + 'title', + ('date_purchased', 'date_added'), + 'item_format', + 'purchase_price', + 'image', + 'item_type', + 'creator', + 'serial_number', + 'isbn', + ), + 'classes': ( + 'show', + 'extrapretty', + 'wide' + ) + } + ), + ('Extra', { + 'fields': ( + 'creator_sort_last_name', + 'creator_sort_first_name', + 'current_price', + 'retail_price', + 'amazon_url', + 'image_url', + ), + 'classes': ( + 'collapse', + 'extrapretty', + 'wide' + ) + }), + ) + + class Media: + js = ('image-loader.js', 'next-prev-links.js') diff --git a/bak/unused_apps/stuff/models.py b/bak/unused_apps/stuff/models.py new file mode 100644 index 0000000..05a7f4f --- /dev/null +++ b/bak/unused_apps/stuff/models.py @@ -0,0 +1,65 @@ +from django.db import models +from django.urls import reverse +from django.utils.html import format_html +from django.utils import timezone +from django.conf import settings + + +class Item(models.Model): + isbn = models.CharField(max_length=100, blank=True, null=True) + serial_number = models.CharField(max_length=100, blank=True, null=True) + ITEM_TYPES = ( + (0, 'Book'), + (1, 'Apparel'), + (2, 'Gadget'), + (3, 'Furniture'), + ) + item_type = models.IntegerField(choices=ITEM_TYPES, default=0) + title = models.CharField(max_length=200) + creator = models.CharField(max_length=200, blank=True, null=True) + creator_sort_last_name = models.CharField(max_length=200, blank=True, null=True) + creator_sort_first_name = models.CharField(max_length=200, blank=True, null=True) + current_price = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) + retail_price = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) + purchase_price = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True) + amazon_url = models.CharField(max_length=400, blank=True, null=True) + image_url = models.CharField(max_length=400, blank=True, null=True) + image = models.FileField(upload_to='stuff/', null=True, blank=True) + item_format = models.CharField(max_length=255, blank=True, null=True) + date_released = models.DateTimeField(blank=True, null=True) + date_purchased = models.DateTimeField(blank=True, null=True, default=timezone.now) + date_added = models.DateTimeField(blank=True, null=True, default=timezone.now) + + class Meta: + ordering = ['-date_added'] + + def __str__(self): + return self.title + + @property + def get_previous_published(self): + return self.get_previous_by_date_added(status__exact=1) + + @property + def get_next_published(self): + return self.get_next_by_date_added(status__exact=1) + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_date_added() + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[n.id]) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[self.get_next_by_date_added().pk]) + except model.DoesNotExist: + return '' + + def get_image_url(self): + return '%s%s' % (settings.MEDIA_URL, self.image) + + def admin_thumbnail(self): + return format_html('<img src="%s" width="100" style="width:100px">' % (self.get_image_url())) + admin_thumbnail.short_description = 'Thumbnail' diff --git a/bak/unused_apps/stuff/temp.py b/bak/unused_apps/stuff/temp.py new file mode 100644 index 0000000..2150563 --- /dev/null +++ b/bak/unused_apps/stuff/temp.py @@ -0,0 +1,115 @@ +from stuff.models import Item +from django.conf import settings +from datetime import datetime +from decimal import Decimal +import csv +item_types = { 'Book':0, 'Apparel': 1,'Gadget': 2, 'Furniture': 3} +""" +csv_file = csv.DictReader(open('stuff.csv', 'rb'), delimiter=',') +for line in csv_file: + if line['release date'][:-6] != '': + date_released = datetime.strptime(line['release date'][:-6], "%Y-%m-%d %H:%M:%S") + else: + date_released = None + if line['purchase date'][:-6] != '': + date_purchased = datetime.strptime(line['purchase date'][:-6], "%Y-%m-%d %H:%M:%S") + else: + date_purchased = None + if line['creation date'][:-6] != '': + date_added = datetime.strptime(line['creation date'][:-6], "%Y-%m-%d %H:%M:%S") + else: + date_added = None + item_type = int(item_types[line['item type']]) + if line['current value'][1:] != '': + try: + current_price = Decimal(line['current value'][1:].replace(",", "")) + except: + current_price = None + else: + current_price = None + if line['retail price'][1:] != '': + try: + retail_price = Decimal(line['retail price'][1:].replace(",", "")) + except: + retail_price =None + else: + retail_price =None + if line['purchase price'][1:] != '' and line['purchase price'] != "Gift": + try: + purchase_price = Decimal(line['purchase price'][1:].replace(",", "").replace('\\xa0', ' ')) + except: + purchase_price =None + else: + purchase_price =None + i, created = Item.objects.get_or_create( + isbn = line['ISBN'], + serial_number = line['serial number'], + type = item_type, + title = line['title'], + creator = line['creator'], + current_price = current_price, + retail_price = retail_price, + purchase_price = purchase_price, + amazon_url = line['amazon link'], + image_url = line['coverImageLargeURLString'], + format = line['format'], + date_released = date_released, + date_purchased = date_purchased, + date_added = date_added + ) + print created, i.title +""" +import datetime +import os +import io +import urllib + + +from django.contrib.contenttypes.models import ContentType +from django.template.defaultfilters import slugify +from django.core.exceptions import ObjectDoesNotExist +from django.conf import settings + +# Required PIL classes may or may not be available from the root namespace +# depending on the installation +try: + import Image + import ImageFile + import ImageFilter + import ImageEnhance +except ImportError: + try: + from PIL import Image + from PIL import ImageFile + from PIL import ImageFilter + from PIL import ImageEnhance + except ImportError: + raise ImportError("Could not import the Python Imaging Library.") + +ImageFile.MAXBLOCK = 1000000 + + +def grab_item_image(item): + crop_dir = settings.IMAGES_ROOT + '/stuff/' + if not os.path.isdir(crop_dir): + os.makedirs(crop_dir) + remote = item.image_url + try: + fname = urllib.request.urlopen(remote) + except: + return False + im = io.BytesIO(fname.read()) + img = Image.open(im) + ext = slugify(item.title)[:40] + filename = '%s/%s.jpg' %(crop_dir, ext) + try: + if img.format == 'JPEG': + img.save(filename, 'JPEG', quality=95, optimize=True) + else: + img.save(filename) + except IOError: + if os.path.isfile(filename): + os.unlink(filename) + pass + return 'images/stuff/%s.jpg'%(ext) + diff --git a/bak/unused_apps/syndication/__init__.py b/bak/unused_apps/syndication/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/syndication/__init__.py diff --git a/bak/unused_apps/syndication/admin.py b/bak/unused_apps/syndication/admin.py new file mode 100644 index 0000000..b873eb7 --- /dev/null +++ b/bak/unused_apps/syndication/admin.py @@ -0,0 +1,20 @@ +from django.contrib import admin + +from .models import Syndicate, SyndicatedItem, FBOAuthToken + + +@admin.register(FBOAuthToken) +class FBOAuthTokenAdmin(admin.ModelAdmin): + list_display = ('__str__', 'expires',) + pass + + +@admin.register(Syndicate) +class SyndicateAdmin(admin.ModelAdmin): + pass + + +@admin.register(SyndicatedItem) +class SyndicatedItemAdmin(admin.ModelAdmin): + list_display = ('__str__', 'syndicated_to',) + pass diff --git a/bak/unused_apps/syndication/migrations/0001_initial.py b/bak/unused_apps/syndication/migrations/0001_initial.py new file mode 100644 index 0000000..9643d51 --- /dev/null +++ b/bak/unused_apps/syndication/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-03-29 10:07 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ] + + operations = [ + migrations.CreateModel( + name='Syndicate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ], + ), + migrations.CreateModel( + name='SyndicatedItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('publish_date', models.DateTimeField()), + ('status', models.CharField(blank=True, choices=[('1', 'Unsent'), ('2', 'Sent')], max_length=1, null=True)), + ('rel_link', models.CharField(blank=True, max_length=300, null=True)), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ('syndicate', models.ManyToManyField(to='syndication.Syndicate')), + ], + ), + ] diff --git a/bak/unused_apps/syndication/migrations/0002_auto_20160628_2149.py b/bak/unused_apps/syndication/migrations/0002_auto_20160628_2149.py new file mode 100644 index 0000000..51812bf --- /dev/null +++ b/bak/unused_apps/syndication/migrations/0002_auto_20160628_2149.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-06-28 21:49 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('syndication', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='FBOAuthToken', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('short_token', models.TextField()), + ('long_token', models.TextField(blank=True, null=True)), + ('expires', models.DateTimeField(blank=True)), + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ], + ), + migrations.AlterField( + model_name='syndicateditem', + name='status', + field=models.CharField(choices=[('1', 'Unsent'), ('2', 'Sent')], max_length=1, null=True), + ), + ] diff --git a/bak/unused_apps/syndication/migrations/0003_auto_20161023_2014.py b/bak/unused_apps/syndication/migrations/0003_auto_20161023_2014.py new file mode 100644 index 0000000..2b311fb --- /dev/null +++ b/bak/unused_apps/syndication/migrations/0003_auto_20161023_2014.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-10-23 20:14 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('syndication', '0002_auto_20160628_2149'), + ] + + operations = [ + migrations.AlterModelOptions( + name='fboauthtoken', + options={'get_latest_by': 'expires', 'ordering': ('-expires',)}, + ), + ] diff --git a/bak/unused_apps/syndication/migrations/__init__.py b/bak/unused_apps/syndication/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/syndication/migrations/__init__.py diff --git a/bak/unused_apps/syndication/models.py b/bak/unused_apps/syndication/models.py new file mode 100644 index 0000000..c348c1b --- /dev/null +++ b/bak/unused_apps/syndication/models.py @@ -0,0 +1,86 @@ +from django.db import models +from django.utils import timezone +from django.conf import settings +import datetime +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.fields import GenericForeignKey +from django.db.models.signals import post_save +from django.dispatch import receiver + +from .syndicators import post_to_medium, build_facebook_feed, post_to_twitter, post_photo_to_flickr, post_to_facebook + + +class FBOAuthToken(models.Model): + short_token = models.TextField() + long_token = models.TextField(null=True, blank=True) + expires = models.DateTimeField(blank=True) + created = models.DateTimeField(default=timezone.now) + + def __str__(self): + return str(self.expires) + + def long_token_link(self): + token_url = "https://graph.facebook.com/oauth/access_token?client_id=%s&client_secret=%s&grant_type=fb_exchange_token&fb_exchange_token=%s" % (settings.FACEBOOK_APP_ID, settings.FACEBOOK_APP_SECRET, self.short_token) + return token_url + + class Meta: + ordering = ('-expires',) + get_latest_by = 'expires' + + def save(self, *args, **kwargs): + self.expires = self.created + datetime.timedelta(60) + super(FBOAuthToken, self).save() + + +class Syndicate(models.Model): + name = models.CharField(max_length=200) + + def __str__(self): + return self.name + + +class SyndicatedItem(models.Model): + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey('content_type', 'object_id') + syndicate = models.ManyToManyField(Syndicate) + publish_date = models.DateTimeField() + STATUS = ( + ('1', "Unsent"), + ('2', "Sent"), + ) + status = models.CharField(max_length=1, choices=STATUS, null=True) + rel_link = models.CharField(max_length=300, null=True, blank=True) + + def __str__(self): + return str(self.content_object) + + def syndicated_to(self): + return ','.join(str(synd) for synd in self.syndicate.all()) + + +@receiver(post_save, sender=SyndicatedItem) +def post_save_events(sender, update_fields, created, instance, **kwargs): + print(instance.status) + if instance.status == "1": + print("---------calling-----------") + for item in instance.syndicate.all(): + print(item.name) + if item.name == "Medium": + instance.rel_link = post_to_medium(instance.content_object) + instance.status = 2 + if item.name == "Facebook Instant Articles": + build_facebook_feed() + instance.status = 2 + if item.name == "Twitter": + print("calling function") + post_to_twitter(instance.content_object, instance.content_type.name) + if item.name == "Flickr": + if instance.content_type.name == "lux image": + post_photo_to_flickr(instance.content_object) + if item.name == "Facebook": + post_to_facebook(instance.content_object, instance.content_type.name) + post_save.disconnect(post_save_events, sender=SyndicatedItem) + instance.status = "2" + instance.save() + post_save.connect(post_save_events, sender=SyndicatedItem) diff --git a/bak/unused_apps/syndication/syndicators.py b/bak/unused_apps/syndication/syndicators.py new file mode 100644 index 0000000..5f8762c --- /dev/null +++ b/bak/unused_apps/syndication/syndicators.py @@ -0,0 +1,123 @@ +import os +from django.conf import settings +from django.test.client import Client + +from twython import Twython +from bs4 import BeautifulSoup +from medium import Client as MediumClient +import flickrapi +import facebook + +from photos.models import LuxImage + + +def absolute_urls_for_syndication(s): + soup = BeautifulSoup(s, "lxml") + for a in soup.find_all('a'): + if a['href'][:1] == "/": + a['href'] = "https://luxagraf.net%s" % a['href'] + return soup + + +def post_to_medium(item): + client = MediumClient(application_id=settings.MEDIUM_CLIENT_ID, application_secret=settings.MEDIUM_CLIENT_SECRET) + client.access_token = settings.MEDIUM_INT_TOKEN + user = client.get_current_user() + head = '<p><i>This was originally posted <a href="https://luxagraf.net%s" rel="canonical">on my own site</a>.</i></p>' % item.get_absolute_url() + body = "%s %s" % (head, absolute_urls_for_syndication(item.body_html)) + # Create a post. + post = client.create_post( + user_id=user["id"], + title=item.title, + content=body, + content_format="html", + publish_status="public", + canonical_url="https://luxagraf.net%s" % item.get_absolute_url(), + license="all-rights-reserved" + ) + return post["url"] + + +def build_facebook_feed(): + print("+++++++++++++building+++++++++++") + c = Client() + response = c.get('/iafeed.xml', HTTP_HOST='127.0.0.1') + f = open("%siafeed.xml" % settings.FLATFILES_ROOT, 'wb') + f.write(response.content) + f.close() + + +def post_to_twitter(obj, ctype): + print("content type is" + ctype) + t = Twython(settings.TWITTER_API_KEY, settings.TWITTER_API_SECRET, settings.TWITTER_ACCESS_TOKEN, settings.TWITTER_ACCESS_SECRET) + imgs = [] + if ctype == "lux image": + p = open(obj.get_largest_image(), 'rb') + if obj.caption: + status = obj.caption + else: + status = obj.title + response = t.upload_media(media=p) + imgs.append(response) + elif ctype == "lux note": + status = obj.body_markdown + # parse out images and send seperately + soup = BeautifulSoup(status, "lxml") + loop = 0 + for img in soup.find_all('img'): + src = img['src'].split("images/")[1] + i = LuxImage.objects.get(image__icontains=src) + p = open(i.get_largest_image(), 'rb') + response = t.upload_media(media=p) + imgs.append(response) + loop = loop+1 + if loop == 3: + break + for t in soup.find_all('img'): + t.replaceWith("") + # truncate message + if len(status) > 140: + try: + status = status.split("|")[0] + obj.get_absolute_url() + except: + status = status[:140] + obj.get_absolute_url() + print(status) + try: + geo = t.reverse_geocode(lat=obj.latitude, lon=obj.longitude, accuracy=1500, granularity="city") + geo_id = geo['result']['places'][0]['id'] + tweet = t.update_status(status=status, media_ids=[img['media_id'] for img in imgs], place_id=geo_id) + except: + try: + tweet = t.update_status(status=status, media_ids=[img['media_id'] for img in imgs]) + except: + tweet = t.update_status(status=status) + + +def post_photo_to_flickr(photo): + TOKEN_FILE = os.path.join(settings.PROJ_ROOT, "config/flickrToken") + token = open(TOKEN_FILE).read() + flickr = flickrapi.FlickrAPI(settings.FLICKR_API_KEY, settings.FLICKR_API_SECRET, token=token) + sent = flickr.upload(filename=photo.get_image_path_by_size("original"), title=photo.title, description=photo.caption) + photo_id = sent.find('photoid').text + photo.flickr_id = photo_id + try: + flickr.photos.geo.setLocation(photo_id=photo_id, lat=photo.latitude, lon=photo.longitude, accuracy=12) + except: + pass + + +def post_to_facebook(obj, ctype): + from syndication.models import FBOAuthToken + token = FBOAuthToken.objects.latest() + graph = facebook.GraphAPI(access_token=token.long_token, version='2.2') + if ctype == "lux image": + p = open(obj.get_largest_image(), 'rb') + if obj.caption: + message = obj.caption + else: + message = obj.title + try: + fb_response = graph.put_photo(p, message=message) + print(fb_response) + except facebook.GraphAPIError as e: + print('Something went wrong:', e.type, e.message) diff --git a/bak/unused_apps/syndication/templatetags/__init__.py b/bak/unused_apps/syndication/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/syndication/templatetags/__init__.py diff --git a/bak/unused_apps/syndication/templatetags/facebook_processor.py b/bak/unused_apps/syndication/templatetags/facebook_processor.py new file mode 100644 index 0000000..973f863 --- /dev/null +++ b/bak/unused_apps/syndication/templatetags/facebook_processor.py @@ -0,0 +1,23 @@ +from django import template +from bs4 import BeautifulSoup + +from syndication.syndicators import absolute_urls_for_syndication + +register = template.Library() + + +def wrap_image_tags(text): + soup = BeautifulSoup(text, 'lxml') + for img in soup.find_all('img'): + if img.parent.name != "figure": + new_tag = soup.new_tag('figure') + img.wrap(new_tag) + return soup + + +def facebook_processor(text): + ia = absolute_urls_for_syndication(text) + ia = wrap_image_tags(str(ia)[12:-14]) + return str(ia)[12:-14] + +register.filter('facebook_processor', facebook_processor) diff --git a/bak/unused_apps/syndication/views.py b/bak/unused_apps/syndication/views.py new file mode 100644 index 0000000..e489846 --- /dev/null +++ b/bak/unused_apps/syndication/views.py @@ -0,0 +1,17 @@ +from django.views.generic import ListView + +from .models import SyndicatedItem + + +class FacebookFeedView(ListView): + """ + Return a main entry and list of Entries in reverse chronological order + """ + queryset = SyndicatedItem.objects.filter(syndicate__name__exact="Facebook")[:16] + + def get_template_names(self): + return "fb-feed.xml" + + def render_to_response(self, context, **response_kwargs): + response_kwargs.update({'content_type': 'text/xml'}) + return super(FacebookFeedView, self).render_to_response(context, **response_kwargs) diff --git a/bak/unused_apps/trips/__init__.py b/bak/unused_apps/trips/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/trips/__init__.py diff --git a/bak/unused_apps/trips/admin.py b/bak/unused_apps/trips/admin.py new file mode 100644 index 0000000..54991f6 --- /dev/null +++ b/bak/unused_apps/trips/admin.py @@ -0,0 +1,34 @@ +from django.contrib import admin +from django.contrib.gis.admin import OSMGeoAdmin + +from .models import Trip + +from utils.widgets import OLAdminBase +from utils.util import get_latlon +from utils.widgets import LGEntryForm + +@admin.register(Trip) +class TripAdmin(OSMGeoAdmin): + list_display = ('title', 'slug') + prepopulated_fields = {'slug': ('title',)} + ordering = ('title',) + search_fields = ['title'] + list_select_related = True + fieldsets = ( + ('Trip', { + 'fields': ( + 'title', + 'subtitle', + 'dek_markdown', + 'route', + 'slug', + 'start_date', + 'end_date', + 'pub_date', + ), + 'classes': ( + 'show', + 'extrapretty' + ) + }), + ) diff --git a/bak/unused_apps/trips/migrations/0001_initial.py b/bak/unused_apps/trips/migrations/0001_initial.py new file mode 100644 index 0000000..3d3c222 --- /dev/null +++ b/bak/unused_apps/trips/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 2.1.7 on 2019-06-15 09:17 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('locations', '0018_auto_20190414_2124'), + ] + + operations = [ + migrations.CreateModel( + name='Trip', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=50)), + ('subtitle', models.CharField(blank=True, max_length=200)), + ('slug', models.SlugField(unique_for_date='pub_date')), + ('dek_markdown', models.TextField()), + ('dek_html', models.TextField(blank=True, null=True)), + ('pub_date', models.DateTimeField(verbose_name='Date published')), + ('start_date', models.DateTimeField(verbose_name='Date started')), + ('end_date', models.DateTimeField(verbose_name='Date ended')), + ('route', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='locations.Route')), + ], + ), + ] diff --git a/bak/unused_apps/trips/migrations/__init__.py b/bak/unused_apps/trips/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bak/unused_apps/trips/migrations/__init__.py diff --git a/bak/unused_apps/trips/models.py b/bak/unused_apps/trips/models.py new file mode 100644 index 0000000..76941f2 --- /dev/null +++ b/bak/unused_apps/trips/models.py @@ -0,0 +1,40 @@ +import json +import requests +from django import forms +from django.urls import reverse +from django.apps import apps +from django.contrib.gis.geos import GEOSGeometry, fromstr, MultiPolygon +from django.contrib.gis.db import models +from django.contrib.sitemaps import Sitemap +from django.utils.safestring import mark_safe +from django.utils import timezone +from django.conf import settings + +from utils.util import render_images, extract_main_image, markdown_to_html +from locations.models import Route + + +class Trip(models.Model): + """Model to define trips""" + title = models.CharField(max_length=50) + subtitle = models.CharField(max_length=200, blank=True) + slug = models.SlugField(unique_for_date='pub_date') + dek_markdown = models.TextField() + dek_html = models.TextField(null=True, blank=True) + pub_date = models.DateTimeField('Date published') + start_date = models.DateTimeField('Date started') + end_date = models.DateTimeField('Date ended') + route = models.ForeignKey(Route, on_delete=models.CASCADE) + + def get_absolute_url(self): + return "/trips/%s/" % (self.slug) + + def __str__(self): + return self.title + + def save(self, *args, **kwargs): + created = self.pk is None + if not created: + md = render_images(self.dek_markdown) + self.dek_html = markdown_to_html(md) + super(Trip, self).save(*args, **kwargs) diff --git a/bak/unused_apps/trips/urls.py b/bak/unused_apps/trips/urls.py new file mode 100644 index 0000000..205c450 --- /dev/null +++ b/bak/unused_apps/trips/urls.py @@ -0,0 +1,24 @@ +from django.urls import path, re_path + +from . import views + +app_name = "trips" + +urlpatterns = [ + path( + r'<str:slug>', + views.LocationDetail.as_view(), + name="location-detail" + ), + path( + r'mapdata/', + views.MapDataList.as_view(), + name="mapdata" + ), + re_path(r'data/(?P<id>\d+)/$', views.data_json), + path( + r'', + views.MapList.as_view(), + name="maplist" + ), +] diff --git a/bak/unused_apps/trips/views.py b/bak/unused_apps/trips/views.py new file mode 100644 index 0000000..3a93c0d --- /dev/null +++ b/bak/unused_apps/trips/views.py @@ -0,0 +1,101 @@ +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.views.generic import ListView +from django.views.generic.detail import DetailView +from django.conf import settings +from django.db.models import Q + +from jrnl.models import Entry +from projects.shortcuts import render_to_geojson +from sightings.models import Sighting + +from .models import Country, Region, Route, Location + +def map_list(request): + context = { + 'object_list': Entry.objects.filter(status__exact=1), + 'country_list': Country.objects.filter(visited=True).exclude(name='default'), + 'route_list': Route.objects.all(), + 'region_list': Region.objects.all() + } + return render_to_response( + 'archives/map.html', + context, + context_instance=RequestContext(request) + ) + +class MapList(ListView): + """ + Return list of Entries on map + """ + context_object_name = 'object_list' + queryset = Entry.objects.filter(status__exact=1) + template_name = 'archives/map.html' + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(MapList, self).get_context_data(**kwargs) + context['country_list'] = Country.objects.filter(visited=True).exclude(name='default'), + context['route_list'] = Route.objects.all(), + context['region_list'] = Region.objects.all() + context['IMAGES_URL'] = settings.IMAGES_URL + return context + + +class MapDataList(ListView): + """ + Build data file for Entries on map + """ + context_object_name = 'object_list' + queryset = Entry.objects.filter(status__exact=1) + template_name = 'archives/map_data.html' + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(MapDataList, self).get_context_data(**kwargs) + context['country_list'] = Country.objects.filter(visited=True).exclude(name='default'), + context['route_list'] = Route.objects.all(), + context['region_list'] = Region.objects.all() + context['IMAGES_URL'] = settings.IMAGES_URL + return context + +def map_data(request): + context = { + 'object_list': Entry.objects.filter(status__exact=1), + 'route_list': Route.objects.all(), + 'country_list': Country.objects.filter(visited=True).exclude(name='default'), + 'region_list': Region.objects.all() + } + return render_to_response( + 'archives/map_data.html', + context, + context_instance=RequestContext(request) + ) + + +def data_json(request, id): + qs = Route.objects.filter(pk=id) + return render_to_geojson( + qs, + included_fields=['id', ], + geom_attribute='geometry', + mimetype='application/json', + pretty_print=True + ) + + +class LocationDetail(DetailView): + model = Location + template_name = "details/location.html" + + def get_context_data(self, **kwargs): + context = super(LocationDetail, self).get_context_data(**kwargs) + context['entry_list'] = Entry.objects.filter( + Q(location=self.get_object()) | + Q(location__in=Location.objects.filter(parent=self.get_object())) + ) + context['sighting_list'] = Sighting.objects.filter( + Q(location=self.get_object()) | + Q(location__in=Location.objects.filter(parent=self.get_object())) + ).order_by('ap_id', 'ap__apclass__kind').distinct("ap") + return context |