summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorluxagraf <sng@luxagraf.net>2022-12-02 14:16:08 -0600
committerluxagraf <sng@luxagraf.net>2022-12-02 14:16:08 -0600
commit656505098a80e653319236ac302fd6dd9f485b33 (patch)
tree03fe2f552496e2a2b459f5227dc11273d1b94211 /app
parentbf2fa131cba6430ba93f584f4693c3444e0c455f (diff)
reset migrations to zero out some changes (deleting the geodata for
example)
Diffstat (limited to 'app')
-rw-r--r--app/accounts/migrations/0001_initial.py55
-rw-r--r--app/accounts/migrations/0002_auto_20190108_2115.py35
-rw-r--r--app/books/migrations/0001_initial.py3
-rw-r--r--app/books/migrations/0002_book_other_link.py18
-rw-r--r--app/lib/django_comments/abstracts.py2
-rw-r--r--app/lib/django_comments/admin.py2
-rw-r--r--app/lib/django_comments/forms.py6
-rw-r--r--app/lib/django_comments/managers.py4
-rw-r--r--app/lib/django_comments/models.py2
-rw-r--r--app/lib/django_comments/moderation.py2
-rw-r--r--app/lib/django_comments/signals.py7
-rw-r--r--app/lib/django_comments/templates/comments/form.html2
-rw-r--r--app/lib/django_comments/templates/comments/preview.html2
-rw-r--r--app/lib/django_comments/templatetags/comments.py4
-rw-r--r--app/lib/django_comments/urls.py20
-rw-r--r--app/lib/django_comments/views/utils.py4
-rw-r--r--app/lib/templatetags/templatetags/number_to_word.py4
-rw-r--r--app/media/0002_auto_20201201_2054.py46
-rw-r--r--app/media/0003_auto_20201201_2055.py118
-rw-r--r--app/media/admin.py49
-rw-r--r--app/media/migrations/0001_initial.py30
-rw-r--r--app/media/migrations/0002_auto_20211030_1634.py38
-rw-r--r--app/media/models.py215
-rw-r--r--app/media/readexif.py76
-rw-r--r--app/media/static/image-preview.js2
-rw-r--r--app/media/templatetags/get_image_by_size.py2
-rw-r--r--app/media/utils.py24
-rw-r--r--app/media/views.py24
-rw-r--r--app/normalize/migrations/0001_initial.py4
-rw-r--r--app/pages/migrations/0001_initial.py12
-rw-r--r--app/pages/migrations/0002_auto_20211030_1634.py44
-rw-r--r--app/pages/templates/pages/luxagraf/homepage.html2
-rw-r--r--app/podcasts/admin.py10
-rw-r--r--app/podcasts/feeds.py229
-rw-r--r--app/podcasts/migrations/0001_initial.py60
-rw-r--r--app/podcasts/migrations/__init__.py (renamed from app/accounts/migrations/__init__.py)0
-rw-r--r--app/podcasts/models.py132
-rw-r--r--app/podcasts/templates/podcasts/detail.html (renamed from app/posts/templates/posts/podcast_detail.html)2
-rw-r--r--app/podcasts/templates/podcasts/list.html (renamed from app/posts/templates/posts/podcast_list.html)8
-rw-r--r--app/podcasts/urls.py19
-rw-r--r--app/podcasts/urls_feeds.py17
-rw-r--r--app/podcasts/views.py29
-rw-r--r--app/posts/migrations/0001_initial.py21
-rw-r--r--app/posts/migrations/0002_alter_post_id.py18
-rw-r--r--app/posts/migrations/0003_auto_20211030_1955.py38
-rw-r--r--app/taxonomy/admin.py3
-rw-r--r--app/taxonomy/migrations/0001_initial.py19
-rw-r--r--app/taxonomy/models.py15
-rw-r--r--app/taxonomy/views.py6
-rw-r--r--app/utils/static/image-loader.js10
-rw-r--r--app/utils/util.py6
-rw-r--r--app/utils/views.py35
-rw-r--r--app/utils/widgets.py2
53 files changed, 1067 insertions, 470 deletions
diff --git a/app/accounts/migrations/0001_initial.py b/app/accounts/migrations/0001_initial.py
deleted file mode 100644
index 36aa6ba..0000000
--- a/app/accounts/migrations/0001_initial.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Generated by Django 2.1.2 on 2018-11-24 04:41
-
-from django.conf import settings
-import django.contrib.auth.models
-import django.contrib.auth.validators
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('auth', '0009_alter_user_last_name_max_length'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='User',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('password', models.CharField(max_length=128, verbose_name='password')),
- ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
- ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
- ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
- ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
- ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
- ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
- ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
- ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
- ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
- ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
- ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
- ],
- options={
- 'ordering': ['-date_joined'],
- },
- managers=[
- ('objects', django.contrib.auth.models.UserManager()),
- ],
- ),
- migrations.CreateModel(
- name='UserProfile',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('photo', models.ImageField(blank=True, null=True, upload_to='profile')),
- ('website', models.CharField(blank=True, default='', max_length=300, null=True)),
- ('location', models.CharField(blank=True, default='', max_length=300, null=True)),
- ('bio', models.TextField(blank=True, default='', null=True)),
- ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- ),
- ]
diff --git a/app/accounts/migrations/0002_auto_20190108_2115.py b/app/accounts/migrations/0002_auto_20190108_2115.py
deleted file mode 100644
index 1ebb280..0000000
--- a/app/accounts/migrations/0002_auto_20190108_2115.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Generated by Django 2.1.2 on 2019-01-09 03:15
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('accounts', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='userprofile',
- name='bio',
- field=models.CharField(blank=True, default='', max_length=350),
- ),
- migrations.AlterField(
- model_name='userprofile',
- name='location',
- field=models.CharField(blank=True, default='', max_length=300),
- ),
- migrations.AlterField(
- model_name='userprofile',
- name='user',
- field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name='userprofile',
- name='website',
- field=models.CharField(blank=True, default='', max_length=300),
- ),
- ]
diff --git a/app/books/migrations/0001_initial.py b/app/books/migrations/0001_initial.py
index 6257e40..03447a1 100644
--- a/app/books/migrations/0001_initial.py
+++ b/app/books/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.2.6 on 2021-08-14 12:50
+# Generated by Django 4.1.3 on 2022-12-02 20:08
import books.models
from django.db import migrations, models
@@ -28,6 +28,7 @@ class Migration(migrations.Migration):
('amazon_link', models.CharField(blank=True, max_length=400, null=True)),
('bookshop_link', models.CharField(blank=True, max_length=400, null=True)),
('thriftbooks_link', models.CharField(blank=True, max_length=400, null=True)),
+ ('other_link', models.CharField(blank=True, max_length=400, null=True)),
('image', models.FileField(blank=True, null=True, upload_to=books.models.get_upload_path)),
],
options={
diff --git a/app/books/migrations/0002_book_other_link.py b/app/books/migrations/0002_book_other_link.py
deleted file mode 100644
index bf64351..0000000
--- a/app/books/migrations/0002_book_other_link.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.2.6 on 2021-08-14 13:03
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('books', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='book',
- name='other_link',
- field=models.CharField(blank=True, max_length=400, null=True),
- ),
- ]
diff --git a/app/lib/django_comments/abstracts.py b/app/lib/django_comments/abstracts.py
index e74ea02..5428f1a 100644
--- a/app/lib/django_comments/abstracts.py
+++ b/app/lib/django_comments/abstracts.py
@@ -9,7 +9,7 @@ from django.utils.html import mark_safe
from django.db import models
from django.utils import timezone
from six import python_2_unicode_compatible
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
try:
from django.urls import reverse
except ImportError:
diff --git a/app/lib/django_comments/admin.py b/app/lib/django_comments/admin.py
index 8451c70..6a1f5f8 100644
--- a/app/lib/django_comments/admin.py
+++ b/app/lib/django_comments/admin.py
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.contrib import admin
from django.contrib.auth import get_user_model
-from django.utils.translation import ugettext_lazy as _, ungettext
+from django.utils.translation import gettext_lazy as _, ngettext
from django_comments import get_model
from django_comments.views.moderation import perform_flag, perform_approve, perform_delete
diff --git a/app/lib/django_comments/forms.py b/app/lib/django_comments/forms.py
index 7b7eafd..b4de54d 100644
--- a/app/lib/django_comments/forms.py
+++ b/app/lib/django_comments/forms.py
@@ -5,10 +5,10 @@ from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.forms.utils import ErrorDict
from django.utils.crypto import salted_hmac, constant_time_compare
-from django.utils.encoding import force_text
+from django.utils.encoding import force_str
from django.utils.text import get_text_list
from django.utils import timezone
-from django.utils.translation import pgettext_lazy, ungettext, ugettext, ugettext_lazy as _
+from django.utils.translation import pgettext_lazy, ngettext, gettext, gettext_lazy as _
from . import get_model
@@ -137,7 +137,7 @@ class CommentDetailsForm(CommentSecurityForm):
"""
return dict(
content_type=ContentType.objects.get_for_model(self.target_object),
- object_pk=force_text(self.target_object._get_pk_val()),
+ object_pk=force_str(self.target_object._get_pk_val()),
user_name=self.cleaned_data["name"],
user_email=self.cleaned_data["email"],
user_url=self.cleaned_data["url"],
diff --git a/app/lib/django_comments/managers.py b/app/lib/django_comments/managers.py
index 9e1fc77..33d9e2a 100644
--- a/app/lib/django_comments/managers.py
+++ b/app/lib/django_comments/managers.py
@@ -1,6 +1,6 @@
from django.db import models
from django.contrib.contenttypes.models import ContentType
-from django.utils.encoding import force_text
+from django.utils.encoding import force_str
class CommentManager(models.Manager):
@@ -18,5 +18,5 @@ class CommentManager(models.Manager):
ct = ContentType.objects.get_for_model(model)
qs = self.get_queryset().filter(content_type=ct)
if isinstance(model, models.Model):
- qs = qs.filter(object_pk=force_text(model._get_pk_val()))
+ qs = qs.filter(object_pk=force_str(model._get_pk_val()))
return qs
diff --git a/app/lib/django_comments/models.py b/app/lib/django_comments/models.py
index 204cf2e..6eac46b 100644
--- a/app/lib/django_comments/models.py
+++ b/app/lib/django_comments/models.py
@@ -2,7 +2,7 @@ from six import python_2_unicode_compatible
from django.conf import settings
from django.db import models
from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from .abstracts import (
COMMENT_MAX_LENGTH, BaseCommentAbstractModel, CommentAbstractModel,
diff --git a/app/lib/django_comments/moderation.py b/app/lib/django_comments/moderation.py
index 3e5c412..39ac356 100644
--- a/app/lib/django_comments/moderation.py
+++ b/app/lib/django_comments/moderation.py
@@ -62,7 +62,7 @@ from django.core.mail import send_mail
from django.db.models.base import ModelBase
from django.template import loader
from django.utils import timezone
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext as _
import django_comments
from django_comments import signals
diff --git a/app/lib/django_comments/signals.py b/app/lib/django_comments/signals.py
index 079afaf..3aac192 100644
--- a/app/lib/django_comments/signals.py
+++ b/app/lib/django_comments/signals.py
@@ -9,13 +9,14 @@ from django.dispatch import Signal
# discarded and a 400 response. This signal is sent at more or less
# the same time (just before, actually) as the Comment object's pre-save signal,
# except that the HTTP request is sent along with this signal.
-comment_will_be_posted = Signal(providing_args=["comment", "request"])
+
+comment_will_be_posted = Signal() # providing_args=["comment", "request"]
# Sent just after a comment was posted. See above for how this differs
# from the Comment object's post-save signal.
-comment_was_posted = Signal(providing_args=["comment", "request"])
+comment_was_posted = Signal() # providing_args=["comment", "request"]
# Sent after a comment was "flagged" in some way. Check the flag to see if this
# was a user requesting removal of a comment, a moderator approving/removing a
# comment, or some other custom user flag.
-comment_was_flagged = Signal(providing_args=["comment", "flag", "created", "request"])
+comment_was_flagged = Signal() # providing_args=["comment", "flag", "created", "request"]
diff --git a/app/lib/django_comments/templates/comments/form.html b/app/lib/django_comments/templates/comments/form.html
index 858d5eb..939f7e1 100644
--- a/app/lib/django_comments/templates/comments/form.html
+++ b/app/lib/django_comments/templates/comments/form.html
@@ -9,7 +9,7 @@
{% if field.errors %}{{ field.errors }}{% endif %}
<p
{% if field.errors %} class="error"{% endif %}
- {% ifequal field.name "honeypot" %} style="display:none;"{% endifequal %}>
+ {% if field.name == "honeypot" %} style="display:none;"{% endif %}>
{{ field.label_tag }} {{ field }}
</p>
{% endif %}
diff --git a/app/lib/django_comments/templates/comments/preview.html b/app/lib/django_comments/templates/comments/preview.html
index e335466..9fd5f1c 100644
--- a/app/lib/django_comments/templates/comments/preview.html
+++ b/app/lib/django_comments/templates/comments/preview.html
@@ -27,7 +27,7 @@
{% if field.errors %}{{ field.errors }}{% endif %}
<p
{% if field.errors %} class="error"{% endif %}
- {% ifequal field.name "honeypot" %} style="display:none;"{% endifequal %}>
+ {% if field.name == "honeypot" %} style="display:none;"{% endif%}>
{{ field.label_tag }} {{ field }}
</p>
{% endif %}
diff --git a/app/lib/django_comments/templatetags/comments.py b/app/lib/django_comments/templatetags/comments.py
index 9b2d1a4..440a8f6 100644
--- a/app/lib/django_comments/templatetags/comments.py
+++ b/app/lib/django_comments/templatetags/comments.py
@@ -3,7 +3,7 @@ from django.template.loader import render_to_string
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.shortcuts import get_current_site
-from django.utils.encoding import smart_text
+from django.utils.encoding import smart_str
import django_comments
@@ -85,7 +85,7 @@ class BaseCommentNode(template.Node):
qs = self.comment_model.objects.filter(
content_type=ctype,
- object_pk=smart_text(object_pk),
+ object_pk=smart_str(object_pk),
site__pk=site_id,
)
diff --git a/app/lib/django_comments/urls.py b/app/lib/django_comments/urls.py
index 45599dc..47d5b48 100644
--- a/app/lib/django_comments/urls.py
+++ b/app/lib/django_comments/urls.py
@@ -1,4 +1,4 @@
-from django.conf.urls import url
+from django.urls import path, re_path
from django.contrib.contenttypes.views import shortcut
from .views.comments import post_comment, comment_done
@@ -8,14 +8,14 @@ from .views.moderation import (
urlpatterns = [
- url(r'^post/$', post_comment, name='comments-post-comment'),
- url(r'^posted/$', comment_done, name='comments-comment-done'),
- url(r'^flag/(\d+)/$', flag, name='comments-flag'),
- url(r'^flagged/$', flag_done, name='comments-flag-done'),
- url(r'^delete/(\d+)/$', delete, name='comments-delete'),
- url(r'^deleted/$', delete_done, name='comments-delete-done'),
- url(r'^approve/(\d+)/$', approve, name='comments-approve'),
- url(r'^approved/$', approve_done, name='comments-approve-done'),
+ re_path(r'^post/$', post_comment, name='comments-post-comment'),
+ re_path(r'^posted/$', comment_done, name='comments-comment-done'),
+ re_path(r'^flag/(\d+)/$', flag, name='comments-flag'),
+ re_path(r'^flagged/$', flag_done, name='comments-flag-done'),
+ re_path(r'^delete/(\d+)/$', delete, name='comments-delete'),
+ re_path(r'^deleted/$', delete_done, name='comments-delete-done'),
+ re_path(r'^approve/(\d+)/$', approve, name='comments-approve'),
+ re_path(r'^approved/$', approve_done, name='comments-approve-done'),
- url(r'^cr/(\d+)/(.+)/$', shortcut, name='comments-url-redirect'),
+ re_path(r'^cr/(\d+)/(.+)/$', shortcut, name='comments-url-redirect'),
]
diff --git a/app/lib/django_comments/views/utils.py b/app/lib/django_comments/views/utils.py
index a5f5c11..793fc43 100644
--- a/app/lib/django_comments/views/utils.py
+++ b/app/lib/django_comments/views/utils.py
@@ -12,7 +12,7 @@ except ImportError: # Python 2
from django.http import HttpResponseRedirect
from django.shortcuts import render, resolve_url
from django.core.exceptions import ObjectDoesNotExist
-from django.utils.http import is_safe_url
+from django.utils.http import url_has_allowed_host_and_scheme
import django_comments
@@ -28,7 +28,7 @@ def next_redirect(request, fallback, **get_kwargs):
Returns an ``HttpResponseRedirect``.
"""
next = request.POST.get('next')
- if not is_safe_url(url=next, allowed_hosts={request.get_host()}):
+ if not url_has_allowed_host_and_scheme(url=next, allowed_hosts={request.get_host()}):
next = resolve_url(fallback)
if get_kwargs:
diff --git a/app/lib/templatetags/templatetags/number_to_word.py b/app/lib/templatetags/templatetags/number_to_word.py
index c153932..5aa4eaf 100644
--- a/app/lib/templatetags/templatetags/number_to_word.py
+++ b/app/lib/templatetags/templatetags/number_to_word.py
@@ -1,4 +1,4 @@
-from django.utils.translation import ungettext, ugettext as _
+from django.utils.translation import gettext as _
import re
from django import template
from django.utils.safestring import mark_safe
@@ -26,4 +26,4 @@ def number_to_word(value):
else:
word = PRIME_NUM[int(value[:1])-1]
return word
- \ No newline at end of file
+
diff --git a/app/media/0002_auto_20201201_2054.py b/app/media/0002_auto_20201201_2054.py
new file mode 100644
index 0000000..843f48b
--- /dev/null
+++ b/app/media/0002_auto_20201201_2054.py
@@ -0,0 +1,46 @@
+# Generated by Django 3.1 on 2020-12-01 19:26
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RunSQL("""
+ INSERT INTO media_luximagesize (
+ id,
+ name,
+ width,
+ height,
+ quality
+ )
+ SELECT
+ id,
+ name,
+ width,
+ height,
+ quality
+ FROM
+ photos_luximagesize;
+ """, reverse_sql="""
+ INSERT INTO photos_luximagesize (
+ id,
+ name,
+ width,
+ height,
+ quality
+ )
+ SELECT
+ id,
+ name,
+ width,
+ height,
+ quality
+ FROM
+ media_luximagesize;
+ """)
+ ]
diff --git a/app/media/0003_auto_20201201_2055.py b/app/media/0003_auto_20201201_2055.py
new file mode 100644
index 0000000..4aeec12
--- /dev/null
+++ b/app/media/0003_auto_20201201_2055.py
@@ -0,0 +1,118 @@
+# Generated by Django 3.1 on 2020-12-01 20:49
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('media', '0002_auto_20201201_2054'),
+ ]
+
+ operations = [
+ migrations.RunSQL("""
+ INSERT INTO media_luximage (
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ height,
+ width,
+ is_public,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ point,
+ location,
+ sizes
+ )
+ SELECT
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ height,
+ width,
+ is_public,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ point,
+ location,
+ sizes
+ FROM
+ photos_luximage;
+ """, reverse_sql="""
+ INSERT INTO photos_luximage (
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ height,
+ width,
+ is_public,
+ sizes,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ point,
+ location
+ )
+ SELECT
+ id,
+ image,
+ title,
+ alt,
+ photo_credit_source,
+ photo_credit_url,
+ caption,
+ pub_date,
+ height,
+ width,
+ is_public,
+ sizes,
+ exif_raw,
+ exif_aperture,
+ exif_make,
+ exif_model,
+ exif_exposure,
+ exif_iso,
+ exif_focal_length,
+ exif_lens,
+ exif_date,
+ point,
+ location
+ FROM
+ media_luximage;
+ """)
+ ]
diff --git a/app/media/admin.py b/app/media/admin.py
index 12d0509..50eb879 100644
--- a/app/media/admin.py
+++ b/app/media/admin.py
@@ -1,12 +1,15 @@
from django.contrib import admin
+from django import forms
from django.contrib.gis.admin import OSMGeoAdmin
-
-from .models import LuxImage, LuxGallery, LuxImageSize, LuxVideo, LuxAudio
+from .models import LuxImage, LuxGallery, LuxImageSize, LuxVideo
+from django.shortcuts import render
+from django.contrib.admin import helpers
+from django.http import HttpResponseRedirect
@admin.register(LuxImageSize)
class LuxImageSizeAdmin(OSMGeoAdmin):
- list_display = ('name', 'width', 'height', 'quality')
+ list_display = ('name','slug', 'width', 'height', 'quality')
@admin.register(LuxVideo)
@@ -18,20 +21,44 @@ class LuxVideoAdmin(OSMGeoAdmin):
class LuxImageAdmin(OSMGeoAdmin):
list_display = ('pk', 'admin_thumbnail', 'pub_date', 'caption')
list_filter = ('pub_date',)
- search_fields = ['title', 'caption']
+ search_fields = ['title', 'caption', 'alt']
# 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', ('is_public'), ('photo_credit_source', 'photo_credit_url'))
+ 'fields': (
+ 'image',
+ 'alt',
+ 'sizes',
+ 'caption',
+ 'pub_date',
+ 'title',
+ )
+ }),
+ ('Exif and Other Data', {
+ 'classes': ('collapse',),
+ 'fields': (
+ 'point',
+ ('is_public'),
+ ('photo_credit_source', 'photo_credit_url'),
+ 'exif_raw', 'exif_aperture', 'exif_make', 'exif_model', 'exif_exposure', 'exif_iso', 'exif_focal_length', 'exif_lens', 'exif_date', 'height', 'width'),
}),
)
+ def save_related(self, request, form, formsets, change):
+ super(LuxImageAdmin, self).save_related(request, form, formsets, change)
+ if not form.instance.sizes.all():
+ print("there are no sizes")
+ form.instance.sizes.add(*LuxImageSize.objects.filter(slug__in=["picwide-sm", "picwide-med", "picwide"]))
+
class Media:
js = ('image-preview.js', 'next-prev-links.js')
-
-
-@admin.register(LuxAudio)
-class LuxAudioAdmin(OSMGeoAdmin):
- list_display = ('pk', 'title', 'pub_date')
- list_filter = ('pub_date',)
diff --git a/app/media/migrations/0001_initial.py b/app/media/migrations/0001_initial.py
index 8ca4631..886a36e 100644
--- a/app/media/migrations/0001_initial.py
+++ b/app/media/migrations/0001_initial.py
@@ -1,6 +1,7 @@
-# Generated by Django 3.1.3 on 2020-11-30 22:44
+# Generated by Django 4.1.3 on 2022-12-02 20:09
import datetime
+import django.contrib.gis.db.models.fields
from django.db import migrations, models
import django.db.models.deletion
import media.models
@@ -17,7 +18,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='LuxAudio',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(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')),
@@ -26,6 +27,7 @@ class Migration(migrations.Migration):
('pub_date', models.DateTimeField(default=datetime.datetime.now)),
('mp3', models.FileField(blank=True, null=True, upload_to=media.models.get_audio_upload_path)),
('ogg', models.FileField(blank=True, null=True, upload_to=media.models.get_audio_upload_path)),
+ ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)),
],
options={
'verbose_name': 'Audio',
@@ -37,8 +39,9 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='LuxImageSize',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=30, null=True)),
+ ('slug', models.SlugField(blank=True, null=True)),
('width', models.IntegerField(blank=True, null=True)),
('height', models.IntegerField(blank=True, null=True)),
('quality', models.IntegerField()),
@@ -51,7 +54,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='LuxVideo',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('video_mp4', models.FileField(blank=True, null=True, upload_to=media.models.get_vid_upload_path)),
('video_webm', models.FileField(blank=True, null=True, upload_to=media.models.get_vid_upload_path)),
('video_poster', models.FileField(blank=True, null=True, upload_to=media.models.get_vid_upload_path)),
@@ -70,7 +73,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='LuxImage',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.FileField(blank=True, null=True, upload_to=media.models.get_upload_path)),
('title', models.CharField(blank=True, max_length=300, null=True)),
('alt', models.CharField(blank=True, max_length=300, null=True)),
@@ -78,10 +81,21 @@ class Migration(migrations.Migration):
('photo_credit_url', models.CharField(blank=True, max_length=300, null=True)),
('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(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)),
- ('sizes', models.ManyToManyField(blank=True, to='media.LuxImageSize')),
+ ('sizes_cache', models.CharField(blank=True, max_length=300, null=True)),
+ ('sizes', models.ManyToManyField(blank=True, related_name='sizes', to='media.luximagesize')),
],
options={
'verbose_name_plural': 'Images',
@@ -92,14 +106,14 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='LuxGallery',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(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)),
('pub_date', models.DateTimeField(null=True)),
('is_public', models.BooleanField(default=True)),
('caption_style', models.CharField(blank=True, max_length=400, null=True)),
- ('images', models.ManyToManyField(to='media.LuxImage')),
+ ('images', models.ManyToManyField(to='media.luximage')),
('thumb', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='gallery_thumb', to='media.luximage')),
],
options={
diff --git a/app/media/migrations/0002_auto_20211030_1634.py b/app/media/migrations/0002_auto_20211030_1634.py
deleted file mode 100644
index abebed9..0000000
--- a/app/media/migrations/0002_auto_20211030_1634.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Generated by Django 3.2.8 on 2021-10-30 16:34
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('media', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='luxaudio',
- name='id',
- field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- migrations.AlterField(
- model_name='luxgallery',
- name='id',
- field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- migrations.AlterField(
- model_name='luximage',
- name='id',
- field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- migrations.AlterField(
- model_name='luximagesize',
- name='id',
- field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- migrations.AlterField(
- model_name='luxvideo',
- name='id',
- field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- ]
diff --git a/app/media/models.py b/app/media/models.py
index 190026f..dcf7ccc 100644
--- a/app/media/models.py
+++ b/app/media/models.py
@@ -1,28 +1,28 @@
import os.path
import io
import datetime
+from pathlib import Path
from PIL import Image
from django.core.exceptions import ValidationError
-from django.db import models
+from django.contrib.gis.db import models
from django.contrib.sitemaps import Sitemap
-from django.utils.encoding import force_text
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+from django.db.models.signals import m2m_changed
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 taggit.managers import TaggableManager
+
+from .readexif import readexif
from .utils import resize_image
-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):
@@ -39,6 +39,7 @@ def get_audio_upload_path(self, filename):
class LuxImageSize(models.Model):
name = models.CharField(null=True, blank=True, max_length=30)
+ slug = models.SlugField(null=True, blank=True)
width = models.IntegerField(null=True, blank=True)
height = models.IntegerField(null=True, blank=True)
quality = models.IntegerField()
@@ -63,10 +64,21 @@ class LuxImage(models.Model):
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)
is_public = models.BooleanField(default=True)
- sizes = models.ManyToManyField(LuxImageSize, blank=True)
+ sizes = models.ManyToManyField(LuxImageSize, blank=True, related_name='sizes')
+ sizes_cache = models.CharField(null=True, blank=True, max_length=300)
class Meta:
ordering = ('-pub_date', 'id')
@@ -85,10 +97,7 @@ class LuxImage(models.Model):
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())
+ return self.get_image_url_by_size(size.name)
def get_largest_image(self):
t = []
@@ -98,74 +107,70 @@ class LuxImage(models.Model):
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_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())
+ def image_name(self):
+ return os.path.basename(self.image.path)[:-4]
@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())
+ def image_ext(self):
+ return self.image.url[-3:]
@cached_property
+ def get_image_filename(self):
+ return os.path.basename(self.image.path)
+
+ @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)
+ srcset += "%s%s/%s_%s.%s %sw" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.image_name, size.slug, self.image_ext, size.width)
if loopnum < length:
srcset += ", "
loopnum = loopnum+1
return srcset
- @cached_property
+ @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())
+ src = self.get_image_url_by_size('picwide-med')
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())
+ size = "".join(size.name for size in self.sizes.all())
+ src = self.get_image_url_by_size(size)
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())
+ def get_image_url_by_size(self, size="original"):
if size == "original":
- return "%soriginal/%s/%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), base, self.get_image_ext())
+ return "%soriginal/%s/%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.image_name, self.image_ext)
+ if size == "admin_insert":
+ return "images/%s/%s.%s" % (self.pub_date.strftime("%Y"), self.image_name, self.image_ext)
else:
- if size != 'tn':
- s = LuxImageSize.objects.get(name=size)
- if s not in self.sizes.all():
- 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())
+ luximagesize = LuxImageSize.objects.get(slug=size)
+ #if luximagesize not in self.get_sizes:
+ #self.sizes.add(luximagesize)
+ return "%s%s/%s_%s.%s" % (settings.IMAGES_URL, self.pub_date.strftime("%Y"), self.image_name, luximagesize.slug, self.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())
+ return self.image.path
else:
- return "%s/%s/%s_%s.%s" % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), base, size, self.get_image_ext())
+ luximagesize = LuxImageSize.objects.get(slug=size)
+ return "%s/%s/%s_%s.%s" % (settings.IMAGES_ROOT, self.pub_date.strftime("%Y"), self.image_name, luximagesize.slug, self.image_ext)
+ @cached_property
def get_thumbnail_url(self):
- return self.get_image_by_size("tn")
+ return self.get_image_url_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")))
+ return format_html('<a href="%s"><img src="%s"></a>' % (self.get_image_url_by_size(), self.get_image_url_by_size("tn")))
admin_thumbnail.short_description = 'Thumbnail'
+ @property
def get_sizes(self):
- return self.sizes.all()
+ return self.sizes_cache.split(",")
@property
def get_previous_published(self):
@@ -196,6 +201,9 @@ class LuxImage(models.Model):
return False
def save(self, *args, **kwargs):
+ created = self.pk is None
+ if not created:
+ self.sizes_cache = ",".join(s.slug for s in self.sizes.all())
super(LuxImage, self).save()
@@ -270,6 +278,7 @@ class LuxAudio(models.Model):
pub_date = models.DateTimeField(default=datetime.datetime.now)
mp3 = models.FileField(blank=True, null=True, upload_to=get_audio_upload_path)
ogg = models.FileField(blank=True, null=True, upload_to=get_audio_upload_path)
+ point = models.PointField(blank=True, null=True)
class Meta:
ordering = ('-pub_date',)
@@ -283,6 +292,9 @@ class LuxAudio(models.Model):
def get_absolute_url(self):
return reverse("prompt:detail", kwargs={"slug": self.slug})
+ def get_image_url_by_size(self, size="original"):
+ pass
+
@property
def get_previous_published(self):
return self.get_previous_by_pub_date(status__exact=1)
@@ -310,51 +322,84 @@ class LuxAudio(models.Model):
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
super(LuxAudio, self).save(*args, **kwargs)
@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
- post_save.disconnect(post_save_events, sender=LuxImage)
- instance.save()
- post_save.connect(post_save_events, sender=LuxImage)
+ if created:
+ if instance.exif_raw == '':
+ instance = readexif(instance)
+ instance.sizes.add(LuxImageSize.objects.get(slug="tn"))
+ img = Image.open(instance.image.path)
+ instance.height = img.height
+ instance.width = img.width
+ 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)
-
-
+ # update the local cache of sizes
+ sizes = instance.sizes.all()
+ if sizes:
+ instance.sizes_cache = ",".join(s.slug for s in sizes)
+ instance.save()
+ for size in instance.get_sizes:
+ print("SIZE is: %s" % size)
+ # check each size and see if there's an image there already
+ my_file = Path(instance.get_image_path_by_size(size))
+ if not my_file.is_file():
+ #file doesn't exist, so create it
+ new_size = LuxImageSize.objects.get(slug=size)
+ if new_size.width:
+ img = Image.open(instance.image.path)
+ try:
+ if new_size.width <= img.width:
+ resize_image(img, new_size.width, None, new_size.quality, instance.get_image_path_by_size(size))
+ 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(new_size)
+ m2m_changed.connect(update_photo_sizes, sender=LuxImage.sizes.through)
+ if new_size.height:
+ img = Image.open(instance.image.path)
+ try:
+ if new_size.height <= img.height:
+ resize_image(img, None, new_size.height, new_size.quality, instance.get_image_path_by_size(size))
+ else:
+ pass
+ except ImageSizeError:
+ m2m_changed.disconnect(update_photo_sizes, sender=LuxImage.sizes.through)
+ instance.sizes.remove(new_size)
+ m2m_changed.connect(update_photo_sizes, sender=LuxImage.sizes.through)
+ else:
+ # file exists, might add something here to force it to do the above when I want
+ print("file %s exists" % size)
+ pass
+
+
+def generate_image(luximage, size):
+ new_size = LuxImageSize.objects.get(slug=size)
+ if new_size.width:
+ img = Image.open(luximage.image.path)
+ try:
+ if new_size.width <= img.width:
+ resize_image(img, new_size.width, None, new_size.quality, luximage.get_image_path_by_size(size))
+ else:
+ raise ValidationError({'items': ["Size is larger than source image"]})
+ except ImageSizeError:
+ print("error creating size")
+ if new_size.height:
+ img = Image.open(luximage.image.path)
+ try:
+ if new_size.height <= img.height:
+ resize_image(img, None, new_size.height, new_size.quality, luximage.get_image_path_by_size(size))
+ else:
+ pass
+ except ImageSizeError:
+ print("error creating size")
diff --git a/app/media/readexif.py b/app/media/readexif.py
new file mode 100644
index 0000000..d9e5d70
--- /dev/null
+++ b/app/media/readexif.py
@@ -0,0 +1,76 @@
+import time
+from fractions import Fraction
+
+from django.contrib.gis.geos import Point
+
+import exiftool
+
+
+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/app/media/static/image-preview.js b/app/media/static/image-preview.js
index b8fead5..c829084 100644
--- a/app/media/static/image-preview.js
+++ b/app/media/static/image-preview.js
@@ -11,7 +11,7 @@ function build_image_preview () {
var img = document.createElement("img");
var request = new XMLHttpRequest();
- request.open('GET', '/photos/luximage/data/admin/preview/'+cur+'/', true);
+ request.open('GET', '/photos/data/admin/preview/'+cur+'/', true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
var data = JSON.parse(request.responseText);
diff --git a/app/media/templatetags/get_image_by_size.py b/app/media/templatetags/get_image_by_size.py
index c56c44e..a0a62f0 100644
--- a/app/media/templatetags/get_image_by_size.py
+++ b/app/media/templatetags/get_image_by_size.py
@@ -4,5 +4,5 @@ register = template.Library()
@register.simple_tag
def get_image_by_size(obj, *args):
- method = getattr(obj, "get_image_by_size")
+ method = getattr(obj, "get_image_url_by_size")
return method(*args)
diff --git a/app/media/utils.py b/app/media/utils.py
index 84e72f5..893663c 100644
--- a/app/media/utils.py
+++ b/app/media/utils.py
@@ -1,28 +1,26 @@
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=""):
+def resize_image(img, width=None, height=None, quality=72, filepath=""):
+ """
+ given an image object, size, and filepath
+ resize the image, then save it , size, and filepath
+ resize the image, then save it at the filepath
+ """
+ base_path = os.path.dirname(filepath)
+ if not os.path.isdir(base_path):
+ os.makedirs(base_path)
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])
-
-
+ newimg.save(filepath, newimg.format, quality=quality)
+ subprocess.call(["jpegoptim", "%s" % filepath])
diff --git a/app/media/views.py b/app/media/views.py
index 915b022..04ac11c 100644
--- a/app/media/views.py
+++ b/app/media/views.py
@@ -1,10 +1,10 @@
import json
-from django.shortcuts import render_to_response, render
+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 .models import LuxGallery, LuxImage
from locations.models import Country, Region
from utils.views import PaginatedListView
@@ -52,10 +52,10 @@ class GalleryList(PaginatedListView):
class OldGalleryList(PaginatedListView):
template_name = 'archives/gallery_list.html'
- model = PhotoGallery
+ model = LuxGallery
def get_queryset(self):
- return PhotoGallery.objects.filter(is_public=True)
+ return LuxGallery.objects.filter(is_public=True)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
@@ -74,7 +74,7 @@ def gallery_list(request, page):
request.page_url = '/photos/%d/'
request.page = int(page)
context = {
- 'object_list': PhotoGallery.objects.all(),
+ 'object_list': LuxGallery.objects.all(),
'page': page,
}
return render(request, "archives/photos.html", context)
@@ -82,13 +82,13 @@ def gallery_list(request, page):
def gallery(request, slug):
context = {
- 'object': PhotoGallery.objects.get(set_slug=slug)
+ 'object': LuxGallery.objects.get(set_slug=slug)
}
- return render_to_response('details/photo_galleries.html', context, context_instance=RequestContext(request))
+ return render(request, 'details/photo_galleries.html', context)
def photo_json(request, slug):
- p = PhotoGallery.objects.filter(set_slug=slug)
+ p = LuxGallery.objects.filter(set_slug=slug)
return HttpResponse(serializers.serialize('json', p), mimetype='application/json')
@@ -103,7 +103,7 @@ def photo_preview_json(request, pk):
def thumb_preview_json(request, pk):
p = LuxImage.objects.get(pk=pk)
data = {}
- data['url'] = p.get_admin_insert()
+ data['url'] = p.get_image_url_by_size('tn')
data = json.dumps(data)
return HttpResponse(data)
@@ -114,10 +114,10 @@ def gallery_list_by_area(request, slug, page):
request.page = int(page)
try:
region = Region.objects.get(slug__exact=slug)
- qs = PhotoGallery.objects.filter(region=region).order_by('-id')
+ qs = LuxGallery.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')
+ qs = LuxGallery.objects.filter(location__state__country=region).order_by('-id')
if not region:
raise Http404
context = {
@@ -127,4 +127,4 @@ def gallery_list_by_area(request, slug, page):
'region': region,
'page': page
}
- return render_to_response("archives/photos.html", context, context_instance=RequestContext(request))
+ return render(request, "archives/photos.html", context)
diff --git a/app/normalize/migrations/0001_initial.py b/app/normalize/migrations/0001_initial.py
index 358677a..08c4f5b 100644
--- a/app/normalize/migrations/0001_initial.py
+++ b/app/normalize/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.1.3 on 2020-11-30 22:45
+# Generated by Django 4.1.3 on 2022-12-02 20:09
from django.db import migrations, models
import django.db.models.deletion
@@ -16,7 +16,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='RelatedPost',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('entry_id', models.IntegerField()),
('title', models.CharField(max_length=200)),
('slug', models.CharField(max_length=50)),
diff --git a/app/pages/migrations/0001_initial.py b/app/pages/migrations/0001_initial.py
index 61213e1..6227712 100644
--- a/app/pages/migrations/0001_initial.py
+++ b/app/pages/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.1.3 on 2020-11-30 22:44
+# Generated by Django 4.1.3 on 2022-12-02 20:08
from django.db import migrations, models
import django.db.models.deletion
@@ -18,7 +18,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Page',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(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=300)),
('slug', models.SlugField()),
@@ -37,13 +37,11 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='HomePage',
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)),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('tag_line', models.CharField(blank=True, max_length=200, null=True)),
('template_name', models.CharField(blank=True, help_text='full path', max_length=200, null=True)),
- ('featured', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='banner', to='posts.post')),
- ('featured_image', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='media.luximage')),
- ('popular', models.ManyToManyField(related_name='popular', to='posts.Post')),
+ ('featured', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='banner', to='posts.post')),
+ ('featured_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='media.luximage')),
],
),
]
diff --git a/app/pages/migrations/0002_auto_20211030_1634.py b/app/pages/migrations/0002_auto_20211030_1634.py
deleted file mode 100644
index 7a8649b..0000000
--- a/app/pages/migrations/0002_auto_20211030_1634.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Generated by Django 3.2.8 on 2021-10-30 16:34
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('posts', '0002_alter_post_id'),
- ('media', '0002_auto_20211030_1634'),
- ('pages', '0001_initial'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='homepage',
- name='image_offset_vertical',
- ),
- migrations.RemoveField(
- model_name='homepage',
- name='popular',
- ),
- migrations.AlterField(
- model_name='homepage',
- name='featured',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='banner', to='posts.post'),
- ),
- migrations.AlterField(
- model_name='homepage',
- name='featured_image',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='media.luximage'),
- ),
- migrations.AlterField(
- model_name='homepage',
- name='id',
- field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- migrations.AlterField(
- model_name='page',
- name='id',
- field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- ]
diff --git a/app/pages/templates/pages/luxagraf/homepage.html b/app/pages/templates/pages/luxagraf/homepage.html
index c8075ea..bde11b9 100644
--- a/app/pages/templates/pages/luxagraf/homepage.html
+++ b/app/pages/templates/pages/luxagraf/homepage.html
@@ -2,7 +2,7 @@
{% load typogrify_tags %}
{% block sitename %}
<head itemscope itemtype="http://schema.org/WebSite">
- <title itemprop='name'>Luxagraf: thoughts on ecology, culture, travel, photography, walking and other ephemera</title>
+ <title itemprop='name'>Libregraf Publishing. Fine books for fine people</title>
<link rel="canonical" href="https://luxagraf.net/">{%endblock%}
{%block extrahead%}
diff --git a/app/podcasts/admin.py b/app/podcasts/admin.py
new file mode 100644
index 0000000..b6adfc6
--- /dev/null
+++ b/app/podcasts/admin.py
@@ -0,0 +1,10 @@
+from django.contrib import admin
+from .models import Podcast
+
+@admin.register(Podcast)
+class PodcastAdmin(admin.ModelAdmin):
+ list_display = ('title',)
+ search_fields = ['title', 'body_markdown']
+
+ class Media:
+ js = ('next-prev-links.js',)
diff --git a/app/podcasts/feeds.py b/app/podcasts/feeds.py
new file mode 100644
index 0000000..24d47bd
--- /dev/null
+++ b/app/podcasts/feeds.py
@@ -0,0 +1,229 @@
+import datetime
+from podcasts.models import Episode, Podcast
+from django.contrib.syndication.views import Feed
+from django.contrib.sites.shortcuts import get_current_site
+from django.views.generic.base import RedirectView
+from django.utils.feedgenerator import rfc2822_date, Rss201rev2Feed, Atom1Feed
+from django.shortcuts import get_object_or_404
+
+from .models import MIME_CHOICES
+
+class ITunesElements(object):
+
+ def add_root_elements(self, handler):
+ """ Add additional elements to the podcast object"""
+ super(ITunesElements, self).add_root_elements(handler)
+
+ podcast = self.feed["podcast"]
+
+ if podcast.featured_image:
+ # grab thumbs here
+ #itunes_sm_url = thumbnailer.get_thumbnail(aliases["itunes_sm"]).url
+ #itunes_lg_url = thumbnailer.get_thumbnail(aliases["itunes_lg"]).url
+ if itunes_sm_url and itunes_lg_url:
+ handler.addQuickElement("itunes:image", attrs={"href": itunes_lg_url})
+ handler.startElement("image", {})
+ handler.addQuickElement("url", itunes_sm_url)
+ handler.addQuickElement("title", self.feed["title"])
+ handler.addQuickElement("link", self.feed["link"])
+ handler.endElement("image")
+
+ handler.addQuickElement("guid", str(podcast.uuid), attrs={"isPermaLink": "false"})
+ handler.addQuickElement("itunes:subtitle", self.feed["subtitle"])
+ handler.addQuickElement("itunes:author", podcast.publisher)
+ handler.startElement("itunes:owner", {})
+ handler.addQuickElement("itunes:name", podcast.publisher)
+ handler.addQuickElement("itunes:email", podcast.publisher_email)
+ handler.endElement("itunes:owner")
+ handler.addQuickElement("itunes:category", attrs={"text": self.feed["categories"][0]})
+ handler.addQuickElement("itunes:summary", podcast.description)
+ handler.addQuickElement("itunes:explicit", "no")
+ handler.addQuickElement("keywords", podcast.keywords)
+ try:
+ handler.addQuickElement("lastBuildDate",
+ rfc2822_date(podcast.episode_set.filter(status=1)[1].pub_date))
+ except IndexError:
+ pass
+ handler.addQuickElement("generator", "Luxagraf's Django Web Framework")
+ handler.addQuickElement("docs", "http://blogs.law.harvard.edu/tech/rss")
+
+ def add_item_elements(self, handler, item):
+ """ Add additional elements to the episode object"""
+ super(ITunesElements, self).add_item_elements(handler, item)
+
+ podcast = item["podcast"]
+ episode = item["episode"]
+ if episode.featured_image:
+ #grab episode thumbs
+ #itunes_sm_url = None
+ #itunes_lg_url = None
+ if itunes_sm_url and itunes_lg_url:
+ handler.addQuickElement("itunes:image", attrs={"href": itunes_lg_url})
+ handler.startElement("image", {})
+ handler.addQuickElement("url", itunes_sm_url)
+ handler.addQuickElement("title", episode.title)
+ handler.addQuickElement("link", episode.get_absolute_url())
+ handler.endElement("image")
+
+ handler.addQuickElement("guid", str(episode.uuid), attrs={"isPermaLink": "false"})
+ handler.addQuickElement("copyright", "{0} {1}".format(podcast.license,
+ datetime.date.today().year))
+ handler.addQuickElement("itunes:author", episode.podcast.publisher)
+ handler.addQuickElement("itunes:subtitle", episode.subtitle)
+ handler.addQuickElement("itunes:summary", episode.description)
+ handler.addQuickElement("itunes:duration", "%02d:%02d:%02d" % (episode.hours,
+ episode.minutes,
+ episode.seconds))
+ handler.addQuickElement("itunes:keywords", episode.keywords)
+ handler.addQuickElement("itunes:explicit", "no")
+ if episode.block:
+ handler.addQuickElement("itunes:block", "yes")
+
+ def namespace_attributes(self):
+ return {"xmlns:itunes": "http://www.itunes.com/dtds/podcast-1.0.dtd"}
+
+
+class AtomITunesFeedGenerator(ITunesElements, Atom1Feed):
+ def root_attributes(self):
+ atom_attrs = super(AtomITunesFeedGenerator, self).root_attributes()
+ atom_attrs.update(self.namespace_attributes())
+ return atom_attrs
+
+
+class RssITunesFeedGenerator(ITunesElements, Rss201rev2Feed):
+ def rss_attributes(self):
+ rss_attrs = super(RssITunesFeedGenerator, self).rss_attributes()
+ rss_attrs.update(self.namespace_attributes())
+ return rss_attrs
+
+
+class ShowFeed(Feed):
+ """
+ A feed of podcasts for iTunes and other compatible podcatchers.
+ """
+ def title(self, podcast):
+ return podcast.title
+
+ def link(self, podcast):
+ return podcast.get_absolute_url()
+
+ def categories(self, podcast):
+ return ("Music",)
+
+ def feed_copyright(self, podcast):
+ return "{0} {1}".format(podcast.license, datetime.date.today().year)
+
+ def ttl(self, podcast):
+ return podcast.ttl
+
+ def items(self, podcast):
+ return podcast.episode_set.filter(status=1)[:300]
+
+ def get_object(self, request, *args, **kwargs):
+ self.mime = [mc[0] for mc in MIME_CHOICES if mc[0] == kwargs["mime_type"]][0]
+ site = get_current_site(request)
+ self.podcast = get_object_or_404(Podcast, slug=kwargs["show_slug"])
+ return self.podcast
+
+ def item_title(self, episode):
+ return episode.title
+
+ def item_description(self, episode):
+ "renders summary for atom"
+ return episode.description
+
+ def item_link(self, episode):
+ return reverse("podcasting_episode_detail",
+ kwargs={"podcast_slug": self.podcast.slug, "slug": episode.slug})
+
+ # def item_author_link(self, episode):
+ # return "todo" #this one doesn't add anything in atom or rss
+ #
+ # def item_author_email(self, episode):
+ # return "todo" #this one doesn't add anything in atom or rss
+
+ def item_pubdate(self, episode):
+ return episode.pub_date
+
+ def item_categories(self, episode):
+ return self.categories(self.podcast)
+
+ def item_enclosure_url(self, episode):
+ try:
+ e = episode.enclosure_set.get(mime=self.mime)
+ return e.url
+ except Enclosure.DoesNotExist:
+ pass
+
+ def item_enclosure_length(self, episode):
+ try:
+ e = episode.enclosure_set.get(mime=self.mime)
+ return e.size
+ except Enclosure.DoesNotExist:
+ pass
+
+ def item_enclosure_mime_type(self, episode):
+ try:
+ e = episode.enclosure_set.get(mime=self.mime)
+ return e.get_mime_display()
+ except Enclosure.DoesNotExist:
+ pass
+
+ def item_keywords(self, episode):
+ return episode.keywords
+
+ def feed_extra_kwargs(self, obj):
+ extra = {}
+ extra["podcast"] = self.podcast
+ return extra
+
+ def item_extra_kwargs(self, item):
+ extra = {}
+ extra["podcast"] = self.podcast
+ extra["episode"] = item
+ return extra
+
+
+class AtomShowFeed(ShowFeed):
+ feed_type = AtomITunesFeedGenerator
+
+ def subtitle(self, show):
+ return show.subtitle
+
+ def author_name(self, show):
+ return show.publisher
+
+ def author_email(self, show):
+ return show.publisher_email
+
+ def author_link(self, show):
+ return show.get_absolute_url()
+
+
+class RssShowFeed(ShowFeed):
+ feed_type = RssITunesFeedGenerator
+
+ def item_guid(self, episode):
+ "ITunesElements can't add isPermaLink attr unless None is returned here."
+ return None
+
+ def description(self, show):
+ return show.description
+
+
+class AtomRedirectView(RedirectView):
+ permanent = False
+
+ def get_redirect_url(self, show_slug, mime_type):
+ return reverse(
+ "podcasts_show_feed_atom",
+ kwargs={"show_slug": show_slug, "mime_type": mime_type})
+
+
+class RssRedirectView(RedirectView):
+ permanent = False
+
+ def get_redirect_url(self, show_slug, mime_type):
+ return reverse(
+ "podcasts_show_feed_rss",
+ kwargs={"show_slug": show_slug, "mime_type": mime_type})
diff --git a/app/podcasts/migrations/0001_initial.py b/app/podcasts/migrations/0001_initial.py
new file mode 100644
index 0000000..644e7dc
--- /dev/null
+++ b/app/podcasts/migrations/0001_initial.py
@@ -0,0 +1,60 @@
+# Generated by Django 4.1.3 on 2022-12-02 20:09
+
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('media', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Podcast',
+ fields=[
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ ('title', models.CharField(max_length=255)),
+ ('subtitle', models.CharField(blank=True, max_length=255, null=True)),
+ ('slug', models.SlugField()),
+ ('publisher', models.CharField(max_length=255)),
+ ('publisher_email', models.CharField(max_length=255)),
+ ('description', models.TextField()),
+ ('keywords', models.CharField(blank=True, help_text='A comma-delimited list of words for searches, up to 12;', max_length=255)),
+ ('license', models.TextField(blank=True, null=True)),
+ ('featured_image', models.ForeignKey(blank=True, help_text='square JPEG (.jpg) or PNG (.png) image at a size of 1400x1400 pixels.', null=True, on_delete=django.db.models.deletion.CASCADE, to='media.luximage')),
+ ],
+ options={
+ 'verbose_name': 'Podcast',
+ 'verbose_name_plural': 'Podcasts',
+ 'ordering': ('title', 'slug'),
+ },
+ ),
+ migrations.CreateModel(
+ name='Episode',
+ fields=[
+ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
+ ('title', models.CharField(max_length=255)),
+ ('subtitle', models.CharField(blank=True, max_length=255, null=True)),
+ ('date_created', models.DateTimeField(auto_now_add=True)),
+ ('date_updated', models.DateTimeField(auto_now=True)),
+ ('pub_date', models.DateTimeField(blank=True, null=True)),
+ ('enable_comments', models.BooleanField(default=True)),
+ ('slug', models.SlugField()),
+ ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=0)),
+ ('description', models.TextField()),
+ ('keywords', models.CharField(blank=True, help_text='A comma-delimited list of words for searches, up to 12;', max_length=255)),
+ ('featured_image', models.ForeignKey(blank=True, help_text='square JPEG (.jpg) or PNG (.png) image at a size of 1400x1400 pixels.', null=True, on_delete=django.db.models.deletion.CASCADE, to='media.luximage')),
+ ('podcast', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='podcasts.podcast')),
+ ],
+ options={
+ 'verbose_name': 'Episode',
+ 'verbose_name_plural': 'Episodes',
+ 'ordering': ('-pub_date', 'slug'),
+ },
+ ),
+ ]
diff --git a/app/accounts/migrations/__init__.py b/app/podcasts/migrations/__init__.py
index e69de29..e69de29 100644
--- a/app/accounts/migrations/__init__.py
+++ b/app/podcasts/migrations/__init__.py
diff --git a/app/podcasts/models.py b/app/podcasts/models.py
new file mode 100644
index 0000000..b574986
--- /dev/null
+++ b/app/podcasts/models.py
@@ -0,0 +1,132 @@
+import os
+import uuid
+from django.db import models
+from django.urls import reverse
+from django.template.defaultfilters import slugify
+from django.conf import settings
+
+# optional external dependencies
+try:
+ from licenses.models import License
+except:
+ License = None
+
+from taggit.managers import TaggableManager
+from mutagen.mp3 import MP3
+
+from media.models import LuxAudio, LuxImage
+
+def get_show_upload_folder(instance, pathname):
+ "A standardized pathname for uploaded files and images."
+ root, ext = os.path.splitext(pathname)
+ return "{0}/podcasts/{1}/{2}{3}".format(
+ settings.PODCASTING_IMG_PATH, instance.slug, slugify(root), ext
+ )
+
+
+def get_episode_upload_folder(instance, pathname):
+ "A standardized pathname for uploaded files and images."
+ root, ext = os.path.splitext(pathname)
+ if instance.shows.count() == 1:
+ return "{0}/podcasts/{1}/episodes/{2}{3}".format(
+ settings.PODCASTING_IMG_PATH, instance.shows.all()[0].slug, slugify(root), ext
+ )
+ else:
+ return "{0}/podcasts/episodes/{1}/{2}{3}".format(
+ settings.PODCASTING_IMG_PATH, instance.slug, slugify(root), ext
+ )
+
+MIME_CHOICES = (
+ ("mp3", "audio/mpeg"),
+ ("mp4", "audio/mp4"),
+ ("ogg", "audio/ogg"),
+)
+
+
+#audio = MP3("example.mp3")
+#print(audio.info.length)
+
+class Podcast(models.Model):
+ uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
+ title = models.CharField(max_length=255)
+ subtitle = models.CharField(max_length=255, blank=True, null=True)
+ slug = models.SlugField()
+ publisher = models.CharField(max_length=255)
+ publisher_email = models.CharField(max_length=255)
+ description = models.TextField()
+ keywords = models.CharField(max_length=255, blank=True, help_text="A comma-delimited list of words for searches, up to 12;")
+ license = models.TextField(blank=True, null=True)
+ featured_image = models.ForeignKey(LuxImage, on_delete=models.CASCADE, null=True, blank=True, help_text=("square JPEG (.jpg) or PNG (.png) image at a size of 1400x1400 pixels."))
+
+
+ class Meta:
+ verbose_name = "Podcast"
+ verbose_name_plural = "Podcasts"
+ ordering = ("title", "slug")
+
+ def __str__(self):
+ return self.title
+
+ def get_absolute_url(self):
+ return reverse("podcasts:list", kwargs={"slug": self.slug})
+
+ def ttl(self):
+ return "1440"
+
+
+class Episode(models.Model):
+ """
+ An individual podcast episode and it's unique attributes.
+ """
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
+ title = models.CharField(max_length=255)
+ subtitle = models.CharField(max_length=255, blank=True, null=True)
+ date_created = models.DateTimeField(auto_now_add=True, editable=False)
+ date_updated = models.DateTimeField(auto_now=True, editable=False)
+ pub_date = models.DateTimeField(null=True, blank=True)
+ podcast = models.ForeignKey(Podcast, on_delete=models.PROTECT)
+ enable_comments = models.BooleanField(default=True)
+ slug = models.SlugField()
+ PUB_STATUS = (
+ (0, 'Draft'),
+ (1, 'Published'),
+ )
+ status = models.IntegerField(choices=PUB_STATUS, default=0)
+ description = models.TextField()
+ featured_image = models.ForeignKey(LuxImage, on_delete=models.CASCADE, null=True, blank=True, help_text=("square JPEG (.jpg) or PNG (.png) image at a size of 1400x1400 pixels."))
+ # iTunes specific fields
+ keywords = models.CharField(max_length=255, blank=True, help_text="A comma-delimited list of words for searches, up to 12;")
+
+ class Meta:
+ verbose_name = "Episode"
+ verbose_name_plural = "Episodes"
+ ordering = ("-pub_date", "slug")
+
+ def __str__(self):
+ return self.title
+
+ def get_absolute_url(self):
+ return reverse("podcasting_episode_detail", kwargs={"show_slug": self.shows.all()[0].slug, "slug": self.slug})
+
+ def get_next(self):
+ next = self.__class__.objects.filter(published__gt=self.published)
+ try:
+ return next[0]
+ except IndexError:
+ return False
+
+ def get_prev(self):
+ prev = self.__class__.objects.filter(published__lt=self.published).order_by("-published")
+ try:
+ return prev[0]
+ except IndexError:
+ return False
+
+ def get_explicit_display(self):
+ return "no"
+
+ def seconds_total(self):
+ try:
+ return self.minutes * 60 + self.seconds
+ except:
+ return 0
diff --git a/app/posts/templates/posts/podcast_detail.html b/app/podcasts/templates/podcasts/detail.html
index 4fec3b1..361d822 100644
--- a/app/posts/templates/posts/podcast_detail.html
+++ b/app/podcasts/templates/podcasts/detail.html
@@ -29,7 +29,7 @@
<div class="entry-footer">
<aside class="narrow donate">
<h3>Support</h3>
- <p>Want to help support Lulu and Birdie? You can buy the book, or you can donate a few dollars.</p>
+ <p>Want to help support Lulu and Birdie? You can <a href="/bookshop/">buy the book</a>, or you can donate a few dollars.</p>
<div class="donate-btn">
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
diff --git a/app/posts/templates/posts/podcast_list.html b/app/podcasts/templates/podcasts/list.html
index 8c73db4..89b7ea8 100644
--- a/app/posts/templates/posts/podcast_list.html
+++ b/app/podcasts/templates/podcasts/list.html
@@ -8,8 +8,8 @@
{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %}
{% block primary %}<main role="main" class="archive-wrapper">
<div class="archive-intro">
- <h1 class="archive-hed">The Adventures of Lulu, Birdie, and Henry: The Podcast.</h1>
- <h2 class="list-subhed">Let's see what happens.</h2>
+ <h1 class="archive-hed">{{podcast.title}}</h1>
+ {% if object.subtitle %}<h2 class="list-subhed">{{podcast.subtitle}}</h2>{% endif %}
</div>
<h1 class="archive-sans">Episodes</h1>{% autopaginate object_list 24 %}
@@ -22,7 +22,9 @@
</a>
</li>
{%endfor%}</ul>
-
+ <a href="{% url 'podcasts_show_feed_atom' podcast.slug 'mp3' %}" >MP3</a>
+ <a href="{% url 'podcasts_show_feed_atom' podcast.slug 'mp4' %}" >MP4</a>
+ <a href="{% url 'podcasts_show_feed_rss' podcast.slug 'mp3' %}" >OGG</a>
</main>
diff --git a/app/podcasts/urls.py b/app/podcasts/urls.py
new file mode 100644
index 0000000..622b203
--- /dev/null
+++ b/app/podcasts/urls.py
@@ -0,0 +1,19 @@
+from django.urls import path, re_path
+
+from . import views
+
+app_name = "podcasts"
+
+urlpatterns = [
+ re_path(
+ r'<str:slug>/<int:page>',
+ views.PodcastListView.as_view(),
+ name="list"
+ ),
+ path(
+ r'<str:slug>/',
+ views.PodcastListView.as_view(),
+ {'page':1},
+ name="list"
+ ),
+]
diff --git a/app/podcasts/urls_feeds.py b/app/podcasts/urls_feeds.py
new file mode 100644
index 0000000..07e02ae
--- /dev/null
+++ b/app/podcasts/urls_feeds.py
@@ -0,0 +1,17 @@
+from django.urls import include, re_path
+
+from .feeds import RssShowFeed, AtomShowFeed, AtomRedirectView, RssRedirectView
+from .models import MIME_CHOICES
+
+
+MIMES = "|".join([enclosure[0] for enclosure in MIME_CHOICES])
+
+
+urlpatterns = [
+ # Episode list feed by podcast (RSS 2.0 and iTunes)
+ re_path(r"^(?P<show_slug>[-\w]+)/(?P<mime_type>{mimes})/rss/$".format(mimes=MIMES),
+ RssShowFeed(), name="podcasts_show_feed_rss"),
+ # Episode list feed by show (Atom)
+ re_path(r"^(?P<show_slug>[-\w]+)/(?P<mime_type>{mimes})/atom/$".format(mimes=MIMES),
+ AtomShowFeed(), name="podcasts_show_feed_atom"),
+]
diff --git a/app/podcasts/views.py b/app/podcasts/views.py
new file mode 100644
index 0000000..a8edbfa
--- /dev/null
+++ b/app/podcasts/views.py
@@ -0,0 +1,29 @@
+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.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 Episode, Podcast
+
+
+class PodcastListView(PaginatedListView):
+ """
+ Return a list of Episodes in reverse chronological order
+ """
+ model = Podcast
+ template_name = "podcasts/list.html"
+ queryset = Episode.objects.filter(podcast=1).filter(status__exact=1).order_by('-pub_date')
+
+ def get_context_data(self, **kwargs):
+ context = super(PodcastListView, self).get_context_data(**kwargs)
+ context['breadcrumbs'] = ['podcast',]
+ context['podcast'] = Podcast.objects.get(title="The Lulu & Birdie Podcast")
+ return context
+
diff --git a/app/posts/migrations/0001_initial.py b/app/posts/migrations/0001_initial.py
index af70a9f..218eed5 100644
--- a/app/posts/migrations/0001_initial.py
+++ b/app/posts/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.1.3 on 2020-11-30 22:44
+# Generated by Django 4.1.3 on 2022-12-02 20:08
from django.db import migrations, models
import django.db.models.deletion
@@ -10,17 +10,16 @@ class Migration(migrations.Migration):
dependencies = [
('books', '__first__'),
- ('normalize', '__first__'),
- ('taxonomy', '__first__'),
- ('sites', '0002_alter_domain_unique'),
('media', '__first__'),
+ ('taxonomy', '__first__'),
+ ('normalize', '__first__'),
]
operations = [
migrations.CreateModel(
name='Post',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('short_title', models.CharField(blank=True, max_length=200, null=True)),
('subtitle', models.CharField(blank=True, max_length=200)),
@@ -37,18 +36,18 @@ class Migration(migrations.Migration):
('last_updated', models.DateTimeField(auto_now=True)),
('enable_comments', models.BooleanField(default=False)),
('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Published')], default=0)),
- ('post_type', models.IntegerField(choices=[(0, 'field test'), (1, 'review'), (2, 'essay'), (3, 'src'), (4, 'jrnl'), (5, 'field note')], default=4)),
- ('template_name', models.IntegerField(choices=[(0, 'single'), (1, 'double'), (2, 'single-dark'), (3, 'double-dark'), (4, 'single-black'), (5, 'double-black')], default=0)),
+ ('post_type', models.IntegerField(choices=[(0, 'Podcast'), (1, 'jrnl'), (2, 'field note')], default=1)),
('has_video', models.BooleanField(blank=True, default=False)),
('has_code', models.BooleanField(blank=True, default=False)),
('disclaimer', models.BooleanField(blank=True, default=False)),
('originally_published_by', models.CharField(blank=True, max_length=400, null=True)),
('originally_published_by_url', models.CharField(blank=True, max_length=400, null=True)),
- ('books', models.ManyToManyField(blank=True, to='books.Book')),
+ ('issue', models.PositiveIntegerField(null=True)),
+ ('books', models.ManyToManyField(blank=True, to='books.book')),
+ ('featured_audio', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='media.luxaudio')),
('featured_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='media.luximage')),
- ('related', models.ManyToManyField(blank=True, to='normalize.RelatedPost')),
- ('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.site')),
- ('topics', models.ManyToManyField(blank=True, to='taxonomy.Category')),
+ ('related', models.ManyToManyField(blank=True, to='normalize.relatedpost')),
+ ('topics', models.ManyToManyField(blank=True, to='taxonomy.category')),
],
options={
'ordering': ('-pub_date',),
diff --git a/app/posts/migrations/0002_alter_post_id.py b/app/posts/migrations/0002_alter_post_id.py
deleted file mode 100644
index a41f999..0000000
--- a/app/posts/migrations/0002_alter_post_id.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.2.8 on 2021-10-30 16:34
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('posts', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='post',
- name='id',
- field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
- ),
- ]
diff --git a/app/posts/migrations/0003_auto_20211030_1955.py b/app/posts/migrations/0003_auto_20211030_1955.py
deleted file mode 100644
index b54cdd8..0000000
--- a/app/posts/migrations/0003_auto_20211030_1955.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Generated by Django 3.2.8 on 2021-10-30 19:55
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('media', '0002_auto_20211030_1634'),
- ('posts', '0002_alter_post_id'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='post',
- name='site',
- ),
- migrations.RemoveField(
- model_name='post',
- name='template_name',
- ),
- migrations.AddField(
- model_name='post',
- name='featured_audio',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='media.luxaudio'),
- ),
- migrations.AddField(
- model_name='post',
- name='issue',
- field=models.PositiveIntegerField(null=True),
- ),
- migrations.AlterField(
- model_name='post',
- name='post_type',
- field=models.IntegerField(choices=[(0, 'Podcast'), (1, 'jrnl'), (2, 'field note')], default=1),
- ),
- ]
diff --git a/app/taxonomy/admin.py b/app/taxonomy/admin.py
index 783584e..45e4e26 100644
--- a/app/taxonomy/admin.py
+++ b/app/taxonomy/admin.py
@@ -14,6 +14,9 @@ class CategoryAdmin(admin.ModelAdmin):
'name',
'color_rgb',
'slug',
+ "pluralized_name",
+ "description",
+ "intro_markdown",
),
'classes': (
'show',
diff --git a/app/taxonomy/migrations/0001_initial.py b/app/taxonomy/migrations/0001_initial.py
index bde5e44..58e7cdb 100644
--- a/app/taxonomy/migrations/0001_initial.py
+++ b/app/taxonomy/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.1.3 on 2020-11-30 22:45
+# Generated by Django 4.1.3 on 2022-12-02 20:08
from django.db import migrations, models
import django.db.models.deletion
@@ -16,9 +16,12 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Category',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=250)),
- ('pluralized_name', models.CharField(max_length=60, null=True)),
+ ('pluralized_name', models.CharField(blank=True, max_length=60, null=True)),
+ ('description', models.CharField(blank=True, max_length=300, null=True)),
+ ('intro_markdown', models.TextField(blank=True, null=True)),
+ ('intro_html', models.TextField(blank=True, null=True)),
('slug', models.SlugField(blank=True)),
('color_rgb', models.CharField(blank=True, max_length=20)),
('date_created', models.DateTimeField(auto_now_add=True)),
@@ -32,9 +35,9 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='LuxTag',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True, verbose_name='name')),
- ('slug', models.SlugField(max_length=100, unique=True, verbose_name='slug')),
+ ('slug', models.SlugField(allow_unicode=True, max_length=100, unique=True, verbose_name='slug')),
('color_rgb', models.CharField(blank=True, max_length=20)),
],
options={
@@ -45,10 +48,10 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='TaggedItems',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.IntegerField(db_index=True, verbose_name='object ID')),
- ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='taxonomy_taggeditems_tagged_items', to='contenttypes.contenttype', verbose_name='content type')),
- ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='taxonomy_taggeditems_items', to='taxonomy.luxtag')),
+ ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_tagged_items', to='contenttypes.contenttype', verbose_name='content type')),
+ ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_items', to='taxonomy.luxtag')),
],
options={
'abstract': False,
diff --git a/app/taxonomy/models.py b/app/taxonomy/models.py
index 4db3294..736fe15 100644
--- a/app/taxonomy/models.py
+++ b/app/taxonomy/models.py
@@ -1,8 +1,10 @@
from django.contrib.gis.db import models
from django.urls import reverse
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from django.utils.functional import cached_property
+from utils.util import markdown_to_html
+
from taggit.models import TagBase, GenericTaggedItemBase
@@ -27,7 +29,10 @@ class TaggedItems(GenericTaggedItemBase):
class Category(models.Model):
""" Generic model for Categories """
name = models.CharField(max_length=250)
- pluralized_name = models.CharField(max_length=60, null=True)
+ pluralized_name = models.CharField(max_length=60, null=True, blank=True)
+ description = models.CharField(max_length=300, null=True, blank=True)
+ intro_markdown = models.TextField(null=True, blank=True)
+ intro_html = models.TextField(null=True, blank=True)
slug = models.SlugField(blank=True)
color_rgb = models.CharField(max_length=20, blank=True)
date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False)
@@ -42,3 +47,9 @@ class Category(models.Model):
def get_absolute_url(self):
return reverse("taxonomy:cat-detail", kwargs={"slug": self.slug})
+
+
+ def save(self, *args, **kwargs):
+ if self.intro_markdown:
+ self.intro_html = markdown_to_html(self.intro_markdown)
+ super(Category, self).save(*args, **kwargs)
diff --git a/app/taxonomy/views.py b/app/taxonomy/views.py
index 2d749ab..354234c 100644
--- a/app/taxonomy/views.py
+++ b/app/taxonomy/views.py
@@ -1,14 +1,12 @@
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 .models import Category
+from utils.views import LuxDetailView
-class CategoryDetailView(DetailView):
+class CategoryDetailView(LuxDetailView):
model = Category
slug_field = "slug"
diff --git a/app/utils/static/image-loader.js b/app/utils/static/image-loader.js
index 2744251..ca96565 100644
--- a/app/utils/static/image-loader.js
+++ b/app/utils/static/image-loader.js
@@ -13,9 +13,9 @@ function add_images(){
var loop = Number(element.dataset.loopcounter);
if (cur != "") {
if (loop <= 100) {
- console.log(loop);
+ console.log('/photos/data/admin/tn/'+cur+'/');
var request = new XMLHttpRequest();
- request.open('GET', '/photos/luximage/data/admin/tn/'+cur+'/', true);
+ request.open('GET', '/photos/data/admin/tn/'+cur+'/', true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
var data = JSON.parse(request.responseText);
@@ -41,7 +41,11 @@ function add_images(){
}
document.addEventListener("DOMContentLoaded", function(event) {
add_images();
- md = document.forms["entry_form"].elements["body_markdown"];
+ if (document.forms["post_form"]) {
+ md = document.forms["post_form"].elements["body_markdown"];
+ } else {
+ md = document.forms["track_form"].elements["body_markdown"];
+ }
md.style.maxHeight = "300rem";
md.style.maxWidth = "300rem";
});
diff --git a/app/utils/util.py b/app/utils/util.py
index dc04b0b..d9b2318 100644
--- a/app/utils/util.py
+++ b/app/utils/util.py
@@ -45,7 +45,7 @@ def extract_main_image(markdown):
try:
image = soup.find_all('img')[0]['id']
img_pk = image.split('image-')[1]
- return apps.get_model('photos', 'LuxImage').objects.get(pk=img_pk)
+ return apps.get_model('media', 'LuxImage').objects.get(pk=img_pk)
except IndexError:
return None
@@ -74,7 +74,7 @@ def parse_image(s):
else:
try:
image_id = img['id'].split("image-")[1]
- i = apps.get_model('photos', 'LuxImage').objects.get(pk=image_id)
+ i = apps.get_model('media', 'LuxImage').objects.get(pk=image_id)
caption = False
exif = False
cluster_class = None
@@ -141,7 +141,7 @@ def parse_reg_bio_page():
try:
image = soup.find_all('img')[0]['id']
img_pk = image.split('image-')[1]
- return apps.get_model('photos', 'LuxImage').objects.get(pk=img_pk)
+ return apps.get_model('media', 'LuxImage').objects.get(pk=img_pk)
except IndexError:
return None
diff --git a/app/utils/views.py b/app/utils/views.py
index 6b69b25..b678a33 100644
--- a/app/utils/views.py
+++ b/app/utils/views.py
@@ -6,16 +6,17 @@ from django.views.generic import ListView, DetailView
from django.apps import apps
from django.shortcuts import render
from django.template import RequestContext
+from django.template.defaultfilters import slugify
from media.models import LuxImage, LuxVideo, LuxAudio
BREADCRUMBS = {
- 'SrcPost':'SRC',
+ 'AP':'dialogue',
'Book':'Book Notes',
'Entry':'Jrnl',
'NewsletterMailing':'lttr',
- 'LuxImage':'lttr'
+ 'LuxImage':'lttr',
}
class PaginatedListView(ListView):
@@ -35,13 +36,6 @@ class PaginatedListView(ListView):
request.base_path = path
return super(PaginatedListView, self).dispatch(request, *args, **kwargs)
- def get_context_data(self, **kwargs):
- '''
- Adds breadcrumb path to every view
- '''
- # Call the base implementation first to get a context
- context = super(PaginatedListView, self).get_context_data(**kwargs)
- print('model=', self.model)
try:
context['breadcrumbs'] = (BREADCRUMBS[self.model.__name__],)
except KeyError:
@@ -61,22 +55,31 @@ class LuxDetailView(DetailView):
context = super(LuxDetailView, self).get_context_data(**kwargs)
print(self.object._meta.verbose_name_plural)
try:
- context['breadcrumbs'] = (BREADCRUMBS[self.object._meta.model],)
+ context['breadcrumbs'] = (BREADCRUMBS[self.object._meta.label.split(".")[1]],)
except KeyError:
if self.object._meta.verbose_name_plural == 'posts':
- context['breadcrumbs'] = (self.object.get_post_type_display()+"s",)
- context['crumb_url'] = "/%ss/" % self.object.get_post_type_display()
+ if self.object.get_post_type_display() != 'src':
+ context['breadcrumbs'] = (self.object.get_post_type_display()+"s",)
+ else:
+ context['breadcrumbs'] = (self.object.get_post_type_display(),)
+ context['crumb_url'] = "/%ss/" % slugify(self.object.get_post_type_display())
else:
context['breadcrumbs'] = (self.object._meta.verbose_name_plural,)
try:
context['crumb_url']
except KeyError:
try:
- context['crumb_url'] = reverse('%s:list' % self.object._meta.verbose_name_plural.slugify())
+ context['crumb_url'] = reverse('%s:list' % slugify(self.object._meta.verbose_name_plural))
except:
- # special case for pages:
- context['breadcrumbs'] = (self.object.title,)
- context['crumb_url'] = None
+ # special case for books:
+ if self.object._meta.verbose_name_plural == 'books':
+ context['crumb_url'] = reverse('books:list')
+ elif self.object._meta.verbose_name_plural == 'Animal/Plant':
+ context['crumb_url'] = reverse('sightings:list')
+ else:
+ # special case for pages:
+ context['breadcrumbs'] = (self.object.title,)
+ context['crumb_url'] = None
return context
diff --git a/app/utils/widgets.py b/app/utils/widgets.py
index f4a7a4a..87b39ec 100644
--- a/app/utils/widgets.py
+++ b/app/utils/widgets.py
@@ -4,7 +4,7 @@ from django.contrib import admin
from django.contrib.admin.widgets import AdminFileWidget
from django.contrib.gis.admin import OSMGeoAdmin
from django.utils.safestring import mark_safe
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string
from django.template import Context
from django.forms.widgets import SelectMultiple