Skip to content

Commit

Permalink
Add form for submit event
Browse files Browse the repository at this point in the history
Adds a new form to send events to the mailing list.

Co-Authored-By: Mario Corchero <mariocj89@gmail.com>
Co-Authored-By: Alex Chamberlain <alex@alexchamberlain.co.uk>

Fixes #1168
  • Loading branch information
bonzanini authored and berkerpeksag committed Dec 30, 2017
1 parent 29ccc8a commit eaece78
Show file tree
Hide file tree
Showing 15 changed files with 343 additions and 63 deletions.
30 changes: 30 additions & 0 deletions boxes/migrations/0003_auto_20171101_2138.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-11-01 21:38
from __future__ import unicode_literals

from django.db import migrations


def migrate_old_content(apps, schema_editor):
Box = apps.get_model('boxes', 'Box')
Box.objects.filter(label='events-subscriptions').update(content=
'<h2 class=\"widget-title\">Python Events Calendars</h2>\r\n\r\n<br/>\r\n\r\n'
'<p>For Python events near you, please have a look at the <a href=\"http://lmorillas.github.io/python_events/\">'
'<b>Python events map</b></a>.</p>\r\n\r\n'
'<p>The Python events calendars are maintained by the <a href=\"https://wiki.python.org/moin/PythonEventsCalendar#Python_Calendar_Team\">events calendar team</a>.</p>\r\n\r\n'
'<p>Please see the <a href=\"https://wiki.python.org/moin/PythonEventsCalendar\">'
'events calendar project page</a> for details on how to <a href=\"/events/submit/\">submit events</a>,'
'<a href=\"https://wiki.python.org/moin/PythonEventsCalendar#Available_Calendars\">subscribe to the calendars</a>,'
'get <a href=\"https://twitter.com/PythonEvents\">Twitter feeds</a> or embed them.</p>\r\n\r\n<p>Thank you.</p>\r\n'
)


class Migration(migrations.Migration):

dependencies = [
('boxes', '0002_auto_20150416_1853'),
]

operations = [
migrations.RunPython(migrate_old_content),
]
50 changes: 50 additions & 0 deletions events/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from django import forms

from django.conf import settings
from django.contrib.sites.models import Site
from django.core.mail import send_mail

from django.template import loader


def set_placeholder(value):
return forms.TextInput(attrs={'placeholder': value, 'required': 'required'})


class EventForm(forms.Form):
event_name = forms.CharField(widget=set_placeholder(
'Name of the event (including the user group name for '
'user group events)'
))
event_type = forms.CharField(widget=set_placeholder(
'conference, bar camp, sprint, user group meeting, etc.'
))
python_focus = forms.CharField(widget=set_placeholder(
'Data analytics, Web Development, Country-wide conference, etc...'
))
expected_attendees = forms.CharField(widget=set_placeholder('300+'))
location = forms.CharField(widget=set_placeholder(
'IFEMA building, Madrid, Spain'
))
date_from = forms.DateField(widget=forms.SelectDateWidget())
date_to = forms.DateField(widget=forms.SelectDateWidget())
recurrence = forms.CharField(widget=set_placeholder(
'None, every second Thursday, monthly, etc.'
))
link = forms.URLField(label='Website URL')
description = forms.CharField(widget=forms.Textarea)

def send_email(self, creator):
context = {
'event': self.cleaned_data,
'creator': creator,
'site': Site.objects.get_current(),
}
text_message_template = loader.get_template('events/email/new_event.txt')
text_message = text_message_template.render(context)
send_mail(
subject='New event submission: "{}"'.format(self.cleaned_data['event_name']),
message=text_message,
from_email=creator.email,
recipient_list=[settings.EVENTS_TO_EMAIL],
)
44 changes: 44 additions & 0 deletions events/tests/test_forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import datetime

from django.test import SimpleTestCase

from ..forms import EventForm


class EventFormTests(SimpleTestCase):

