From 486c66b198147b3c30efc9ff71a4dbb74b6927b4 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Thu, 31 Mar 2016 11:59:21 +0100 Subject: [PATCH 01/21] Update requirements.txt before starting to test for issues --- requirements.txt | 50 +++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/requirements.txt b/requirements.txt index 532cadea..f74f08e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,33 +1,35 @@ +beautifulsoup4==4.4.1 diff-match-patch==20121119 -dj-database-url==0.3.0 +dj-database-url==0.4.0 dj-static==0.0.6 -Django==1.8.2 -django-debug-toolbar==1.3.0 +Django==1.9.4 +django-debug-toolbar==1.4 django-ical==1.3 -django-recaptcha==1.0.4 -django-registration-redux==1.2 -django-reversion==1.8.7 +django-recaptcha==1.0.5 +django-registration-redux==1.4 +django-reversion==1.10.1 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 -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 +django-widget-tweaks==1.4.1 +gunicorn==19.4.5 +icalendar==3.9.2 +lxml==3.6.0 +Markdown==2.6.6 +Pillow==3.1.1 +psycopg2==2.6.1 +Pygments==2.1.3 +PyPDF2==1.25.1 +python-dateutil==2.5.2 +pytz==2016.3 +raven==5.12.0 +reportlab==3.3.0 selenium==2.53.1 -simplejson==3.7.2 -six==1.9.0 -sqlparse==0.1.15 +simplejson==3.8.2 +six==1.10.0 +sqlparse==0.1.19 static3==0.6.1 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.0.0 +zope.event==4.2.0 +zope.interface==4.1.3 zope.schema==4.4.2 From 0ee37b1cd31a4af4fb0cd314ac7b97237a3710ee Mon Sep 17 00:00:00 2001 From: Tom Price Date: Thu, 31 Mar 2016 12:07:07 +0100 Subject: [PATCH 02/21] Fix issues with python2 imports --- RIGS/admin.py | 14 +++++++------- RIGS/forms.py | 2 +- RIGS/models.py | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/RIGS/admin.py b/RIGS/admin.py index 18dc554e..f8107a9e 100644 --- a/RIGS/admin.py +++ b/RIGS/admin.py @@ -2,15 +2,15 @@ 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 # Register your models here. -admin.site.register(models.Person, reversion.VersionAdmin) -admin.site.register(models.Organisation, reversion.VersionAdmin) -admin.site.register(models.VatRate, reversion.VersionAdmin) -admin.site.register(models.Venue, reversion.VersionAdmin) -admin.site.register(models.Event, reversion.VersionAdmin) -admin.site.register(models.EventItem, reversion.VersionAdmin) +admin.site.register(models.Person, VersionAdmin) +admin.site.register(models.Organisation, VersionAdmin) +admin.site.register(models.VatRate, VersionAdmin) +admin.site.register(models.Venue, VersionAdmin) +admin.site.register(models.Event, VersionAdmin) +admin.site.register(models.EventItem, VersionAdmin) admin.site.register(models.Invoice) admin.site.register(models.Payment) diff --git a/RIGS/forms.py b/RIGS/forms.py index e1e95012..d1b826c3 100644 --- a/RIGS/forms.py +++ b/RIGS/forms.py @@ -45,7 +45,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/models.py b/RIGS/models.py index fc06911f..05066323 100644 --- a/RIGS/models.py +++ b/RIGS/models.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import AbstractUser from django.conf import settings from django.utils.functional import cached_property from django.utils.encoding import python_2_unicode_compatible -import reversion +from reversion import revisions as reversion import string import random from collections import Counter @@ -552,4 +552,4 @@ class Payment(models.Model): method = models.CharField(max_length=2, choices=METHODS, null=True, blank=True) def __str__(self): - return "%s: %d" % (self.get_method_display(), self.amount) \ No newline at end of file + return "%s: %d" % (self.get_method_display(), self.amount) From cd2aed00d724bc38c21275fda99fb57482b4864a Mon Sep 17 00:00:00 2001 From: Tom Price Date: Thu, 31 Mar 2016 12:09:04 +0100 Subject: [PATCH 03/21] Update login URL in settings so redirects work correctly. --- PyRIGS/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PyRIGS/settings.py b/PyRIGS/settings.py index d85ff887..396e893f 100644 --- a/PyRIGS/settings.py +++ b/PyRIGS/settings.py @@ -144,8 +144,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 From 98ee9bb0db3c5fbac9ee7cca315c11835494ca48 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Thu, 31 Mar 2016 12:13:11 +0100 Subject: [PATCH 04/21] Fix imports in tests --- RIGS/test_functional.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RIGS/test_functional.py b/RIGS/test_functional.py index 154fdfb2..ed563be3 100644 --- a/RIGS/test_functional.py +++ b/RIGS/test_functional.py @@ -11,7 +11,7 @@ import re import os from datetime import date, timedelta from django.db import transaction -import reversion +from reversion import revisions as reversion import json From d43e4b2465c1023f12a2f429eb4db9079cef78ae Mon Sep 17 00:00:00 2001 From: Tom Price Date: Thu, 31 Mar 2016 12:33:46 +0100 Subject: [PATCH 05/21] Update settings and urls to fix new deprecations --- PyRIGS/settings.py | 53 +++++---- PyRIGS/urls.py | 8 +- RIGS/urls.py | 272 ++++++++++++++++++++++----------------------- 3 files changed, 169 insertions(+), 164 deletions(-) diff --git a/PyRIGS/settings.py b/PyRIGS/settings.py index 396e893f..13ca4874 100644 --- a/PyRIGS/settings.py +++ b/PyRIGS/settings.py @@ -10,6 +10,7 @@ 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__)) SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') @@ -18,13 +19,12 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # 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 -TEMPLATE_DEBUG = True - ALLOWED_HOSTS = ['pyrigs.nottinghamtec.co.uk', 'rigs.nottinghamtec.co.uk', 'pyrigs.herokuapp.com'] INTERNAL_IPS = ['127.0.0.1'] @@ -33,7 +33,6 @@ ADMINS = ( ('Tom Price', 'tomtom5152@gmail.com') ) - # Application definition INSTALLED_APPS = ( @@ -69,7 +68,6 @@ ROOT_URLCONF = 'PyRIGS.urls' WSGI_APPLICATION = 'PyRIGS.wsgi.application' - # Database # https://docs.djangoproject.com/en/1.7/ref/settings/#databases DATABASES = { @@ -81,6 +79,7 @@ DATABASES = { if not DEBUG: import dj_database_url + DATABASES['default'] = dj_database_url.config() # Logging @@ -111,12 +110,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', @@ -183,19 +182,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/ @@ -206,10 +193,28 @@ 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.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", + ] + }, + }, +] -USE_GRAVATAR=True +USE_GRAVATAR = True TERMS_OF_HIRE_URL = "http://www.nottinghamtec.co.uk/terms.pdf" diff --git a/PyRIGS/urls.py b/PyRIGS/urls.py index 9821ae20..f8993d48 100644 --- a/PyRIGS/urls.py +++ b/PyRIGS/urls.py @@ -6,19 +6,19 @@ 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() diff --git a/RIGS/urls.py b/RIGS/urls.py index 4338df08..fa6f9872 100644 --- a/RIGS/urls.py +++ b/RIGS/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import patterns, include, 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 @@ -6,153 +7,152 @@ from django.views.generic import RedirectView from PyRIGS.decorators import permission_required_with_403 from PyRIGS.decorators import api_key_required -urlpatterns = patterns('', - # 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'), +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(r'^user/password_reset/$', 'django.contrib.auth.views.password_reset', {'password_reset_form':forms.PasswordReset}), + url('^user/login/$', views.login, name='login'), + 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()), - name='person_list'), - url(r'^people/add/$', - permission_required_with_403('RIGS.add_person')(views.PersonCreate.as_view()), - name='person_create'), - url(r'^people/(?P\d+)/$', - permission_required_with_403('RIGS.view_person')(views.PersonDetail.as_view()), - name='person_detail'), - url(r'^people/(?P\d+)/history/$', - permission_required_with_403('RIGS.view_person')(versioning.VersionHistory.as_view()), - name='person_history', kwargs={'model': models.Person}), - url(r'^people/(?P\d+)/edit/$', - permission_required_with_403('RIGS.change_person')(views.PersonUpdate.as_view()), - name='person_update'), + # People + url(r'^people/$', permission_required_with_403('RIGS.view_person')(views.PersonList.as_view()), + name='person_list'), + url(r'^people/add/$', + permission_required_with_403('RIGS.add_person')(views.PersonCreate.as_view()), + name='person_create'), + url(r'^people/(?P\d+)/$', + permission_required_with_403('RIGS.view_person')(views.PersonDetail.as_view()), + name='person_detail'), + url(r'^people/(?P\d+)/history/$', + permission_required_with_403('RIGS.view_person')(versioning.VersionHistory.as_view()), + name='person_history', kwargs={'model': models.Person}), + url(r'^people/(?P\d+)/edit/$', + permission_required_with_403('RIGS.change_person')(views.PersonUpdate.as_view()), + name='person_update'), - # Organisations - url(r'^organisations/$', - permission_required_with_403('RIGS.view_organisation')(views.OrganisationList.as_view()), - name='organisation_list'), - url(r'^organisations/add/$', - permission_required_with_403('RIGS.add_organisation')(views.OrganisationCreate.as_view()), - name='organisation_create'), - url(r'^organisations/(?P\d+)/$', - permission_required_with_403('RIGS.view_organisation')(views.OrganisationDetail.as_view()), - name='organisation_detail'), - url(r'^organisations/(?P\d+)/history/$', - permission_required_with_403('RIGS.view_organisation')(versioning.VersionHistory.as_view()), - name='organisation_history', kwargs={'model': models.Organisation}), - url(r'^organisations/(?P\d+)/edit/$', - permission_required_with_403('RIGS.change_organisation')(views.OrganisationUpdate.as_view()), - name='organisation_update'), + # Organisations + url(r'^organisations/$', + permission_required_with_403('RIGS.view_organisation')(views.OrganisationList.as_view()), + name='organisation_list'), + url(r'^organisations/add/$', + permission_required_with_403('RIGS.add_organisation')(views.OrganisationCreate.as_view()), + name='organisation_create'), + url(r'^organisations/(?P\d+)/$', + permission_required_with_403('RIGS.view_organisation')(views.OrganisationDetail.as_view()), + name='organisation_detail'), + url(r'^organisations/(?P\d+)/history/$', + permission_required_with_403('RIGS.view_organisation')(versioning.VersionHistory.as_view()), + name='organisation_history', kwargs={'model': models.Organisation}), + url(r'^organisations/(?P\d+)/edit/$', + permission_required_with_403('RIGS.change_organisation')(views.OrganisationUpdate.as_view()), + name='organisation_update'), - # Venues - url(r'^venues/$', - permission_required_with_403('RIGS.view_venue')(views.VenueList.as_view()), - name='venue_list'), - url(r'^venues/add/$', - permission_required_with_403('RIGS.add_venue')(views.VenueCreate.as_view()), - name='venue_create'), - url(r'^venues/(?P\d+)/$', - permission_required_with_403('RIGS.view_venue')(views.VenueDetail.as_view()), - name='venue_detail'), - url(r'^venues/(?P\d+)/history/$', - permission_required_with_403('RIGS.view_venue')(versioning.VersionHistory.as_view()), - name='venue_history', kwargs={'model': models.Venue}), - url(r'^venues/(?P\d+)/edit/$', - permission_required_with_403('RIGS.change_venue')(views.VenueUpdate.as_view()), - name='venue_update'), + # Venues + url(r'^venues/$', + permission_required_with_403('RIGS.view_venue')(views.VenueList.as_view()), + name='venue_list'), + url(r'^venues/add/$', + permission_required_with_403('RIGS.add_venue')(views.VenueCreate.as_view()), + name='venue_create'), + url(r'^venues/(?P\d+)/$', + permission_required_with_403('RIGS.view_venue')(views.VenueDetail.as_view()), + name='venue_detail'), + url(r'^venues/(?P\d+)/history/$', + permission_required_with_403('RIGS.view_venue')(versioning.VersionHistory.as_view()), + name='venue_history', kwargs={'model': models.Venue}), + url(r'^venues/(?P\d+)/edit/$', + permission_required_with_403('RIGS.change_venue')(views.VenueUpdate.as_view()), + name='venue_update'), - # Rigboard - url(r'^rigboard/$', login_required(rigboard.RigboardIndex.as_view()), name='rigboard'), - url(r'^rigboard/calendar/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), - url(r'^rigboard/calendar/(?P(month|week|day))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), - url(r'^rigboard/calendar/(?P(month|week|day))/(?P(\d{4}-\d{2}-\d{2}))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), - url(r'^rigboard/archive/$', RedirectView.as_view(permanent=True,pattern_name='event_archive')), - url(r'^rigboard/activity/$', - permission_required_with_403('RIGS.view_event')(versioning.ActivityTable.as_view()), - name='activity_table'), - url(r'^rigboard/activity/feed/$', - permission_required_with_403('RIGS.view_event')(versioning.ActivityFeed.as_view()), - name='activity_feed'), + # Rigboard + url(r'^rigboard/$', login_required(rigboard.RigboardIndex.as_view()), name='rigboard'), + url(r'^rigboard/calendar/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), + url(r'^rigboard/calendar/(?P(month|week|day))/$', login_required()(rigboard.WebCalendar.as_view()), + name='web_calendar'), + url(r'^rigboard/calendar/(?P(month|week|day))/(?P(\d{4}-\d{2}-\d{2}))/$', + login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'), + url(r'^rigboard/archive/$', RedirectView.as_view(permanent=True, pattern_name='event_archive')), + url(r'^rigboard/activity/$', + permission_required_with_403('RIGS.view_event')(versioning.ActivityTable.as_view()), + name='activity_table'), + url(r'^rigboard/activity/feed/$', + permission_required_with_403('RIGS.view_event')(versioning.ActivityFeed.as_view()), + name='activity_feed'), - url(r'^event/(?P\d+)/$', - permission_required_with_403('RIGS.view_event')(rigboard.EventDetail.as_view()), - name='event_detail'), - url(r'^event/(?P\d+)/print/$', - permission_required_with_403('RIGS.view_event')(rigboard.EventPrint.as_view()), - name='event_print'), - url(r'^event/create/$', - permission_required_with_403('RIGS.add_event')(rigboard.EventCreate.as_view()), - name='event_create'), - url(r'^event/(?P\d+)/edit/$', - permission_required_with_403('RIGS.change_event')(rigboard.EventUpdate.as_view()), - name='event_update'), - url(r'^event/(?P\d+)/duplicate/$', - permission_required_with_403('RIGS.add_event')(rigboard.EventDuplicate.as_view()), - name='event_duplicate'), - url(r'^event/archive/$', login_required()(rigboard.EventArchive.as_view()), - name='event_archive'), + url(r'^event/(?P\d+)/$', + permission_required_with_403('RIGS.view_event')(rigboard.EventDetail.as_view()), + name='event_detail'), + url(r'^event/(?P\d+)/print/$', + permission_required_with_403('RIGS.view_event')(rigboard.EventPrint.as_view()), + name='event_print'), + url(r'^event/create/$', + permission_required_with_403('RIGS.add_event')(rigboard.EventCreate.as_view()), + name='event_create'), + url(r'^event/(?P\d+)/edit/$', + permission_required_with_403('RIGS.change_event')(rigboard.EventUpdate.as_view()), + name='event_update'), + url(r'^event/(?P\d+)/duplicate/$', + permission_required_with_403('RIGS.add_event')(rigboard.EventDuplicate.as_view()), + name='event_duplicate'), + url(r'^event/archive/$', login_required()(rigboard.EventArchive.as_view()), + name='event_archive'), - url(r'^event/(?P\d+)/history/$', - permission_required_with_403('RIGS.view_event')(versioning.VersionHistory.as_view()), - name='event_history', kwargs={'model': models.Event}), + url(r'^event/(?P\d+)/history/$', + permission_required_with_403('RIGS.view_event')(versioning.VersionHistory.as_view()), + name='event_history', kwargs={'model': models.Event}), - + # Finance + url(r'^invoice/$', + permission_required_with_403('RIGS.view_invoice')(finance.InvoiceIndex.as_view()), + name='invoice_list'), + url(r'^invoice/archive/$', + permission_required_with_403('RIGS.view_invoice')(finance.InvoiceArchive.as_view()), + name='invoice_archive'), + url(r'^invoice/waiting/$', + permission_required_with_403('RIGS.add_invoice')(finance.InvoiceWaiting.as_view()), + name='invoice_waiting'), - # Finance - url(r'^invoice/$', - permission_required_with_403('RIGS.view_invoice')(finance.InvoiceIndex.as_view()), - name='invoice_list'), - url(r'^invoice/archive/$', - permission_required_with_403('RIGS.view_invoice')(finance.InvoiceArchive.as_view()), - name='invoice_archive'), - url(r'^invoice/waiting/$', - permission_required_with_403('RIGS.add_invoice')(finance.InvoiceWaiting.as_view()), - name='invoice_waiting'), + url(r'^event/(?P\d+)/invoice/$', + permission_required_with_403('RIGS.add_invoice')(finance.InvoiceEvent.as_view()), + name='invoice_event'), - url(r'^event/(?P\d+)/invoice/$', - permission_required_with_403('RIGS.add_invoice')(finance.InvoiceEvent.as_view()), - name='invoice_event'), + url(r'^invoice/(?P\d+)/$', + permission_required_with_403('RIGS.view_invoice')(finance.InvoiceDetail.as_view()), + name='invoice_detail'), + url(r'^invoice/(?P\d+)/print/$', + permission_required_with_403('RIGS.view_invoice')(finance.InvoicePrint.as_view()), + name='invoice_print'), + url(r'^invoice/(?P\d+)/void/$', + permission_required_with_403('RIGS.change_invoice')(finance.InvoiceVoid.as_view()), + name='invoice_void'), + url(r'^payment/create/$', + permission_required_with_403('RIGS.add_payment')(finance.PaymentCreate.as_view()), + name='payment_create'), + url(r'^payment/(?P\d+)/delete/$', + permission_required_with_403('RIGS.add_payment')(finance.PaymentDelete.as_view()), + name='payment_delete'), - url(r'^invoice/(?P\d+)/$', - permission_required_with_403('RIGS.view_invoice')(finance.InvoiceDetail.as_view()), - name='invoice_detail'), - url(r'^invoice/(?P\d+)/print/$', - permission_required_with_403('RIGS.view_invoice')(finance.InvoicePrint.as_view()), - name='invoice_print'), - url(r'^invoice/(?P\d+)/void/$', - permission_required_with_403('RIGS.change_invoice')(finance.InvoiceVoid.as_view()), - name='invoice_void'), - url(r'^payment/create/$', - permission_required_with_403('RIGS.add_payment')(finance.PaymentCreate.as_view()), - name='payment_create'), - url(r'^payment/(?P\d+)/delete/$', - permission_required_with_403('RIGS.add_payment')(finance.PaymentDelete.as_view()), - name='payment_delete'), + # User editing + url(r'^user/$', login_required(views.ProfileDetail.as_view()), name='profile_detail'), + url(r'^user/(?P\d+)/$', + permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()), + name='profile_detail'), + url(r'^user/edit/$', login_required(views.ProfileUpdateSelf.as_view()), + name='profile_update_self'), + url(r'^user/reset_api_key$', login_required(views.ResetApiKey.as_view(permanent=False)), name='reset_api_key'), - # User editing - url(r'^user/$', login_required(views.ProfileDetail.as_view()), name='profile_detail'), - url(r'^user/(?P\d+)/$', - permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()), - name='profile_detail'), - url(r'^user/edit/$', login_required(views.ProfileUpdateSelf.as_view()), - name='profile_update_self'), - url(r'^user/reset_api_key$', login_required(views.ResetApiKey.as_view(permanent=False)), name='reset_api_key'), + # ICS Calendar - API key authentication + url(r'^ical/(?P\d+)/(?P\w+)/rigs.ics$', api_key_required(ical.CalendarICS()), name="ics_calendar"), - # ICS Calendar - API key authentication - url(r'^ical/(?P\d+)/(?P\w+)/rigs.ics$', api_key_required(ical.CalendarICS()), name="ics_calendar"), - - # API - url(r'^api/(?P\w+)/$', login_required(views.SecureAPIRequest.as_view()), name="api_secure"), - url(r'^api/(?P\w+)/(?P\d+)/$', login_required(views.SecureAPIRequest.as_view()), name="api_secure"), - - # Legacy URL's - url(r'^rig/show/(?P\d+)/$', 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')), -) + # API + url(r'^api/(?P\w+)/$', login_required(views.SecureAPIRequest.as_view()), name="api_secure"), + url(r'^api/(?P\w+)/(?P\d+)/$', login_required(views.SecureAPIRequest.as_view()), name="api_secure"), + # Legacy URL's + url(r'^rig/show/(?P\d+)/$', 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')), +] From c1d164bd7374a305f75ea4c1208b1f7a722180da Mon Sep 17 00:00:00 2001 From: Tom Price Date: Thu, 31 Mar 2016 13:12:35 +0100 Subject: [PATCH 06/21] Remove the db.sqlite3 file This was loooong overdue --- .gitignore | 1 + db.sqlite3 | Bin 84992 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 db.sqlite3 diff --git a/.gitignore b/.gitignore index d31b5aa9..64848594 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ coverage.xml # Django stuff: *.log +db.sqlite3 # Sphinx documentation docs/_build/ diff --git a/db.sqlite3 b/db.sqlite3 deleted file mode 100644 index 07462b3f640fb99ec6f986f85dfa5d5315cc4d15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 84992 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 From 6da688cc9ec34063c0539dd80c2a646d5a33a1da Mon Sep 17 00:00:00 2001 From: Tom Price Date: Thu, 31 Mar 2016 13:15:53 +0100 Subject: [PATCH 07/21] Migrate VAT rate to use a single date field rather than datetime. There is never any need to track the time as VAT rate hardly ever changes and will always do so at midnight. We were already assuming this anyway but it was generating loads of warnings/errors. This will break your local VAT rate database if using sqlite, but it is tested with postgres and works fine. --- RIGS/migrations/0025_auto_20160331_1302.py | 26 ++++++++++++++++++++++ RIGS/models.py | 21 ++++++++--------- 2 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 RIGS/migrations/0025_auto_20160331_1302.py 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/models.py b/RIGS/models.py index 05066323..e77a2b93 100644 --- a/RIGS/models.py +++ b/RIGS/models.py @@ -1,9 +1,10 @@ import hashlib import datetime, pytz -from django.db import models, connection +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 @@ -172,7 +173,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() @@ -187,7 +188,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) @@ -238,11 +239,11 @@ 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(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=datetime.date.today()) # Canceled but not started + 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 @@ -264,12 +265,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 From 463c4d147cd2a8ad934bfa60172f6ee54906887b Mon Sep 17 00:00:00 2001 From: Tom Price Date: Thu, 31 Mar 2016 15:14:54 +0100 Subject: [PATCH 08/21] Update settings for django10 support Allow env.EMAIL_PORT to be None without error Change template context preprocessor in favour of the new syntax. --- PyRIGS/settings.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/PyRIGS/settings.py b/PyRIGS/settings.py index 13ca4874..f8747618 100644 --- a/PyRIGS/settings.py +++ b/PyRIGS/settings.py @@ -158,7 +158,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))) @@ -203,12 +203,12 @@ TEMPLATES = [ 'OPTIONS': { '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.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", ] }, From 8cfda6971796dfa6577247bf91c3136c2690b730 Mon Sep 17 00:00:00 2001 From: Tom Price Date: Thu, 31 Mar 2016 16:03:07 +0100 Subject: [PATCH 09/21] Set wercker up to use postgres for tests again. Modify the tests to be postgres compatible by using more pointers to things rather than getting them everytime. --- RIGS/test_functional.py | 10 +- RIGS/test_models.py | 199 +++++++++++++++++++++++----------------- wercker.yml | 20 ++-- 3 files changed, 128 insertions(+), 101 deletions(-) diff --git a/RIGS/test_functional.py b/RIGS/test_functional.py index ed563be3..1b87cd08 100644 --- a/RIGS/test_functional.py +++ b/RIGS/test_functional.py @@ -430,7 +430,7 @@ 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 @@ -487,7 +487,7 @@ class EventTest(LiveServerTestCase): # Attempt to save save.click() - self.assertNotIn("N0000%d"%testEvent.pk, self.browser.find_element_by_xpath('//h1').text) + self.assertNotIn("N%05d"%testEvent.pk, self.browser.find_element_by_xpath('//h1').text) # Check the new items are visible table = self.browser.find_element_by_id('item-table') # ID number is known, see above @@ -496,7 +496,7 @@ 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.assertIn("N%05d"%testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) @@ -504,7 +504,7 @@ class EventTest(LiveServerTestCase): #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 items are as they were table = self.browser.find_element_by_id('item-table') # ID number is known, see above @@ -623,7 +623,7 @@ 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/') diff --git a/RIGS/test_models.py b/RIGS/test_models.py index a7896cbe..b84fc7a6 100644 --- a/RIGS/test_models.py +++ b/RIGS/test_models.py @@ -15,71 +15,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 @@ -101,17 +112,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) @@ -120,33 +137,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") @@ -184,20 +211,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 diff --git a/wercker.yml b/wercker.yml index c2749a3e..0e2efea2 100644 --- a/wercker.yml +++ b/wercker.yml @@ -10,10 +10,10 @@ box: heroku/python # You can also use services such as databases. Read more on our dev center: # http://devcenter.wercker.com/docs/services/index.html services: -# - id: postgres -# env: -# POSTGRES_PASSWORD: pyrigstesting -# POSTGRES_USER: pyrigs + - id: postgres + env: + POSTGRES_PASSWORD: pyrigstesting + POSTGRES_USER: pyrigs # http://devcenter.wercker.com/docs/services/postgresql.html # - mongodb @@ -69,11 +69,11 @@ build: echo "pip version $(pip --version) running" # Django uses this to connect to the database -# - script: -# name: set environment -# code: | -# export DEBUG=0 -# export DATABASE_URL="postgres://pyrigs:pyrigstesting@$POSTGRES_PORT_5432_TCP_ADDR:$POSTGRES_PORT_5432_TCP_PORT$POSTGRES_NAME" + - script: + name: set environment + code: | + export DEBUG=0 + export DATABASE_URL="postgres://pyrigs:pyrigstesting@$POSTGRES_PORT_5432_TCP_ADDR:$POSTGRES_PORT_5432_TCP_PORT$POSTGRES_NAME" # A step that executes `pip install` command. - pip-install @@ -100,4 +100,4 @@ build: name: collect coverage data code: | coverage report -m --include=PyRIGS/*.*,RIGS/*.* --omit=*/migrations/* | tee coverage.txt - coverage html --include=PyRIGS/*.*,RIGS/*.* --omit=*/migrations/* \ No newline at end of file + coverage html --include=PyRIGS/*.*,RIGS/*.* --omit=*/migrations/* From 9b7c84cf0890788a08a3dec71e00cbe78748b1fb Mon Sep 17 00:00:00 2001 From: Tom Price Date: Tue, 28 Mar 2017 16:43:15 +0100 Subject: [PATCH 10/21] Change invoice filename format Change the invoice filename format to include the event number. Closes #279 --- RIGS/finance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RIGS/finance.py b/RIGS/finance.py index 1e0c91a7..26ca0df8 100644 --- a/RIGS/finance.py +++ b/RIGS/finance.py @@ -78,7 +78,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 From e573088c5e9102b956bd4c6bd50380335c39dc4f Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 17:51:55 +0100 Subject: [PATCH 11/21] Fix some issues caused by changes made over the last year --- RIGS/admin.py | 7 +++---- RIGS/management/commands/generateSampleData.py | 2 +- RIGS/models.py | 7 ------- RIGS/test_functional.py | 18 ++++++------------ RIGS/views.py | 5 ++--- 5 files changed, 12 insertions(+), 27 deletions(-) diff --git a/RIGS/admin.py b/RIGS/admin.py index 89779738..58b27e4c 100644 --- a/RIGS/admin.py +++ b/RIGS/admin.py @@ -12,11 +12,10 @@ 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.Person, VersionAdmin) -admin.site.register(models.Organisation, VersionAdmin) admin.site.register(models.VatRate, VersionAdmin) -admin.site.register(models.Venue, VersionAdmin) admin.site.register(models.Event, VersionAdmin) admin.site.register(models.EventItem, VersionAdmin) admin.site.register(models.Invoice) @@ -44,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'] 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/models.py b/RIGS/models.py index cbdc3e74..b245f543 100644 --- a/RIGS/models.py +++ b/RIGS/models.py @@ -12,18 +12,11 @@ from reversion import revisions as reversion import string import random -import string 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. diff --git a/RIGS/test_functional.py b/RIGS/test_functional.py index 2cd3af8d..3b867652 100644 --- a/RIGS/test_functional.py +++ b/RIGS/test_functional.py @@ -499,12 +499,8 @@ class EventTest(LiveServerTestCase): # Attempt to save save.click() -<<<<<<< HEAD self.assertNotIn("N%05d"%testEvent.pk, self.browser.find_element_by_xpath('//h1').text) -======= - 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 ->>>>>>> 9b7c84cf0890788a08a3dec71e00cbe78748b1fb # Check the new items are visible table = self.browser.find_element_by_id('item-table') # ID number is known, see above @@ -513,13 +509,12 @@ class EventTest(LiveServerTestCase): self.assertIn("Test Item 3", table.text) infoPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Event Info")]/..') -<<<<<<< HEAD + self.assertIn("N%05d"%testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) -======= - self.assertIn("N0000%d"%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) ->>>>>>> 9b7c84cf0890788a08a3dec71e00cbe78748b1fb + @@ -527,13 +522,12 @@ class EventTest(LiveServerTestCase): #Check that based-on hasn't crept into the old event infoPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Event Info")]/..') -<<<<<<< HEAD + self.assertNotIn("N%05d"%testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) -======= - self.assertNotIn("N0000%d"%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) ->>>>>>> 9b7c84cf0890788a08a3dec71e00cbe78748b1fb + # Check the items are as they were table = self.browser.find_element_by_id('item-table') # ID number is known, see above 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 From 7f680dcffb425398f19ad0c41a11043f9e75634d Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 18:06:17 +0100 Subject: [PATCH 12/21] Fix activity feed --- RIGS/versioning.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RIGS/versioning.py b/RIGS/versioning.py index 6d524fc0..d22f425c 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( @@ -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 From 83028418fe0bfa99e6479392614c591fba1befde Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 18:30:17 +0100 Subject: [PATCH 13/21] Fix deprecation warnings for django 1.10 --- PyRIGS/settings.py | 5 ++--- RIGS/admin.py | 3 +-- RIGS/test_models.py | 6 +++--- RIGS/urls.py | 8 ++++---- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/PyRIGS/settings.py b/PyRIGS/settings.py index fc593be4..70d41064 100644 --- a/PyRIGS/settings.py +++ b/PyRIGS/settings.py @@ -26,8 +26,6 @@ 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 - ALLOWED_HOSTS = ['pyrigs.nottinghamtec.co.uk', 'rigs.nottinghamtec.co.uk', 'pyrigs.herokuapp.com'] if STAGING: @@ -221,7 +219,8 @@ TEMPLATES = [ "django.template.context_processors.tz", "django.template.context_processors.request", "django.contrib.messages.context_processors.messages", - ] + ], + 'debug': DEBUG }, }, ] diff --git a/RIGS/admin.py b/RIGS/admin.py index 58b27e4c..49b8aa1e 100644 --- a/RIGS/admin.py +++ b/RIGS/admin.py @@ -95,8 +95,7 @@ class AssociateAdmin(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/test_models.py b/RIGS/test_models.py index b84fc7a6..1809c73b 100644 --- a/RIGS/test_models.py +++ b/RIGS/test_models.py @@ -275,14 +275,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/urls.py b/RIGS/urls.py index f6b3fe14..30eb7051 100644 --- a/RIGS/urls.py +++ b/RIGS/urls.py @@ -9,16 +9,16 @@ 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()), @@ -167,4 +167,4 @@ urlpatterns = patterns('', url(r'^rig/show/(?P\d+)/$', 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')), - ) + ] From 9b1cc965c7e6260c0f16d1040029d28b2945d202 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 18:41:17 +0100 Subject: [PATCH 14/21] Update to Django 1.10 --- PyRIGS/decorators.py | 12 ++++++------ PyRIGS/settings.py | 3 +++ PyRIGS/urls.py | 2 +- RIGS/urls.py | 2 +- requirements.txt | 3 ++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/PyRIGS/decorators.py b/PyRIGS/decorators.py index 055901ca..1ae73e7d 100644 --- a/PyRIGS/decorators.py +++ b/PyRIGS/decorators.py @@ -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 = RequestContext(request) + 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_to_response('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_to_response('403.html', context=RequestContext(request)) 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_to_response('403.html', context=RequestContext(request)) error_resp.status_code = 403 if key is None: diff --git a/PyRIGS/settings.py b/PyRIGS/settings.py index 70d41064..d29c0e6b 100644 --- a/PyRIGS/settings.py +++ b/PyRIGS/settings.py @@ -31,6 +31,9 @@ ALLOWED_HOSTS = ['pyrigs.nottinghamtec.co.uk', 'rigs.nottinghamtec.co.uk', 'pyri if STAGING: ALLOWED_HOSTS.append('.herokuapp.com') +if DEBUG: + ALLOWED_HOSTS.append('localhost') + SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') if not DEBUG: SECURE_SSL_REDIRECT = True # Redirect all http requests to https diff --git a/PyRIGS/urls.py b/PyRIGS/urls.py index f8993d48..cbfcbfa0 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 diff --git a/RIGS/urls.py b/RIGS/urls.py index 30eb7051..5af3fd9a 100644 --- a/RIGS/urls.py +++ b/RIGS/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls import url from django.contrib.auth.views import password_reset from django.contrib.auth.decorators import login_required diff --git a/requirements.txt b/requirements.txt index f74f08e3..d9fbe723 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ beautifulsoup4==4.4.1 +contextlib2==0.5.5 diff-match-patch==20121119 dj-database-url==0.4.0 dj-static==0.0.6 -Django==1.9.4 +Django==1.10.7 django-debug-toolbar==1.4 django-ical==1.3 django-recaptcha==1.0.5 From d916c1ca19554b65c16ec740761aef65244b8ada Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 20:05:36 +0100 Subject: [PATCH 15/21] Update all the things, and fix some upgrade issues --- PyRIGS/decorators.py | 10 +++++----- PyRIGS/settings.py | 2 ++ PyRIGS/urls.py | 5 +++++ RIGS/migrations/0026_auto_20170510_1846.py | 21 +++++++++++++++++++++ RIGS/test_unit.py | 2 ++ RIGS/versioning.py | 6 +++--- 6 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 RIGS/migrations/0026_auto_20170510_1846.py diff --git a/PyRIGS/decorators.py b/PyRIGS/decorators.py index 1ae73e7d..68549449 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: - context = 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_to_response('login_redirect.html', context=context) + 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=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=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 d29c0e6b..5c010a23 100644 --- a/PyRIGS/settings.py +++ b/PyRIGS/settings.py @@ -33,6 +33,7 @@ if STAGING: if DEBUG: ALLOWED_HOSTS.append('localhost') + ALLOWED_HOSTS.append('example.com') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') if not DEBUG: @@ -66,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', diff --git a/PyRIGS/urls.py b/PyRIGS/urls.py index cbfcbfa0..65bf2e63 100644 --- a/PyRIGS/urls.py +++ b/PyRIGS/urls.py @@ -22,3 +22,8 @@ urlpatterns = [ if settings.DEBUG: urlpatterns += staticfiles_urlpatterns() + + import debug_toolbar + urlpatterns = [ + url(r'^__debug__/', include(debug_toolbar.urls)), + ] + urlpatterns 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/test_unit.py b/RIGS/test_unit.py index 82a7acca..0b22d84e 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()) diff --git a/RIGS/versioning.py b/RIGS/versioning.py index d22f425c..65d4328a 100644 --- a/RIGS/versioning.py +++ b/RIGS/versioning.py @@ -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 @@ -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 From 872e5e72f3a0cb7f81c25e6eb91b6eaa5fcdc605 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 20:11:12 +0100 Subject: [PATCH 16/21] Update the requirements.txt file, always a useful thing to do --- requirements.txt | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/requirements.txt b/requirements.txt index d9fbe723..5e6106d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,36 +1,36 @@ -beautifulsoup4==4.4.1 +beautifulsoup4==4.6.0 contextlib2==0.5.5 diff-match-patch==20121119 -dj-database-url==0.4.0 +dj-database-url==0.4.2 dj-static==0.0.6 -Django==1.10.7 -django-debug-toolbar==1.4 -django-ical==1.3 -django-recaptcha==1.0.5 -django-registration-redux==1.4 -django-reversion==1.10.1 +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.4.1 -gunicorn==19.4.5 -icalendar==3.9.2 -lxml==3.6.0 -Markdown==2.6.6 -Pillow==3.1.1 -psycopg2==2.6.1 -Pygments==2.1.3 -PyPDF2==1.25.1 -python-dateutil==2.5.2 -pytz==2016.3 -raven==5.12.0 -reportlab==3.3.0 +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.8.2 +simplejson==3.10.0 six==1.10.0 -sqlparse==0.1.19 -static3==0.6.1 +sqlparse==0.2.3 +static3==0.7.0 svg2rlg==0.3 yolk==0.4.3 -z3c.rml==3.0.0 +z3c.rml==3.2.0 zope.event==4.2.0 -zope.interface==4.1.3 +zope.interface==4.4.0 zope.schema==4.4.2 From 38a8ac1eb477b69c5a3fdef8541cc158b2c71aea Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 20:25:14 +0100 Subject: [PATCH 17/21] Add failing tests for event paperwork printing --- RIGS/test_unit.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/RIGS/test_unit.py b/RIGS/test_unit.py index 0b22d84e..5b531636 100644 --- a/RIGS/test_unit.py +++ b/RIGS/test_unit.py @@ -216,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): From 0d726b2b60626a8d88e6794d2305ba08b444ab7a Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 20:25:41 +0100 Subject: [PATCH 18/21] Fix paperwork printing --- RIGS/finance.py | 6 +++--- RIGS/rigboard.py | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/RIGS/finance.py b/RIGS/finance.py index 26ca0df8..87764189 100644 --- a/RIGS/finance.py +++ b/RIGS/finance.py @@ -55,8 +55,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 +66,7 @@ class InvoicePrint(generic.View): }, 'invoice': invoice, 'current_user': request.user, - }) + } rml = template.render(context) buffer = StringIO.StringIO() diff --git a/RIGS/rigboard.py b/RIGS/rigboard.py index 81cf564e..0d2c6ec8 100644 --- a/RIGS/rigboard.py +++ b/RIGS/rigboard.py @@ -136,6 +136,7 @@ class EventDuplicate(EventUpdate): context["duplicate"] = True return context + class EventPrint(generic.View): def get(self, request, pk): object = get_object_or_404(models.Event, pk=pk) @@ -144,9 +145,9 @@ class EventPrint(generic.View): merger = PdfFileMerger() - for copy in copies: + for thisCopy in copies: - context = RequestContext(request, { # this should be outside the loop, but bug in 1.8.2 prevents this + context = { # this should be outside the loop, but bug in 1.8.2 prevents this 'object': object, 'fonts': { 'opensans': { @@ -154,9 +155,9 @@ class EventPrint(generic.View): 'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF', } }, - 'copy':copy, - 'current_user':request.user, - }) + 'copy': thisCopy, + 'current_user': request.user, + } # context['copy'] = copy # this is the way to do it once we upgrade to Django 1.8.3 From 374c31e8b4f54a89e6659f9726d74ad9fbdd97e7 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 23:03:35 +0100 Subject: [PATCH 19/21] Fix date/time/datetime field types --- RIGS/finance.py | 3 +++ RIGS/forms.py | 4 ++++ RIGS/templates/RIGS/event_form.html | 12 ++++++------ RIGS/templates/RIGS/payment_form.html | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/RIGS/finance.py b/RIGS/finance.py index 87764189..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 diff --git a/RIGS/forms.py b/RIGS/forms.py index d1b826c3..e0d57f70 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): diff --git a/RIGS/templates/RIGS/event_form.html b/RIGS/templates/RIGS/event_form.html index b2b4dcd7..6832d571 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" %}
@@ -337,7 +337,7 @@ class="col-sm-4 control-label">{{ form.meet_at.label }}
- {% render_field form.meet_at type="datetime-local" class+="form-control" %} + {% render_field form.meet_at class+="form-control" %}
diff --git a/RIGS/templates/RIGS/payment_form.html b/RIGS/templates/RIGS/payment_form.html index 41bbf15b..13f4a694 100644 --- a/RIGS/templates/RIGS/payment_form.html +++ b/RIGS/templates/RIGS/payment_form.html @@ -16,7 +16,7 @@ for="{{ form.date.id_for_label }}">{{ form.date.label }}
- {% render_field form.date type="date" class+="form-control" %} + {% render_field form.date class+="form-control" %}
From 1f4e53ad27108cc024364fb6348f0b1c93aaad08 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 23:40:48 +0100 Subject: [PATCH 20/21] Add dependency badge to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) 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 # [![Build Status](https://travis-ci.org/nottinghamtec/PyRIGS.svg?branch=develop)](https://travis-ci.org/nottinghamtec/PyRIGS) [![Coverage Status](https://coveralls.io/repos/github/nottinghamtec/PyRIGS/badge.svg?branch=develop)](https://coveralls.io/github/nottinghamtec/PyRIGS?branch=develop) +[![Dependency Status](https://gemnasium.com/badges/github.com/nottinghamtec/PyRIGS.svg)](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. From e547e3e858ebda4f7941bf55ee8ae6851ca33307 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 10 May 2017 23:54:23 +0100 Subject: [PATCH 21/21] Delete unused wercker config --- wercker.yml | 103 ---------------------------------------------------- 1 file changed, 103 deletions(-) delete mode 100644 wercker.yml diff --git a/wercker.yml b/wercker.yml deleted file mode 100644 index 0e2efea2..00000000 --- a/wercker.yml +++ /dev/null @@ -1,103 +0,0 @@ -# This references the default Python container from -# the Docker Hub with the 2.7 tag: -# https://registry.hub.docker.com/_/python/ -# If you want to use a slim Python container with -# version 3.4.3 you would use: python:3.4-slim -# If you want Google's container you would reference google/python -# Read more about containers on our dev center -# http://devcenter.wercker.com/docs/containers/index.html -box: heroku/python -# You can also use services such as databases. Read more on our dev center: -# http://devcenter.wercker.com/docs/services/index.html -services: - - id: postgres - env: - POSTGRES_PASSWORD: pyrigstesting - POSTGRES_USER: pyrigs - # http://devcenter.wercker.com/docs/services/postgresql.html - - # - mongodb - # http://devcenter.wercker.com/docs/services/mongodb.html - -# This is the build pipeline. Pipelines are the core of wercker -# Read more about pipelines on our dev center -# http://devcenter.wercker.com/docs/pipelines/index.html -build: - # The steps that will be executed on build - # Steps make up the actions in your pipeline - # Read more about steps on our dev center: - # http://devcenter.wercker.com/docs/steps/index.html - steps: - - install-packages: - packages: firefox xvfb - - - script: - name: Enable virtual display - code: |- - # Start xvfb which gives the context an virtual display - # which is required for tests that require an GUI - export DISPLAY=:99.0 - start-stop-daemon --start --quiet --pidfile /tmp/xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1024x768x24 -ac +extension GLX +render -noreset - # Give xvfb time to start. 3 seconds is the default for all xvfb-run commands. - # sleep 3 - - - script: - name: virtualenv install - code: | - pip install virtualenv - - # A step that sets up the python virtual environment - - virtualenv: - name: setup virtual environment - install_wheel: false # Enable wheel to speed up builds (experimental) - - # # Use this virtualenv step for python 3.2 - # - virtualenv - # name: setup virtual environment - # python_location: /usr/bin/python3.2 - - # # This pip-install clears the local wheel cache - # - pip-install: - # clean_wheel_dir: true - - # A custom script step, name value is used in the UI - # and the code value contains the command that get executed - - script: - name: Python Version - code: | - echo "python version $(python --version) running" - echo "pip version $(pip --version) running" - - # Django uses this to connect to the database - - script: - name: set environment - code: | - export DEBUG=0 - export DATABASE_URL="postgres://pyrigs:pyrigstesting@$POSTGRES_PORT_5432_TCP_ADDR:$POSTGRES_PORT_5432_TCP_PORT$POSTGRES_NAME" - - # A step that executes `pip install` command. - - pip-install - - # Install coverage - - script: - name: install coverage - code: | - pip install coverage - - # Collect static files so the manifest storage knows where to look - - script: - name: collect static - code: | - python manage.py collectstatic --noinput - - # Run python tests - - script: - name: run tests - code: | - coverage run manage.py test RIGS - - - script: - name: collect coverage data - code: | - coverage report -m --include=PyRIGS/*.*,RIGS/*.* --omit=*/migrations/* | tee coverage.txt - coverage html --include=PyRIGS/*.*,RIGS/*.* --omit=*/migrations/*