An email is now sent when a new speaker- or eventproposal is created and
when any such is updated.
This commit is contained in:
Stephan Telling 2017-07-11 22:02:19 +02:00
parent a67f9ee4a5
commit fcd85f680a
No known key found for this signature in database
GPG key ID: D4892289F36ADA9B
12 changed files with 247 additions and 14 deletions

102
src/program/email.py Normal file
View file

@ -0,0 +1,102 @@
from django.core.exceptions import ObjectDoesNotExist
from utils.email import add_outgoing_email
from teams.models import Team
import logging
logger = logging.getLogger("bornhack.%s" % __name__)
def add_new_speakerproposal_email(speakerproposal):
formatdict = {
'proposal': speakerproposal
}
try:
content_team = Team.objects.get(
camp=speakerproposal.camp, name='Content'
)
return add_outgoing_email(
text_template='emails/new_speakerproposal.txt',
html_template='emails/new_speakerproposal.html',
to_recipients=content_team.mailing_list,
formatdict=formatdict,
subject='New speaker proposal for {}'.format(
speakerproposal.camp.title
)
)
except ObjectDoesNotExist as e:
logger.info('There is no team with name Content: {}'.format(e))
return False
def add_new_eventproposal_email(eventproposal):
formatdict = {
'proposal': eventproposal
}
try:
content_team = Team.objects.get(
camp=eventproposal.camp, name='Content'
)
return add_outgoing_email(
text_template='emails/new_eventproposal.txt',
html_template='emails/new_eventproposal.html',
to_recipients=content_team.mailing_list,
formatdict=formatdict,
subject='New event proposal for {}'.format(
eventproposal.camp.title
)
)
except ObjectDoesNotExist as e:
logger.info('There is no team with name Content: {}'.format(e))
return False
def add_speakerproposal_updated_email(speakerproposal):
formatdict = {
'proposal': speakerproposal
}
try:
content_team = Team.objects.get(
camp=speakerproposal.camp, name='Content'
)
return add_outgoing_email(
text_template='emails/update_speakerproposal.txt',
html_template='emails/update_speakerproposal.html',
to_recipients=content_team.mailing_list,
formatdict=formatdict,
subject='Updated speaker proposal for {}'.format(
speakerproposal.camp.title
)
)
except ObjectDoesNotExist as e:
logger.info('There is no team with name Content: {}'.format(e))
return False
def add_eventproposal_updated_email(eventproposal):
formatdict = {
'proposal': eventproposal
}
try:
content_team = Team.objects.get(
camp=eventproposal.camp, name='Content'
)
return add_outgoing_email(
text_template='emails/update_eventproposal.txt',
html_template='emails/update_eventproposal.html',
to_recipients=content_team.mailing_list,
formatdict=formatdict,
subject='New event proposal for {}'.format(
eventproposal.camp.title
)
)
except ObjectDoesNotExist as e:
logger.info('There is no team with name Content: {}'.format(e))
return False

View file