def test_valid_form(self):
data = {
'event_name': 'PyConES17',
'event_type': 'conference',
'python_focus': 'Country-wide conference',
'expected_attendees': '500',
'location': 'Complejo San Francisco, Caceres, Spain',
'date_from': datetime.datetime(2017, 9, 22),
'date_to': datetime.datetime(2017, 9, 25),
'recurrence': 'None',
'link': 'https://2017.es.pycon.org/en/',
'description': 'A conference no one can afford to miss',
}
form = EventForm(data=data)
self.assertTrue(form.is_valid(), form.errors)
self.assertEqual(form.errors, {})

def test_invalid_form(self):
data = {
'event_name': 'PyConES17',
'event_type': 'conference',
'python_focus': 'Country-wide conference',
'expected_attendees': '500',
'location': 'Complejo San Francisco, Caceres, Spain',
'date_to': datetime.datetime(2017, 9, 25),
'recurrence': 'None',
'link': 'https://2017.es.pycon.org/en/',
'description': 'A conference no one can afford to miss',
}
form = EventForm(data=data)
self.assertFalse(form.is_valid(), form.errors)
self.assertEqual(
form.errors,
{'date_from': ['This field is required.']}
)
63 changes: 62 additions & 1 deletion events/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import datetime

from django.contrib.auth import get_user_model
from django.urls import reverse
from django.core import mail
from django.urls import reverse, reverse_lazy
from django.test import TestCase
from django.utils import timezone

from ..models import Calendar, Event, EventCategory, EventLocation, RecurringRule
from ..templatetags.events import get_events_upcoming
from users.factories import UserFactory


class EventsViewsTests(TestCase):
Expand Down Expand Up @@ -154,3 +156,62 @@ def test_upcoming_tag(self):
self.rule.finish = self.now - datetime.timedelta(days=2)
self.rule.save()
self.assertEqual(len(get_events_upcoming()), 0)


class EventSubmitTests(TestCase):
event_submit_url = reverse_lazy('events:event_submit')

@classmethod
def setUpTestData(cls):
cls.user = UserFactory(password='password')
cls.post_data = {
'event_name': 'PyConES17',
'event_type': 'conference',
'python_focus': 'Country-wide conference',
'expected_attendees': '500',
'location': 'Complejo San Francisco, Caceres, Spain',
'date_from': '2017-9-22',
'date_to': '2017-9-24',
'recurrence': 'None',
'link': 'https://2017.es.pycon.org/en/',
'description': 'A conference no one can afford to miss',
}

def user_login(self):
self.client.login(username=self.user.username, password='password')

def test_submit_not_logged_in_is_redirected(self):
response = self.client.post(self.event_submit_url, self.post_data)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, '/accounts/login/?next=/events/submit/')

def test_submit_without_data_is_rejected(self):
self.user_login()
response = self.client.post(self.event_submit_url, {})
# On invalid data, Django will return 200 with the
# fields marked as error.
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 0)

def test_submit_success_sends_email(self):
self.user_login()
response = self.client.post(self.event_submit_url, self.post_data)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, reverse('events:event_thanks'))
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(
mail.outbox[0].subject,
'New event submission: "{}"'.format(self.post_data['event_name'])
)

def test_badheadererror(self):
self.user_login()
post_data = self.post_data.copy()
post_data['event_name'] = 'invalid\ntitle'
response = self.client.post(
self.event_submit_url, post_data, follow=True
)
self.assertEqual(response.status_code, 200)
messages = list(response.context['messages'])
self.assertEqual(len(messages), 1)
self.assertEqual(messages[0].message, 'Invalid header found.')
3 changes: 3 additions & 0 deletions events/urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from django.conf.urls import url
from django.views.generic import TemplateView

from . import views

