fix #133
An email is now sent when a new speaker- or eventproposal is created and when any such is updated.
This commit is contained in:
parent
a67f9ee4a5
commit
fcd85f680a
102
src/program/email.py
Normal file
102
src/program/email.py
Normal 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
|
|
@ -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)
|
||||
)
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
||||
|
|
10
src/program/templates/emails/new_eventproposal.html
Normal file
10
src/program/templates/emails/new_eventproposal.html
Normal 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>
|
10
src/program/templates/emails/new_eventproposal.txt
Normal file
10
src/program/templates/emails/new_eventproposal.txt
Normal 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
|
10
src/program/templates/emails/new_speakerproposal.html
Normal file
10
src/program/templates/emails/new_speakerproposal.html
Normal 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>
|
10
src/program/templates/emails/new_speakerproposal.txt
Normal file
10
src/program/templates/emails/new_speakerproposal.txt
Normal 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
|
10
src/program/templates/emails/update_eventproposal.html
Normal file
10
src/program/templates/emails/update_eventproposal.html
Normal 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>
|
10
src/program/templates/emails/update_eventproposal.txt
Normal file
10
src/program/templates/emails/update_eventproposal.txt
Normal 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
|
10
src/program/templates/emails/update_speakerproposal.html
Normal file
10
src/program/templates/emails/update_speakerproposal.html
Normal 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>
|
10
src/program/templates/emails/update_speakerproposal.txt
Normal file
10
src/program/templates/emails/update_speakerproposal.txt
Normal 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
|
|
@ -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 %}
|
||||
<a href="{% url 'speakerproposal_update' camp_slug=camp.slug pk=speakerproposal.uuid %}" class="btn btn-primary btn-xs">Modify</a>
|
||||
{% 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 %}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue