From eea8117ac3b58d8ac9eb1c9f94d27ac28fc67008 Mon Sep 17 00:00:00 2001 From: luxagraf Date: Mon, 3 Aug 2020 18:38:32 -0400 Subject: added all lttr updates to backup --- app/lttr/admin.py | 4 +- app/lttr/management/commands/send_newsletter.py | 31 ++++ app/lttr/migrations/0006_auto_20200419_1617.py | 42 +++++ app/lttr/migrations/0007_auto_20200419_1627.py | 24 +++ app/lttr/migrations/0008_auto_20200419_1629.py | 18 ++ .../migrations/0009_newslettermailing_subtitle.py | 18 ++ .../migrations/0010_newslettermailing_books.py | 19 +++ app/lttr/models.py | 76 ++++++++- app/lttr/templates/lttr/friends_detail.html | 185 +++++++++++++++++++++ .../templates/lttr/newslettermailing_detail.html | 154 +++++++++++++++++ app/lttr/templates/lttr/subscriber_form.html | 19 ++- app/lttr/urls.py | 2 +- app/lttr/views.py | 18 +- 13 files changed, 587 insertions(+), 23 deletions(-) create mode 100644 app/lttr/management/commands/send_newsletter.py create mode 100644 app/lttr/migrations/0006_auto_20200419_1617.py create mode 100644 app/lttr/migrations/0007_auto_20200419_1627.py create mode 100644 app/lttr/migrations/0008_auto_20200419_1629.py create mode 100644 app/lttr/migrations/0009_newslettermailing_subtitle.py create mode 100644 app/lttr/migrations/0010_newslettermailing_books.py create mode 100644 app/lttr/templates/lttr/friends_detail.html create mode 100644 app/lttr/templates/lttr/newslettermailing_detail.html (limited to 'app/lttr') diff --git a/app/lttr/admin.py b/app/lttr/admin.py index 4ca30ce..13e2607 100644 --- a/app/lttr/admin.py +++ b/app/lttr/admin.py @@ -21,11 +21,13 @@ class NewsletterMailingAdmin(admin.ModelAdmin): fieldsets = ( ('Entry', { 'fields': ( - 'title', + ('title', "newsletter", "issue"), + 'subtitle', 'body_markdown', ('pub_date', 'status'), 'slug', 'featured_image', + 'books' ), 'classes': ( 'show', diff --git a/app/lttr/management/commands/send_newsletter.py b/app/lttr/management/commands/send_newsletter.py new file mode 100644 index 0000000..0f36183 --- /dev/null +++ b/app/lttr/management/commands/send_newsletter.py @@ -0,0 +1,31 @@ +"""Command for sending the newsletter""" +from django.conf import settings +from django.utils.translation import activate +from django.core.management.base import NoArgsCommand + +from lttr.mailer import Mailer +from lttr.models import NewsletterMailing + + +class Command(NoArgsCommand): + """Send the newsletter in queue""" + help = 'Send the newsletter in queue' + + def handle_noargs(self, **options): + verbose = int(options['verbosity']) + + if verbose: + print('Starting sending newsletters...') + + activate(settings.LANGUAGE_CODE) + + for newsletter in NewsletterMailing.objects.exclude( + status=Newsletter.DRAFT).exclude(status=Newsletter.SENT): + mailer = Mailer(newsletter, verbose=verbose) + if mailer.can_send: + if verbose: + print('Start emailing %s' % newsletter.title) + mailer.run() + + if verbose: + print('End session sending') diff --git a/app/lttr/migrations/0006_auto_20200419_1617.py b/app/lttr/migrations/0006_auto_20200419_1617.py new file mode 100644 index 0000000..5f547c9 --- /dev/null +++ b/app/lttr/migrations/0006_auto_20200419_1617.py @@ -0,0 +1,42 @@ +# Generated by Django 2.1.2 on 2020-04-19 16:17 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('lttr', '0005_auto_20200205_1606'), + ] + + operations = [ + migrations.CreateModel( + name='MailingStatus', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.IntegerField(choices=[(-1, 'sent in test'), (0, 'sent'), (1, 'error'), (2, 'invalid email'), (4, 'opened'), (5, 'opened on site'), (6, 'link opened'), (7, 'unsubscription')], verbose_name='status')), + ('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='creation date')), + ], + options={ + 'verbose_name': 'subscriber mailing status', + 'verbose_name_plural': 'subscriber mailing statuses', + 'ordering': ('-creation_date',), + }, + ), + migrations.AddField( + model_name='newslettermailing', + name='newsletter', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='lttr.Newsletter'), + ), + migrations.AddField( + model_name='mailingstatus', + name='newsletter_mailing', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='lttr.NewsletterMailing', verbose_name='newsletter'), + ), + migrations.AddField( + model_name='mailingstatus', + name='subscriber', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='lttr.Subscriber', verbose_name='subscriber'), + ), + ] diff --git a/app/lttr/migrations/0007_auto_20200419_1627.py b/app/lttr/migrations/0007_auto_20200419_1627.py new file mode 100644 index 0000000..7c39f44 --- /dev/null +++ b/app/lttr/migrations/0007_auto_20200419_1627.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1.2 on 2020-04-19 16:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('lttr', '0006_auto_20200419_1617'), + ] + + operations = [ + migrations.AddField( + model_name='newslettermailing', + name='issue', + field=models.PositiveIntegerField(null=True), + ), + migrations.AlterField( + model_name='newslettermailing', + name='newsletter', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='lttr.Newsletter'), + ), + ] diff --git a/app/lttr/migrations/0008_auto_20200419_1629.py b/app/lttr/migrations/0008_auto_20200419_1629.py new file mode 100644 index 0000000..5e8bd42 --- /dev/null +++ b/app/lttr/migrations/0008_auto_20200419_1629.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2020-04-19 16:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lttr', '0007_auto_20200419_1627'), + ] + + operations = [ + migrations.AlterField( + model_name='newslettermailing', + name='issue', + field=models.PositiveIntegerField(), + ), + ] diff --git a/app/lttr/migrations/0009_newslettermailing_subtitle.py b/app/lttr/migrations/0009_newslettermailing_subtitle.py new file mode 100644 index 0000000..411364f --- /dev/null +++ b/app/lttr/migrations/0009_newslettermailing_subtitle.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2020-04-19 21:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lttr', '0008_auto_20200419_1629'), + ] + + operations = [ + migrations.AddField( + model_name='newslettermailing', + name='subtitle', + field=models.CharField(blank=True, max_length=250, null=True), + ), + ] diff --git a/app/lttr/migrations/0010_newslettermailing_books.py b/app/lttr/migrations/0010_newslettermailing_books.py new file mode 100644 index 0000000..1b63a93 --- /dev/null +++ b/app/lttr/migrations/0010_newslettermailing_books.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.2 on 2020-04-20 13:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('books', '0011_auto_20200205_1617'), + ('lttr', '0009_newslettermailing_subtitle'), + ] + + operations = [ + migrations.AddField( + model_name='newslettermailing', + name='books', + field=models.ManyToManyField(blank=True, related_name='mailing_books', to='books.Book'), + ), + ] diff --git a/app/lttr/models.py b/app/lttr/models.py index 33a6735..91d5e22 100644 --- a/app/lttr/models.py +++ b/app/lttr/models.py @@ -3,6 +3,7 @@ from django.contrib.gis.db import models from django.contrib.sites.models import Site from django.template.loader import select_template from django.core.mail import EmailMultiAlternatives +from django.utils.translation import ugettext_lazy as _ from django.utils import timezone from django.utils.text import slugify from django.urls import reverse @@ -14,6 +15,7 @@ from taggit.managers import TaggableManager from utils.util import render_images, parse_video, markdown_to_html from taxonomy.models import TaggedItems from photos.models import LuxImage, LuxImageSize +from books.models import Book # Possible actions that user can perform @@ -101,7 +103,9 @@ class Newsletter(models.Model): class NewsletterMailing(models.Model): """ A model for Newletter Mailings, the things actually sent out """ + newsletter = models.ForeignKey(Newsletter, on_delete=models.CASCADE) title = models.CharField(max_length=250) + subtitle = models.CharField(max_length=250, null=True, blank=True) slug = models.SlugField(unique_for_date='pub_date', blank=True) body_html = models.TextField(blank=True) body_markdown = models.TextField() @@ -113,7 +117,9 @@ class NewsletterMailing(models.Model): (1, 'Published'), ) status = models.IntegerField(choices=PUB_STATUS, default=0) + issue = models.PositiveIntegerField() date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False) + books = models.ManyToManyField(Book, related_name="mailing_books", blank=True) class Meta: ordering = ('-title', '-date_created') @@ -122,7 +128,36 @@ class NewsletterMailing(models.Model): return self.title def get_absolute_url(self): - return reverse("newsletter:mailing-detail", kwargs={"slug": self.slug}) + return reverse("lttr:detail", kwargs={"slug": self.newsletter.slug, "issue": self.get_issue_str(), "mailing":self.slug}) + + def get_issue_str(self): + issue = self.issue + if self.issue < 100: + issue = "0%s" % self.issue + if self.issue < 10: + issue = "00%s" % self.issue + return issue + + @property + def get_previous_published(self): + return self.get_previous_by_pub_date(status__exact=1,newsletter=self.newsletter) + + @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, newsletter=self.newsletter) + + @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): created = self.pk is None @@ -134,7 +169,7 @@ class NewsletterMailing(models.Model): 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") + s = LuxImageSize.objects.get(name="navigation_thumb") ss = LuxImageSize.objects.get(name="picwide-med") self.featured_image.sizes.add(s) self.featured_image.sizes.add(ss) @@ -319,3 +354,40 @@ def get_address(name, email): return u'%s <%s>' % (name, email) else: return u'%s' % email + + +class MailingStatus(models.Model): + """Status of the reception""" + SENT_TEST = -1 + SENT = 0 + ERROR = 1 + INVALID = 2 + OPENED = 4 + OPENED_ON_SITE = 5 + LINK_OPENED = 6 + UNSUBSCRIPTION = 7 + + STATUS_CHOICES = ((SENT_TEST, _('sent in test')), + (SENT, _('sent')), + (ERROR, _('error')), + (INVALID, _('invalid email')), + (OPENED, _('opened')), + (OPENED_ON_SITE, _('opened on site')), + (LINK_OPENED, _('link opened')), + (UNSUBSCRIPTION, _('unsubscription')), + ) + + newsletter_mailing = models.ForeignKey(NewsletterMailing, on_delete=models.CASCADE, verbose_name=_('newsletter')) + subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE, verbose_name=_('subscriber')) + status = models.IntegerField(_('status'), choices=STATUS_CHOICES) + creation_date = models.DateTimeField(_('creation date'), auto_now_add=True) + + def __str__(self): + return '%s : %s : %s' % (self.newsletter.__str__(), + self.contact.__str__(), + self.get_status_display()) + + class Meta: + ordering = ('-creation_date',) + verbose_name = _('subscriber mailing status') + verbose_name_plural = _('subscriber mailing statuses') diff --git a/app/lttr/templates/lttr/friends_detail.html b/app/lttr/templates/lttr/friends_detail.html new file mode 100644 index 0000000..eb3e81b --- /dev/null +++ b/app/lttr/templates/lttr/friends_detail.html @@ -0,0 +1,185 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% load get_image_by_size %} +{%block htmlclass%}{%endblock%} +{% block sitename %} + + {{object.title|safe}} by Scott Gilbertson + {%endblock%} + + {%block extrahead%} + + + + + + + + + + + + + + + + + + +{%endblock%} +{%block bodyid%}id="home" class="archive"{%endblock%} +{% block breadcrumbs %} + +{% endblock %} +{% block primary %} +
+
+ {%with image=object.featured_image%} + {{image.alt}} photographed by {% if image.photo_credit_source %}{{image.photo_credit_source}}{%else%}luxagraf{%endif%} + {%endwith%} +
+
+
+

{{object.title|smartypants|safe}}

+
+ + +
+
+
+ {{object.body_html|safe|smartypants}} +
+ {%if object.books.all %}
+ + {% with object.get_next_published as next %} + {% with object.get_previous_published as prev %} + + {% if object.related.all %}
+ +
{%endif%} +
+{% endblock %} + +{% block js %}{% comment %} {% endcomment%}{% endblock%} diff --git a/app/lttr/templates/lttr/newslettermailing_detail.html b/app/lttr/templates/lttr/newslettermailing_detail.html new file mode 100644 index 0000000..71aa294 --- /dev/null +++ b/app/lttr/templates/lttr/newslettermailing_detail.html @@ -0,0 +1,154 @@ +{% extends 'base.html' %} +{% load typogrify_tags %} +{% block sitename %} + + Luxagraf: thoughts on ecology, culture, travel, photography, walking and other ephemera + {%endblock%} + + {%block extrahead%} + + + + + + + + + + + + + + + + + + +{%endblock%} +{%block bodyid%}id="home" class="archive"{%endblock%} + +{% block primary %} +{% block breadcrumbs %}{% include "lib/breadcrumbs.html" with breadcrumbs=breadcrumbs %}{% endblock %} +
+
+
+ {%with image=object.featured_image%} + {{image.alt}} photographed by {% if image.photo_credit_source %}{{image.photo_credit_source}}{%else%}luxagraf{%endif%} + {%endwith%} +
+
+

{%if object.template_name == 1 or object.template_name == 3 %}{{object.title|smartypants|safe}}{%else%}{{object.title|smartypants|safe}}{%endif%}

+ {% if object.subtitle %}

{{object.subtitle|smartypants|safe}}

{%endif%} +
+ {% if object.location %}
+

{% if object.location.country_name == "United States" %}{{object.location.name|smartypants|safe}}, {{object.location.state_name|safe}}, U.S.{%else%}{{object.location.name|smartypants|safe}}, {{object.location.country_name|safe}}{%endif%}

+ – Map +
{%endif%} + + +
+
+
+ {{object.body_html|safe|smartypants}} +
+ {%if wildlife or object.field_notes.all or object.books.all %}{%endif%} +
+ {% with object.get_next_published as next %} + {% with object.get_previous_published as prev %} + + {% if object.related.all %}
+ +
{%endif%} +
+{% endblock %} + +{% block js %}{% comment %} {% endcomment%}{% endblock%} diff --git a/app/lttr/templates/lttr/subscriber_form.html b/app/lttr/templates/lttr/subscriber_form.html index 83e1e28..c2adcc0 100644 --- a/app/lttr/templates/lttr/subscriber_form.html +++ b/app/lttr/templates/lttr/subscriber_form.html @@ -22,17 +22,20 @@ {% if field.errors %}{{field.errors}}{% endif %} {%endfor%}

Say what?

-

Friends of a Long Year is an infrequent mailing that will keep you up-to-date with luxagraf and offer some thoughts on topics like travel, photography, the natural world, tools, walking and other ephemera. It comes about twice a month, sometimes less, sometimes more. Unsubscribing is easy. It's all self-hosted, secure, and private.

-

The name comes from the great early 20th century explorer and desert rat, Mary Hunter Austin, whose collected essays, Lost Borders is dedicated to the "Friends of a Long Year". This somewhat inscrutable dedication grabbed me, and seemed like the perfect name for this mailing list.

+

Friends of a Long Year is a monthly letter about living outdoors, travel, reading, walking, and other ephemera. Unsubscribing is easy. It's all self-hosted, secure, and private.

+

The name Friends of a Long Year comes from the early 20th century explorer and desert rat, Mary Hunter Austin, whose collected essays, Lost Borders is dedicated to the "Friends of a Long Year".

+

While I came up with this name last year, it seems particularly fitting in 2020, which is shaping up to be a long year. If you like to travel with friends, mentally for now, please, join us.

Letters

-