urlpatterns = [
url(r'calendars/$', views.CalendarList.as_view(), name='calendar_list'),
url(r'^submit/$', views.EventSubmit.as_view(), name='event_submit'),
url(r'^submit/thanks/$', TemplateView.as_view(template_name='events/event_form_thanks.html'), name='event_thanks'),
url(r'(?P<calendar_slug>[-_\w]+)/categories/(?P<slug>[-_\w]+)/$', views.EventListByCategory.as_view(), name='eventlist_category'),
url(r'(?P<calendar_slug>[-_\w]+)/categories/$', views.EventCategoryList.as_view(), name='eventcategory_list'),
url(r'(?P<calendar_slug>[-_\w]+)/locations/(?P<pk>\d+)/$', views.EventListByLocation.as_view(), name='eventlist_location'),
Expand Down
25 changes: 22 additions & 3 deletions events/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# Create your views here.
import datetime

from django.shortcuts import get_object_or_404
from django.contrib import messages
from django.core.mail import BadHeaderError
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse_lazy
from django.utils import timezone
from django.views.generic import DetailView, ListView
from django.views.generic import DetailView, ListView, FormView

from pydotorg.mixins import LoginRequiredMixin

from .models import Calendar, Event, EventCategory, EventLocation
from .forms import EventForm


class CalendarList(ListView):
Expand Down Expand Up @@ -127,3 +132,17 @@ class EventLocationList(ListView):

def get_queryset(self):
return self.model.objects.filter(calendar__slug=self.kwargs['calendar_slug'])


class EventSubmit(LoginRequiredMixin, FormView):
template_name = 'events/event_form.html'
form_class = EventForm
success_url = reverse_lazy('events:event_thanks')

def form_valid(self, form):
try:
form.send_email(self.request.user)
except BadHeaderError:
messages.add_message(self.request, messages.ERROR, 'Invalid header found.')
return redirect('events:event_submit')
return super().form_valid(form)
2 changes: 1 addition & 1 deletion fixtures/boxes.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
{
"fields": {
"label": "events-subscriptions",
"content": "<h2 class=\"widget-title\">Python Events Calendars</h2>\r\n\r\n<br/>\r\n\r\n<p>For Python events near you, please have a look at the <a href=\"http://lmorillas.github.io/python_events/\"><b>Python events map</b></a>.</p>\r\n\r\n<p>The Python events calendars are maintained by the <a href=\"https://wiki.python.org/moin/PythonEventsCalendar#Python_Calendar_Team\">events calendar team</a>.</p>\r\n\r\n<p>Please see the <a href=\"https://wiki.python.org/moin/PythonEventsCalendar\">events calendar project page</a> for details on how to <a href=\"https://wiki.python.org/moin/PythonEventsCalendar#Submitting_an_Event\">submit events</a>, <a href=\"https://wiki.python.org/moin/PythonEventsCalendar#Available_Calendars\">subscribe to the calendars</a>, get <a href=\"https://twitter.com/PythonEvents\">Twitter feeds</a> or embed them.</p>\r\n\r\n<p>Thank you.</p>\r\n",
"content": "<h2 class=\"widget-title\">Python Events Calendars</h2>\r\n\r\n<br/>\r\n\r\n<p>For Python events near you, please have a look at the <a href=\"http://lmorillas.github.io/python_events/\"><b>Python events map</b></a>.</p>\r\n\r\n<p>The Python events calendars are maintained by the <a href=\"https://wiki.python.org/moin/PythonEventsCalendar#Python_Calendar_Team\">events calendar team</a>.</p>\r\n\r\n<p>Please see the <a href=\"https://wiki.python.org/moin/PythonEventsCalendar\">events calendar project page</a> for details on how to <a href=\"/events/submit/\">submit events</a>, <a href=\"https://wiki.python.org/moin/PythonEventsCalendar#Available_Calendars\">subscribe to the calendars</a>, get <a href=\"https://twitter.com/PythonEvents\">Twitter feeds</a> or embed them.</p>\r\n\r\n<p>Thank you.</p>\r\n",
"content_markup_type": "html"
}
},
Expand Down
3 changes: 3 additions & 0 deletions pydotorg/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@
JOB_THRESHOLD_DAYS = 90
JOB_FROM_EMAIL = 'jobs@python.org'

# Events
EVENTS_TO_EMAIL = 'events@python.org'

# Mail
DEFAULT_FROM_EMAIL = 'noreply@python.org'

Expand Down
3 changes: 3 additions & 0 deletions static/sass/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,9 @@ input#s,
.fontface .page-title span:before {
font-size: .875em; }

.event-form .page-title {
margin-top: 0 !important; }

/* For when we dont need to extra size buyt do want the margin */
.default-title {
word-spacing: .15em; }
Expand Down
4 changes: 4 additions & 0 deletions static/sass/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,10 @@ input#s,
@include fontface-adjust( $kilo );
}

