diff --git a/.gitignore b/.gitignore
index 1793954a..b17c3115 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,6 +53,7 @@ coverage.xml
# Django stuff:
*.log
+db.sqlite3
# Sphinx documentation
docs/_build/
diff --git a/PyRIGS/decorators.py b/PyRIGS/decorators.py
index f1023faf..4b897923 100644
--- a/PyRIGS/decorators.py
+++ b/PyRIGS/decorators.py
@@ -1,5 +1,5 @@
from django.contrib.auth import REDIRECT_FIELD_NAME
-from django.shortcuts import render_to_response
+from django.shortcuts import render
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
@@ -26,15 +26,15 @@ def user_passes_test_with_403(test_func, login_url=None, oembed_view=None):
return view_func(request, *args, **kwargs)
elif not request.user.is_authenticated():
if oembed_view is not None:
- extra_context = {}
- extra_context['oembed_url'] = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], reverse(oembed_view, kwargs=kwargs))
- extra_context['login_url'] = "{0}?{1}={2}".format(login_url, REDIRECT_FIELD_NAME, request.get_full_path())
- resp = render_to_response('login_redirect.html', extra_context, context_instance=RequestContext(request))
+ context = {}
+ context['oembed_url'] = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], reverse(oembed_view, kwargs=kwargs))
+ context['login_url'] = "{0}?{1}={2}".format(login_url, REDIRECT_FIELD_NAME, request.get_full_path())
+ resp = render(request, 'login_redirect.html', context=context)
return resp
else:
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, request.get_full_path()))
else:
- resp = render_to_response('403.html', context_instance=RequestContext(request))
+ resp = render(request, '403.html')
resp.status_code = 403
return resp
_checklogin.__doc__ = view_func.__doc__
@@ -62,7 +62,7 @@ def api_key_required(function):
userid = kwargs.get('api_pk')
key = kwargs.get('api_key')
- error_resp = render_to_response('403.html', context_instance=RequestContext(request))
+ error_resp = render(request, '403.html')
error_resp.status_code = 403
if key is None:
diff --git a/PyRIGS/settings.py b/PyRIGS/settings.py
index 1cdbcc19..21f36848 100644
--- a/PyRIGS/settings.py
+++ b/PyRIGS/settings.py
@@ -10,26 +10,31 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
+
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = os.environ.get('SECRET_KEY') if os.environ.get('SECRET_KEY') else 'gxhy(a#5mhp289_=6xx$7jh=eh$ymxg^ymc+di*0c*geiu3p_e'
+SECRET_KEY = os.environ.get('SECRET_KEY') if os.environ.get(
+ 'SECRET_KEY') else 'gxhy(a#5mhp289_=6xx$7jh=eh$ymxg^ymc+di*0c*geiu3p_e'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool(int(os.environ.get('DEBUG'))) if os.environ.get('DEBUG') else True
-STAGING = bool(int(os.environ.get('STAGING'))) if os.environ.get('STAGING') else False
-TEMPLATE_DEBUG = True
+STAGING = bool(int(os.environ.get('STAGING'))) if os.environ.get('STAGING') else False
ALLOWED_HOSTS = ['pyrigs.nottinghamtec.co.uk', 'rigs.nottinghamtec.co.uk', 'pyrigs.herokuapp.com']
if STAGING:
ALLOWED_HOSTS.append('.herokuapp.com')
+if DEBUG:
+ ALLOWED_HOSTS.append('localhost')
+ ALLOWED_HOSTS.append('example.com')
+
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
if not DEBUG:
SECURE_SSL_REDIRECT = True # Redirect all http requests to https
@@ -40,7 +45,6 @@ ADMINS = (
('Tom Price', 'tomtom5152@gmail.com')
)
-
# Application definition
INSTALLED_APPS = (
@@ -63,6 +67,7 @@ INSTALLED_APPS = (
MIDDLEWARE_CLASSES = (
'raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware',
'django.middleware.security.SecurityMiddleware',
+ 'debug_toolbar.middleware.DebugToolbarMiddleware',
'reversion.middleware.RevisionMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
@@ -77,7 +82,6 @@ ROOT_URLCONF = 'PyRIGS.urls'
WSGI_APPLICATION = 'PyRIGS.wsgi.application'
-
# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
DATABASES = {
@@ -89,6 +93,7 @@ DATABASES = {
if not DEBUG:
import dj_database_url
+
DATABASES['default'] = dj_database_url.config()
# Logging
@@ -119,12 +124,12 @@ LOGGING = {
'mail_admins': {
'class': 'django.utils.log.AdminEmailHandler',
'level': 'ERROR',
- # But the emails are plain text by default - HTML is nicer
+ # But the emails are plain text by default - HTML is nicer
'include_html': True,
},
},
'loggers': {
- # Again, default Django configuration to email unhandled exceptions
+ # Again, default Django configuration to email unhandled exceptions
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
@@ -152,8 +157,8 @@ RAVEN_CONFIG = {
AUTH_USER_MODEL = 'RIGS.Profile'
LOGIN_REDIRECT_URL = '/'
-LOGIN_URL = '/user/login'
-LOGOUT_URL = '/user/logout'
+LOGIN_URL = '/user/login/'
+LOGOUT_URL = '/user/logout/'
ACCOUNT_ACTIVATION_DAYS = 7
@@ -167,7 +172,7 @@ EMAILER_TEST = False
if not DEBUG or EMAILER_TEST:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.environ.get('EMAIL_HOST')
- EMAIL_PORT = int(os.environ.get('EMAIL_PORT'))
+ EMAIL_PORT = int(os.environ.get('EMAIL_PORT', 25))
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
EMAIL_USE_TLS = bool(int(os.environ.get('EMAIL_USE_TLS', 0)))
@@ -191,19 +196,7 @@ USE_L10N = True
USE_TZ = True
-DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M','%Y-%m-%dT%H:%M:%S')
-
-TEMPLATE_CONTEXT_PROCESSORS = (
- "django.contrib.auth.context_processors.auth",
- "django.core.context_processors.debug",
- "django.core.context_processors.i18n",
- "django.core.context_processors.media",
- "django.core.context_processors.static",
- "django.core.context_processors.tz",
- "django.core.context_processors.request",
- "django.contrib.messages.context_processors.messages",
-)
-
+DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M', '%Y-%m-%dT%H:%M:%S')
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/
@@ -214,11 +207,30 @@ STATIC_DIRS = (
os.path.join(BASE_DIR, 'static/')
)
-TEMPLATE_DIRS = (
- os.path.join(BASE_DIR, 'templates'),
-)
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [
+ os.path.join(BASE_DIR, 'templates'),
+ ],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ "django.contrib.auth.context_processors.auth",
+ "django.template.context_processors.debug",
+ "django.template.context_processors.i18n",
+ "django.template.context_processors.media",
+ "django.template.context_processors.static",
+ "django.template.context_processors.tz",
+ "django.template.context_processors.request",
+ "django.contrib.messages.context_processors.messages",
+ ],
+ 'debug': DEBUG
+ },
+ },
+]
-USE_GRAVATAR=True
+USE_GRAVATAR = True
TERMS_OF_HIRE_URL = "http://www.nottinghamtec.co.uk/terms.pdf"
AUTHORISATION_NOTIFICATION_ADDRESS = 'productions@nottinghamtec.co.uk'
diff --git a/PyRIGS/urls.py b/PyRIGS/urls.py
index 9821ae20..65bf2e63 100644
--- a/PyRIGS/urls.py
+++ b/PyRIGS/urls.py
@@ -1,4 +1,4 @@
-from django.conf.urls import patterns, include, url
+from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.conf import settings
@@ -6,19 +6,24 @@ from registration.backends.default.views import RegistrationView
import RIGS
from RIGS import regbackend
-urlpatterns = patterns('',
+urlpatterns = [
# Examples:
# url(r'^$', 'PyRIGS.views.home', name='home'),
# url(r'^blog/', include('blog.urls')),
url(r'^', include('RIGS.urls')),
- url('^user/register/$', RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail),
+ url('^user/register/$', RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail),
name="registration_register"),
url('^user/', include('django.contrib.auth.urls')),
url('^user/', include('registration.backends.default.urls')),
url(r'^admin/', include(admin.site.urls)),
-)
+]
if settings.DEBUG:
- urlpatterns += staticfiles_urlpatterns()
\ No newline at end of file
+ urlpatterns += staticfiles_urlpatterns()
+
+ import debug_toolbar
+ urlpatterns = [
+ url(r'^__debug__/', include(debug_toolbar.urls)),
+ ] + urlpatterns
diff --git a/README.md b/README.md
index 787502ef..625bac90 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# TEC PA & Lighting - PyRIGS #
[](https://travis-ci.org/nottinghamtec/PyRIGS)
[](https://coveralls.io/github/nottinghamtec/PyRIGS?branch=develop)
+[](https://gemnasium.com/github.com/nottinghamtec/PyRIGS)
+
Welcome to TEC PA & Lightings PyRIGS program. This is a reimplementation of the existing Rig Information Gathering System (RIGS) that was developed using Ruby on Rails.
diff --git a/RIGS/admin.py b/RIGS/admin.py
index a351aed0..49b8aa1e 100644
--- a/RIGS/admin.py
+++ b/RIGS/admin.py
@@ -2,7 +2,7 @@ from django.contrib import admin
from RIGS import models, forms
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext_lazy as _
-import reversion
+from reversion.admin import VersionAdmin
from django.contrib.admin import helpers
from django.template.response import TemplateResponse
@@ -12,10 +12,12 @@ from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Count
from django.forms import ModelForm
+from reversion import revisions as reversion
+
# Register your models here.
-admin.site.register(models.VatRate, reversion.VersionAdmin)
-admin.site.register(models.Event, reversion.VersionAdmin)
-admin.site.register(models.EventItem, reversion.VersionAdmin)
+admin.site.register(models.VatRate, VersionAdmin)
+admin.site.register(models.Event, VersionAdmin)
+admin.site.register(models.EventItem, VersionAdmin)
admin.site.register(models.Invoice)
admin.site.register(models.Payment)
@@ -41,7 +43,7 @@ class ProfileAdmin(UserAdmin):
add_form = forms.ProfileCreationForm
-class AssociateAdmin(reversion.VersionAdmin):
+class AssociateAdmin(VersionAdmin):
list_display = ('id', 'name', 'number_of_events')
search_fields = ['id', 'name']
list_display_links = ['id', 'name']
@@ -93,8 +95,7 @@ class AssociateAdmin(reversion.VersionAdmin):
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
'forms': forms
}
- return TemplateResponse(request, 'RIGS/admin_associate_merge.html', context,
- current_app=self.admin_site.name)
+ return TemplateResponse(request, 'RIGS/admin_associate_merge.html', context)
@admin.register(models.Person)
diff --git a/RIGS/finance.py b/RIGS/finance.py
index 1e0c91a7..2c9f21c0 100644
--- a/RIGS/finance.py
+++ b/RIGS/finance.py
@@ -15,6 +15,9 @@ from z3c.rml import rml2pdf
from RIGS import models
+from django import forms
+forms.DateField.widget = forms.DateInput(attrs={'type': 'date'})
+
class InvoiceIndex(generic.ListView):
model = models.Invoice
@@ -55,8 +58,8 @@ class InvoicePrint(generic.View):
invoice = get_object_or_404(models.Invoice, pk=pk)
object = invoice.event
template = get_template('RIGS/event_print.xml')
- copies = ('TEC', 'Client')
- context = RequestContext(request, {
+
+ context = {
'object': object,
'fonts': {
'opensans': {
@@ -66,7 +69,7 @@ class InvoicePrint(generic.View):
},
'invoice': invoice,
'current_user': request.user,
- })
+ }
rml = template.render(context)
buffer = StringIO.StringIO()
@@ -78,7 +81,7 @@ class InvoicePrint(generic.View):
escapedEventName = re.sub('[^a-zA-Z0-9 \n\.]', '', object.name)
response = HttpResponse(content_type='application/pdf')
- response['Content-Disposition'] = "filename=Invoice %05d | %s.pdf" % (invoice.pk, escapedEventName)
+ response['Content-Disposition'] = "filename=Invoice %05d - N%05d | %s.pdf" % (invoice.pk, invoice.event.pk, escapedEventName)
response.write(pdfData)
return response
diff --git a/RIGS/forms.py b/RIGS/forms.py
index 752b7fb8..5d5fb0e6 100644
--- a/RIGS/forms.py
+++ b/RIGS/forms.py
@@ -10,6 +10,10 @@ import simplejson
from RIGS import models
+# Override the django form defaults to use the HTML date/time/datetime UI elements
+forms.DateField.widget = forms.DateInput(attrs={'type': 'date'})
+forms.TimeField.widget = forms.DateInput(attrs={'type': 'time'})
+forms.DateTimeField.widget = forms.DateInput(attrs={'type': 'datetime-local'})
# Registration
class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
@@ -45,7 +49,7 @@ class ProfileChangeForm(UserChangeForm):
# Events Shit
class EventForm(forms.ModelForm):
- datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + settings.DATETIME_INPUT_FORMATS
+ datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + list(settings.DATETIME_INPUT_FORMATS)
meet_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False)
access_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False)
diff --git a/RIGS/management/commands/generateSampleData.py b/RIGS/management/commands/generateSampleData.py
index bf1ce7d2..59c39c97 100644
--- a/RIGS/management/commands/generateSampleData.py
+++ b/RIGS/management/commands/generateSampleData.py
@@ -1,7 +1,7 @@
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import Group, Permission
from django.db import transaction
-import reversion
+from reversion import revisions as reversion
import datetime
import random
diff --git a/RIGS/migrations/0025_auto_20160331_1302.py b/RIGS/migrations/0025_auto_20160331_1302.py
new file mode 100644
index 00000000..eacc7bfd
--- /dev/null
+++ b/RIGS/migrations/0025_auto_20160331_1302.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.4 on 2016-03-31 12:02
+from __future__ import unicode_literals
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('RIGS', '0024_auto_20160229_2042'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='profile',
+ name='username',
+ field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], verbose_name='username'),
+ ),
+ migrations.AlterField(
+ model_name='vatrate',
+ name='start_at',
+ field=models.DateField(),
+ ),
+ ]
diff --git a/RIGS/migrations/0026_auto_20170510_1846.py b/RIGS/migrations/0026_auto_20170510_1846.py
new file mode 100644
index 00000000..0a350f10
--- /dev/null
+++ b/RIGS/migrations/0026_auto_20170510_1846.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-10 17:46
+from __future__ import unicode_literals
+
+import django.contrib.auth.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('RIGS', '0025_auto_20160331_1302'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='profile',
+ name='username',
+ field=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.ASCIIUsernameValidator()], verbose_name='username'),
+ ),
+ ]
diff --git a/RIGS/models.py b/RIGS/models.py
index 89ec852b..ebb31ca6 100644
--- a/RIGS/models.py
+++ b/RIGS/models.py
@@ -1,19 +1,22 @@
import datetime
import hashlib
-import pytz
-import random
+import datetime, pytz
+
+from django.db import models
+from django.contrib.auth.models import AbstractUser
+from django.conf import settings
+from django.utils import timezone
+from django.utils.functional import cached_property
+from django.utils.encoding import python_2_unicode_compatible
+from reversion import revisions as reversion
import string
+
+import random
from collections import Counter
from decimal import Decimal
-import reversion
-from django.conf import settings
-from django.contrib.auth.models import AbstractUser
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse_lazy
-from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
-from django.utils.functional import cached_property
# Create your models here.
@@ -175,7 +178,7 @@ class Organisation(models.Model, RevisionMixin):
class VatManager(models.Manager):
def current_rate(self):
- return self.find_rate(datetime.datetime.now())
+ return self.find_rate(timezone.now())
def find_rate(self, date):
# return self.filter(startAt__lte=date).latest()
@@ -190,7 +193,7 @@ class VatManager(models.Manager):
@reversion.register
@python_2_unicode_compatible
class VatRate(models.Model, RevisionMixin):
- start_at = models.DateTimeField()
+ start_at = models.DateField()
rate = models.DecimalField(max_digits=6, decimal_places=6)
comment = models.CharField(max_length=255)
@@ -241,18 +244,12 @@ class Venue(models.Model, RevisionMixin):
class EventManager(models.Manager):
def current_events(self):
events = self.filter(
- (models.Q(start_date__gte=datetime.date.today(), end_date__isnull=True, dry_hire=False) & ~models.Q(
- status=Event.CANCELLED)) | # Starts after with no end
- (models.Q(end_date__gte=datetime.date.today(), dry_hire=False) & ~models.Q(
- status=Event.CANCELLED)) | # Ends after
- (models.Q(dry_hire=True, start_date__gte=datetime.date.today()) & ~models.Q(
- status=Event.CANCELLED)) | # Active dry hire
- (models.Q(dry_hire=True, checked_in_by__isnull=True) & (
- models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) | # Active dry hire GT
- models.Q(status=Event.CANCELLED, start_date__gte=datetime.date.today()) # Canceled but not started
- ).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person',
- 'organisation',
- 'venue', 'mic')
+ (models.Q(start_date__gte=timezone.now().date(), end_date__isnull=True, dry_hire=False) & ~models.Q(status=Event.CANCELLED)) | # Starts after with no end
+ (models.Q(end_date__gte=timezone.now().date(), dry_hire=False) & ~models.Q(status=Event.CANCELLED)) | # Ends after
+ (models.Q(dry_hire=True, start_date__gte=timezone.now().date()) & ~models.Q(status=Event.CANCELLED)) | # Active dry hire
+ (models.Q(dry_hire=True, checked_in_by__isnull=True) & (models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) | # Active dry hire GT
+ models.Q(status=Event.CANCELLED, start_date__gte=timezone.now().date()) # Canceled but not started
+ ).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person', 'organisation', 'venue', 'mic')
return events
def events_in_bounds(self, start, end):
@@ -275,12 +272,12 @@ class EventManager(models.Manager):
def rig_count(self):
event_count = self.filter(
- (models.Q(start_date__gte=datetime.date.today(), end_date__isnull=True, dry_hire=False,
+ (models.Q(start_date__gte=timezone.now().date(), end_date__isnull=True, dry_hire=False,
is_rig=True) & ~models.Q(
status=Event.CANCELLED)) | # Starts after with no end
- (models.Q(end_date__gte=datetime.date.today(), dry_hire=False, is_rig=True) & ~models.Q(
+ (models.Q(end_date__gte=timezone.now().date(), dry_hire=False, is_rig=True) & ~models.Q(
status=Event.CANCELLED)) | # Ends after
- (models.Q(dry_hire=True, start_date__gte=datetime.date.today(), is_rig=True) & ~models.Q(
+ (models.Q(dry_hire=True, start_date__gte=timezone.now().date(), is_rig=True) & ~models.Q(
status=Event.CANCELLED)) | # Active dry hire
(models.Q(dry_hire=True, checked_in_by__isnull=True, is_rig=True) & (
models.Q(status=Event.BOOKED) | models.Q(status=Event.CONFIRMED))) # Active dry hire GT
diff --git a/RIGS/rigboard.py b/RIGS/rigboard.py
index 80c3c4b8..dbe7e6c7 100644
--- a/RIGS/rigboard.py
+++ b/RIGS/rigboard.py
@@ -151,7 +151,7 @@ class EventPrint(generic.View):
merger = PdfFileMerger()
- context = RequestContext(request, {
+ context = {
'object': object,
'fonts': {
'opensans': {
@@ -161,7 +161,7 @@ class EventPrint(generic.View):
},
'quote': True,
'current_user': request.user,
- })
+ }
rml = template.render(context)
diff --git a/RIGS/templates/RIGS/event_form.html b/RIGS/templates/RIGS/event_form.html
index 71e91fe6..24f6aa78 100644
--- a/RIGS/templates/RIGS/event_form.html
+++ b/RIGS/templates/RIGS/event_form.html
@@ -289,10 +289,10 @@
- {% render_field form.start_date type="date" class+="form-control" %}
+ {% render_field form.start_date class+="form-control" %}
- {% render_field form.start_time type="time" class+="form-control" %}
+ {% render_field form.start_time class+="form-control" %}
@@ -304,10 +304,10 @@
- {% render_field form.end_date type="date" class+="form-control" %}
+ {% render_field form.end_date class+="form-control" %}
- {% render_field form.end_time type="time" class+="form-control" %}
+ {% render_field form.end_time class+="form-control" %}
@@ -329,7 +329,7 @@
class="col-sm-4 control-label">{{ form.access_at.label }}
- {% render_field form.access_at type="datetime-local" class+="form-control" %}
+ {% render_field form.access_at class+="form-control" %}
diff --git a/RIGS/test_functional.py b/RIGS/test_functional.py
index 96d074ca..231ade20 100644
--- a/RIGS/test_functional.py
+++ b/RIGS/test_functional.py
@@ -18,6 +18,14 @@ from selenium.webdriver.support.ui import WebDriverWait
from RIGS import models
+import re
+import os
+from datetime import date, timedelta
+from django.db import transaction
+from reversion import revisions as reversion
+import json
+
+
class UserRegistrationTest(LiveServerTestCase):
def setUp(self):
@@ -434,7 +442,8 @@ class EventTest(LiveServerTestCase):
# See redirected to success page
successTitle = self.browser.find_element_by_xpath('//h1').text
event = models.Event.objects.get(name='Test Event Name')
- self.assertIn("N0000%d | Test Event Name" % event.pk, successTitle)
+
+ self.assertIn("N%05d | Test Event Name"%event.pk, successTitle)
except WebDriverException:
# This is a dirty workaround for wercker being a bit funny and not running it correctly.
# Waiting for wercker to get back to me about this
@@ -496,9 +505,9 @@ class EventTest(LiveServerTestCase):
# Attempt to save
save.click()
- self.assertNotIn("N0000%d" % testEvent.pk, self.browser.find_element_by_xpath('//h1').text)
- self.assertNotIn("Event data duplicated but not yet saved",
- self.browser.find_element_by_id('content').text) # Check info message not visible
+
+ self.assertNotIn("N%05d"%testEvent.pk, self.browser.find_element_by_xpath('//h1').text)
+ self.assertNotIn("Event data duplicated but not yet saved", self.browser.find_element_by_id('content').text) # Check info message not visible
# Check the new items are visible
table = self.browser.find_element_by_id('item-table') # ID number is known, see above
@@ -507,15 +516,22 @@ class EventTest(LiveServerTestCase):
self.assertIn("Test Item 3", table.text)
infoPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Event Info")]/..')
- self.assertIn("N0000%d" % testEvent.pk,
- infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text)
- self.browser.get(self.live_server_url + '/event/' + str(testEvent.pk)) # Go back to the old event
- # Check that based-on hasn't crept into the old event
+ self.assertIn("N%05d"%testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text)
+
+ # Check the PO hasn't carried through
+ self.assertNotIn("TESTPO", infoPanel.find_element_by_xpath('//dt[text()="PO"]/following-sibling::dd[1]').text)
+
+ self.browser.get(self.live_server_url + '/event/' + str(testEvent.pk)) #Go back to the old event
+
+ #Check that based-on hasn't crept into the old event
infoPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Event Info")]/..')
- self.assertNotIn("N0000%d" % testEvent.pk,
- infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text)
+
+ self.assertNotIn("N%05d"%testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text)
+
+ # Check the PO remains on the old event
+ self.assertIn("TESTPO", infoPanel.find_element_by_xpath('//dt[text()="PO"]/following-sibling::dd[1]').text)
# Check the items are as they were
table = self.browser.find_element_by_id('item-table') # ID number is known, see above
@@ -630,8 +646,9 @@ class EventTest(LiveServerTestCase):
# See redirected to success page
successTitle = self.browser.find_element_by_xpath('//h1').text
event = models.Event.objects.get(name='Test Event Name')
- self.assertIn("N0000%d | Test Event Name" % event.pk, successTitle)
+ self.assertIn("N%05d | Test Event Name"%event.pk, successTitle)
+
def testRigNonRig(self):
self.browser.get(self.live_server_url + '/event/create/')
# Gets redirected to login and back
diff --git a/RIGS/test_models.py b/RIGS/test_models.py
index 1f36be43..1fd8b4a0 100644
--- a/RIGS/test_models.py
+++ b/RIGS/test_models.py
@@ -17,71 +17,82 @@ class ProfileTestCase(TestCase):
class VatRateTestCase(TestCase):
- def setUp(self):
- models.VatRate.objects.create(start_at='2014-03-01', rate=0.20, comment='test1')
- models.VatRate.objects.create(start_at='2016-03-01', rate=0.15, comment='test2')
+ @classmethod
+ def setUpTestData(cls):
+ cls.rates = {
+ 0: models.VatRate.objects.create(start_at='2014-03-01', rate=0.20, comment='test1'),
+ 1: models.VatRate.objects.create(start_at='2016-03-01', rate=0.15, comment='test2'),
+ }
def test_find_correct(self):
r = models.VatRate.objects.find_rate('2015-03-01')
- self.assertEqual(r.comment, 'test1')
+ self.assertEqual(r, self.rates[0])
r = models.VatRate.objects.find_rate('2016-03-01')
- self.assertEqual(r.comment, 'test2')
+ self.assertEqual(r, self.rates[1])
def test_percent_correct(self):
- r = models.VatRate.objects.get(rate=0.20)
- self.assertEqual(r.as_percent, 20)
+ self.assertEqual(self.rates[0].as_percent, 20)
class EventTestCase(TestCase):
- def setUp(self):
- self.all_events = set(range(1, 18))
- self.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18)
- self.not_current_events = set(self.all_events) - set(self.current_events)
+ @classmethod
+ def setUpTestData(cls):
+ cls.all_events = set(range(1, 18))
+ cls.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18)
+ cls.not_current_events = set(cls.all_events) - set(cls.current_events)
- self.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
- self.profile = models.Profile.objects.create(username="testuser1", email="1@test.com")
+ cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
+ cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com")
- # produce 7 normal events - 5 current
- models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6),
- description="start future no end")
- models.Event.objects.create(name="TE E2", start_date=date.today(), description="start today no end")
- models.Event.objects.create(name="TE E3", start_date=date.today(), end_date=date.today(),
- description="start today with end today")
- models.Event.objects.create(name="TE E4", start_date='2014-03-20', description="start past no end")
- models.Event.objects.create(name="TE E5", start_date='2014-03-20', end_date='2014-03-21',
- description="start past with end past")
- models.Event.objects.create(name="TE E6", start_date=date.today() - timedelta(days=2),
- end_date=date.today() + timedelta(days=2), description="start past, end future")
- models.Event.objects.create(name="TE E7", start_date=date.today() + timedelta(days=2),
- end_date=date.today() + timedelta(days=2), description="start + end in future")
+ cls.events = {
+ # produce 7 normal events - 5 current
+ 1: models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6),
+ description="start future no end"),
+ 2: models.Event.objects.create(name="TE E2", start_date=date.today(), description="start today no end"),
+ 3: models.Event.objects.create(name="TE E3", start_date=date.today(), end_date=date.today(),
+ description="start today with end today"),
+ 4: models.Event.objects.create(name="TE E4", start_date='2014-03-20', description="start past no end"),
+ 5: models.Event.objects.create(name="TE E5", start_date='2014-03-20', end_date='2014-03-21',
+ description="start past with end past"),
+ 6: models.Event.objects.create(name="TE E6", start_date=date.today() - timedelta(days=2),
+ end_date=date.today() + timedelta(days=2),
+ description="start past, end future"),
+ 7: models.Event.objects.create(name="TE E7", start_date=date.today() + timedelta(days=2),
+ end_date=date.today() + timedelta(days=2),
+ description="start + end in future"),
- # 2 cancelled - 1 current
- models.Event.objects.create(name="TE E8", start_date=date.today() + timedelta(days=2),
- end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
- description="cancelled in future")
- models.Event.objects.create(name="TE E9", start_date=date.today() - timedelta(days=1),
- end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
- description="cancelled and started")
+ # 2 cancelled - 1 current
+ 8: models.Event.objects.create(name="TE E8", start_date=date.today() + timedelta(days=2),
+ end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
+ description="cancelled in future"),
+ 9: models.Event.objects.create(name="TE E9", start_date=date.today() - timedelta(days=1),
+ end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
+ description="cancelled and started"),
- # 5 dry hire - 3 current
- models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True, description="dryhire today")
- models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile,
- description="dryhire today, checked in")
- models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True,
- status=models.Event.BOOKED, description="dryhire past")
- models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True,
- checked_in_by=self.profile, description="dryhire past checked in")
- models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True,
- status=models.Event.CANCELLED, description="dryhire today cancelled")
+ # 5 dry hire - 3 current
+ 10: models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True,
+ description="dryhire today"),
+ 11: models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True,
+ checked_in_by=cls.profile,
+ description="dryhire today, checked in"),
+ 12: models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True,
+ status=models.Event.BOOKED, description="dryhire past"),
+ 13: models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True,
+ checked_in_by=cls.profile, description="dryhire past checked in"),
+ 14: models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True,
+ status=models.Event.CANCELLED, description="dryhire today cancelled"),
- # 4 non rig - 3 current
- models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False, description="non rig today")
- models.Event.objects.create(name="TE E16", start_date=date.today() + timedelta(days=1), is_rig=False,
- description="non rig tomorrow")
- models.Event.objects.create(name="TE E17", start_date=date.today() - timedelta(days=1), is_rig=False,
- description="non rig yesterday")
- models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED,
- description="non rig today cancelled")
+ # 4 non rig - 3 current
+ 15: models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False,
+ description="non rig today"),
+ 16: models.Event.objects.create(name="TE E16", start_date=date.today() + timedelta(days=1), is_rig=False,
+ description="non rig tomorrow"),
+ 17: models.Event.objects.create(name="TE E17", start_date=date.today() - timedelta(days=1), is_rig=False,
+ description="non rig yesterday"),
+ 18: models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False,
+ status=models.Event.CANCELLED,
+ description="non rig today cancelled"),
+ }
def test_count(self):
# Santiy check we have the expected events created
@@ -103,17 +114,23 @@ class EventTestCase(TestCase):
def test_related_venue(self):
v1 = models.Venue.objects.create(name="TE V1")
v2 = models.Venue.objects.create(name="TE V2")
- events = models.Event.objects.all()
- for event in events[:2]:
- event.venue = v1
- event.save()
- for event in events[3:4]:
- event.venue = v2
+
+ e1 = []
+ e2 = []
+ for (key, event) in self.events.iteritems():
+ if event.pk % 2:
+ event.venue = v1
+ e1.append(event)
+ else:
+ event.venue = v2
+ e2.append(event)
event.save()
- events = models.Event.objects.all()
- self.assertItemsEqual(events[:2], v1.latest_events)
- self.assertItemsEqual(events[3:4], v2.latest_events)
+ self.assertItemsEqual(e1, v1.latest_events)
+ self.assertItemsEqual(e2, v2.latest_events)
+
+ for (key, event) in self.events.iteritems():
+ event.venue = None
def test_related_vatrate(self):
self.assertEqual(self.vatrate, models.Event.objects.all()[0].vat_rate)
@@ -122,33 +139,43 @@ class EventTestCase(TestCase):
p1 = models.Person.objects.create(name="TE P1")
p2 = models.Person.objects.create(name="TE P2")
- events = models.Event.objects.all()
- for event in events[:2]:
- event.person = p1
- event.save()
- for event in events[3:4]:
- event.person = p2
+ e1 = []
+ e2 = []
+ for (key, event) in self.events.iteritems():
+ if event.pk % 2:
+ event.person = p1
+ e1.append(event)
+ else:
+ event.person = p2
+ e2.append(event)
event.save()
- events = models.Event.objects.all()
- self.assertItemsEqual(events[:2], p1.latest_events)
- self.assertItemsEqual(events[3:4], p2.latest_events)
+ self.assertItemsEqual(e1, p1.latest_events)
+ self.assertItemsEqual(e2, p2.latest_events)
+
+ for (key, event) in self.events.iteritems():
+ event.person = None
def test_related_organisation(self):
o1 = models.Organisation.objects.create(name="TE O1")
o2 = models.Organisation.objects.create(name="TE O2")
- events = models.Event.objects.all()
- for event in events[:2]:
- event.organisation = o1
- event.save()
- for event in events[3:4]:
- event.organisation = o2
+ e1 = []
+ e2 = []
+ for (key, event) in self.events.iteritems():
+ if event.pk % 2:
+ event.organisation = o1
+ e1.append(event)
+ else:
+ event.organisation = o2
+ e2.append(event)
event.save()
- events = models.Event.objects.all()
- self.assertItemsEqual(events[:2], o1.latest_events)
- self.assertItemsEqual(events[3:4], o2.latest_events)
+ self.assertItemsEqual(e1, o1.latest_events)
+ self.assertItemsEqual(e2, o2.latest_events)
+
+ for (key, event) in self.events.iteritems():
+ event.organisation = None
def test_organisation_person_join(self):
p1 = models.Person.objects.create(name="TE P1")
@@ -186,20 +213,20 @@ class EventTestCase(TestCase):
self.assertEqual(len(o2.persons), 1)
def test_cancelled_property(self):
- event = models.Event.objects.all()[0]
- event.status = models.Event.CANCELLED
- event.save()
- event = models.Event.objects.all()[0]
+ edit = self.events[1]
+ edit.status = models.Event.CANCELLED
+ edit.save()
+ event = models.Event.objects.get(pk=edit.pk)
self.assertEqual(event.status, models.Event.CANCELLED)
self.assertTrue(event.cancelled)
event.status = models.Event.PROVISIONAL
event.save()
def test_confirmed_property(self):
- event = models.Event.objects.all()[0]
- event.status = models.Event.CONFIRMED
- event.save()
- event = models.Event.objects.all()[0]
+ edit = self.events[1]
+ edit.status = models.Event.CONFIRMED
+ edit.save()
+ event = models.Event.objects.get(pk=edit.pk)
self.assertEqual(event.status, models.Event.CONFIRMED)
self.assertTrue(event.confirmed)
event.status = models.Event.PROVISIONAL
@@ -250,14 +277,14 @@ class EventTestCase(TestCase):
# basic checks
manager.create(name='TE IB2', start_date='2016-01-02', end_date='2016-01-04'),
manager.create(name='TE IB3', start_date='2015-12-31', end_date='2016-01-03'),
- manager.create(name='TE IB4', start_date='2016-01-04', access_at='2016-01-03'),
- manager.create(name='TE IB5', start_date='2016-01-04', meet_at='2016-01-02'),
+ manager.create(name='TE IB4', start_date='2016-01-04', access_at=self.create_datetime(2016, 01, 03, 00, 00)),
+ manager.create(name='TE IB5', start_date='2016-01-04', meet_at=self.create_datetime(2016, 01, 02, 00, 00)),
# negative check
manager.create(name='TE IB6', start_date='2015-12-31', end_date='2016-01-01'),
]
- in_bounds = manager.events_in_bounds(datetime(2016, 1, 2), datetime(2016, 1, 3))
+ in_bounds = manager.events_in_bounds(self.create_datetime(2016, 1, 2, 0, 0), self.create_datetime(2016, 1, 3, 0, 0))
self.assertIn(events[0], in_bounds)
self.assertIn(events[1], in_bounds)
self.assertIn(events[2], in_bounds)
diff --git a/RIGS/test_unit.py b/RIGS/test_unit.py
index 82a7acca..5b531636 100644
--- a/RIGS/test_unit.py
+++ b/RIGS/test_unit.py
@@ -164,6 +164,8 @@ class TestInvoiceDelete(TestCase):
def setUpTestData(cls):
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, is_active=True, is_staff=True)
+ cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
+
cls.events = {
1: models.Event.objects.create(name="TE E1", start_date=date.today()),
2: models.Event.objects.create(name="TE E2", start_date=date.today())
@@ -214,6 +216,39 @@ class TestInvoiceDelete(TestCase):
self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk))
+class TestPrintPaperwork(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True, is_active=True, is_staff=True)
+
+ cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
+
+ cls.events = {
+ 1: models.Event.objects.create(name="TE E1", start_date=date.today()),
+ }
+
+ cls.invoices = {
+ 1: models.Invoice.objects.create(event=cls.events[1]),
+ }
+
+ def setUp(self):
+ self.profile.set_password('testuser')
+ self.profile.save()
+ self.assertTrue(self.client.login(username=self.profile.username, password='testuser'))
+
+ def test_print_paperwork_success(self):
+ request_url = reverse('event_print', kwargs={'pk': self.events[1].pk})
+
+ response = self.client.get(request_url, follow=True)
+ self.assertEqual(response.status_code, 200)
+
+ def test_print_invoice_success(self):
+ request_url = reverse('invoice_print', kwargs={'pk': self.invoices[1].pk})
+
+ response = self.client.get(request_url, follow=True)
+ self.assertEqual(response.status_code, 200)
+
+
class TestEmbeddedViews(TestCase):
@classmethod
def setUpTestData(cls):
diff --git a/RIGS/urls.py b/RIGS/urls.py
index 9c9f1628..4d1c67ec 100644
--- a/RIGS/urls.py
+++ b/RIGS/urls.py
@@ -1,4 +1,6 @@
-from django.conf.urls import patterns, url
+from django.conf.urls import url
+from django.contrib.auth.views import password_reset
+
from django.contrib.auth.decorators import login_required
from RIGS import models, views, rigboard, finance, ical, versioning, forms
from django.views.generic import RedirectView
@@ -7,17 +9,17 @@ from django.views.decorators.clickjacking import xframe_options_exempt
from PyRIGS.decorators import permission_required_with_403
from PyRIGS.decorators import api_key_required
-urlpatterns = patterns('',
+urlpatterns = [
# Examples:
# url(r'^$', 'PyRIGS.views.home', name='home'),
# url(r'^blog/', include('blog.urls')),
url('^$', login_required(views.Index.as_view()), name='index'),
url(r'^closemodal/$', views.CloseModal.as_view(), name='closemodal'),
- url('^user/login/$', 'RIGS.views.login', name='login'),
+ url('^user/login/$', views.login, name='login'),
url('^user/login/embed/$', xframe_options_exempt(views.login_embed), name='login_embed'),
- url(r'^user/password_reset/$', 'django.contrib.auth.views.password_reset',
- {'password_reset_form': forms.PasswordReset}),
+
+ url(r'^user/password_reset/$', password_reset, {'password_reset_form': forms.PasswordReset}),
# People
url(r'^people/$', permission_required_with_403('RIGS.view_person')(views.PersonList.as_view()),
@@ -188,4 +190,4 @@ urlpatterns = patterns('',
RedirectView.as_view(permanent=True, pattern_name='event_detail')),
url(r'^bookings/$', RedirectView.as_view(permanent=True, pattern_name='rigboard')),
url(r'^bookings/past/$', RedirectView.as_view(permanent=True, pattern_name='event_archive')),
- )
+ ]
diff --git a/RIGS/versioning.py b/RIGS/versioning.py
index 6d524fc0..65d4328a 100644
--- a/RIGS/versioning.py
+++ b/RIGS/versioning.py
@@ -158,7 +158,7 @@ def get_previous_version(version):
thisId = version.object_id
thisVersionId = version.pk
- versions = reversion.get_for_object_reference(version.content_type.model_class(), thisId)
+ versions = reversion.revisions.get_for_object_reference(version.content_type.model_class(), thisId)
try:
previousVersions = versions.filter(revision_id__lt=version.revision_id).latest(
@@ -199,7 +199,7 @@ def get_changes_for_version(newVersion, oldVersion=None):
class VersionHistory(generic.ListView):
- model = reversion.revisions.Version
+ model = Version
template_name = "RIGS/version_history.html"
paginate_by = 25
@@ -207,7 +207,7 @@ class VersionHistory(generic.ListView):
thisModel = self.kwargs['model']
# thisObject = get_object_or_404(thisModel, pk=self.kwargs['pk'])
- versions = reversion.get_for_object_reference(thisModel, self.kwargs['pk'])
+ versions = reversion.revisions.get_for_object_reference(thisModel, self.kwargs['pk'])
return versions
@@ -236,7 +236,7 @@ class VersionHistory(generic.ListView):
class ActivityTable(generic.ListView):
- model = reversion.revisions.Version
+ model = Version
template_name = "RIGS/activity_table.html"
paginate_by = 25
@@ -260,7 +260,7 @@ class ActivityTable(generic.ListView):
class ActivityFeed(generic.ListView):
- model = reversion.revisions.Version
+ model = Version
template_name = "RIGS/activity_feed_data.html"
paginate_by = 25
diff --git a/RIGS/views.py b/RIGS/views.py
index c0186bed..b681c1bb 100644
--- a/RIGS/views.py
+++ b/RIGS/views.py
@@ -30,7 +30,7 @@ class Index(generic.TemplateView):
def login(request, **kwargs):
if request.user.is_authenticated():
- next = request.REQUEST.get('next', '/')
+ next = request.GET.get('next', '/')
return HttpResponseRedirect(next)
else:
from django.contrib.auth.views import login
@@ -44,9 +44,8 @@ def login(request, **kwargs):
# check for it before logging the user in
@csrf_exempt
def login_embed(request, **kwargs):
- print("Running LOGIN")
if request.user.is_authenticated():
- next = request.REQUEST.get('next', '/')
+ next = request.GET.get('next', '/')
return HttpResponseRedirect(next)
else:
from django.contrib.auth.views import login
diff --git a/db.sqlite3 b/db.sqlite3
deleted file mode 100644
index 07462b3f..00000000
Binary files a/db.sqlite3 and /dev/null differ
diff --git a/requirements.txt b/requirements.txt
index 4bfce216..a285e33e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,34 +1,37 @@
+beautifulsoup4==4.6.0
+contextlib2==0.5.5
diff-match-patch==20121119
-dj-database-url==0.3.0
+dj-database-url==0.4.2
dj-static==0.0.6
-Django==1.8.2
-django-debug-toolbar==1.3.0
-django-ical==1.3
-django-recaptcha==1.0.4
-django-registration-redux==1.2
-django-reversion==1.8.7
+Django==1.11.1
+django-debug-toolbar==1.8
+django-ical==1.4
+django-recaptcha==1.3.0
+django-registration-redux==1.6
+django-reversion==1.10.2
django-toolbelt==0.0.1
-django-widget-tweaks==1.3
-gunicorn==19.3.0
-icalendar==3.9.0
-lxml==3.4.4
-Pillow==2.8.1
premailer==3.0.1
-psycopg2==2.6
-Pygments==2.0.2
-PyPDF2==1.24
-python-dateutil==2.4.2
-pytz==2015.4
-raven==5.8.1
-reportlab==3.1.44
-selenium==2.53.6
-simplejson==3.7.2
-six==1.9.0
-sqlparse==0.1.15
-static3==0.6.1
+django-widget-tweaks==1.4.1
+gunicorn==19.7.1
+icalendar==3.11.4
+lxml==3.7.3
+Markdown==2.6.8
+Pillow==4.1.1
+psycopg2==2.7.1
+Pygments==2.2.0
+PyPDF2==1.26.0
+python-dateutil==2.6.0
+pytz==2017.2
+raven==6.0.0
+reportlab==3.4.0
+selenium==2.53.1
+simplejson==3.10.0
+six==1.10.0
+sqlparse==0.2.3
+static3==0.7.0
svg2rlg==0.3
yolk==0.4.3
-z3c.rml==2.8.1
-zope.event==4.0.3
-zope.interface==4.1.2
+z3c.rml==3.2.0
+zope.event==4.2.0
+zope.interface==4.4.0
zope.schema==4.4.2