@ -1,10 +1,15 @@
import uuid
import os
import icalendar
import CommonMark
import logging
from datetime import timedelta
from django.contrib.postgres.fields import DateTimeRangeField
from django.contrib import messages
from django.db import models
from django.dispatch import receiver
from django.utils.text import slugify
from django.conf import settings
from django.core.exceptions import ValidationError
@ -13,11 +18,11 @@ from django.core.files.storage import FileSystemStorage
from django.urls import reverse
from django.apps import apps
from django.core.files.base import ContentFile
import icalendar
import CommonMark
from django.db.models.signals import post_save
from utils.models import CreatedUpdatedModel, CampRelatedModel
from .email import add_new_speakerproposal_email, add_new_eventproposal_email
logger = logging.getLogger("bornhack.%s" % __name__)
class CustomUrlStorage(FileSystemStorage):
@ -80,12 +85,14 @@ class UserSubmittedModel(CampRelatedModel):
PROPOSAL_PENDING = 'pending'
PROPOSAL_APPROVED = 'approved'
PROPOSAL_REJECTED = 'rejected'
PROPOSAL_MODIFIED_AFTER_APPROVAL = 'modified after approval'
PROPOSAL_STATUSES = [
PROPOSAL_DRAFT,
PROPOSAL_PENDING,
PROPOSAL_APPROVED,
PROPOSAL_REJECTED
PROPOSAL_REJECTED,
PROPOSAL_MODIFIED_AFTER_APPROVAL
]
PROPOSAL_STATUS_CHOICES = [
@ -93,6 +100,7 @@ class UserSubmittedModel(CampRelatedModel):
(PROPOSAL_PENDING, 'Pending approval'),
(PROPOSAL_APPROVED, 'Approved'),
(PROPOSAL_REJECTED, 'Rejected'),
(PROPOSAL_MODIFIED_AFTER_APPROVAL, 'Modified after approval'),
]
proposal_status = models.CharField(
@ -265,6 +273,21 @@ class EventProposal(UserSubmittedModel):
self.save()
@receiver(post_save, sender=EventProposal)
@receiver(post_save, sender=SpeakerProposal)
def notify_content_team(sender, created, instance, **kwargs):
if created and isinstance(instance, SpeakerProposal):
if not add_new_speakerproposal_email(instance):
logger.error(
'Error adding speaker proposal email to outgoing queue for {}'.format(instance)
)
if created and isinstance(instance, EventProposal):
if not add_new_eventproposal_email(instance):
logger.error(
'Error adding event proposal email to outgoing queue for {}'.format(instance)
)
###############################################################################

View file

@ -0,0 +1,10 @@
Hello!<br>
<br>
Event {{ proposal.name }} was just submitted as a new proposal for {{ proposal.camp }}.
<br>
<br>
More info <a href="https://bornhack.dk/admin/program/eventproposal/{{ proposal.uuid }}/change/">here</a>.
<br>
Best regards,<br>
<br>
The BornHack Team<br>

View file

@ -0,0 +1,10 @@
Hello!
Event {{ proposal.name }} was just submitted as a new proposal for {{ proposal.camp }}.
More info: https://bornhack.dk/admin/program/speakerproposal/{{ proposal.uuid }}/change/
Best regards,
The BornHack Team

View file

@ -0,0 +1,10 @@
Hello!<br>
<br>
Speaker {{ proposal.name }} was just submitted as a new proposal for {{ proposal.camp }}.
<br>
<br>
More info <a href="https://bornhack.dk/admin/program/speakerproposal/{{ proposal.uuid }}/change/">here</a>.
<br>
Best regards,<br>
<br>
The BornHack Team<br>

View file

@ -0,0 +1,10 @@
Hello!
Speaker {{ proposal.name }} was just submitted as a new proposal for {{ proposal.camp }}.
More info: https://bornhack.dk/admin/program/speakerproposal/{{ proposal.uuid }}/change/
Best regards,
The BornHack Team

View file

@ -0,0 +1,10 @@
Hello!<br>
<br>
Event {{ proposal.name }} for {{ proposal.camp }} was just updated!
<br>
<br>
More info <a href="https://bornhack.dk/admin/program/eventproposal/{{ proposal.uuid }}/change/">here</a>.
<br>
Best regards,<br>
<br>
The BornHack Team<br>

View file

@ -0,0 +1,10 @@
Hello!
Event {{ proposal.name }} for {{ proposal.camp }} was just updated!
More info: https://bornhack.dk/admin/program/eventproposal/{{ proposal.uuid }}/change/
Best regards,
The BornHack Team

View file

@ -0,0 +1,10 @@
Hello!<br>
<br>
Speaker {{ proposal.name }} for {{ proposal.camp }} was just updated!
<br>
<br>
More info <a href="https://bornhack.dk/admin/program/speakerproposal/{{ proposal.uuid }}/change/">here</a>.
<br>
Best regards,<br>
<br>
The BornHack Team<br>

View file

@ -0,0 +1,10 @@
Hello!
Speaker {{ proposal.name }} was just submitted as a new speaker proposal for {{ proposal.camp }}.
More info: https://bornhack.dk/admin/program/speakerproposal/{{ proposal.uuid }}/change/
Best regards,
The BornHack Team

View file

@ -24,11 +24,7 @@ Proposals | {{ block.super }}
<td>
<a href="{% url 'speakerproposal_detail' camp_slug=camp.slug pk=speakerproposal.uuid %}" class="btn btn-primary btn-xs">Details</a>
{% if not camp.read_only %}
{% if speakerproposal.proposal_status == "approved" %}
<a href="{% url 'speakerproposal_update' camp_slug=camp.slug pk=speakerproposal.uuid %}" class="btn btn-primary btn-xs btn-disabled" disabled>Modify</a>
{% else %}
<a href="{% url 'speakerproposal_update' camp_slug=camp.slug pk=speakerproposal.uuid %}" class="btn btn-primary btn-xs">Modify</a>
{% endif %}
{% if speakerproposal.proposal_status == "pending" or speakerproposal.proposal_status == "approved" %}
<a href="{% url 'speakerproposal_submit' camp_slug=camp.slug pk=speakerproposal.uuid %}" class="btn btn-primary btn-xs btn-disabled" disabled>Submit</a>
{% else %}

View file

@ -1,4 +1,6 @@
import datetime, os
import datetime
import logging
import os
from django.views.generic import ListView, TemplateView, DetailView, View
from django.views.generic.edit import CreateView, UpdateView
@ -8,14 +10,26 @@ from django.http import Http404, HttpResponse
from django.utils.decorators import method_decorator
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib import messages
from django.shortcuts import redirect
from django.urls import reverse
import icalendar
from camps.mixins import CampViewMixin
from .mixins import CreateProposalMixin, EnsureUnapprovedProposalMixin, EnsureUserOwnsProposalMixin, EnsureWritableCampMixin, PictureViewMixin, EnsureCFSOpenMixin
from .mixins import (
CreateProposalMixin,
EnsureUnapprovedProposalMixin,
EnsureUserOwnsProposalMixin,
EnsureWritableCampMixin,
PictureViewMixin,
EnsureCFSOpenMixin
)
from .email import (
add_speakerproposal_updated_email,
add_eventproposal_updated_email
)
from . import models
import logging
logger = logging.getLogger("bornhack.%s" % __name__)
############## ical calendar ########################################################
@ -84,7 +98,7 @@ class SpeakerProposalCreateView(LoginRequiredMixin, CampViewMixin, CreateProposa
return reverse('proposal_list', kwargs={'camp_slug': self.camp.slug})
class SpeakerProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureUserOwnsProposalMixin, EnsureUnapprovedProposalMixin, EnsureWritableCampMixin, EnsureCFSOpenMixin, UpdateView):
class SpeakerProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureUserOwnsProposalMixin, EnsureWritableCampMixin, EnsureCFSOpenMixin, UpdateView):
model = models.SpeakerProposal
fields = ['name', 'biography', 'picture_small', 'picture_large']
template_name = 'speakerproposal_form.html'
@ -96,6 +110,15 @@ class SpeakerProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureUserOwn
if form.instance.proposal_status == models.UserSubmittedModel.PROPOSAL_PENDING:
messages.warning(self.request, "Your speaker proposal has been reverted to status draft. Please submit it again when you are ready.")
form.instance.proposal_status = models.UserSubmittedModel.PROPOSAL_DRAFT
if form.instance.proposal_status == models.UserSubmittedModel.PROPOSAL_APPROVED:
messages.warning(self.request, "Your speaker proposal has been set to modified after approval. Please await approval of the changes.")
form.instance.proposal_status = models.UserSubmittedModel.PROPOSAL_MODIFIED_AFTER_APPROVAL
if not add_speakerproposal_updated_email(form.instance):
logger.error(
'Unable to add update email to queue for speaker: {}'.format(form.instance)
)
return super().form_valid(form)
@ -149,7 +172,7 @@ class EventProposalCreateView(LoginRequiredMixin, CampViewMixin, CreateProposalM
return context
class EventProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureUserOwnsProposalMixin, EnsureUnapprovedProposalMixin, EnsureWritableCampMixin, EnsureCFSOpenMixin, UpdateView):
class EventProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureUserOwnsProposalMixin, EnsureWritableCampMixin, EnsureCFSOpenMixin, UpdateView):
model = models.EventProposal
fields = ['title', 'abstract', 'event_type', 'speakers']
template_name = 'eventproposal_form.html'
@ -161,6 +184,15 @@ class EventProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureUserOwnsP
if form.instance.proposal_status == models.UserSubmittedModel.PROPOSAL_PENDING:
messages.warning(self.request, "Your event proposal has been reverted to status draft. Please submit it again when you are ready.")
form.instance.proposal_status = models.UserSubmittedModel.PROPOSAL_DRAFT
if form.instance.proposal_status == models.UserSubmittedModel.PROPOSAL_APPROVED:
messages.warning(self.request, "Your event proposal has been set to status modified after approval. Please await approval of the changes.")
form.instance.proposal_status = models.UserSubmittedModel.PROPOSAL_MODIFIED_AFTER_APPROVAL
if not add_eventproposal_updated_email(form.instance):
logger.error(
'Unable to add update email to queue for event: {}'.format(form.instance)
)
return super().form_valid(form)