.event-form .page-title {
margin-top: 0 !important;
}

/* For when we dont need to extra size buyt do want the margin */
.default-title { word-spacing: .15em; }

Expand Down
53 changes: 53 additions & 0 deletions templates/events/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{% extends "base.html" %}
{% load boxes %}

{% block content_attributes %}with-right-sidebar{% endblock %}

{% block right_sidebar %}
<aside class="right-sidebar" role="secondary">
<div class="sidebar-widget subscribe-widget">
{% if calendar %}
<h2 class="widget-title">Python Event Subscriptions</h2>
<p>Subscribe to Python Event Calendars:</p>
<ul class="menu">
{% if calendar.embed %}<li><a href="{{ calendar.embed }}"><span aria-hidden="true" class="icon-embed"></span>Embeddable widget</a></li>{% endif %}
{% if calendar.rss %}<li><a href="{{ calendar.rss }}"><span aria-hidden="true" class="icon-feed"></span>Events via RSS</a></li>{% endif %}
{% if calendar.url %}<li><a href="{{ calendar.url }}"><span aria-hidden="true" class="icon-ical"></span>Events in iCal format</a></li>{% endif %}
{% if calendar.twitter %}<li><a href="{{ calendar.twitter }}"><span aria-hidden="true" class="icon-twitter"></span>Events on Twitter</a></li>{% endif %}
</ul>
{% endif %}
{% box 'events-subscriptions' %}
</div>

{% if event_categories %}
<div class="sidebar-widget eventtypes-widget">
<h2 class="widget-title">Event Categories</h2>
<p class="give-me-more"><a href="#" title="More Event Categories">More</a></p>
<ul class="menu">
{% for category in event_categories %}
<li><a href="{{ category.get_absolute_url }}">{{ category.name }}</a></li>
{% endfor %}
<li><a class="readmore" href="{% url 'events:eventcategory_list' calendar_slug=view.kwargs.calendar_slug %}">More Types</a></li>
</ul>
</div>
{% endif %}

{% comment %}
Less useful to look at and use than I thought they might be.
Titles of locations are the whole address most times, and unless someone in the DB controlled the way they were written and presented, this might always be a mess
{% if event_locations %}
<div class="sidebar-widget authors-widget">
<h2 class="widget-title">Event Locations</h2>
<p class="give-me-more"><a href="#" title="More Event Locations">More</a></p>
<ul class="menu">
{% for location in event_locations %}
<li><a href="{{ location.get_absolute_url }}">{{ location.name }}</a></li>
{% endfor %}
<li><a class="readmore" href="{% url 'events:eventlocation_list' calendar_slug=view.kwargs.calendar_slug %}">More Locations</a></li>
</ul>
</div>
{% endif %}
{% endcomment %}

</aside>
{% endblock right_sidebar %}
13 changes: 13 additions & 0 deletions templates/events/email/new_event.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
The following event was submitted for approval:

Name: {{ event.event_name }}
Type: {{ event.event_type }}
Focus: {{ event.python_focus }}
Approximate Number of Attendees: {{ event.expected_attendees }}
Location: {{ event.location }}
Date/Time: {{ event.date_from }} - {{ event.date_to }}
Recurrence: {{ event.recurrence }}
Link: <a href="{{ event.link }}">{{ event.event_name }}</a>

Description:
{{ event.description }}
Loading

0 comments on commit eaece78

Please sign in to comment.