Compare commits

...

3 Commits

Author SHA1 Message Date
David Taylor
a93f337d17 Fixed random line break 2016-08-09 20:57:48 +01:00
Tom Price
bd0832f284 Revert changes that crossed over from feature/ember branch 2016-08-09 19:48:47 +01:00
Tom Price
567f899a39 Reformat all the things
Python code is now formatted to PEP8

All other files are defined in .editorconfig as far as possible.
2016-08-09 19:42:39 +01:00
75 changed files with 3613 additions and 2670 deletions

29
.editorconfig Normal file
View File

@@ -0,0 +1,29 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.hbs]
insert_final_newline = false
[*.{diff,md}]
trim_trailing_whitespace = false
# Python: PEP8 defines 4 spaces for indentation
[*.py]
indent_style = space
indent_size = 4
# Salt state files, YAML format, 2 spaces
[*.sls, *.yaml, *.yml]
indent_style = space
indent_size = 2

2
.idea/modules.xml generated
View File

@@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/pyrigs.iml" filepath="$PROJECT_DIR$/.idea/pyrigs.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/PyRIGS.iml" filepath="$PROJECT_DIR$/.idea/PyRIGS.iml" />
</modules>
</component>
</project>

View File

@@ -1,54 +1,66 @@
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
def user_passes_test_with_403(test_func, login_url=None):
"""
Decorator for views that checks that the user passes the given test.
Anonymous users will be redirected to login_url, while users that fail
the test will be given a 403 error.
"""
if not login_url:
from django.conf import settings
login_url = settings.LOGIN_URL
def _dec(view_func):
def _checklogin(request, *args, **kwargs):
if test_func(request.user):
return view_func(request, *args, **kwargs)
elif not request.user.is_authenticated():
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, request.get_full_path()))
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_instance=RequestContext(request))
resp.status_code = 403
return resp
_checklogin.__doc__ = view_func.__doc__
_checklogin.__dict__ = view_func.__dict__
return _checklogin
return _dec
def permission_required_with_403(perm, login_url=None):
"""
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page or rendering a 403 as necessary.
"""
return user_passes_test_with_403(lambda u: u.has_perm(perm), login_url=login_url)
return user_passes_test_with_403(
lambda u: u.has_perm(perm), login_url=login_url)
from RIGS import models
def api_key_required(function):
"""
Decorator for views that checks api_pk and api_key.
Failed users will be given a 403 error.
Should only be used for urls which include <api_pk> and <api_key> kwargs
"""
def wrap(request, *args, **kwargs):
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_instance=RequestContext(request))
error_resp.status_code = 403
if key is None:
@@ -64,4 +76,5 @@ def api_key_required(function):
if user_object.api_key != key:
return error_resp
return function(request, *args, **kwargs)
return wrap
return wrap

View File

@@ -2,4 +2,4 @@ from __future__ import unicode_literals
DATETIME_FORMAT = ('d/m/Y H:i')
DATE_FORMAT = ('d/m/Y')
TIME_FORMAT = ('H:i')
TIME_FORMAT = ('H:i')

View File

@@ -10,29 +10,35 @@ 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
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']
ALLOWED_HOSTS = [
'pyrigs.nottinghamtec.co.uk',
'rigs.nottinghamtec.co.uk',
'pyrigs.herokuapp.com']
if STAGING:
ALLOWED_HOSTS.append('.herokuapp.com')
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
if not DEBUG:
SECURE_SSL_REDIRECT = True # Redirect all http requests to https
SECURE_SSL_REDIRECT = True # Redirect all http requests to https
INTERNAL_IPS = ['127.0.0.1']
@@ -40,7 +46,6 @@ ADMINS = (
('Tom Price', 'tomtom5152@gmail.com')
)
# Application definition
INSTALLED_APPS = (
@@ -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,9 +93,10 @@ DATABASES = {
if not DEBUG:
import dj_database_url
DATABASES['default'] = dj_database_url.config()
# Logging
# Logging
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
@@ -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',
@@ -146,6 +151,7 @@ RAVEN_CONFIG = {
# If you are using git, you can also automatically configure the
# release based on the git info.
# 'release': raven.fetch_git_sha(os.path.dirname(os.path.dirname(__file__))),
'debug': DEBUG,
}
# User system
@@ -191,7 +197,7 @@ USE_L10N = True
USE_TZ = True
DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M','%Y-%m-%dT%H:%M:%S')
DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M', '%Y-%m-%dT%H:%M:%S')
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
@@ -204,7 +210,6 @@ TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.messages.context_processors.messages",
)
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
@@ -215,9 +220,9 @@ STATIC_DIRS = (
)
TEMPLATE_DIRS = (
os.path.join(BASE_DIR, 'templates'),
os.path.join(BASE_DIR, 'templates'),
)
USE_GRAVATAR=True
USE_GRAVATAR = True
TERMS_OF_HIRE_URL = "http://www.nottinghamtec.co.uk/terms.pdf"

View File

@@ -1,24 +1,25 @@
from django.conf import settings
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.conf import settings
from registration.backends.default.views import RegistrationView
import RIGS
from RIGS import regbackend
urlpatterns = patterns('',
# Examples:
# url(r'^$', 'PyRIGS.views.home', name='home'),
# url(r'^blog/', include('blog.urls')),
# 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),
name="registration_register"),
url('^user/', include('django.contrib.auth.urls')),
url('^user/', include('registration.backends.default.urls')),
url(r'^', include('RIGS.urls')),
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)),
)
url(r'^admin/', include(admin.site.urls)),
)
if settings.DEBUG:
urlpatterns += staticfiles_urlpatterns()
urlpatterns += staticfiles_urlpatterns()

View File

@@ -7,6 +7,7 @@ For more information on this file, see
https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "PyRIGS.settings")
from django.core.wsgi import get_wsgi_application

View File

@@ -1,16 +1,16 @@
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 django.contrib.admin import helpers
from django.template.response import TemplateResponse
from django.contrib import admin
from django.contrib import messages
from django.db import transaction
from django.contrib.admin import helpers
from django.contrib.auth.admin import UserAdmin
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.db.models import Count
from django.forms import ModelForm
from django.template.response import TemplateResponse
from django.utils.translation import ugettext_lazy as _
from RIGS import models, forms
# Register your models here.
admin.site.register(models.VatRate, reversion.VersionAdmin)
@@ -50,7 +50,8 @@ class AssociateAdmin(reversion.VersionAdmin):
merge_fields = ['name']
def get_queryset(self, request):
return super(AssociateAdmin, self).get_queryset(request).annotate(event_count=Count('event'))
return super(AssociateAdmin, self).get_queryset(
request).annotate(event_count=Count('event'))
def number_of_events(self, obj):
return obj.latest_events.count()
@@ -58,12 +59,16 @@ class AssociateAdmin(reversion.VersionAdmin):
number_of_events.admin_order_field = 'event_count'
def merge(self, request, queryset):
if request.POST.get('post'): # Has the user confirmed which is the master record?
if request.POST.get(
'post'): # Has the user confirmed which is the master record?
try:
masterObjectPk = request.POST.get('master')
masterObject = queryset.get(pk=masterObjectPk)
except ObjectDoesNotExist:
self.message_user(request, "An error occured. Did you select a 'master' record?", level=messages.ERROR)
self.message_user(
request,
"An error occured. Did you select a 'master' record?",
level=messages.ERROR)
return
with transaction.atomic(), reversion.create_revision():
@@ -79,6 +84,7 @@ class AssociateAdmin(reversion.VersionAdmin):
else: # Present the confirmation screen
class TempForm(ModelForm):
class Meta:
model = queryset.model
fields = self.merge_fields
@@ -106,10 +112,22 @@ class PersonAdmin(AssociateAdmin):
@admin.register(models.Venue)
class VenueAdmin(AssociateAdmin):
list_display = ('id', 'name', 'phone', 'email', 'number_of_events')
merge_fields = ['name', 'phone', 'email', 'address', 'notes', 'three_phase_available']
merge_fields = [
'name',
'phone',
'email',
'address',
'notes',
'three_phase_available']
@admin.register(models.Organisation)
class OrganisationAdmin(AssociateAdmin):
list_display = ('id', 'name', 'phone', 'email', 'number_of_events')
merge_fields = ['name', 'phone', 'email', 'address', 'notes', 'union_account']
merge_fields = [
'name',
'phone',
'email',
'address',
'notes',
'union_account']

View File

@@ -4,13 +4,13 @@ import re
from django.contrib import messages
from django.core.urlresolvers import reverse_lazy
from django.db.models import Q
from django.http import Http404, HttpResponseRedirect
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.template import RequestContext
from django.template.loader import get_template
from django.views import generic
from django.db.models import Q
from z3c.rml import rml2pdf
from RIGS import models
@@ -30,7 +30,8 @@ class InvoiceIndex(generic.ListView):
return context
def get_queryset(self):
# Manual query is the only way I have found to do this efficiently. Not ideal but needs must
# Manual query is the only way I have found to do this efficiently. Not
# ideal but needs must
sql = "SELECT * FROM " \
"(SELECT " \
"(SELECT COUNT(p.amount) FROM \"RIGS_payment\" AS p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payment_count\", " \
@@ -78,7 +79,8 @@ 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 | %s.pdf" % (invoice.pk, escapedEventName)
response.write(pdfData)
return response
@@ -92,7 +94,9 @@ class InvoiceVoid(generic.View):
if object.void:
return HttpResponseRedirect(reverse_lazy('invoice_list'))
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': object.pk}))
return HttpResponseRedirect(reverse_lazy(
'invoice_detail', kwargs={'pk': object.pk}))
class InvoiceDelete(generic.DeleteView):
model = models.Invoice
@@ -100,20 +104,27 @@ class InvoiceDelete(generic.DeleteView):
def get(self, request, pk):
obj = self.get_object()
if obj.payment_set.all().count() > 0:
messages.info(self.request, 'To delete an invoice, delete the payments first.')
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': obj.pk}))
messages.info(
self.request,
'To delete an invoice, delete the payments first.')
return HttpResponseRedirect(reverse_lazy(
'invoice_detail', kwargs={'pk': obj.pk}))
return super(InvoiceDelete, self).get(pk)
def post(self, request, pk):
obj = self.get_object()
if obj.payment_set.all().count() > 0:
messages.info(self.request, 'To delete an invoice, delete the payments first.')
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': obj.pk}))
messages.info(
self.request,
'To delete an invoice, delete the payments first.')
return HttpResponseRedirect(reverse_lazy(
'invoice_detail', kwargs={'pk': obj.pk}))
return super(InvoiceDelete, self).post(pk)
def get_success_url(self):
return self.request.POST.get('next')
class InvoiceArchive(generic.ListView):
model = models.Invoice
template_name = 'RIGS/invoice_list_archive.html'
@@ -141,12 +152,14 @@ class InvoiceWaiting(generic.ListView):
# @todo find a way to select items
events = self.model.objects.filter(
(
Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end
Q(end_date__lte=datetime.date.today()) # Has end date, finishes before
) & Q(invoice__isnull=True) # Has not already been invoiced
& Q(is_rig=True) # Is a rig (not non-rig)
).order_by('start_date') \
# Starts before with no end
Q(start_date__lte=datetime.date.today(), end_date__isnull=True) |
# Has end date, finishes before
Q(end_date__lte=datetime.date.today())
) & Q(invoice__isnull=True) # Has not already been invoiced
& Q(is_rig=True) # Is a rig (not non-rig)
).order_by('start_date') \
.select_related('person',
'organisation',
'venue', 'mic') \
@@ -165,7 +178,8 @@ class InvoiceEvent(generic.View):
invoice.invoice_date = datetime.date.today()
messages.success(self.request, 'Invoice created successfully')
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': invoice.pk}))
return HttpResponseRedirect(reverse_lazy(
'invoice_detail', kwargs={'pk': invoice.pk}))
class PaymentCreate(generic.CreateView):
@@ -174,8 +188,10 @@ class PaymentCreate(generic.CreateView):
def get_initial(self):
initial = super(generic.CreateView, self).get_initial()
invoicepk = self.request.GET.get('invoice', self.request.POST.get('invoice', None))
if invoicepk == None:
invoicepk = self.request.GET.get(
'invoice', self.request.POST.get(
'invoice', None))
if invoicepk is None:
raise Http404()
invoice = get_object_or_404(models.Invoice, pk=invoicepk)
initial.update({'invoice': invoice})

View File

@@ -1,12 +1,12 @@
__author__ = 'Ghost'
from django import forms
from django.utils import formats
from django.conf import settings
from django.core import serializers
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AuthenticationForm, PasswordResetForm
from registration.forms import RegistrationFormUniqueEmail
from captcha.fields import ReCaptchaField
import simplejson
from captcha.fields import ReCaptchaField
from django import forms
from django.conf import settings
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, PasswordResetForm
from django.core import serializers
from django.utils import formats
from registration.forms import RegistrationFormUniqueEmail
from RIGS import models
@@ -17,14 +17,22 @@ class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
class Meta:
model = models.Profile
fields = ('username', 'email', 'first_name', 'last_name', 'initials', 'phone')
fields = (
'username',
'email',
'first_name',
'last_name',
'initials',
'phone')
def clean_initials(self):
"""
Validate that the supplied initials are unique.
"""
if models.Profile.objects.filter(initials__iexact=self.cleaned_data['initials']):
raise forms.ValidationError("These initials are already in use. Please supply different initials.")
if models.Profile.objects.filter(
initials__iexact=self.cleaned_data['initials']):
raise forms.ValidationError(
"These initials are already in use. Please supply different initials.")
return self.cleaned_data['initials']
@@ -45,9 +53,14 @@ class ProfileChangeForm(UserChangeForm):
# Events Shit
class EventForm(forms.ModelForm):
datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + 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)
datetime_input_formats = formats.get_format_lazy(
"DATETIME_INPUT_FORMATS") + 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)
items_json = forms.CharField()
@@ -89,7 +102,8 @@ class EventForm(forms.ModelForm):
items = {}
for key in data:
pk = int(key)
items[pk] = self._get_or_initialise_item(pk, data[key]['fields'], event)
items[pk] = self._get_or_initialise_item(
pk, data[key]['fields'], event)
return items

View File

@@ -1,17 +1,18 @@
from RIGS import models, forms
from django_ical.views import ICalFeed
from django.db.models import Q
from django.core.urlresolvers import reverse_lazy, reverse, NoReverseMatch
from django.utils import timezone
from django.conf import settings
import datetime
import pytz
from django.conf import settings
from django.db.models import Q
from django_ical.views import ICalFeed
from RIGS import models
import datetime, pytz
class CalendarICS(ICalFeed):
"""
A simple event calender
"""
#Metadata which is passed on to clients
# Metadata which is passed on to clients
product_id = 'RIGS'
title = 'RIGS Calendar'
timezone = settings.TIME_ZONE
@@ -27,50 +28,58 @@ class CalendarICS(ICalFeed):
def get_object(self, request, *args, **kwargs):
params = {}
params['dry-hire'] = request.GET.get('dry-hire','true') == 'true'
params['non-rig'] = request.GET.get('non-rig','true') == 'true'
params['rig'] = request.GET.get('rig','true') == 'true'
params['dry-hire'] = request.GET.get('dry-hire', 'true') == 'true'
params['non-rig'] = request.GET.get('non-rig', 'true') == 'true'
params['rig'] = request.GET.get('rig', 'true') == 'true'
params['cancelled'] = request.GET.get('cancelled','false') == 'true'
params['provisional'] = request.GET.get('provisional','true') == 'true'
params['confirmed'] = request.GET.get('confirmed','true') == 'true'
params['cancelled'] = request.GET.get('cancelled', 'false') == 'true'
params['provisional'] = request.GET.get(
'provisional', 'true') == 'true'
params['confirmed'] = request.GET.get('confirmed', 'true') == 'true'
return params
def description(self,params):
desc = "Calendar generated by RIGS system. This includes event types: " + ('Rig, ' if params['rig'] else '') + ('Non-rig, ' if params['non-rig'] else '') + ('Dry Hire ' if params['dry-hire'] else '') + '\n'
desc = desc + "Includes events with status: " + ('Cancelled, ' if params['cancelled'] else '') + ('Provisional, ' if params['provisional'] else '') + ('Confirmed/Booked, ' if params['confirmed'] else '')
def description(self, params):
desc = "Calendar generated by RIGS system. This includes event types: " + ('Rig, ' if params['rig'] else '') + (
'Non-rig, ' if params['non-rig'] else '') + ('Dry Hire ' if params['dry-hire'] else '') + '\n'
desc = desc + "Includes events with status: " + ('Cancelled, ' if params['cancelled'] else '') + (
'Provisional, ' if params['provisional'] else '') + ('Confirmed/Booked, ' if params['confirmed'] else '')
return desc
def items(self, params):
#include events from up to 1 year ago
# include events from up to 1 year ago
start = datetime.datetime.now() - datetime.timedelta(days=365)
filter = Q(start_date__gte=start)
typeFilters = Q(pk=None) #Need something that is false for every entry
# Need something that is false for every entry
typeFilters = Q(pk=None)
if params['dry-hire']:
typeFilters = typeFilters | Q(dry_hire=True, is_rig=True)
if params['non-rig']:
typeFilters = typeFilters | Q(is_rig=False)
if params['rig']:
typeFilters = typeFilters | Q(is_rig=True, dry_hire=False)
statusFilters = Q(pk=None) #Need something that is false for every entry
# Need something that is false for every entry
statusFilters = Q(pk=None)
if params['cancelled']:
statusFilters = statusFilters | Q(status=models.Event.CANCELLED)
if params['provisional']:
statusFilters = statusFilters | Q(status=models.Event.PROVISIONAL)
if params['confirmed']:
statusFilters = statusFilters | Q(status=models.Event.CONFIRMED) | Q(status=models.Event.BOOKED)
statusFilters = statusFilters | Q(
status=models.Event.CONFIRMED) | Q(
status=models.Event.BOOKED)
filter = filter & typeFilters & statusFilters
return models.Event.objects.filter(filter).order_by('-start_date').select_related('person', 'organisation', 'venue', 'mic')
return models.Event.objects.filter(filter).order_by(
'-start_date').select_related('person', 'organisation', 'venue', 'mic')
def item_title(self, item):
title = ''
@@ -87,9 +96,9 @@ class CalendarICS(ICalFeed):
# Add the rig name
title += item.name
# Add the status
title += ' ('+str(item.get_status_display())+')'
title += ' (' + str(item.get_status_display()) + ')'
return title
@@ -97,12 +106,13 @@ class CalendarICS(ICalFeed):
return item.earliest_time
def item_end_datetime(self, item):
if type(item.latest_time) is datetime.date: # Ical end_datetime is non-inclusive, so add a day
if isinstance(
item.latest_time, datetime.date): # Ical end_datetime is non-inclusive, so add a day
return item.latest_time + datetime.timedelta(days=1)
return item.latest_time
def item_location(self,item):
def item_location(self, item):
return item.venue
def item_description(self, item):
@@ -111,34 +121,40 @@ class CalendarICS(ICalFeed):
tz = pytz.timezone(self.timezone)
desc = 'Rig ID = '+str(item.pk)+'\n'
desc = 'Rig ID = ' + str(item.pk) + '\n'
desc += 'Event = ' + item.name + '\n'
desc += 'Venue = ' + (item.venue.name if item.venue else '---') + '\n'
if item.is_rig and item.person:
desc += 'Client = ' + item.person.name + ( (' for '+item.organisation.name) if item.organisation else '') + '\n'
desc += 'Client = ' + item.person.name + \
((' for ' + item.organisation.name)
if item.organisation else '') + '\n'
desc += 'Status = ' + str(item.get_status_display()) + '\n'
desc += 'MIC = ' + (item.mic.name if item.mic else '---') + '\n'
desc += '\n'
if item.meet_at:
desc += 'Crew Meet = ' + (item.meet_at.astimezone(tz).strftime('%Y-%m-%d %H:%M') if item.meet_at else '---') + '\n'
desc += 'Crew Meet = ' + \
(item.meet_at.astimezone(tz).strftime(
'%Y-%m-%d %H:%M') if item.meet_at else '---') + '\n'
if item.access_at:
desc += 'Access At = ' + (item.access_at.astimezone(tz).strftime('%Y-%m-%d %H:%M') if item.access_at else '---') + '\n'
desc += 'Access At = ' + (item.access_at.astimezone(tz).strftime(
'%Y-%m-%d %H:%M') if item.access_at else '---') + '\n'
if item.start_date:
desc += 'Event Start = ' + item.start_date.strftime('%Y-%m-%d') + ((' '+item.start_time.strftime('%H:%M')) if item.has_start_time else '') + '\n'
desc += 'Event Start = ' + item.start_date.strftime('%Y-%m-%d') + (
(' ' + item.start_time.strftime('%H:%M')) if item.has_start_time else '') + '\n'
if item.end_date:
desc += 'Event End = ' + item.end_date.strftime('%Y-%m-%d') + ((' '+item.end_time.strftime('%H:%M')) if item.has_end_time else '') + '\n'
desc += 'Event End = ' + item.end_date.strftime('%Y-%m-%d') + (
(' ' + item.end_time.strftime('%H:%M')) if item.has_end_time else '') + '\n'
desc += '\n'
if item.description:
desc += 'Event Description:\n'+item.description+'\n\n'
desc += 'Event Description:\n' + item.description + '\n\n'
# if item.notes: // Need to add proper keyholder checks before this gets put back
# desc += 'Notes:\n'+item.notes+'\n\n'
# desc += 'Notes:\n'+item.notes+'\n\n'
base_url = "http://rigs.nottinghamtec.co.uk"
desc += 'URL = '+base_url+str(item.get_absolute_url())
desc += 'URL = ' + base_url + str(item.get_absolute_url())
return desc
def item_link(self, item):
@@ -149,8 +165,8 @@ class CalendarICS(ICalFeed):
# def item_created(self, item): #TODO - Implement created date-time (using django-reversion?) - not really necessary though
# return ''
def item_updated(self, item): # some ical clients will display this
def item_updated(self, item): # some ical clients will display this
return item.last_edited_at
def item_guid(self, item): # use the rig-id as the ical unique event identifier
return item.pk
def item_guid(self, item): # use the rig-id as the ical unique event identifier
return item.pk

View File

@@ -1,12 +1,14 @@
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import Group, Permission
from django.db import transaction
import reversion
import datetime
import random
import reversion
from django.contrib.auth.models import Group, Permission
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from RIGS import models
class Command(BaseCommand):
help = 'Adds sample data to use for testing'
can_import_settings = True
@@ -19,32 +21,43 @@ class Command(BaseCommand):
keyholder_group = None
finance_group = None
def handle(self, *args, **options):
from django.conf import settings
if not (settings.DEBUG or settings.STAGING):
raise CommandError('You cannot run this command in production')
random.seed('Some object to seed the random number generator') # otherwise it is done by time, which could lead to inconsistant tests
random.seed(
'Some object to seed the random number generator') # otherwise it is done by time, which could lead to inconsistant tests
with transaction.atomic():
models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1')
models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
self.setupGenericProfiles()
self.setupPeople()
self.setupOrganisations()
self.setupVenues()
self.setupGroups()
self.setupEvents()
self.setupUsefulProfiles()
def setupPeople(self):
names = ["Regulus Black","Sirius Black","Lavender Brown","Cho Chang","Vincent Crabbe","Vincent Crabbe","Bartemius Crouch","Fleur Delacour","Cedric Diggory","Alberforth Dumbledore","Albus Dumbledore","Dudley Dursley","Petunia Dursley","Vernon Dursley","Argus Filch","Seamus Finnigan","Nicolas Flamel","Cornelius Fudge","Goyle","Gregory Goyle","Hermione Granger","Rubeus Hagrid","Igor Karkaroff","Viktor Krum","Bellatrix Lestrange","Alice Longbottom","Frank Longbottom","Neville Longbottom","Luna Lovegood","Xenophilius Lovegood","Remus Lupin","Draco Malfoy","Lucius Malfoy","Narcissa Malfoy","Olympe Maxime","Minerva McGonagall","Mad-Eye Moody","Peter Pettigrew","Harry Potter","James Potter","Lily Potter","Quirinus Quirrell","Tom Riddle","Mary Riddle","Lord Voldemort","Rita Skeeter","Severus Snape","Nymphadora Tonks","Dolores Janes Umbridge","Arthur Weasley","Bill Weasley","Charlie Weasley","Fred Weasley","George Weasley","Ginny Weasley","Molly Weasley","Percy Weasley","Ron Weasley","Dobby","Fluffy","Hedwig","Moaning Myrtle","Aragog","Grawp"]
names = ["Regulus Black", "Sirius Black", "Lavender Brown", "Cho Chang", "Vincent Crabbe", "Vincent Crabbe",
"Bartemius Crouch", "Fleur Delacour", "Cedric Diggory", "Alberforth Dumbledore", "Albus Dumbledore",
"Dudley Dursley", "Petunia Dursley", "Vernon Dursley", "Argus Filch", "Seamus Finnigan",
"Nicolas Flamel", "Cornelius Fudge", "Goyle", "Gregory Goyle", "Hermione Granger", "Rubeus Hagrid",
"Igor Karkaroff", "Viktor Krum", "Bellatrix Lestrange", "Alice Longbottom", "Frank Longbottom",
"Neville Longbottom", "Luna Lovegood", "Xenophilius Lovegood", "Remus Lupin", "Draco Malfoy",
"Lucius Malfoy", "Narcissa Malfoy", "Olympe Maxime", "Minerva McGonagall", "Mad-Eye Moody",
"Peter Pettigrew", "Harry Potter", "James Potter", "Lily Potter", "Quirinus Quirrell", "Tom Riddle",
"Mary Riddle", "Lord Voldemort", "Rita Skeeter", "Severus Snape", "Nymphadora Tonks",
"Dolores Janes Umbridge", "Arthur Weasley", "Bill Weasley", "Charlie Weasley", "Fred Weasley",
"George Weasley", "Ginny Weasley", "Molly Weasley", "Percy Weasley", "Ron Weasley", "Dobby", "Fluffy",
"Hedwig", "Moaning Myrtle", "Aragog", "Grawp"]
for i, name in enumerate(names):
with reversion.create_revision():
reversion.set_user(random.choice(self.profiles))
@@ -66,7 +79,32 @@ class Command(BaseCommand):
self.people.append(newPerson)
def setupOrganisations(self):
names = ["Acme, inc.","Widget Corp","123 Warehousing","Demo Company","Smith and Co.","Foo Bars","ABC Telecom","Fake Brothers","QWERTY Logistics","Demo, inc.","Sample Company","Sample, inc","Acme Corp","Allied Biscuit","Ankh-Sto Associates","Extensive Enterprise","Galaxy Corp","Globo-Chem","Mr. Sparkle","Globex Corporation","LexCorp","LuthorCorp","North Central Positronics","Omni Consimer Products","Praxis Corporation","Sombra Corporation","Sto Plains Holdings","Tessier-Ashpool","Wayne Enterprises","Wentworth Industries","ZiffCorp","Bluth Company","Strickland Propane","Thatherton Fuels","Three Waters","Water and Power","Western Gas & Electric","Mammoth Pictures","Mooby Corp","Gringotts","Thrift Bank","Flowers By Irene","The Legitimate Businessmens Club","Osato Chemicals","Transworld Consortium","Universal Export","United Fried Chicken","Virtucon","Kumatsu Motors","Keedsler Motors","Powell Motors","Industrial Automation","Sirius Cybernetics Corporation","U.S. Robotics and Mechanical Men","Colonial Movers","Corellian Engineering Corporation","Incom Corporation","General Products","Leeding Engines Ltd.","Blammo","Input, Inc.","Mainway Toys","Videlectrix","Zevo Toys","Ajax","Axis Chemical Co.","Barrytron","Carrys Candles","Cogswell Cogs","Spacely Sprockets","General Forge and Foundry","Duff Brewing Company","Dunder Mifflin","General Services Corporation","Monarch Playing Card Co.","Krustyco","Initech","Roboto Industries","Primatech","Sonky Rubber Goods","St. Anky Beer","Stay Puft Corporation","Vandelay Industries","Wernham Hogg","Gadgetron","Burleigh and Stronginthearm","BLAND Corporation","Nordyne Defense Dynamics","Petrox Oil Company","Roxxon","McMahon and Tate","Sixty Second Avenue","Charles Townsend Agency","Spade and Archer","Megadodo Publications","Rouster and Sideways","C.H. Lavatory and Sons","Globo Gym American Corp","The New Firm","SpringShield","Compuglobalhypermeganet","Data Systems","Gizmonic Institute","Initrode","Taggart Transcontinental","Atlantic Northern","Niagular","Plow King","Big Kahuna Burger","Big T Burgers and Fries","Chez Quis","Chotchkies","The Frying Dutchman","Klimpys","The Krusty Krab","Monks Diner","Milliways","Minuteman Cafe","Taco Grande","Tip Top Cafe","Moes Tavern","Central Perk","Chasers"]
names = ["Acme, inc.", "Widget Corp", "123 Warehousing", "Demo Company", "Smith and Co.", "Foo Bars",
"ABC Telecom", "Fake Brothers", "QWERTY Logistics", "Demo, inc.", "Sample Company", "Sample, inc",
"Acme Corp", "Allied Biscuit", "Ankh-Sto Associates", "Extensive Enterprise", "Galaxy Corp",
"Globo-Chem", "Mr. Sparkle", "Globex Corporation", "LexCorp", "LuthorCorp",
"North Central Positronics", "Omni Consimer Products", "Praxis Corporation", "Sombra Corporation",
"Sto Plains Holdings", "Tessier-Ashpool", "Wayne Enterprises", "Wentworth Industries", "ZiffCorp",
"Bluth Company", "Strickland Propane", "Thatherton Fuels", "Three Waters", "Water and Power",
"Western Gas & Electric", "Mammoth Pictures", "Mooby Corp", "Gringotts", "Thrift Bank",
"Flowers By Irene", "The Legitimate Businessmens Club", "Osato Chemicals", "Transworld Consortium",
"Universal Export", "United Fried Chicken", "Virtucon", "Kumatsu Motors", "Keedsler Motors",
"Powell Motors", "Industrial Automation", "Sirius Cybernetics Corporation",
"U.S. Robotics and Mechanical Men", "Colonial Movers", "Corellian Engineering Corporation",
"Incom Corporation", "General Products", "Leeding Engines Ltd.", "Blammo", "Input, Inc.",
"Mainway Toys", "Videlectrix", "Zevo Toys", "Ajax", "Axis Chemical Co.", "Barrytron", "Carrys Candles",
"Cogswell Cogs", "Spacely Sprockets", "General Forge and Foundry", "Duff Brewing Company",
"Dunder Mifflin", "General Services Corporation", "Monarch Playing Card Co.", "Krustyco", "Initech",
"Roboto Industries", "Primatech", "Sonky Rubber Goods", "St. Anky Beer", "Stay Puft Corporation",
"Vandelay Industries", "Wernham Hogg", "Gadgetron", "Burleigh and Stronginthearm", "BLAND Corporation",
"Nordyne Defense Dynamics", "Petrox Oil Company", "Roxxon", "McMahon and Tate", "Sixty Second Avenue",
"Charles Townsend Agency", "Spade and Archer", "Megadodo Publications", "Rouster and Sideways",
"C.H. Lavatory and Sons", "Globo Gym American Corp", "The New Firm", "SpringShield",
"Compuglobalhypermeganet", "Data Systems", "Gizmonic Institute", "Initrode",
"Taggart Transcontinental", "Atlantic Northern", "Niagular", "Plow King", "Big Kahuna Burger",
"Big T Burgers and Fries", "Chez Quis", "Chotchkies", "The Frying Dutchman", "Klimpys",
"The Krusty Krab", "Monks Diner", "Milliways", "Minuteman Cafe", "Taco Grande", "Tip Top Cafe",
"Moes Tavern", "Central Perk", "Chasers"]
for i, name in enumerate(names):
with reversion.create_revision():
reversion.set_user(random.choice(self.profiles))
@@ -90,7 +128,17 @@ class Command(BaseCommand):
self.organisations.append(newOrganisation)
def setupVenues(self):
names = ["Bear Island","Crossroads Inn","Deepwood Motte","The Dreadfort","The Eyrie","Greywater Watch","The Iron Islands","Karhold","Moat Cailin","Oldstones","Raventree Hall","Riverlands","The Ruby Ford","Saltpans","Seagard","Torrhen's Square","The Trident","The Twins","The Vale of Arryn","The Whispering Wood","White Harbor","Winterfell","The Arbor","Ashemark","Brightwater Keep","Casterly Rock","Clegane's Keep","Dragonstone","Dorne","God's Eye","The Golden Tooth","Harrenhal","Highgarden","Horn Hill","Fingers","King's Landing","Lannisport","Oldtown","Rainswood","Storm's End","Summerhall","Sunspear","Tarth","Castle Black","Craster's Keep","Fist of the First Men","The Frostfangs","The Gift","The Skirling Pass","The Wall","Asshai","Astapor","Braavos","The Dothraki Sea","Lys","Meereen","Myr","Norvos","Pentos","Qarth","Qohor","The Red Waste","Tyrosh","Vaes Dothrak","Valyria","Village of the Lhazareen","Volantis","Yunkai"]
names = ["Bear Island", "Crossroads Inn", "Deepwood Motte", "The Dreadfort", "The Eyrie", "Greywater Watch",
"The Iron Islands", "Karhold", "Moat Cailin", "Oldstones", "Raventree Hall", "Riverlands",
"The Ruby Ford", "Saltpans", "Seagard", "Torrhen's Square", "The Trident", "The Twins",
"The Vale of Arryn", "The Whispering Wood", "White Harbor", "Winterfell", "The Arbor", "Ashemark",
"Brightwater Keep", "Casterly Rock", "Clegane's Keep", "Dragonstone", "Dorne", "God's Eye",
"The Golden Tooth", "Harrenhal", "Highgarden", "Horn Hill", "Fingers", "King's Landing", "Lannisport",
"Oldtown", "Rainswood", "Storm's End", "Summerhall", "Sunspear", "Tarth", "Castle Black",
"Craster's Keep", "Fist of the First Men", "The Frostfangs", "The Gift", "The Skirling Pass",
"The Wall", "Asshai", "Astapor", "Braavos", "The Dothraki Sea", "Lys", "Meereen", "Myr", "Norvos",
"Pentos", "Qarth", "Qohor", "The Red Waste", "Tyrosh", "Vaes Dothrak", "Valyria",
"Village of the Lhazareen", "Volantis", "Yunkai"]
for i, name in enumerate(names):
with reversion.create_revision():
reversion.set_user(random.choice(self.profiles))
@@ -117,8 +165,14 @@ class Command(BaseCommand):
self.keyholder_group = Group.objects.create(name='Keyholders')
self.finance_group = Group.objects.create(name='Finance')
keyholderPerms = ["add_event","change_event","view_event","add_eventitem","change_eventitem","delete_eventitem","add_organisation","change_organisation","view_organisation","add_person","change_person","view_person","view_profile","add_venue","change_venue","view_venue"]
financePerms = ["change_event","view_event","add_eventitem","change_eventitem","add_invoice","change_invoice","view_invoice","add_organisation","change_organisation","view_organisation","add_payment","change_payment","delete_payment","add_person","change_person","view_person"]
keyholderPerms = ["add_event", "change_event", "view_event", "add_eventitem", "change_eventitem",
"delete_eventitem", "add_organisation", "change_organisation", "view_organisation",
"add_person", "change_person", "view_person", "view_profile", "add_venue", "change_venue",
"view_venue"]
financePerms = ["change_event", "view_event", "add_eventitem", "change_eventitem", "add_invoice",
"change_invoice", "view_invoice", "add_organisation", "change_organisation",
"view_organisation", "add_payment", "change_payment", "delete_payment", "add_person",
"change_person", "view_person"]
for permId in keyholderPerms:
self.keyholder_group.permissions.add(Permission.objects.get(codename=permId))
@@ -127,11 +181,13 @@ class Command(BaseCommand):
self.finance_group.permissions.add(Permission.objects.get(codename=permId))
def setupGenericProfiles(self):
names = ["Clara Oswin Oswald","Rory Williams","Amy Pond","River Song","Martha Jones","Donna Noble","Jack Harkness","Mickey Smith","Rose Tyler"]
names = ["Clara Oswin Oswald", "Rory Williams", "Amy Pond", "River Song", "Martha Jones", "Donna Noble",
"Jack Harkness", "Mickey Smith", "Rose Tyler"]
for i, name in enumerate(names):
newProfile = models.Profile.objects.create(username=name.replace(" ",""), first_name=name.split(" ")[0], last_name=name.split(" ")[-1],
email=name.replace(" ","")+"@example.com",
initials="".join([ j[0].upper() for j in name.split() ]))
newProfile = models.Profile.objects.create(username=name.replace(" ", ""), first_name=name.split(" ")[0],
last_name=name.split(" ")[-1],
email=name.replace(" ", "") + "@example.com",
initials="".join([j[0].upper() for j in name.split()]))
if i % 2 == 0:
newProfile.phone = "01234 567894"
@@ -139,110 +195,127 @@ class Command(BaseCommand):
self.profiles.append(newProfile)
def setupUsefulProfiles(self):
superUser = models.Profile.objects.create(username="superuser", first_name="Super", last_name="User", initials="SU",
email="superuser@example.com", is_superuser=True, is_active=True, is_staff=True)
superUser = models.Profile.objects.create(username="superuser", first_name="Super", last_name="User",
initials="SU",
email="superuser@example.com", is_superuser=True, is_active=True,
is_staff=True)
superUser.set_password('superuser')
superUser.save()
financeUser = models.Profile.objects.create(username="finance", first_name="Finance", last_name="User", initials="FU",
email="financeuser@example.com", is_active=True)
financeUser = models.Profile.objects.create(username="finance", first_name="Finance", last_name="User",
initials="FU",
email="financeuser@example.com", is_active=True)
financeUser.groups.add(self.finance_group)
financeUser.groups.add(self.keyholder_group)
financeUser.set_password('finance')
financeUser.save()
keyholderUser = models.Profile.objects.create(username="keyholder", first_name="Keyholder", last_name="User", initials="KU",
email="keyholderuser@example.com", is_active=True)
keyholderUser = models.Profile.objects.create(username="keyholder", first_name="Keyholder", last_name="User",
initials="KU",
email="keyholderuser@example.com", is_active=True)
keyholderUser.groups.add(self.keyholder_group)
keyholderUser.set_password('keyholder')
keyholderUser.save()
basicUser = models.Profile.objects.create(username="basic", first_name="Basic", last_name="User", initials="BU",
email="basicuser@example.com", is_active=True)
basicUser = models.Profile.objects.create(username="basic", first_name="Basic", last_name="User", initials="BU",
email="basicuser@example.com", is_active=True)
basicUser.set_password('basic')
basicUser.save()
def setupEvents(self):
names = ["Outdoor Concert","Hall Open Mic Night","Festival","Weekend Event","Magic Show","Society Ball","Evening Show","Talent Show","Acoustic Evening","Hire of Things","SU Event","End of Term Show","Theatre Show","Outdoor Fun Day","Summer Carnival","Open Days","Magic Show","Awards Ceremony","Debating Event","Club Night","DJ Evening","Building Projection","Choir Concert"]
descriptions = ["A brief desciption of the event","This event is boring","Probably wont happen","Warning: this has lots of kit"]
notes = ["The client came into the office at some point","Who knows if this will happen", "Probably should check this event", "Maybe not happening", "Run away!"]
names = ["Outdoor Concert", "Hall Open Mic Night", "Festival", "Weekend Event", "Magic Show", "Society Ball",
"Evening Show", "Talent Show", "Acoustic Evening", "Hire of Things", "SU Event", "End of Term Show",
"Theatre Show", "Outdoor Fun Day", "Summer Carnival", "Open Days", "Magic Show", "Awards Ceremony",
"Debating Event", "Club Night", "DJ Evening", "Building Projection", "Choir Concert"]
descriptions = ["A brief desciption of the event", "This event is boring", "Probably wont happen",
"Warning: this has lots of kit"]
notes = ["The client came into the office at some point", "Who knows if this will happen",
"Probably should check this event", "Maybe not happening", "Run away!"]
itemOptions = [{'name': 'Speakers', 'description': 'Some really really big speakers \n these are very loud', 'quantity': 2, 'cost': 200.00},
{'name': 'Projector', 'description': 'Some kind of video thinamejig, probably with unnecessary processing for free', 'quantity': 1, 'cost': 500.00},
{'name': 'Lighting Desk', 'description': 'Cannot provide guarentee that it will work', 'quantity': 1, 'cost': 200.52},
{'name': 'Moving lights', 'description': 'Flashy lights, with the copper', 'quantity': 8, 'cost': 50.00},
{'name': 'Microphones', 'description': 'Make loud noise \n you will want speakers with this', 'quantity': 5, 'cost': 0.50},
{'name': 'Sound Mixer Thing', 'description': 'Might be analogue, might be digital', 'quantity': 1, 'cost': 100.00},
{'name': 'Electricity', 'description': 'You need this', 'quantity': 1, 'cost': 200.00},
{'name': 'Crew', 'description': 'Costs nothing, because reasons', 'quantity': 1, 'cost': 0.00},
{'name': 'Loyalty Discount', 'description': 'Have some negative moneys', 'quantity': 1, 'cost': -50.00}]
itemOptions = [
{'name': 'Speakers', 'description': 'Some really really big speakers \n these are very loud', 'quantity': 2,
'cost': 200.00},
{'name': 'Projector',
'description': 'Some kind of video thinamejig, probably with unnecessary processing for free',
'quantity': 1, 'cost': 500.00},
{'name': 'Lighting Desk', 'description': 'Cannot provide guarentee that it will work', 'quantity': 1,
'cost': 200.52},
{'name': 'Moving lights', 'description': 'Flashy lights, with the copper', 'quantity': 8, 'cost': 50.00},
{'name': 'Microphones', 'description': 'Make loud noise \n you will want speakers with this', 'quantity': 5,
'cost': 0.50},
{'name': 'Sound Mixer Thing', 'description': 'Might be analogue, might be digital', 'quantity': 1,
'cost': 100.00},
{'name': 'Electricity', 'description': 'You need this', 'quantity': 1, 'cost': 200.00},
{'name': 'Crew', 'description': 'Costs nothing, because reasons', 'quantity': 1, 'cost': 0.00},
{'name': 'Loyalty Discount', 'description': 'Have some negative moneys', 'quantity': 1, 'cost': -50.00}]
dayDelta = -120 # start adding events from 4 months ago
dayDelta = -120 # start adding events from 4 months ago
for i in range(150): # Let's add 100 events
for i in range(150): # Let's add 100 events
with reversion.create_revision():
reversion.set_user(random.choice(self.profiles))
name = names[i%len(names)]
name = names[i % len(names)]
startDate = datetime.date.today() + datetime.timedelta(days=dayDelta)
dayDelta = dayDelta + random.randint(0,3)
dayDelta = dayDelta + random.randint(0, 3)
newEvent = models.Event.objects.create(name=name, start_date=startDate)
if random.randint(0,2) > 1: # 1 in 3 have a start time
newEvent.start_time = datetime.time(random.randint(15,20))
if random.randint(0,2) > 1: # of those, 1 in 3 have an end time on the same day
newEvent.end_time = datetime.time(random.randint(21,23))
elif random.randint(0,1)>0: # half of the others finish early the next day
if random.randint(0, 2) > 1: # 1 in 3 have a start time
newEvent.start_time = datetime.time(random.randint(15, 20))
if random.randint(0, 2) > 1: # of those, 1 in 3 have an end time on the same day
newEvent.end_time = datetime.time(random.randint(21, 23))
elif random.randint(0, 1) > 0: # half of the others finish early the next day
newEvent.end_date = newEvent.start_date + datetime.timedelta(days=1)
newEvent.end_time = datetime.time(random.randint(0,5))
elif random.randint(0,2)>1: # 1 in 3 of the others finish a few days ahead
newEvent.end_date = newEvent.start_date + datetime.timedelta(days=random.randint(1,4))
newEvent.end_time = datetime.time(random.randint(0, 5))
elif random.randint(0, 2) > 1: # 1 in 3 of the others finish a few days ahead
newEvent.end_date = newEvent.start_date + datetime.timedelta(days=random.randint(1, 4))
if random.randint(0,6) > 0: # 5 in 6 have MIC
if random.randint(0, 6) > 0: # 5 in 6 have MIC
newEvent.mic = random.choice(self.profiles)
if random.randint(0,6) > 0: # 5 in 6 have organisation
if random.randint(0, 6) > 0: # 5 in 6 have organisation
newEvent.organisation = random.choice(self.organisations)
if random.randint(0,6) > 0: # 5 in 6 have person
if random.randint(0, 6) > 0: # 5 in 6 have person
newEvent.person = random.choice(self.people)
if random.randint(0,6) > 0: # 5 in 6 have venue
if random.randint(0, 6) > 0: # 5 in 6 have venue
newEvent.venue = random.choice(self.venues)
# Could have any status, equally weighted
newEvent.status = random.choice([models.Event.BOOKED,models.Event.CONFIRMED,models.Event.PROVISIONAL, models.Event.CANCELLED])
newEvent.status = random.choice(
[models.Event.BOOKED, models.Event.CONFIRMED, models.Event.PROVISIONAL, models.Event.CANCELLED])
newEvent.dry_hire = (random.randint(0,7)==0) # 1 in 7 are dry hire
newEvent.dry_hire = (random.randint(0, 7) == 0) # 1 in 7 are dry hire
if random.randint(0,1) > 0: # 1 in 2 have description
if random.randint(0, 1) > 0: # 1 in 2 have description
newEvent.description = random.choice(descriptions)
if random.randint(0,1) > 0: # 1 in 2 have notes
if random.randint(0, 1) > 0: # 1 in 2 have notes
newEvent.notes = random.choice(notes)
newEvent.save()
# Now add some items
for j in range(random.randint(1,5)):
itemData = itemOptions[random.randint(0,len(itemOptions)-1)]
for j in range(random.randint(1, 5)):
itemData = itemOptions[random.randint(0, len(itemOptions) - 1)]
newItem = models.EventItem.objects.create(event=newEvent, order=j, **itemData)
newItem.save()
while newEvent.sum_total < 0:
itemData = itemOptions[random.randint(0,len(itemOptions)-1)]
itemData = itemOptions[random.randint(0, len(itemOptions) - 1)]
newItem = models.EventItem.objects.create(event=newEvent, order=j, **itemData)
newItem.save()
with reversion.create_revision():
reversion.set_user(random.choice(self.profiles))
if newEvent.start_date < datetime.date.today(): # think about adding an invoice
if random.randint(0,2) > 0: # 2 in 3 have had paperwork sent to treasury
if newEvent.start_date < datetime.date.today(): # think about adding an invoice
if random.randint(0, 2) > 0: # 2 in 3 have had paperwork sent to treasury
newInvoice = models.Invoice.objects.create(event=newEvent)
if newEvent.status is models.Event.CANCELLED: # void cancelled events
if newEvent.status is models.Event.CANCELLED: # void cancelled events
newInvoice.void = True
elif random.randint(0,2)>1: # 1 in 3 have been paid
models.Payment.objects.create(invoice=newInvoice, amount=newInvoice.balance, date=datetime.date.today())
elif random.randint(0, 2) > 1: # 1 in 3 have been paid
models.Payment.objects.create(invoice=newInvoice, amount=newInvoice.balance,
date=datetime.date.today())

View File

@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.core.validators
import django.utils.timezone
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
]
@@ -19,18 +18,32 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(max_length=30, unique=True, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', verbose_name='username', validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username.', 'invalid')])),
('is_superuser', models.BooleanField(default=False,
help_text='Designates that this user has all permissions without explicitly assigning them.',
verbose_name='superuser status')),
('username', models.CharField(max_length=30, unique=True,
help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.',
verbose_name='username', validators=[
django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username.', 'invalid')])),
('first_name', models.CharField(max_length=30, blank=True, verbose_name='first name')),
('last_name', models.CharField(max_length=30, blank=True, verbose_name='last name')),
('email', models.EmailField(max_length=75, blank=True, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('is_staff', models.BooleanField(default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status')),
('is_active', models.BooleanField(default=True,
help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.',
verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('initials', models.CharField(max_length=5, unique=True)),
('phone', models.CharField(max_length=13, blank=True, null=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.', verbose_name='groups', related_name='user_set', related_query_name='user', to='auth.Group')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions', related_name='user_set', related_query_name='user', to='auth.Permission')),
('groups', models.ManyToManyField(blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.',
verbose_name='groups', related_name='user_set',
related_query_name='user', to='auth.Group')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.',
verbose_name='user permissions', related_name='user_set',
related_query_name='user', to='auth.Permission')),
],
options={
'verbose_name_plural': 'users',

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0001_initial'),
]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0002_modelcomment_person'),
]

View File

@@ -2,11 +2,11 @@
from __future__ import unicode_literals
from django.db import models, migrations
import RIGS.models
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0003_auto_20141031_0219'),
]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0004_organisation'),
]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0012_auto_20141106_0253'),
]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0013_auto_20141202_0041'),
]

View File

@@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0014_auto_20141208_0220'),
]

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0015_auto_20141208_0233'),
]
@@ -30,7 +29,9 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('date', models.DateField()),
('amount', models.DecimalField(help_text=b'Please use ex. VAT', max_digits=10, decimal_places=2)),
('method', models.CharField(max_length=2, choices=[(b'C', b'Cash'), (b'I', b'Internal'), (b'E', b'External'), (b'SU', b'SU Core'), (b'M', b'Members')])),
('method', models.CharField(max_length=2,
choices=[(b'C', b'Cash'), (b'I', b'Internal'), (b'E', b'External'),
(b'SU', b'SU Core'), (b'M', b'Members')])),
('invoice', models.ForeignKey(to='RIGS.Invoice')),
],
options={
@@ -40,7 +41,8 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='event',
name='mic',
field=models.ForeignKey(related_name='event_mic', verbose_name=b'MIC', blank=True, to=settings.AUTH_USER_MODEL, null=True),
field=models.ForeignKey(related_name='event_mic', verbose_name=b'MIC', blank=True,
to=settings.AUTH_USER_MODEL, null=True),
preserve_default=True,
),
]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0018_auto_20150130_0016'),
]
@@ -14,7 +13,9 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='payment',
name='method',
field=models.CharField(blank=True, max_length=2, null=True, choices=[(b'C', b'Cash'), (b'I', b'Internal'), (b'E', b'External'), (b'SU', b'SU Core')]),
field=models.CharField(blank=True, max_length=2, null=True,
choices=[(b'C', b'Cash'), (b'I', b'Internal'), (b'E', b'External'),
(b'SU', b'SU Core')]),
preserve_default=True,
),
]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0019_auto_20150131_1919'),
]
@@ -14,7 +13,9 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='payment',
name='method',
field=models.CharField(blank=True, max_length=2, null=True, choices=[(b'C', b'Cash'), (b'I', b'Internal'), (b'E', b'External'), (b'SU', b'SU Core'), (b'T', b'TEC Adjustment')]),
field=models.CharField(blank=True, max_length=2, null=True,
choices=[(b'C', b'Cash'), (b'I', b'Internal'), (b'E', b'External'),
(b'SU', b'SU Core'), (b'T', b'TEC Adjustment')]),
preserve_default=True,
),
]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0020_auto_20150303_0243'),
]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0021_auto_20150420_1155'),
]

View File

@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.core.validators
import django.contrib.auth.models
import django.core.validators
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0022_auto_20150424_2104'),
]
@@ -46,7 +45,10 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='profile',
name='groups',
field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups'),
field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group',
blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
verbose_name='groups'),
),
migrations.AlterField(
model_name='profile',
@@ -56,7 +58,12 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='profile',
name='username',
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username'),
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'},
max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$',
'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.',
'invalid')],
help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.',
unique=True, verbose_name='username'),
),
migrations.AlterField(
model_name='venue',

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.db.models.deletion
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0023_auto_20150529_0048'),
]
@@ -15,6 +14,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='event',
name='based_on',
field=models.ForeignKey(related_name='future_events', on_delete=django.db.models.deletion.SET_NULL, blank=True, to='RIGS.Event', null=True),
field=models.ForeignKey(related_name='future_events', on_delete=django.db.models.deletion.SET_NULL,
blank=True, to='RIGS.Event', null=True),
),
]

View File

@@ -1,11 +1,11 @@
import datetime
import hashlib
import pytz
import random
import string
from collections import Counter
from decimal import Decimal
import pytz
import reversion
from django.conf import settings
from django.contrib.auth.models import AbstractUser
@@ -19,22 +19,31 @@ from django.utils.functional import cached_property
# Create your models here.
@python_2_unicode_compatible
class Profile(AbstractUser):
initials = models.CharField(max_length=5, unique=True, null=True, blank=False)
initials = models.CharField(
max_length=5,
unique=True,
null=True,
blank=False)
phone = models.CharField(max_length=13, null=True, blank=True)
api_key = models.CharField(max_length=40, blank=True, editable=False, null=True)
api_key = models.CharField(
max_length=40,
blank=True,
editable=False,
null=True)
@classmethod
def make_api_key(cls):
size = 20
chars = string.ascii_letters + string.digits
new_api_key = ''.join(random.choice(chars) for x in range(size))
return new_api_key;
return new_api_key
@property
def profile_picture(self):
url = ""
if settings.USE_GRAVATAR or settings.USE_GRAVATAR is None:
url = "https://www.gravatar.com/avatar/" + hashlib.md5(self.email).hexdigest() + "?d=wavatar&s=500"
url = "https://www.gravatar.com/avatar/" + \
hashlib.md5(self.email).hexdigest() + "?d=wavatar&s=500"
return url
@property
@@ -46,7 +55,8 @@ class Profile(AbstractUser):
@property
def latest_events(self):
return self.event_mic.order_by('-start_date').select_related('person', 'organisation', 'venue', 'mic')
return self.event_mic.order_by(
'-start_date').select_related('person', 'organisation', 'venue', 'mic')
def __str__(self):
return self.name
@@ -107,7 +117,8 @@ class Person(models.Model, RevisionMixin):
@property
def organisations(self):
o = []
for e in Event.objects.filter(person=self).select_related('organisation'):
for e in Event.objects.filter(
person=self).select_related('organisation'):
if e.organisation:
o.append(e.organisation)
@@ -118,7 +129,8 @@ class Person(models.Model, RevisionMixin):
@property
def latest_events(self):
return self.event_set.order_by('-start_date').select_related('person', 'organisation', 'venue', 'mic')
return self.event_set.order_by(
'-start_date').select_related('person', 'organisation', 'venue', 'mic')
def get_absolute_url(self):
return reverse_lazy('person_detail', kwargs={'pk': self.pk})
@@ -151,7 +163,8 @@ class Organisation(models.Model, RevisionMixin):
@property
def persons(self):
p = []
for e in Event.objects.filter(organisation=self).select_related('person'):
for e in Event.objects.filter(
organisation=self).select_related('person'):
if e.person:
p.append(e.person)
@@ -162,7 +175,8 @@ class Organisation(models.Model, RevisionMixin):
@property
def latest_events(self):
return self.event_set.order_by('-start_date').select_related('person', 'organisation', 'venue', 'mic')
return self.event_set.order_by(
'-start_date').select_related('person', 'organisation', 'venue', 'mic')
def get_absolute_url(self):
return reverse_lazy('organisation_detail', kwargs={'pk': self.pk})
@@ -205,7 +219,8 @@ class VatRate(models.Model, RevisionMixin):
get_latest_by = 'start_at'
def __str__(self):
return self.comment + " " + str(self.start_at) + " @ " + str(self.as_percent) + "%"
return self.comment + " " + \
str(self.start_at) + " @ " + str(self.as_percent) + "%"
@reversion.register
@@ -227,7 +242,8 @@ class Venue(models.Model, RevisionMixin):
@property
def latest_events(self):
return self.event_set.order_by('-start_date').select_related('person', 'organisation', 'venue', 'mic')
return self.event_set.order_by(
'-start_date').select_related('person', 'organisation', 'venue', 'mic')
def get_absolute_url(self):
return reverse_lazy('venue_detail', kwargs={'pk': self.pk})
@@ -249,7 +265,10 @@ class EventManager(models.Manager):
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
# Canceled but not started
models.Q(
status=Event.CANCELLED,
start_date__gte=datetime.date.today())
).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person',
'organisation',
'venue', 'mic')
@@ -257,16 +276,24 @@ class EventManager(models.Manager):
def events_in_bounds(self, start, end):
events = self.filter(
(models.Q(start_date__gte=start.date(), start_date__lte=end.date())) | # Start date in bounds
(models.Q(end_date__gte=start.date(), end_date__lte=end.date())) | # End date in bounds
(models.Q(access_at__gte=start, access_at__lte=end)) | # Access at in bounds
# Start date in bounds
(models.Q(start_date__gte=start.date(), start_date__lte=end.date())) |
# End date in bounds
(models.Q(end_date__gte=start.date(), end_date__lte=end.date())) |
# Access at in bounds
(models.Q(access_at__gte=start, access_at__lte=end)) |
(models.Q(meet_at__gte=start, meet_at__lte=end)) | # Meet at in bounds
(models.Q(start_date__lte=start, end_date__gte=end)) | # Start before, end after
(models.Q(access_at__lte=start, start_date__gte=end)) | # Access before, start after
(models.Q(access_at__lte=start, end_date__gte=end)) | # Access before, end after
(models.Q(meet_at__lte=start, start_date__gte=end)) | # Meet before, start after
(models.Q(meet_at__lte=start, end_date__gte=end)) # Meet before, end after
# Start before, end after
(models.Q(start_date__lte=start, end_date__gte=end)) |
# Access before, start after
(models.Q(access_at__lte=start, start_date__gte=end)) |
# Access before, end after
(models.Q(access_at__lte=start, end_date__gte=end)) |
# Meet before, start after
(models.Q(meet_at__lte=start, start_date__gte=end)) |
# Meet before, end after
(models.Q(meet_at__lte=start, end_date__gte=end))
).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person',
'organisation',
@@ -309,7 +336,9 @@ class Event(models.Model, RevisionMixin):
venue = models.ForeignKey('Venue', blank=True, null=True)
description = models.TextField(blank=True, null=True)
notes = models.TextField(blank=True, null=True)
status = models.IntegerField(choices=EVENT_STATUS_CHOICES, default=PROVISIONAL)
status = models.IntegerField(
choices=EVENT_STATUS_CHOICES,
default=PROVISIONAL)
dry_hire = models.BooleanField(default=False)
is_rig = models.BooleanField(default=True)
based_on = models.ForeignKey('Event', on_delete=models.SET_NULL, related_name='future_events', blank=True,
@@ -325,15 +354,27 @@ class Event(models.Model, RevisionMixin):
meet_info = models.CharField(max_length=255, blank=True, null=True)
# Crew management
checked_in_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True, null=True)
checked_in_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='event_checked_in',
blank=True,
null=True)
mic = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_mic', blank=True, null=True,
verbose_name="MIC")
# Monies
payment_method = models.CharField(max_length=255, blank=True, null=True)
payment_received = models.CharField(max_length=255, blank=True, null=True)
purchase_order = models.CharField(max_length=255, blank=True, null=True, verbose_name='PO')
collector = models.CharField(max_length=255, blank=True, null=True, verbose_name='collected by')
purchase_order = models.CharField(
max_length=255,
blank=True,
null=True,
verbose_name='PO')
collector = models.CharField(
max_length=255,
blank=True,
null=True,
verbose_name='collected by')
# Calculated values
"""
@@ -409,9 +450,11 @@ class Event(models.Model, RevisionMixin):
# If there is no start time defined, pretend it's midnight
startTimeFaked = False
if self.has_start_time:
startDateTime = datetime.datetime.combine(self.start_date, self.start_time)
startDateTime = datetime.datetime.combine(
self.start_date, self.start_time)
else:
startDateTime = datetime.datetime.combine(self.start_date, datetime.time(00, 00))
startDateTime = datetime.datetime.combine(
self.start_date, datetime.time(00, 00))
startTimeFaked = True
# timezoneIssues - apply the default timezone to the naiive datetime
@@ -419,7 +462,8 @@ class Event(models.Model, RevisionMixin):
startDateTime = tz.localize(startDateTime)
datetime_list.append(startDateTime) # then add it to the list
earliest = min(datetime_list).astimezone(tz) # find the earliest datetime in the list
# find the earliest datetime in the list
earliest = min(datetime_list).astimezone(tz)
# if we faked it & it's the earliest, better own up
if startTimeFaked and earliest == startDateTime:
@@ -455,12 +499,14 @@ class Event(models.Model, RevisionMixin):
def clean(self):
if self.end_date and self.start_date > self.end_date:
raise ValidationError('Unless you\'ve invented time travel, the event can\'t finish before it has started.')
raise ValidationError(
'Unless you\'ve invented time travel, the event can\'t finish before it has started.')
startEndSameDay = not self.end_date or self.end_date == self.start_date
hasStartAndEnd = self.has_start_time and self.has_end_time
if startEndSameDay and hasStartAndEnd and self.start_time > self.end_time:
raise ValidationError('Unless you\'ve invented time travel, the event can\'t finish before it has started.')
raise ValidationError(
'Unless you\'ve invented time travel, the event can\'t finish before it has started.')
def save(self, *args, **kwargs):
"""Call :meth:`full_clean` before saving."""
@@ -489,7 +535,8 @@ class EventItem(models.Model):
ordering = ['order']
def __str__(self):
return str(self.event.pk) + "." + str(self.order) + ": " + self.event.name + " | " + self.name
return str(self.event.pk) + "." + str(self.order) + \
": " + self.event.name + " | " + self.name
class EventCrew(models.Model):
@@ -557,8 +604,15 @@ class Payment(models.Model):
invoice = models.ForeignKey('Invoice')
date = models.DateField()
amount = models.DecimalField(max_digits=10, decimal_places=2, help_text='Please use ex. VAT')
method = models.CharField(max_length=2, choices=METHODS, null=True, blank=True)
amount = models.DecimalField(
max_digits=10,
decimal_places=2,
help_text='Please use ex. VAT')
method = models.CharField(
max_length=2,
choices=METHODS,
null=True,
blank=True)
def __str__(self):
return "%s: %d" % (self.get_method_display(), self.amount)

View File

@@ -1,13 +1,15 @@
from RIGS.models import Profile
from RIGS.forms import ProfileRegistrationFormUniqueEmail
def user_created(sender, user, request, **kwargs):
form = ProfileRegistrationFormUniqueEmail(request.POST)
user.first_name = form.data['first_name']
user.last_name = form.data['last_name']
user.initials = form.data['initials']
user.phone = form.data['phone']
user.save()
form = ProfileRegistrationFormUniqueEmail(request.POST)
user.first_name = form.data['first_name']
user.last_name = form.data['last_name']
user.initials = form.data['initials']
user.phone = form.data['phone']
user.save()
from registration.signals import user_registered
user_registered.connect(user_created)
user_registered.connect(user_created)

View File

@@ -1,24 +1,23 @@
import os
import cStringIO as StringIO
from io import BytesIO
import copy
import datetime
import re
import urllib2
from io import BytesIO
from django.views import generic
from PyPDF2 import PdfFileMerger, PdfFileReader
from django.conf import settings
from django.contrib import messages
from django.core.urlresolvers import reverse_lazy
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.template import RequestContext
from django.template.loader import get_template
from django.conf import settings
from django.http import HttpResponse
from django.db.models import Q
from django.contrib import messages
from django.views import generic
from z3c.rml import rml2pdf
from PyPDF2 import PdfFileMerger, PdfFileReader
from RIGS import models, forms
import datetime
import re
import copy
__author__ = 'ghost'
@@ -34,15 +33,17 @@ class RigboardIndex(generic.TemplateView):
context['events'] = models.Event.objects.current_events()
return context
class WebCalendar(generic.TemplateView):
template_name = 'RIGS/calendar.html'
def get_context_data(self, **kwargs):
context = super(WebCalendar, self).get_context_data(**kwargs)
context['view'] = kwargs.get('view','')
context['date'] = kwargs.get('date','')
context['view'] = kwargs.get('view', '')
context['date'] = kwargs.get('date', '')
return context
class EventDetail(generic.DetailView):
model = models.Event
@@ -58,10 +59,12 @@ class EventCreate(generic.CreateView):
form = context['form']
if re.search('"-\d+"', form['items_json'].value()):
messages.info(self.request, "Your item changes have been saved. Please fix the errors and save the event.")
messages.info(
self.request,
"Your item changes have been saved. Please fix the errors and save the event.")
# Get some other objects to include in the form. Used when there are errors but also nice and quick.
# Get some other objects to include in the form. Used when there are
# errors but also nice and quick.
for field, model in form.related_models.iteritems():
value = form[field].value()
if value is not None and value != '':
@@ -82,7 +85,8 @@ class EventUpdate(generic.UpdateView):
form = context['form']
# Get some other objects to include in the form. Used when there are errors but also nice and quick.
# Get some other objects to include in the form. Used when there are
# errors but also nice and quick.
for field, model in form.related_models.iteritems():
value = form[field].value()
if value is not None and value != '':
@@ -92,16 +96,21 @@ class EventUpdate(generic.UpdateView):
def get_success_url(self):
return reverse_lazy('event_detail', kwargs={'pk': self.object.pk})
class EventDuplicate(EventUpdate):
def get_object(self, queryset=None):
old = super(EventDuplicate, self).get_object(queryset) # Get the object (the event you're duplicating)
new = copy.copy(old) # Make a copy of the object in memory
new.based_on = old # Make the new event based on the old event
# Get the object (the event you're duplicating)
old = super(EventDuplicate, self).get_object(queryset)
new = copy.copy(old) # Make a copy of the object in memory
new.based_on = old # Make the new event based on the old event
if self.request.method in ('POST', 'PUT'): # This only happens on save (otherwise items won't display in editor)
new.pk = None # This means a new event will be created on save, and all items will be re-created
if self.request.method in (
'POST', 'PUT'): # This only happens on save (otherwise items won't display in editor)
new.pk = None # This means a new event will be created on save, and all items will be re-created
messages.info(self.request, 'Event data duplicated but not yet saved. Click save to complete operation.')
messages.info(
self.request,
'Event data duplicated but not yet saved. Click save to complete operation.')
return new
@@ -110,6 +119,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)
@@ -119,8 +129,7 @@ class EventPrint(generic.View):
merger = PdfFileMerger()
for copy in copies:
context = RequestContext(request, { # this should be outside the loop, but bug in 1.8.2 prevents this
context = RequestContext(request, { # this should be outside the loop, but bug in 1.8.2 prevents this
'object': object,
'fonts': {
'opensans': {
@@ -128,11 +137,12 @@ class EventPrint(generic.View):
'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF',
}
},
'copy':copy,
'current_user':request.user,
'copy': copy,
'current_user': request.user,
})
# context['copy'] = copy # this is the way to do it once we upgrade to Django 1.8.3
# context['copy'] = copy # this is the way to do it once we upgrade
# to Django 1.8.3
rml = template.render(context)
buffer = StringIO.StringIO()
@@ -153,10 +163,12 @@ class EventPrint(generic.View):
escapedEventName = re.sub('[^a-zA-Z0-9 \n\.]', '', object.name)
response['Content-Disposition'] = "filename=N%05d | %s.pdf" % (object.pk, escapedEventName)
response[
'Content-Disposition'] = "filename=N%05d | %s.pdf" % (object.pk, escapedEventName)
response.write(merged.getvalue())
return response
class EventArchive(generic.ArchiveIndexView):
model = models.Event
date_field = "start_date"
@@ -190,6 +202,9 @@ class EventArchive(generic.ArchiveIndexView):
qs.select_related('person', 'organisation', 'venue', 'mic')
if len(qs) == 0:
messages.add_message(self.request, messages.WARNING, "No events have been found matching those criteria.")
messages.add_message(
self.request,
messages.WARNING,
"No events have been found matching those criteria.")
return qs
return qs

View File

@@ -1,104 +1,104 @@
$(document).ready(function() {
clearSelectionLabel = '(no selection)';
$(document).ready(function () {
clearSelectionLabel = '(no selection)';
function changeSelectedValue(obj,pk,text,update_url) { //Pass in JQuery object and new parameters
function changeSelectedValue(obj, pk, text, update_url) { //Pass in JQuery object and new parameters
//console.log('Changing selected value');
obj.find('option').remove(); //Remove all the available options
obj.append( //Add the new option
$("<option></option>")
.attr("value",pk)
.text(text)
.data('update_url',update_url)
);
obj.selectpicker('render'); //Re-render the UI
obj.selectpicker('refresh'); //Re-render the UI
obj.selectpicker('val', pk); //Set the new value to be selected
obj.change(); //Trigger the change function manually
}
$("<option></option>")
.attr("value", pk)
.text(text)
.data('update_url', update_url)
);
obj.selectpicker('render'); //Re-render the UI
obj.selectpicker('refresh'); //Re-render the UI
obj.selectpicker('val', pk); //Set the new value to be selected
obj.change(); //Trigger the change function manually
}
function refreshUpdateHref(obj) {
//console.log('Refreshing Update URL');
targetObject = $('#'+obj.attr('id')+'-update');
update_url = $('option:selected', obj).data('update_url');
function refreshUpdateHref(obj) {
//console.log('Refreshing Update URL');
targetObject = $('#' + obj.attr('id') + '-update');
update_url = $('option:selected', obj).data('update_url');
if (update_url=="") { //Probably "clear selection" has been chosen
// console.log('Trying to disable');
targetObject.attr('disabled', true);
} else {
targetObject.attr('href', update_url);
targetObject.attr('disabled', false);
}
}
if (update_url == "") { //Probably "clear selection" has been chosen
// console.log('Trying to disable');
targetObject.attr('disabled', true);
} else {
targetObject.attr('href', update_url);
targetObject.attr('disabled', false);
}
}
$(".selectpicker").each(function() {
var options = {
ajax: {
url: $(this).data('sourceurl'),
type: 'GET',
dataType: 'json',
// Use "{{{q}}}" as a placeholder and Ajax Bootstrap Select will
// automatically replace it with the value of the search query.
data: {
term: '{{{q}}}'
}
},
locale: {
emptyTitle: ''
},
clearOnEmpty:false,
//log: 3,
preprocessData: function (data) {
var i, l = data.length, array = [];
array.push({
text: clearSelectionLabel,
value: '',
data:{
update_url: '',
subtext:''
}
});
$(".selectpicker").each(function () {
if (l) {
for(i = 0; i < l; i++){
array.push($.extend(true, data[i], {
text: data[i]['label'],
value: data[i]['pk'],
data:{
update_url: data[i]['update'],
subtext:''
}
}));
}
}
return array;
var options = {
ajax: {
url: $(this).data('sourceurl'),
type: 'GET',
dataType: 'json',
// Use "{{{q}}}" as a placeholder and Ajax Bootstrap Select will
// automatically replace it with the value of the search query.
data: {
term: '{{{q}}}'
}
},
locale: {
emptyTitle: ''
},
clearOnEmpty: false,
//log: 3,
preprocessData: function (data) {
var i, l = data.length, array = [];
array.push({
text: clearSelectionLabel,
value: '',
data: {
update_url: '',
subtext: ''
}
});
if (l) {
for (i = 0; i < l; i++) {
array.push($.extend(true, data[i], {
text: data[i]['label'],
value: data[i]['pk'],
data: {
update_url: data[i]['update'],
subtext: ''
}
}));
}
}
return array;
}
};
$(this).prepend($("<option></option>")
.attr("value",'')
.text(clearSelectionLabel)
.data('update_url','')); //Add "clear selection" option
.attr("value", '')
.text(clearSelectionLabel)
.data('update_url', '')); //Add "clear selection" option
$(this).selectpicker().ajaxSelectPicker(options); //Initiaise selectPicker
$(this).change(function(){ //on change, update the edit button href
// console.log('Selectbox Changed');
refreshUpdateHref($(this));
});
$(this).selectpicker().ajaxSelectPicker(options); //Initiaise selectPicker
refreshUpdateHref($(this)); //Ensure href is correct at the beginning
});
//When update/edit modal box submitted
$('#modal').on('hide.bs.modal', function (e) {
if (modaltarget != undefined && modalobject != "") {
//Update the selector with new values
changeSelectedValue($(modaltarget),modalobject[0]['pk'],modalobject[0]['fields']['name'],modalobject[0]['update_url']);
}
$(this).change(function () { //on change, update the edit button href
// console.log('Selectbox Changed');
refreshUpdateHref($(this));
});
});
refreshUpdateHref($(this)); //Ensure href is correct at the beginning
});
//When update/edit modal box submitted
$('#modal').on('hide.bs.modal', function (e) {
if (modaltarget != undefined && modalobject != "") {
//Update the selector with new values
changeSelectedValue($(modaltarget), modalobject[0]['pk'], modalobject[0]['fields']['name'], modalobject[0]['update_url']);
}
});
});

View File

@@ -1,139 +1,139 @@
function setupItemTable(items_json) {
objectitems = JSON.parse(items_json)
$.each(objectitems, function (key, val) {
objectitems[key] = JSON.parse(val);
})
newitem = -1;
objectitems = JSON.parse(items_json);
$.each(objectitems, function (key, val) {
objectitems[key] = JSON.parse(val);
});
newitem = -1;
}
function nl2br (str, is_xhtml) {
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
function nl2br(str, is_xhtml) {
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
}
function escapeHtml (str) {
return $('<div/>').text(str).html();
function escapeHtml(str) {
return $('<div/>').text(str).html();
}
function updatePrices() {
// individual rows
var sum = 0;
for (var pk in objectitems) {
var fields = objectitems[pk].fields;
var sub = fields.cost * fields.quantity;
$('#item-' + pk + ' .sub-total').html(parseFloat(sub).toFixed(2)).data('subtotal', sub);
// individual rows
var sum = 0;
for (var pk in objectitems) {
var fields = objectitems[pk].fields;
var sub = fields.cost * fields.quantity;
$('#item-' + pk + ' .sub-total').html(parseFloat(sub).toFixed(2)).data('subtotal', sub);
sum += Number(sub);
}
sum += Number(sub);
}
$('#sumtotal').text(parseFloat(sum).toFixed(2));
var vat = sum * Number($('#vat-rate').data('rate'));
$('#vat').text(parseFloat(vat).toFixed(2));
$('#total').text(parseFloat(sum + vat).toFixed(2));
$('#sumtotal').text(parseFloat(sum).toFixed(2));
var vat = sum * Number($('#vat-rate').data('rate'));
$('#vat').text(parseFloat(vat).toFixed(2));
$('#total').text(parseFloat(sum + vat).toFixed(2));
}
$('#item-table').on('click', '.item-delete', function () {
delete objectitems[$(this).data('pk')]
$('#item-' + $(this).data('pk')).remove();
updatePrices();
delete objectitems[$(this).data('pk')];
$('#item-' + $(this).data('pk')).remove();
updatePrices();
});
$('#item-table').on('click', '.item-add', function () {
$('#item-form').data('pk', newitem);
$('#item-form').data('pk', newitem);
// Set the form values
$('#item_name').val('');
$('#item_description').val('');
$('#item_quantity').val('');
$('#item_cost').val('');
// Set the form values
$('#item_name').val('');
$('#item_description').val('');
$('#item_quantity').val('');
$('#item_cost').val('');
$($(this).data('target')).modal('show');
$($(this).data('target')).modal('show');
});
$('#item-table').on('click', '.item-edit', function () {
// set the pk as we will need this later
var pk = $(this).data('pk');
$('#item-form').data('pk', pk);
// set the pk as we will need this later
var pk = $(this).data('pk');
$('#item-form').data('pk', pk);
// Set the form values
var fields = objectitems[pk].fields;
$('#item_name').val(fields.name);
$('#item_description').val(fields.description);
$('#item_quantity').val(fields.quantity);
$('#item_cost').val(fields.cost);
// Set the form values
var fields = objectitems[pk].fields;
$('#item_name').val(fields.name);
$('#item_description').val(fields.description);
$('#item_quantity').val(fields.quantity);
$('#item_cost').val(fields.cost);
$($(this).data('target')).modal('show');
$($(this).data('target')).modal('show');
});
$('body').on('submit', '#item-form', function (e) {
e.preventDefault();
var pk = $(this).data('pk');
$('#itemModal').modal('hide');
e.preventDefault();
var pk = $(this).data('pk');
$('#itemModal').modal('hide');
var fields;
if (pk == newitem--) {
// Create the new data structure and add it on.
fields = new Object();
fields['name'] = $('#item_name').val()
fields['description'] = $('#item_description').val();
fields['cost'] = $('#item_cost').val();
fields['quantity'] = $('#item_quantity').val();
var order = 0;
for (item in objectitems) {
order++;
}
fields['order'] = order;
objectitems[pk] = new Object();
objectitems[pk]['fields'] = fields;
// Add the new table
$('#new-item-row').clone().attr('id', 'item-' + pk).data('pk', pk).appendTo('#item-table-body');
$('#item-'+pk+' .item-delete, #item-'+pk+' .item-edit').data('pk', pk)
} else {
// Existing item
// update data structure
fields = objectitems[pk].fields;
fields.name = $('#item_name').val()
fields.description = $('#item_description').val();
fields.cost = $('#item_cost').val();
fields.quantity = $('#item_quantity').val();
objectitems[pk].fields = fields;
var fields;
if (pk == newitem--) {
// Create the new data structure and add it on.
fields = {};
fields['name'] = $('#item_name').val();
fields['description'] = $('#item_description').val();
fields['cost'] = $('#item_cost').val();
fields['quantity'] = $('#item_quantity').val();
var order = 0;
for (item in objectitems) {
order++;
}
// update the table
$row = $('#item-' + pk);
$row.find('.name').html(escapeHtml(fields.name));
$row.find('.description').html(nl2br(escapeHtml(fields.description)));
$row.find('.cost').html(parseFloat(fields.cost).toFixed(2));
$row.find('.quantity').html(fields.quantity);
updatePrices();
fields['order'] = order;
objectitems[pk] = {};
objectitems[pk]['fields'] = fields;
// Add the new table
$('#new-item-row').clone().attr('id', 'item-' + pk).data('pk', pk).appendTo('#item-table-body');
$('#item-' + pk + ' .item-delete, #item-' + pk + ' .item-edit').data('pk', pk)
} else {
// Existing item
// update data structure
fields = objectitems[pk].fields;
fields.name = $('#item_name').val();
fields.description = $('#item_description').val();
fields.cost = $('#item_cost').val();
fields.quantity = $('#item_quantity').val();
objectitems[pk].fields = fields;
}
// update the table
$row = $('#item-' + pk);
$row.find('.name').html(escapeHtml(fields.name));
$row.find('.description').html(nl2br(escapeHtml(fields.description)));
$row.find('.cost').html(parseFloat(fields.cost).toFixed(2));
$row.find('.quantity').html(fields.quantity);
updatePrices();
});
$('body').on('submit', '.itemised_form', function (e) {
$('#id_items_json').val(JSON.stringify(objectitems));
$('#id_items_json').val(JSON.stringify(objectitems));
});
// Return a helper with preserved width of cells
var fixHelper = function (e, ui) {
ui.children().each(function () {
$(this).width($(this).width());
});
return ui;
ui.children().each(function () {
$(this).width($(this).width());
});
return ui;
};
$("#item-table tbody").sortable({
helper: fixHelper,
update: function (e, ui) {
info = $(this).sortable("toArray");
itemorder = new Array();
$.each(info, function (key, value) {
pk = $('#' + value).data('pk');
objectitems[pk].fields.order = key;
});
helper: fixHelper,
update: function (e, ui) {
info = $(this).sortable("toArray");
itemorder = [];
$.each(info, function (key, value) {
pk = $('#' + value).data('pk');
objectitems[pk].fields.order = key;
});
}
});
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,6 @@
@import "jq-ui-bootstrap/_autocomplete";
@import "jq-ui-bootstrap/_menu";
@import "jq-ui-bootstrap/_tooltip";
@import "compass/css3/animation";
@import "compass/css3/transform";
@@ -90,60 +89,62 @@ ins {
margin: 30px auto 0;
.circle {
background-color: rgba(0,0,0,0);
border: 5px solid rgba(0,183,229,0.9);
opacity: .9;
border-right: 5px solid rgba(0,0,0,0);
border-left: 5px solid rgba(0,0,0,0);
border-radius: 50px;
box-shadow: 0 0 35px #2187e7;
width: 50px;
height: 50px;
margin: 0 auto;
@include animation(spinPulse 1s infinite ease-in-out);
background-color: rgba(0, 0, 0, 0);
border: 5px solid rgba(0, 183, 229, 0.9);
opacity: .9;
border-right: 5px solid rgba(0, 0, 0, 0);
border-left: 5px solid rgba(0, 0, 0, 0);
border-radius: 50px;
box-shadow: 0 0 35px #2187e7;
width: 50px;
height: 50px;
margin: 0 auto;
@include animation(spinPulse 1s infinite ease-in-out);
}
.circle1 {
background-color: rgba(0,0,0,0);
border: 5px solid rgba(0,183,229,0.9);
opacity: .9;
border-left: 5px solid rgba(0,0,0,0);
border-right: 5px solid rgba(0,0,0,0);
border-radius: 50px;
box-shadow: 0 0 15px #2187e7;
width: 30px;
height: 30px;
margin: 0 auto;
position: relative;
top: -40px;
@include animation(spinoffPulse 1s infinite linear);
background-color: rgba(0, 0, 0, 0);
border: 5px solid rgba(0, 183, 229, 0.9);
opacity: .9;
border-left: 5px solid rgba(0, 0, 0, 0);
border-right: 5px solid rgba(0, 0, 0, 0);
border-radius: 50px;
box-shadow: 0 0 15px #2187e7;
width: 30px;
height: 30px;
margin: 0 auto;
position: relative;
top: -40px;
@include animation(spinoffPulse 1s infinite linear);
}
@include keyframes(spinPulse) {
0% {
@include rotate(160deg);
opacity: 0;
box-shadow: 0 0 1px #2187e7;
}
0% {
@include rotate(160deg);
opacity: 0;
box-shadow: 0 0 1px #2187e7;
}
50% {
@include rotate(145deg);
opacity: 1;
}
50% {
@include rotate(145deg);
opacity: 1;
}
100% {
@include rotate(-320deg);
opacity: 0;
};
100% {
@include rotate(-320deg);
opacity: 0;
}
;
}
@include keyframes(spinoffPulse) {
0% {
@include rotate(0deg);
}
0% {
@include rotate(0deg);
}
100% {
@include rotate(360deg);
};
100% {
@include rotate(360deg);
}
;
}
}

View File

@@ -1,66 +1,66 @@
{% load static %}
{% block js %}
<script src="{% static "js/tooltip.js" %}"></script>
<script src="{% static "js/popover.js" %}"></script>
<script src="{% static "js/moment.min.js" %}"></script>
<script src="{% static "js/moment-twitter.js" %}"></script>
<script>
$(function () {
$('[data-toggle="popover"]').popover().click(function(){
if($(this).attr('href')){
window.location.href = $(this).attr('href');
}
});
<script src="{% static "js/tooltip.js" %}"></script>
<script src="{% static "js/popover.js" %}"></script>
<script src="{% static "js/moment.min.js" %}"></script>
<script src="{% static "js/moment-twitter.js" %}"></script>
<script>
$(function () {
$('[data-toggle="popover"]').popover().click(function () {
if ($(this).attr('href')) {
window.location.href = $(this).attr('href');
}
});
// This keeps timeago values correct, but uses an insane amount of resources
// $(function () {
// setInterval(function() {
// $('.date').each(function (index, dateElem) {
// var $dateElem = $(dateElem);
// var formatted = moment($dateElem.attr('data-date')).fromNow();
// $dateElem.text(formatted);
// })
// });
// }, 10000);
moment().twitter();
})
$(document).ready(function() {
$(function () {
$( "#activity" ).hide();
$( "#activity" ).load( "{% url 'activity_feed' %}", function() {
$('#activity_loading').slideUp('slow',function(){
$('#activity').slideDown('slow');
});
$('#activity [data-toggle="popover"]').popover();
$('.date').each(function (index, dateElem) {
var $dateElem = $(dateElem);
var formatted = moment($dateElem.attr('data-date')).twitterLong();
$dateElem.text(formatted);
});
// This keeps timeago values correct, but uses an insane amount of resources
// $(function () {
// setInterval(function() {
// $('.date').each(function (index, dateElem) {
// var $dateElem = $(dateElem);
// var formatted = moment($dateElem.attr('data-date')).fromNow();
// $dateElem.text(formatted);
// })
// });
// }, 10000);
moment().twitter();
});
});
});
</script>
$(document).ready(function () {
$(function () {
$("#activity").hide();
$("#activity").load("{% url 'activity_feed' %}", function () {
$('#activity_loading').slideUp('slow', function () {
$('#activity').slideDown('slow');
});
$('#activity [data-toggle="popover"]').popover();
$('.date').each(function (index, dateElem) {
var $dateElem = $(dateElem);
var formatted = moment($dateElem.attr('data-date')).twitterLong();
$dateElem.text(formatted);
});
});
});
});
</script>
{% endblock %}
<div class="panel panel-default">
<div class="panel-heading">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Recent Changes</h4>
</div>
<div class="list-group">
</div>
<div class="list-group">
<div id="activity_loading" class="list-group-item loading-animation">
<div class="circle"></div>
<div class="circle1"></div>
<div class="circle"></div>
<div class="circle1"></div>
</div>
<div id="activity">
</div>
</div>
</div>
</div>

View File

@@ -5,50 +5,52 @@
{% load to_class_name from filters %}
{% block content %}
<div class="list-group-item">
<div class="media">
{% for version in object_list %}
<div class="media">
{% for version in object_list %}
{% if not version.withPrevious %}
{% if not forloop.first %}
</div> {#/.media-body#}
</div> {#/.media#}
</div>
<div class="list-group-item">
<div class="media">
{% endif %}
<div class="media-left">
{% if not forloop.first %}
</div> {#/.media-body#}
</div> {#/.media#}
</div>
<div class="list-group-item">
<div class="media">
{% endif %}
<div class="media-left">
{% if version.revision.user %}
<a href="{% url 'profile_detail' pk=version.revision.user.pk %}" class="modal-href">
<img class="media-object img-rounded" src="{{ version.revision.user.profile_picture}}" />
</a>
<a href="{% url 'profile_detail' pk=version.revision.user.pk %}" class="modal-href">
<img class="media-object img-rounded"
src="{{ version.revision.user.profile_picture }}"/>
</a>
{% endif %}
</div>
<div class="media-body">
<h5>{{ version.revision.user.name }}
<span class="pull-right"><small><span class="date" data-date="{{version.revision.date_created|date:"c"}}"></span></small></span>
</h5>
</div>
<div class="media-body">
<h5>{{ version.revision.user.name }}
<span class="pull-right"><small><span class="date"
data-date="{{ version.revision.date_created|date:"c" }}"></span></small></span>
</h5>
{% endif %}
<p>
<small>
<p>
<small>
{% if version.old == None %}
Created
Created
{% else %}
Changed {% include 'RIGS/version_changes.html' %} in
Changed {% include 'RIGS/version_changes.html' %} in
{% endif %}
{% include 'RIGS/object_button.html' with object=version.new %}
{% if version.revision.comment %}
({{ version.revision.comment }})
({{ version.revision.comment }})
{% endif %}
</small>
</p>
</small>
</p>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
{% endfor %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -10,39 +10,39 @@
<script src="{% static "js/popover.js" %}"></script>
<script src="{% static "js/moment.min.js" %}"></script>
<script>
$(function () {
$('[data-toggle="popover"]').popover().click(function(){
if($(this).attr('href')){
window.location.href = $(this).attr('href');
}
});
$(function () {
$('[data-toggle="popover"]').popover().click(function () {
if ($(this).attr('href')) {
window.location.href = $(this).attr('href');
}
});
// This keeps timeago values correct, but uses an insane amount of resources
// $(function () {
// setInterval(function() {
// $('.date').each(function (index, dateElem) {
// var $dateElem = $(dateElem);
// var formatted = moment($dateElem.attr('data-date')).fromNow();
// $dateElem.text(formatted);
// })
// });
// }, 10000);
// This keeps timeago values correct, but uses an insane amount of resources
// $(function () {
// setInterval(function() {
// $('.date').each(function (index, dateElem) {
// var $dateElem = $(dateElem);
// var formatted = moment($dateElem.attr('data-date')).fromNow();
// $dateElem.text(formatted);
// })
// });
// }, 10000);
$('.date').each(function (index, dateElem) {
var $dateElem = $(dateElem);
var formatted = moment($dateElem.attr('data-date')).fromNow();
$dateElem.text(formatted);
});
});
})
})
</script>
{% endblock %}
{% block content %}
<div class="col-sm-12">
<div class="col-sm-12">
<div class="row">
<div class="col-sm-12">
<h3>Rigboard Activity Stream</h3>
@@ -51,7 +51,7 @@
</div>
<div class="table-responsive">
<table class="table table-striped">
<table class="table table-striped">
<thead>
<tr>
<td>Date</td>
@@ -63,23 +63,25 @@
</tr>
</thead>
<tbody>
{% for version in object_list %}
<tr>
<td>{{ version.revision.date_created }}</td>
<td><a href="{{ version.new.get_absolute_url }}">{{version.new|to_class_name}} {{ version.new.pk|stringformat:"05d" }}</a></td>
<td>{{ version.version.pk }}|{{ version.revision.pk }}</td>
<td>{{ version.revision.user.name }}</td>
<td>
{% if version.old == None %}
{{version.new|to_class_name}} Created
{% else %}
{% include 'RIGS/version_changes.html' %}
{% endif %} </td>
<td>{{ version.revision.comment }}</td>
</tr>
{% endfor %}
{% for version in object_list %}
<tr>
<td>{{ version.revision.date_created }}</td>
<td>
<a href="{{ version.new.get_absolute_url }}">{{ version.new|to_class_name }} {{ version.new.pk|stringformat:"05d" }}</a>
</td>
<td>{{ version.version.pk }}|{{ version.revision.pk }}</td>
<td>{{ version.revision.user.name }}</td>
<td>
{% if version.old == None %}
{{ version.new|to_class_name }} Created
{% else %}
{% include 'RIGS/version_changes.html' %}
{% endif %} </td>
<td>{{ version.revision.comment }}</td>
</tr>
{% endfor %}
</tbody>
</table>
@@ -87,4 +89,4 @@
</div>
<div class="align-right">{% paginator %}</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -2,39 +2,40 @@
{% load i18n l10n %}
{% block content %}
<form action="" method="post">{% csrf_token %}
<p>The following objects will be merged. Please select the 'master' record which you would like to keep. Other records will have associated events moved to the 'master' copy, and then will be deleted.</p>
<form action="" method="post">{% csrf_token %}
<p>The following objects will be merged. Please select the 'master' record which you would like to keep. Other
records will have associated events moved to the 'master' copy, and then will be deleted.</p>
<table>
{% for form in forms %}
{% if forloop.first %}
<tr>
<th></th>
<th> ID </th>
{% for field in form %}
<th>{{ field.label }}</th>
{% endfor %}
</tr>
{% endif %}
<tr>
<td><input type="radio" name="master" value="{{form.instance.pk|unlocalize}}"></td>
<td>{{form.instance.pk}}</td>
{% for field in form %}
<td> {{ field.value }} </td>
<table>
{% for form in forms %}
{% if forloop.first %}
<tr>
<th></th>
<th> ID</th>
{% for field in form %}
<th>{{ field.label }}</th>
{% endfor %}
</tr>
{% endif %}
<tr>
<td><input type="radio" name="master" value="{{ form.instance.pk|unlocalize }}"></td>
<td>{{ form.instance.pk }}</td>
{% for field in form %}
<td> {{ field.value }} </td>
{% endfor %}
</tr>
{% endfor %}
</tr>
{% endfor %}
</table>
</table>
<div>
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
{% endfor %}
<input type="hidden" name="action" value="merge" />
<input type="hidden" name="post" value="yes" />
<input type="submit" value="Merge them" />
</div>
</form>
{% endblock %}
<div>
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}"/>
{% endfor %}
<input type="hidden" name="action" value="merge"/>
<input type="hidden" name="post" value="yes"/>
<input type="submit" value="Merge them"/>
</div>
</form>
{% endblock %}

View File

@@ -5,8 +5,8 @@
{% block title %}Calendar{% endblock %}
{% block css %}
<link href="{% static "css/fullcalendar.css" %}" rel='stylesheet' />
<link href="{% static "css/fullcalendar.print.css" %}" rel='stylesheet' media='print' />
<link href="{% static "css/fullcalendar.css" %}" rel='stylesheet'/>
<link href="{% static "css/fullcalendar.print.css" %}" rel='stylesheet' media='print'/>
{% endblock %}
{% block js %}
@@ -17,24 +17,24 @@
function getUrlVars() {
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
vars[key] = value;
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
vars[key] = value;
});
return vars;
}
$(document).ready(function() {
$(document).ready(function () {
viewToUrl = {
'agendaWeek':'week',
'agendaDay':'day',
'month':'month'
}
'agendaWeek': 'week',
'agendaDay': 'day',
'month': 'month'
};
viewFromUrl = {
'week':'agendaWeek',
'day':'agendaDay',
'month':'month'
}
'week': 'agendaWeek',
'day': 'agendaDay',
'month': 'month'
};
$('#calendar').fullCalendar({
editable: false,
@@ -50,16 +50,16 @@
// options apply to agendaWeek and agendaDay views
},
week: {
columnFormat:'ddd D/M'
columnFormat: 'ddd D/M'
},
day: {
// options apply to basicDay and agendaDay views
}
},
header:false,
header: false,
events: function (start_moment, end_moment, timezone, callback) {
events: function(start_moment, end_moment, timezone, callback) {
$.ajax({
url: '/api/event',
dataType: 'json',
@@ -67,18 +67,19 @@
start: moment(start_moment).format("YYYY-MM-DD[T]HH:mm:ss"),
end: moment(end_moment).format("YYYY-MM-DD[T]HH:mm:ss")
},
success: function(doc) {
success: function (doc) {
var events = [];
colours = {'Provisional': '#f0ad4e',
'Confirmed': '#5cb85c' ,
'Booked': '#5cb85c' ,
'Cancelled': 'grey' ,
colours = {
'Provisional': '#f0ad4e',
'Confirmed': '#5cb85c',
'Booked': '#5cb85c',
'Cancelled': 'grey',
'non-rig': '#5bc0de'
};
$(doc).each(function() {
end = $(this).attr('latest')
if(end.indexOf("T") < 0){ //If latest does not contain a time
end = moment(end).add(1, 'days') //End date is non-inclusive, so add a day
$(doc).each(function () {
end = $(this).attr('latest');
if (end.indexOf("T") < 0) { //If latest does not contain a time
end = moment(end).add(1, 'days'); //End date is non-inclusive, so add a day
}
thisEvent = {
@@ -87,14 +88,14 @@
'className': 'modal-href',
'title': $(this).attr('title'),
'url': $(this).attr('url')
}
if($(this).attr('is_rig')==true || $(this).attr('status') == "Cancelled"){
};
if ($(this).attr('is_rig') == true || $(this).attr('status') == "Cancelled") {
thisEvent['color'] = colours[$(this).attr('status')];
}else{
} else {
thisEvent['color'] = colours['non-rig'];
}
events.push(thisEvent);
});
callback(events);
@@ -102,20 +103,20 @@
});
},
viewRender: function(view, element){
viewRender: function (view, element) {
// Set the title of the view
$('#calendar-header').text(view.title);
// Enable/Disable "Today" button as required
if(moment().isBetween(view.intervalStart, view.intervalEnd)){
if (moment().isBetween(view.intervalStart, view.intervalEnd)) {
//Today is within the current view
$('#today-button').prop('disabled', true);
}else{
} else {
$('#today-button').prop('disabled', false);
}
// Set active view select button
switch(view.name){
switch (view.name) {
case 'month':
$('#month-button').addClass('active');
$('#week-button').removeClass('active');
@@ -134,53 +135,64 @@
$('#day-button').addClass('active');
break;
}
history.replaceState(null,null,'{% url 'web_calendar' %}'+viewToUrl[view.name]+'/'+view.intervalStart.format('YYYY-MM-DD')+'/');
history.replaceState(null, null, '{% url 'web_calendar' %}' + viewToUrl[view.name] + '/' + view.intervalStart.format('YYYY-MM-DD') + '/');
}
});
// set some button listeners
$('#next-button').click(function(){ $('#calendar').fullCalendar('next') });
$('#prev-button').click(function(){ $('#calendar').fullCalendar('prev') });
$('#today-button').click(function(){ $('#calendar').fullCalendar('today') });
$('#next-button').click(function () {
$('#calendar').fullCalendar('next')
});
$('#prev-button').click(function () {
$('#calendar').fullCalendar('prev')
});
$('#today-button').click(function () {
$('#calendar').fullCalendar('today')
});
$('#month-button').click(function(){ $('#calendar').fullCalendar('changeView','month') });
$('#week-button').click(function(){ $('#calendar').fullCalendar('changeView','agendaWeek') });
$('#day-button').click(function(){ $('#calendar').fullCalendar('changeView','agendaDay') });
$('#month-button').click(function () {
$('#calendar').fullCalendar('changeView', 'month')
});
$('#week-button').click(function () {
$('#calendar').fullCalendar('changeView', 'agendaWeek')
});
$('#day-button').click(function () {
$('#calendar').fullCalendar('changeView', 'agendaDay')
});
$('#go-to-date-input').change(function(){
if( moment($('#go-to-date-input').val()).isValid() ){
$('#go-to-date-input').change(function () {
if (moment($('#go-to-date-input').val()).isValid()) {
$('#go-to-date-button').prop('disabled', false);
}else{
} else {
$('#go-to-date-button').prop('disabled', true);
}
});
$('#go-to-date-button').click(function(){
day = moment($('#go-to-date-input').val()) ;
if(day.isValid()){
$('#calendar').fullCalendar( 'gotoDate', day);
}else{
$('#go-to-date-button').click(function () {
day = moment($('#go-to-date-input').val());
if (day.isValid()) {
$('#calendar').fullCalendar('gotoDate', day);
} else {
alert('Invalid Date');
}
});
// Go to the initial settings, if they're valid
view = viewFromUrl['{{view}}'];
$('#calendar').fullCalendar( 'changeView', view);
$('#calendar').fullCalendar('changeView', view);
day = moment('{{date}}');
if(day.isValid()){
$('#calendar').fullCalendar( 'gotoDate', day);
}else{
if (day.isValid()) {
$('#calendar').fullCalendar('gotoDate', day);
} else {
console.log('Supplied date is invalid - using default')
}
});
</script>
@@ -189,7 +201,7 @@
{% block content %}
<div class="row">
<div class="col-sm-12">
<div class="pull-left">
<span id="calendar-header" class="h2"></span>
@@ -197,33 +209,35 @@
<div class="form-inline pull-right btn-page">
<div class="input-group">
<input type="date" class="form-control" id="go-to-date-input" placeholder="Go to date...">
<span class="input-group-btn">
<input type="date" class="form-control" id="go-to-date-input" placeholder="Go to date...">
<span class="input-group-btn">
<button class="btn btn-default" id="go-to-date-button" type="button" disabled>Go!</button>
</span>
</div>
<div class="btn-group">
<button type="button" class="btn btn-primary" id="today-button">Today</button>
<button type="button" class="btn btn-primary" id="today-button">Today</button>
</div>
<div class="btn-group">
<button type="button" class="btn btn-default" id="prev-button"><span class="glyphicon glyphicon-chevron-left"></span></button>
<button type="button" class="btn btn-default" id="next-button"><span class="glyphicon glyphicon-chevron-right"></span></button>
<button type="button" class="btn btn-default" id="prev-button"><span
class="glyphicon glyphicon-chevron-left"></span></button>
<button type="button" class="btn btn-default" id="next-button"><span
class="glyphicon glyphicon-chevron-right"></span></button>
</div>
<div class="btn-group">
<button type="button" class="btn btn-default" id="month-button">Month</button>
<button type="button" class="btn btn-default" id="week-button">Week</button>
<button type="button" class="btn btn-default" id="day-button">Day</button>
<button type="button" class="btn btn-default" id="month-button">Month</button>
<button type="button" class="btn btn-default" id="week-button">Week</button>
<button type="button" class="btn btn-default" id="day-button">Day</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div id='calendar'>
</div>
<div class="col-sm-12">
<div id='calendar'>
</div>
{% endblock %}
</div>
{% endblock %}

View File

@@ -11,14 +11,17 @@
<form class="form-inline">
<div class="form-group">
<label for="start">Start</label>
<input type="date" name="start" id="start" value="{{ request.GET.start }}" placeholder="Start" class="form-control" />
<input type="date" name="start" id="start" value="{{ request.GET.start }}" placeholder="Start"
class="form-control"/>
</div>
<div class="form-group">
<label for="end">End</label>
<input type="date" name="end" id="end" value="{% if request.GET.end %}{{ request.GET.end }}{% else %}{% now "Y-m-d" %}{% endif %}" placeholder="End" class="form-control" />
<input type="date" name="end" id="end"
value="{% if request.GET.end %}{{ request.GET.end }}{% else %}{% now "Y-m-d" %}{% endif %}"
placeholder="End" class="form-control"/>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" />
<input type="submit" class="btn btn-primary"/>
</div>
</form>
</div>
@@ -42,4 +45,4 @@
</div>
</div>
{% endif %}
{% endblock %}
{% endblock %}

View File

@@ -1,31 +1,32 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %}
{% block title %}{% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %} | {{object.name}}{% endblock %}
{% block title %}{% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %} |
{{ object.name }}{% endblock %}
{% block content %}
<div class="row">
{% if not request.is_ajax %}
<div class="col-sm-12">
<h1>
{% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %}
| {{ object.name }} {% if event.dry_hire %}<span class="badge">Dry Hire</span>{% endif %}
</h1>
</div>
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-edit"></span> <span
<div class="row">
{% if not request.is_ajax %}
<div class="col-sm-12">
<h1>
{% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %}
| {{ object.name }} {% if event.dry_hire %}<span class="badge">Dry Hire</span>{% endif %}
</h1>
</div>
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-edit"></span> <span
class="hidden-xs">Edit</span></a>
{% if event.is_rig %}
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
class="glyphicon glyphicon-print"></span> <span
class="hidden-xs">Print</span></a>
{% endif %}
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
class="glyphicon glyphicon-duplicate"></span> <span
{% if event.is_rig %}
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
class="glyphicon glyphicon-print"></span> <span
class="hidden-xs">Print</span></a>
{% endif %}
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
class="glyphicon glyphicon-duplicate"></span> <span
class="hidden-xs">Duplicate</span></a>
{% if event.is_rig %}
{% if perms.RIGS.add_invoice %}
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
{% if event.is_rig %}
{% if perms.RIGS.add_invoice %}
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
{% if event.invoice and event.invoice.is_closed %}
btn-success
{% elif event.invoice %}
@@ -34,171 +35,180 @@
btn-danger
{% endif %}
" title="Invoice Rig"><span
class="glyphicon glyphicon-gbp"></span>
class="glyphicon glyphicon-gbp"></span>
<span class="hidden-xs">Invoice</span></a>
{% endif %}
{% endif %}
{% endif %}
</div>
</div>
{% endif %}
{% if object.is_rig %}
{# only need contact details for a rig #}
<div class="col-sm-12 col-md-6 col-lg-5">
<div class="panel panel-default">
<div class="panel-heading">Contact Details</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>Person</dt>
<dd>
{% if object.person %}
<a href="{% url 'person_detail' object.person.pk %}" class="modal-href">
{{ object.person }}
</a>
{% endif %}
</dd>
<dt>Email</dt>
<dd>
<a href="mailto:{{object.person.email}}" target="_blank">
<span class="overflow-ellipsis">{{ object.person.email }}</span>
</a>
</dd>
<dt>Phone Number</dt>
<dd><a href="tel:{{object.person.phone}}">{{ object.person.phone }}</a></dd>
</dl>
</div>
</div>
{% if event.organisation %}
{% endif %}
{% if object.is_rig %}
{# only need contact details for a rig #}
<div class="col-sm-12 col-md-6 col-lg-5">
<div class="panel panel-default">
<div class="panel-heading">Organisation</div>
<div class="panel-heading">Contact Details</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>Organisation</dt>
<dt>Person</dt>
<dd>
{% if object.organisation %}
<a href="{% url 'organisation_detail' object.organisation.pk %}" class="modal-href">
{{ object.organisation }}
{% if object.person %}
<a href="{% url 'person_detail' object.person.pk %}" class="modal-href">
{{ object.person }}
</a>
{% endif %}
</dd>
<dt>Phone Number</dt>
<dt>Email</dt>
<dd>
<a href="tel:{{object.person.phone}}">
{{ object.organisation.phone }}
<a href="mailto:{{ object.person.email }}" target="_blank">
<span class="overflow-ellipsis">{{ object.person.email }}</span>
</a>
</dd>
<dt>Has SU Account</dt>
<dd>{{ event.organisation.union_account|yesno|capfirst }}</dd>
<dt>Phone Number</dt>
<dd><a href="tel:{{ object.person.phone }}">{{ object.person.phone }}</a></dd>
</dl>
</div>
</div>
{% endif %}
</div>
{% endif %}
<div class="col-sm-12 {% if event.is_rig %}col-md-6 col-lg-7{% endif %}">
<div class="panel panel-info">
<div class="panel-heading">Event Info</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>Event Venue</dt>
<dd>
{% if object.venue %}
<a href="{% url 'venue_detail' object.venue.pk %}" class="modal-href">
{{ object.venue }}
</a>
{% endif %}
</dd>
{% if event.organisation %}
<div class="panel panel-default">
<div class="panel-heading">Organisation</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>Organisation</dt>
<dd>
{% if object.organisation %}
<a href="{% url 'organisation_detail' object.organisation.pk %}"
class="modal-href">
{{ object.organisation }}
</a>
{% endif %}
</dd>
{% if event.is_rig %}
<dt>Event MIC</dt>
<dt>Phone Number</dt>
<dd>
<a href="tel:{{ object.person.phone }}">
{{ object.organisation.phone }}
</a>
</dd>
<dt>Has SU Account</dt>
<dd>{{ event.organisation.union_account|yesno|capfirst }}</dd>
</dl>
</div>
</div>
{% endif %}
</div>
{% endif %}
<div class="col-sm-12 {% if event.is_rig %}col-md-6 col-lg-7{% endif %}">
<div class="panel panel-info">
<div class="panel-heading">Event Info</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>Event Venue</dt>
<dd>
{% if event.mic and perms.RIGS.view_profile %}
<a href="{% url 'profile_detail' event.mic.pk %}" class="modal-href">
{{ event.mic.name }}
{% if object.venue %}
<a href="{% url 'venue_detail' object.venue.pk %}" class="modal-href">
{{ object.venue }}
</a>
{% else %}
{{ event.mic.name }}
{% endif %}
</dd>
{% endif %}
<dt>Status</dt>
<dd>{{ event.get_status_display }}</dd>
<dd>&nbsp;</dd>
{% if event.is_rig %}
<dt>Crew Meet</dt>
<dd>{{ event.meet_at|date:"D d M Y H:i"|default:"" }}</dd>
<dd>{{ event.meet_info|default:"" }}</dd>
<dt>Access From</dt>
<dd>{{ event.access_at|date:"D d M Y H:i"|default:"" }}</dd>
{% endif %}
<dt>Event Starts</dt>
<dd>{{ event.start_date|date:"D d M Y" }} {{ event.start_time|date:"H:i" }}</dd>
<dt>Event Ends</dt>
<dd>{{ event.end_date|date:"D d M Y" }} {{ event.end_time|date:"H:i" }}</dd>
<dd>&nbsp;</dd>
<dt>Event Description</dt>
<dd>{{ event.description|linebreaksbr }}</dd>
<dd>&nbsp;</dd>
<dt>Based On</dt>
<dd>
{% if object.based_on %}
<a href="{% url 'event_detail' pk=object.based_on.pk %}">
{% if object.based_on.is_rig %}N{{ object.based_on.pk|stringformat:"05d" }}{% else %}
{{ object.based_on.pk }}{% endif %}
{{ object.based_on.name }} {% if object.based_on.mic %}by {{ object.based_on.mic.name }}{% endif %}
</a>
{% if event.is_rig %}
<dt>Event MIC</dt>
<dd>
{% if event.mic and perms.RIGS.view_profile %}
<a href="{% url 'profile_detail' event.mic.pk %}" class="modal-href">
{{ event.mic.name }}
</a>
{% else %}
{{ event.mic.name }}
{% endif %}
</dd>
{% endif %}
</dd>
{% if event.dry_hire %}
<dt>Checked In By</dt>
<dd>{{ object.checked_in_by.name }}</dd>
{% endif %}
<dt>Status</dt>
<dd>{{ event.get_status_display }}</dd>
{% if event.is_rig %}
<dt>Collected By</dt>
<dd>{{ object.collector }}</dd>
{% endif %}
<dd>&nbsp;</dd>
{% if event.is_rig %}
<dt>PO</dt>
<dd>{{ object.purchase_order }}</dd>
{% endif %}
</dl>
{% if event.is_rig %}
<dt>Crew Meet</dt>
<dd>{{ event.meet_at|date:"D d M Y H:i"|default:"" }}</dd>
<dd>{{ event.meet_info|default:"" }}</dd>
<dt>Access From</dt>
<dd>{{ event.access_at|date:"D d M Y H:i"|default:"" }}</dd>
{% endif %}
<dt>Event Starts</dt>
<dd>{{ event.start_date|date:"D d M Y" }} {{ event.start_time|date:"H:i" }}</dd>
<dt>Event Ends</dt>
<dd>{{ event.end_date|date:"D d M Y" }} {{ event.end_time|date:"H:i" }}</dd>
<dd>&nbsp;</dd>
<dt>Event Description</dt>
<dd>{{ event.description|linebreaksbr }}</dd>
<dd>&nbsp;</dd>
<dt>Based On</dt>
<dd>
{% if object.based_on %}
<a href="{% url 'event_detail' pk=object.based_on.pk %}">
{% if object.based_on.is_rig %}
N{{ object.based_on.pk|stringformat:"05d" }}
{% else %}
{{ object.based_on.pk }}
{% endif %}
{{ object.based_on.name }}
{% if object.based_on.mic %}
by {{ object.based_on.mic.name }}
{% endif %}
</a>
{% endif %}
</dd>
{% if event.dry_hire %}
<dt>Checked In By</dt>
<dd>{{ object.checked_in_by.name }}</dd>
{% endif %}
{% if event.is_rig %}
<dt>Collected By</dt>
<dd>{{ object.collector }}</dd>
{% endif %}
{% if event.is_rig %}
<dt>PO</dt>
<dd>{{ object.purchase_order }}</dd>
{% endif %}
</dl>
</div>
</div>
</div>
</div>
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-edit"></span> <span
class="hidden-xs">Edit</span></a>
{% if event.is_rig %}
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
class="glyphicon glyphicon-print"></span> <span
class="hidden-xs">Print</span></a>
{% endif %}
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
class="glyphicon glyphicon-duplicate"></span> <span
class="hidden-xs">Duplicate</span></a>
{% if event.is_rig %}
{% if perms.RIGS.add_invoice %}
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-edit"></span> <span
class="hidden-xs">Edit</span></a>
{% if event.is_rig %}
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
class="glyphicon glyphicon-print"></span> <span
class="hidden-xs">Print</span></a>
{% endif %}
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
class="glyphicon glyphicon-duplicate"></span> <span
class="hidden-xs">Duplicate</span></a>
{% if event.is_rig %}
{% if perms.RIGS.add_invoice %}
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
{% if event.invoice and event.invoice.is_closed %}
btn-success
{% elif event.invoice %}
@@ -207,43 +217,44 @@
btn-danger
{% endif %}
" title="Invoice Rig"><span
class="glyphicon glyphicon-gbp"></span>
<span class="hidden-xs">Invoice</span></a>
{% endif %}
{% endif %}
</div>
</div>
{% endif %}
{% if event.is_rig %}
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Event Details</div>
<div class="panel-body">
<div class="well well-sm">
<h4>Notes</h4>
{{ event.notes|linebreaksbr }}
</div>
{% include 'RIGS/item_table.html' %}
class="glyphicon glyphicon-gbp"></span>
<span class="hidden-xs">Invoice</span></a>
{% endif %}
{% endif %}
</div>
</div>
</div>
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-edit"></span> <span
class="hidden-xs">Edit</span></a>
{% if event.is_rig %}
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
class="glyphicon glyphicon-print"></span> <span
class="hidden-xs">Print</span></a>
{% endif %}
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default" title="Duplicate Rig"><span
class="glyphicon glyphicon-duplicate"></span> <span
class="hidden-xs">Duplicate</span></a>
{% if event.is_rig %}
{% if perms.RIGS.add_invoice %}
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
{% endif %}
{% if event.is_rig %}
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Event Details</div>
<div class="panel-body">
<div class="well well-sm">
<h4>Notes</h4>
{{ event.notes|linebreaksbr }}
</div>
{% include 'RIGS/item_table.html' %}
</div>
</div>
</div>
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'event_update' event.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-edit"></span> <span
class="hidden-xs">Edit</span></a>
{% if event.is_rig %}
<a href="{% url 'event_print' event.pk %}" target="_blank" class="btn btn-default"><span
class="glyphicon glyphicon-print"></span> <span
class="hidden-xs">Print</span></a>
{% endif %}
<a href="{% url 'event_duplicate' event.pk %}" class="btn btn-default"
title="Duplicate Rig"><span
class="glyphicon glyphicon-duplicate"></span> <span
class="hidden-xs">Duplicate</span></a>
{% if event.is_rig %}
{% if perms.RIGS.add_invoice %}
<a id="invoiceDropdownLabel" href="{% url 'invoice_event' event.pk %}" class="btn
{% if event.invoice and event.invoice.is_closed %}
btn-success
{% elif event.invoice %}
@@ -252,24 +263,24 @@
btn-danger
{% endif %}
" title="Invoice Rig"><span
class="glyphicon glyphicon-gbp"></span>
<span class="hidden-xs">Invoice</span></a>
{% endif %}
{% endif %}
</div>
</div>
class="glyphicon glyphicon-gbp"></span>
<span class="hidden-xs">Invoice</span></a>
{% endif %}
{% endif %}
</div>
</div>
{% endif %}
{% endif %}
{% endif %}
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
<div>
<a href="{% url 'event_history' object.pk %}" title="View Revision History">
Last edited at {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
</a>
{% if not request.is_ajax %}
<div class="col-sm-12 text-right">
<div>
<a href="{% url 'event_history' object.pk %}" title="View Revision History">
Last edited at {{ object.last_edited_at }} by {{ object.last_edited_by.name }}
</a>
</div>
</div>
</div>
{% endif %}
</div>
{% endif %}
</div>
{% endblock %}
{% if request.is_ajax %}

View File

@@ -32,12 +32,12 @@
var id_start = "{{ form.start_date.id_for_label }}";
var id_end_date = "{{ form.end_date.id_for_label }}";
var id_end_time = "{{ form.end_time.id_for_label }}";
if ($('#'+id_start).val() == $('#'+id_end_date).val()) {
var end_date = new Date($('#'+id_end_date).val());
if ($('#' + id_start).val() == $('#' + id_end_date).val()) {
var end_date = new Date($('#' + id_end_date).val());
end_date.setDate(end_date.getDate() + 1);
$('#'+id_end_date).val(end_date.getISOString());
$('#' + id_end_date).val(end_date.getISOString());
}
$('#'+id_end_time).val('02:00');
$('#' + id_end_time).val('02:00');
}
$(document).ready(function () {
@@ -74,33 +74,34 @@
function supportsDate() {
//return false; //for development
var input = document.createElement('input');
input.setAttribute('type','date');
input.setAttribute('type', 'date');
var notADateValue = 'not-a-date';
input.setAttribute('value', notADateValue);
input.setAttribute('value', notADateValue);
return !(input.value === notADateValue);
}
if(supportsDate()){
if (supportsDate()) {
//Good, we'll use the browser implementation
}else{
} else {
//Rubbish browser - do JQuery backup
$('<link>')
.appendTo('head')
.attr({type : 'text/css', rel : 'stylesheet'})
.attr('href', '{% static "css/bootstrap-datetimepicker.min.css" %}');
.appendTo('head')
.attr({type: 'text/css', rel: 'stylesheet'})
.attr('href', '{% static "css/bootstrap-datetimepicker.min.css" %}');
$.when(
$.getScript( "{% static "js/moment.min.js" %}" ),
$.getScript( "{% static "js/bootstrap-datetimepicker.min.js" %}" ),
$.Deferred(function( deferred ){
$( deferred.resolve );
})
).done(function(){
$('input[type=date]').attr('type','text').datetimepicker({
$.getScript("{% static "js/moment.min.js" %}"),
$.getScript("{% static "js/bootstrap-datetimepicker.min.js" %}"),
$.Deferred(function (deferred) {
$(deferred.resolve);
})
).done(function () {
$('input[type=date]').attr('type', 'text').datetimepicker({
format: 'YYYY-MM-DD',
});
$('input[type=time]').attr('type','text').datetimepicker({
$('input[type=time]').attr('type', 'text').datetimepicker({
format: 'HH:mm',
});
$('input[type=datetime-local]').attr('type','text').datetimepicker({
$('input[type=datetime-local]').attr('type', 'text').datetimepicker({
format: 'YYYY-MM-DD[T]HH:mm',
sideBySide: true,
});
@@ -113,168 +114,90 @@
setupItemTable($("#{{ form.items_json.id_for_label }}").val());
});
$(function () {
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="tooltip"]').tooltip();
})
</script>
{% endblock %}
{% block content %}
<form class="form-horizontal itemised_form" role="form" method="POST">{% csrf_token %}
<div class="row">
<div class="col-sm-12">
<div class="col-sm-8">
<h2>
{% if duplicate %}
Duplicate of Event N{{ object.pk|stringformat:"05d" }}
{% elif object.pk %}
Event N{{ object.pk|stringformat:"05d" }}
{% else %}
New Event
{% endif %}
</h2>
</div>
<div class="col-sm-4 text-right">
<div class="btn-group btn-page">
<button type="submit" class="btn btn-default" title="Save"><span
class="glyphicon glyphicon-floppy-disk"></span></button>
<div class="row">
<div class="col-sm-12">
<div class="col-sm-8">
<h2>
{% if duplicate %}
Duplicate of Event N{{ object.pk|stringformat:"05d" }}
{% elif object.pk %}
Event N{{ object.pk|stringformat:"05d" }}
{% else %}
New Event
{% endif %}
</h2>
</div>
<div class="col-sm-4 text-right">
<div class="btn-group btn-page">
<button type="submit" class="btn btn-default" title="Save"><span
class="glyphicon glyphicon-floppy-disk"></span></button>
</div>
</div>
</div>
</div>
</div>
{% include 'form_errors.html' %}
{% render_field form.is_rig style="display: none" %}
<input type="hidden" name="{{ form.items_json.name }}" id="{{ form.items_json.id_for_label }}"
value="{{ form.items_json.value }}"/>
{% include 'form_errors.html' %}
{% render_field form.is_rig style="display: none" %}
<input type="hidden" name="{{ form.items_json.name }}" id="{{ form.items_json.id_for_label }}"
value="{{ form.items_json.value }}"/>
{# New rig buttons #}
{% if not object.pk %}
<div class="col-md-12 well">
<div class="form-group" id="is_rig-selector">
<div class="col-sm-12">
{# New rig buttons #}
{% if not object.pk %}
<div class="col-md-12 well">
<div class="form-group" id="is_rig-selector">
<div class="col-sm-12">
<span class="col-sm-6" data-toggle="tooltip"
title="Anything that involves TEC kit, crew, or otherwise us providing a service to anyone.">
<button type="button" class="btn btn-primary col-xs-12" data-is_rig="1">Rig</button>
</span>
<span class="col-sm-6" data-toggle="tooltip"
title="Things that aren't service-based, like training, meetings and site visits.">
<span class="col-sm-6" data-toggle="tooltip"
title="Things that aren't service-based, like training, meetings and site visits.">
<button type="button" class="btn btn-info col-xs-12" data-is_rig="0">Non-Rig</button>
</span>
</div>
</div>
</div>
{% endif %}
{# Contact details #}
<div class="col-sm-12 col-md-6">
<div class="panel panel-default form-hws form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="panel-heading">Contact Details</div>
<div class="panel-body">
<div class="form-group" data-toggle="tooltip" title="The main contact for the event, can be left blank if purely an organisation">
<label for="{{ form.person.id_for_label }}"
class="col-sm-4 control-label">{{ form.person.label }}</label>
<div class="col-sm-8">
<div class="row">
<div class="col-sm-9 col-md-7 col-lg-8">
<select id="{{ form.person.id_for_label }}" name="{{ form.person.name }}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='person' %}">
{% if person %}
<option value="{{form.person.value}}" selected="selected" data-update_url="{% url 'person_update' form.person.value %}">{{ person }}</option>
{% endif %}
</select>
</div>
<div class="col-sm-3 col-md-5 col-lg-4 align-right">
<div class="btn-group">
<a href="{% url 'person_create' %}" class="btn btn-default modal-href"
data-target="#{{ form.person.id_for_label }}">
<span class="glyphicon glyphicon-plus"></span>
</a>
<a href="{% if form.person.value %}{% url 'person_update' form.person.value %}{% endif %}" class="btn btn-default modal-href" id="{{ form.person.id_for_label }}-update" data-target="#{{ form.person.id_for_label }}">
<span class="glyphicon glyphicon-pencil"></span>
</a>
</div>
</div>
</div>
</div>
</div>
<div class="form-group" data-toggle="tooltip" title="The client organisation, leave blank if client is an individual">
<label for="{{ form.organisation.id_for_label }}"
class="col-sm-4 control-label">{{ form.organisation.label }}</label>
<div class="col-sm-8">
<div class="row">
<div class="col-sm-9 col-md-7 col-lg-8">
<select id="{{ form.organisation.id_for_label }}" name="{{ form.organisation.name }}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='organisation' %}" >
{% if organisation %}
<option value="{{form.organisation.value}}" selected="selected" data-update_url="{% url 'organisation_update' form.organisation.value %}">{{ organisation }}</option>
{% endif %}
</select>
</div>
<div class="col-sm-3 col-md-5 col-lg-4 align-right">
<div class="btn-group">
<a href="{% url 'organisation_create' %}" class="btn btn-default modal-href"
data-target="#{{ form.organisation.id_for_label }}">
<span class="glyphicon glyphicon-plus"></span>
</a>
<a href="{% if form.organisation.value %}{% url 'organisation_update' form.organisation.value %}{% endif %}" class="btn btn-default modal-href" id="{{ form.organisation.id_for_label }}-update" data-target="#{{ form.organisation.id_for_label }}">
<span class="glyphicon glyphicon-pencil"></span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default form-hws form-non_rig">
<div class="panel-heading">Event Description</div>
<div class="panel-body">
<div class="form-group" data-toggle="tooltip" title="A short description of the event, shown on rigboard and on paperwork">
<label for="{{ form.description.id_for_label }}"
class="col-sm-4 control-label">{{ form.description.label }}</label>
{% endif %}
<div class="col-sm-8">
{% render_field form.description class+="form-control" %}
</div>
</div>
</div>
</div>
</div>
<!-- /.col-sm-12 .col-md-6 -->
{# Event details #}
<div class="col-sm-12 col-md-6">
<div class="panel panel-default form-hws form-non_rig">
<div class="panel-heading">Event Details</div>
<div class="panel-body">
<div id="form-hws">
<div class="form-group" data-toggle="tooltip" title="Name of the event, displays on rigboard and on paperwork">
<label for="{{ form.name.id_for_label }}"
class="col-sm-4 control-label">{{ form.name.label }}</label>
<div class="col-sm-8">
{% render_field form.name class+="form-control" %}
</div>
</div>
<div class="form-group" data-toggle="tooltip" title="The venue for the rig, leave blank if unknown (e.g. for a dry hire)">
<label for="{{ form.venue.id_for_label }}"
class="col-sm-4 control-label">{{ form.venue.label }}</label>
{# Contact details #}
<div class="col-sm-12 col-md-6">
<div class="panel panel-default form-hws form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="panel-heading">Contact Details</div>
<div class="panel-body">
<div class="form-group" data-toggle="tooltip"
title="The main contact for the event, can be left blank if purely an organisation">
<label for="{{ form.person.id_for_label }}"
class="col-sm-4 control-label">{{ form.person.label }}</label>
<div class="col-sm-8">
<div class="row">
<div class="col-sm-9 col-md-7 col-lg-8">
<select id="{{ form.venue.id_for_label }}" name="{{ form.venue.name }}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='venue' %}">
{% if venue %}
<option value="{{form.venue.value}}" selected="selected" data-update_url="{% url 'venue_update' form.venue.value %}">{{ venue }}</option>
{% endif %}
<select id="{{ form.person.id_for_label }}" name="{{ form.person.name }}"
class="form-control selectpicker" data-live-search="true"
data-sourceurl="{% url 'api_secure' model='person' %}">
{% if person %}
<option value="{{ form.person.value }}" selected="selected"
data-update_url="{% url 'person_update' form.person.value %}">{{ person }}</option>
{% endif %}
</select>
</div>
<div class="col-sm-3 col-md-5 col-lg-4 align-right">
<div class="btn-group">
<a href="{% url 'venue_create' %}" class="btn btn-default modal-href"
data-target="#{{ form.venue.id_for_label }}">
<a href="{% url 'person_create' %}" class="btn btn-default modal-href"
data-target="#{{ form.person.id_for_label }}">
<span class="glyphicon glyphicon-plus"></span>
</a>
<a href="{% if object.venue %}{% url 'venue_update' object.venue.pk %}{% endif %}" class="btn btn-default modal-href" id="{{ form.venue.id_for_label }}-update" data-target="#{{ form.venue.id_for_label }}">
<a href="
{% if form.person.value %}{% url 'person_update' form.person.value %}{% endif %}"
class="btn btn-default modal-href" id="{{ form.person.id_for_label }}-update"
data-target="#{{ form.person.id_for_label }}">
<span class="glyphicon glyphicon-pencil"></span>
</a>
</div>
@@ -282,166 +205,292 @@
</div>
</div>
</div>
<div class="form-group">
<label for="{{ form.start_date.id_for_label }}"
class="col-sm-4 control-label">{{ form.start_date.label }}</label>
<div class="form-group" data-toggle="tooltip"
title="The client organisation, leave blank if client is an individual">
<label for="{{ form.organisation.id_for_label }}"
class="col-sm-4 control-label">{{ form.organisation.label }}</label>
<div class="col-sm-8">
<div class="row">
<div class="col-sm-12 col-md-7" data-toggle="tooltip" title="Start date for event, required">
{% render_field form.start_date type="date" class+="form-control" %}
<div class="col-sm-9 col-md-7 col-lg-8">
<select id="{{ form.organisation.id_for_label }}"
name="{{ form.organisation.name }}" class="form-control selectpicker"
data-live-search="true"
data-sourceurl="{% url 'api_secure' model='organisation' %}">
{% if organisation %}
<option value="{{ form.organisation.value }}" selected="selected"
data-update_url="{% url 'organisation_update' form.organisation.value %}">{{ organisation }}</option>
{% endif %}
</select>
</div>
<div class="col-sm-12 col-md-5" data-toggle="tooltip" title="Start time of event, can be left blank">
{% render_field form.start_time type="time" class+="form-control" %}
</div>
</div>
</div>
</div>
<div class="form-group">
<label for="{{ form.end_date.id_for_label }}"
class="col-sm-4 control-label">{{ form.end_date.label }}</label>
<div class="col-sm-3 col-md-5 col-lg-4 align-right">
<div class="btn-group">
<a href="{% url 'organisation_create' %}" class="btn btn-default modal-href"
data-target="#{{ form.organisation.id_for_label }}">
<span class="glyphicon glyphicon-plus"></span>
</a>
<a href="
<div class="col-sm-8">
<div class="row">
<div class="col-sm-12 col-md-7" data-toggle="tooltip" title="End date of event, leave blank if unknown or same as start date">
{% render_field form.end_date type="date" class+="form-control" %}
</div>
<div class="col-sm-12 col-md-5" data-toggle="tooltip" title="End time of event, leave blank if unknown">
{% render_field form.end_time type="time" class+="form-control" %}
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-offset-7 col-md-5">
<div class="btn-group btn-group-justified">
<btn class="btn btn-default btn-xs" onclick="setTime23Hours()">23:00</btn>
<btn class="btn btn-default btn-xs" onclick="setTime02Hours()">02:00</btn>
{% if form.organisation.value %}{% url 'organisation_update' form.organisation.value %}{% endif %}"
class="btn btn-default modal-href"
id="{{ form.organisation.id_for_label }}-update"
data-target="#{{ form.organisation.id_for_label }}">
<span class="glyphicon glyphicon-pencil"></span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default form-hws form-non_rig">
<div class="panel-heading">Event Description</div>
<div class="panel-body">
<div class="form-group" data-toggle="tooltip"
title="A short description of the event, shown on rigboard and on paperwork">
<label for="{{ form.description.id_for_label }}"
class="col-sm-4 control-label">{{ form.description.label }}</label>
{# Rig only information #}
<div class="form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="form-group" data-toggle="tooltip" title="The date/time at which TEC have access to the venue">
<label for="{{ form.access_at.id_for_label }}"
class="col-sm-4 control-label">{{ form.access_at.label }}</label>
<div class="col-sm-8">
{% render_field form.description class+="form-control" %}
</div>
</div>
</div>
</div>
</div>
<!-- /.col-sm-12 .col-md-6 -->
{# Event details #}
<div class="col-sm-12 col-md-6">
<div class="panel panel-default form-hws form-non_rig">
<div class="panel-heading">Event Details</div>
<div class="panel-body">
<div id="form-hws">
<div class="form-group" data-toggle="tooltip"
title="Name of the event, displays on rigboard and on paperwork">
<label for="{{ form.name.id_for_label }}"
class="col-sm-4 control-label">{{ form.name.label }}</label>
<div class="col-sm-8">
{% render_field form.access_at type="datetime-local" class+="form-control" %}
{% render_field form.name class+="form-control" %}
</div>
</div>
<div class="form-group" data-toggle="tooltip" title="The date/time at which crew should meet for this event">
<label for="{{ form.meet_at.id_for_label }}"
class="col-sm-4 control-label">{{ form.meet_at.label }}</label>
<div class="form-group" data-toggle="tooltip"
title="The venue for the rig, leave blank if unknown (e.g. for a dry hire)">
<label for="{{ form.venue.id_for_label }}"
class="col-sm-4 control-label">{{ form.venue.label }}</label>
<div class="col-sm-8">
{% render_field form.meet_at type="datetime-local" class+="form-control" %}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<div class="checkbox">
<label data-toggle="tooltip" title="Mark this event as a dry-hire, so it needs to be checked in at the end">
{% render_field form.dry_hire %}{{ form.dry_hire.label }}
</label>
<div class="row">
<div class="col-sm-9 col-md-7 col-lg-8">
<select id="{{ form.venue.id_for_label }}" name="{{ form.venue.name }}"
class="form-control selectpicker" data-live-search="true"
data-sourceurl="{% url 'api_secure' model='venue' %}">
{% if venue %}
<option value="{{ form.venue.value }}" selected="selected"
data-update_url="{% url 'venue_update' form.venue.value %}">{{ venue }}</option>
{% endif %}
</select>
</div>
<div class="col-sm-3 col-md-5 col-lg-4 align-right">
<div class="btn-group">
<a href="{% url 'venue_create' %}" class="btn btn-default modal-href"
data-target="#{{ form.venue.id_for_label }}">
<span class="glyphicon glyphicon-plus"></span>
</a>
<a href="
{% if object.venue %}{% url 'venue_update' object.venue.pk %}{% endif %}"
class="btn btn-default modal-href"
id="{{ form.venue.id_for_label }}-update"
data-target="#{{ form.venue.id_for_label }}">
<span class="glyphicon glyphicon-pencil"></span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
{# Status is needed on all events types and it looks good here in the form #}
<div class="form-group" data-toggle="tooltip" title="The current status of the event. Only mark as booked once paperwork is received">
<label for="{{ form.status.id_for_label }}"
class="col-sm-4 control-label">{{ form.status.label }}</label>
<div class="col-sm-8">
{% render_field form.status class+="form-control" %}
</div>
</div>
<div class="form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="form-group" data-toggle="tooltip" title="The Member in Charge of this event">
<label for="{{ form.mic.id_for_label }}"
class="col-sm-4 control-label">{{ form.mic.label }}</label>
<div class="form-group">
<label for="{{ form.start_date.id_for_label }}"
class="col-sm-4 control-label">{{ form.start_date.label }}</label>
<div class="col-sm-8">
<select id="{{ form.mic.id_for_label }}" name="{{ form.mic.name }}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if mic %}
<option value="{{form.mic.value}}" selected="selected" >{{ mic.name }}</option>
{% endif %}
</select>
<div class="row">
<div class="col-sm-12 col-md-7" data-toggle="tooltip"
title="Start date for event, required">
{% render_field form.start_date type="date" class+="form-control" %}
</div>
<div class="col-sm-12 col-md-5" data-toggle="tooltip"
title="Start time of event, can be left blank">
{% render_field form.start_time type="time" class+="form-control" %}
</div>
</div>
</div>
</div>
<div class="form-group">
<label for="{{ form.end_date.id_for_label }}"
class="col-sm-4 control-label">{{ form.end_date.label }}</label>
<div class="col-sm-8">
<div class="row">
<div class="col-sm-12 col-md-7" data-toggle="tooltip"
title="End date of event, leave blank if unknown or same as start date">
{% render_field form.end_date type="date" class+="form-control" %}
</div>
<div class="col-sm-12 col-md-5" data-toggle="tooltip"
title="End time of event, leave blank if unknown">
{% render_field form.end_time type="time" class+="form-control" %}
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-offset-7 col-md-5">
<div class="btn-group btn-group-justified">
<btn class="btn btn-default btn-xs" onclick="setTime23Hours()">23:00</btn>
<btn class="btn btn-default btn-xs" onclick="setTime02Hours()">02:00</btn>
</div>
</div>
</div>
</div>
</div>
{% if object.dry_hire %}
<div class="form-group" data-toggle="tooltip" title="The person who checked-in this dry hire">
<label for="{{ form.checked_in_by.id_for_label }}"
class="col-sm-4 control-label">{{ form.checked_in_by.label }}</label>
{# Rig only information #}
<div class="form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="form-group" data-toggle="tooltip"
title="The date/time at which TEC have access to the venue">
<label for="{{ form.access_at.id_for_label }}"
class="col-sm-4 control-label">{{ form.access_at.label }}</label>
<div class="col-sm-8">
<select id="{{ form.checked_in_by.id_for_label }}" name="{{ form.checked_in_by.name }}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if checked_in_by %}
<option value="{{form.checked_in_by.value}}" selected="selected" >{{ checked_in_by.name }}</option>
{% render_field form.access_at type="datetime-local" class+="form-control" %}
</div>
</div>
<div class="form-group" data-toggle="tooltip"
title="The date/time at which crew should meet for this event">
<label for="{{ form.meet_at.id_for_label }}"
class="col-sm-4 control-label">{{ form.meet_at.label }}</label>
<div class="col-sm-8">
{% render_field form.meet_at type="datetime-local" class+="form-control" %}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<div class="checkbox">
<label data-toggle="tooltip"
title="Mark this event as a dry-hire, so it needs to be checked in at the end">
{% render_field form.dry_hire %}{{ form.dry_hire.label }}
</label>
</div>
</div>
</div>
</div>
{# Status is needed on all events types and it looks good here in the form #}
<div class="form-group" data-toggle="tooltip"
title="The current status of the event. Only mark as booked once paperwork is received">
<label for="{{ form.status.id_for_label }}"
class="col-sm-4 control-label">{{ form.status.label }}</label>
<div class="col-sm-8">
{% render_field form.status class+="form-control" %}
</div>
</div>
<div class="form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="form-group" data-toggle="tooltip" title="The Member in Charge of this event">
<label for="{{ form.mic.id_for_label }}"
class="col-sm-4 control-label">{{ form.mic.label }}</label>
<div class="col-sm-8">
<select id="{{ form.mic.id_for_label }}" name="{{ form.mic.name }}"
class="form-control selectpicker" data-live-search="true"
data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if mic %}
<option value="{{ form.mic.value }}"
selected="selected">{{ mic.name }}</option>
{% endif %}
</select>
</div>
</div>
{% endif %}
<div class="form-group" data-toggle="tooltip" title="The student ID of the client who collected the dry-hire">
<label for="{{ form.collector.id_for_label }}"
class="col-sm-4 control-label">{{ form.collector.label }}</label>
{% if object.dry_hire %}
<div class="form-group" data-toggle="tooltip"
title="The person who checked-in this dry hire">
<label for="{{ form.checked_in_by.id_for_label }}"
class="col-sm-4 control-label">{{ form.checked_in_by.label }}</label>
<div class="col-sm-8">
{% render_field form.collector class+="form-control" %}
<div class="col-sm-8">
<select id="{{ form.checked_in_by.id_for_label }}"
name="{{ form.checked_in_by.name }}" class="form-control selectpicker"
data-live-search="true"
data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if checked_in_by %}
<option value="{{ form.checked_in_by.value }}"
selected="selected">{{ checked_in_by.name }}</option>
{% endif %}
</select>
</div>
</div>
{% endif %}
<div class="form-group" data-toggle="tooltip"
title="The student ID of the client who collected the dry-hire">
<label for="{{ form.collector.id_for_label }}"
class="col-sm-4 control-label">{{ form.collector.label }}</label>
<div class="col-sm-8">
{% render_field form.collector class+="form-control" %}
</div>
</div>
</div>
<div class="form-group" data-toggle="tooltip" title="The purchase order number (for external clients)">
<label for="{{ form.purchase_order.id_for_label }}"
class="col-sm-4 control-label">{{ form.purchase_order.label }}</label>
<div class="form-group" data-toggle="tooltip"
title="The purchase order number (for external clients)">
<label for="{{ form.purchase_order.id_for_label }}"
class="col-sm-4 control-label">{{ form.purchase_order.label }}</label>
<div class="col-sm-8">
{% render_field form.purchase_order class+="form-control" %}
<div class="col-sm-8">
{% render_field form.purchase_order class+="form-control" %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /.col-sm-12 .col-md-6 -->
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<button type="submit" class="btn btn-default" title="Save"><span
class="glyphicon glyphicon-floppy-disk"></span>
</button>
</div>
</div>
{# Notes and item shit #}
<div class="col-sm-12">
<div class="panel panel-default form-hws form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="panel-body">
<div class="col-sm-12">
<div class="form-group" data-toggle="tooltip" title="Notes on the event. This is only visible to keyholders, and is not displayed on the paperwork">
<label for="{{ form.notes.id_for_label }}">{{ form.notes.label }}</label>
{% render_field form.notes class+="form-control" %}
</div>
</div>
{% include "RIGS/item_table.html" %}
<!-- /.col-sm-12 .col-md-6 -->
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<button type="submit" class="btn btn-default" title="Save"><span
class="glyphicon glyphicon-floppy-disk"></span>
</button>
</div>
</div>
</div>
<div class="col-sm-12 text-right form-hws form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="btn-group btn-page">
<button type="submit" class="btn btn-default" title="Save"><span
class="glyphicon glyphicon-floppy-disk"></span>
</button>
{# Notes and item shit #}
<div class="col-sm-12">
<div class="panel panel-default form-hws form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="panel-body">
<div class="col-sm-12">
<div class="form-group" data-toggle="tooltip"
title="Notes on the event. This is only visible to keyholders, and is not displayed on the paperwork">
<label for="{{ form.notes.id_for_label }}">{{ form.notes.label }}</label>
{% render_field form.notes class+="form-control" %}
</div>
</div>
{% include "RIGS/item_table.html" %}
</div>
</div>
</div>
<div class="col-sm-12 text-right form-hws form-is_rig {% if object.pk and not object.is_rig %}hidden{% endif %}">
<div class="btn-group btn-page">
<button type="submit" class="btn btn-default" title="Save"><span
class="glyphicon glyphicon-floppy-disk"></span>
</button>
</div>
</div>
</div>
</form>
{% include 'RIGS/item_modal.html' %}

View File

@@ -5,17 +5,17 @@
{% block title %}Events for Invoice{% endblock %}
{% block js %}
<script src="{% static "js/tooltip.js" %}"></script>
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip();
});
</script>
<script src="{% static "js/tooltip.js" %}"></script>
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip();
});
</script>
{% endblock %}
{% block content %}
<div class="col-sm-12">
<h2>Events for Invoice ({{count}} Events, £ {{ total|floatformat:2 }})</h2>
<h2>Events for Invoice ({{ count }} Events, £ {{ total|floatformat:2 }})</h2>
<p>These events have happened, but paperwork has not yet been sent to treasury</p>
{% if is_paginated %}
<div class="col-md-6 col-md-offset-6 col-sm-12 text-right">
@@ -77,7 +77,8 @@
{% endif %}
</td>
<td class="text-right">
<a href="{% url 'invoice_event' object.pk %}" class="btn btn-default" data-toggle="tooltip" title="'Invoice' this event - click this when paperwork has been sent to treasury">
<a href="{% url 'invoice_event' object.pk %}" class="btn btn-default" data-toggle="tooltip"
title="'Invoice' this event - click this when paperwork has been sent to treasury">
<span class="glyphicon glyphicon-gbp"></span>
</a>
</td>
@@ -92,4 +93,4 @@
</div>
{% endif %}
</div>
{% endblock %}
{% endblock %}

View File

@@ -16,29 +16,30 @@
<color id="DarkGray" RGB="#707070"/>
</initialize>
<paraStyle name="style.para" fontName="OpenSans" />
<paraStyle name="style.para" fontName="OpenSans"/>
<paraStyle name="blockPara" spaceAfter="5" spaceBefore="5"/>
<paraStyle name="style.Heading1" fontName="OpenSans" fontSize="16" leading="18" spaceAfter="0"/>
<paraStyle name="style.Heading2" fontName="OpenSans-Bold" fontSize="10" spaceAfter="2"/>
<paraStyle name="style.Heading3" fontName="OpenSans" fontSize="10" spaceAfter="0"/>
<paraStyle name="center" alignment="center"/>
<paraStyle name="invoice-head" alignment="center" fontName="OpenSans-Bold" fontSize="16" leading="18" spaceAfter="0"/>
<paraStyle name="invoice-head" alignment="center" fontName="OpenSans-Bold" fontSize="16" leading="18"
spaceAfter="0"/>
<paraStyle name="style.event_description" fontName="OpenSans" textColor="DarkGray" />
<paraStyle name="style.item_description" fontName="OpenSans" textColor="DarkGray" leftIndent="10" />
<paraStyle name="style.specific_description" fontName="OpenSans" textColor="DarkGray" fontSize="10" />
<paraStyle name="style.times" fontName="OpenSans" fontSize="10" />
<paraStyle name="style.invoice_titles" fontName="OpenSans-Bold" fontSize="10" />
<paraStyle name="style.invoice_numbers" fontName="OpenSans" fontSize="10" />
<paraStyle name="style.event_description" fontName="OpenSans" textColor="DarkGray"/>
<paraStyle name="style.item_description" fontName="OpenSans" textColor="DarkGray" leftIndent="10"/>
<paraStyle name="style.specific_description" fontName="OpenSans" textColor="DarkGray" fontSize="10"/>
<paraStyle name="style.times" fontName="OpenSans" fontSize="10"/>
<paraStyle name="style.invoice_titles" fontName="OpenSans-Bold" fontSize="10"/>
<paraStyle name="style.invoice_numbers" fontName="OpenSans" fontSize="10"/>
<blockTableStyle id="eventSpecifics">
<blockValign value="top"/>
<lineStyle kind="LINEAFTER" colorName="LightGrey" start="0,0" stop="1,0" thickness="1"/>
<lineStyle kind="LINEAFTER" colorName="LightGrey" start="0,0" stop="1,0" thickness="1"/>
</blockTableStyle>
<blockTableStyle id="invoiceLayout">
<blockValign value="top"/>
</blockTableStyle>
<blockTableStyle id="eventDetails">
@@ -71,7 +72,7 @@
</blockTableStyle>
<blockTableStyle id="signatureTable">
<blockTopPadding length="20" />
<blockTopPadding length="20"/>
<blockLeftPadding start="0,0" stop="0,-1" length="0"/>
<lineStyle kind="linebelow" start="1,0" stop="1,0" colorName="black"/>
<lineStyle kind="linebelow" start="3,0" stop="3,0" colorName="black"/>
@@ -79,53 +80,64 @@
</blockTableStyle>
</stylesheet>
<template > {# Note: page is 595x842 points (1 point=1/72in) #}
<pageTemplate id="Headed" >
<pageGraphics>
<image file="RIGS/static/imgs/paperwork/corner-tr-su.jpg" x="395" y="642" height="200" width="200"/>
<image file="RIGS/static/imgs/paperwork/corner-bl.jpg" x="0" y="0" height="200" width="200"/>
<template> {# Note: page is 595x842 points (1 point=1/72in) #}
<pageTemplate id="Headed">
<pageGraphics>
<image file="RIGS/static/imgs/paperwork/corner-tr-su.jpg" x="395" y="642" height="200" width="200"/>
<image file="RIGS/static/imgs/paperwork/corner-bl.jpg" x="0" y="0" height="200" width="200"/>
{# logo positioned 42 from left, 33 from top #}
<image file="RIGS/static/imgs/paperwork/tec-logo.jpg" x="42" y="719" height="90" width="84"/>
<setFont name="OpenSans-Bold" size="22.5" leading="10"/>
<drawString x="137" y="780">TEC PA &amp; Lighting</drawString>
{# logo positioned 42 from left, 33 from top #}
<image file="RIGS/static/imgs/paperwork/tec-logo.jpg" x="42" y="719" height="90" width="84"/>
<setFont name="OpenSans" size="9"/>
<drawString x="137" y="760">Portland Building, University Park, Nottingham, NG7 2RD</drawString>
<drawString x="137" y="746">www.nottinghamtec.co.uk</drawString>
<drawString x="265" y="746">info@nottinghamtec.co.uk</drawString>
<drawString x="137" y="732">Phone: (0115) 846 8720</drawString>
<setFont name="OpenSans-Bold" size="22.5" leading="10"/>
<drawString x="137" y="780">TEC PA &amp; Lighting</drawString>
<setFont name="OpenSans" size="9"/>
<drawString x="137" y="760">Portland Building, University Park, Nottingham, NG7 2RD</drawString>
<drawString x="137" y="746">www.nottinghamtec.co.uk</drawString>
<drawString x="265" y="746">info@nottinghamtec.co.uk</drawString>
<drawString x="137" y="732">Phone: (0115) 846 8720</drawString>
<setFont name="OpenSans" size="10" />
{% if not invoice %}<drawCenteredString x="302.5" y="50">[{{ copy }} Copy]</drawCenteredString>{% endif %}
<drawCenteredString x="302.5" y="38">[Page <pageNumber/> of <getName id="lastPage" default="0" />]</drawCenteredString>
<setFont name="OpenSans" size="7" />
<drawCenteredString x="302.5" y="26">[Paperwork generated by {{current_user.name}} | {% now "d/m/Y H:i" %} | {{object.current_version_id}}]</drawCenteredString>
</pageGraphics>
<frame id="main" x1="50" y1="65" width="495" height="645"/>
</pageTemplate>
<pageTemplate id="Main">
<pageGraphics>
<image file="RIGS/static/imgs/paperwork/corner-tr.jpg" x="395" y="642" height="200" width="200"/>
<image file="RIGS/static/imgs/paperwork/corner-bl.jpg" x="0" y="0" height="200" width="200"/>
<setFont name="OpenSans" size="10"/>
{% if not invoice %}<drawCenteredString x="302.5" y="50">[{{ copy }} Copy]</drawCenteredString>{% endif %}
<drawCenteredString x="302.5" y="38">[Page <pageNumber/> of <getName id="lastPage" default="0" />]</drawCenteredString>
<setFont name="OpenSans" size="7" />
<drawCenteredString x="302.5" y="26">[Paperwork generated by {{current_user.name}} | {% now "d/m/Y H:i" %} | {{object.current_version_id}}]</drawCenteredString>
</pageGraphics>
<frame id="main" x1="50" y1="65" width="495" height="727"/>
</pageTemplate>
<setFont name="OpenSans" size="10"/>
{% if not invoice %}
<drawCenteredString x="302.5" y="50">[{{ copy }} Copy]</drawCenteredString>{% endif %}
<drawCenteredString x="302.5" y="38">[Page
<pageNumber/>
of<getName id="lastPage" default="0"/>]
</drawCenteredString>
<setFont name="OpenSans" size="7"/>
<drawCenteredString x="302.5" y="26">[Paperwork generated by {{ current_user.name }}
| {% now "d/m/Y H:i" %} | {{ object.current_version_id }}]
</drawCenteredString>
</pageGraphics>
<frame id="main" x1="50" y1="65" width="495" height="645"/>
</pageTemplate>
<pageTemplate id="Main">
<pageGraphics>
<image file="RIGS/static/imgs/paperwork/corner-tr.jpg" x="395" y="642" height="200" width="200"/>
<image file="RIGS/static/imgs/paperwork/corner-bl.jpg" x="0" y="0" height="200" width="200"/>
<setFont name="OpenSans" size="10"/>
{% if not invoice %}
<drawCenteredString x="302.5" y="50">[{{ copy }} Copy]</drawCenteredString>{% endif %}
<drawCenteredString x="302.5" y="38">[Page
<pageNumber/>
of<getName id="lastPage" default="0"/>]
</drawCenteredString>
<setFont name="OpenSans" size="7"/>
<drawCenteredString x="302.5" y="26">[Paperwork generated by {{ current_user.name }}
| {% now "d/m/Y H:i" %} | {{ object.current_version_id }}]
</drawCenteredString>
</pageGraphics>
<frame id="main" x1="50" y1="65" width="495" height="727"/>
</pageTemplate>
</template>
<story firstPageTemplate="Headed">
{% include "RIGS/event_print_page.xml" %}
{% include "RIGS/event_print_page.xml" %}
</story>
</document>
</document>

View File

@@ -3,21 +3,25 @@
{% if invoice %}
<blockTable style="invoiceLayout" colWidths="330,165">
<tr>
<blockTable style="invoiceLayout" colWidths="330,165">
<tr>
<td>
{% endif %}
{% endif %}
<h1><b>N{{ object.pk|stringformat:"05d" }}:</b> '{{ object.name }}'<small></small></h1>
<h1>
<b>N{{ object.pk|stringformat:"05d" }}:</b>
'{{ object.name }}'
<small></small>
</h1>
<para style="style.event_description">
<b>{{object.start_date|date:"D jS N Y"}}</b>
<b>{{ object.start_date|date:"D jS N Y" }}</b>
</para>
<keepInFrame>
<para style="style.event_description">
{{ object.description|default_if_none:""|linebreaksbr }}
</para>
{{ object.description|default_if_none:""|linebreaksbr }}
</para>
</keepInFrame>
{% if invoice %}
@@ -28,19 +32,25 @@
<spacer length="10"/>
<blockTable style="eventDetails" colWidths="100,175">
<tr>
<td><para style="invoice_titles">Invoice Number</para></td>
<td>
<para style="invoice_titles">Invoice Number</para>
</td>
<td>
<para style="invoice_numbers">{{ invoice.pk|stringformat:"05d" }}</para>
</td>
</tr>
<tr>
<td><para style="invoice_titles">Invoice Date</para></td>
<td>
<para style="invoice_titles">Invoice Date</para>
</td>
<td>
<para style="invoice_numbers">{{ invoice.invoice_date|date:"d/m/Y" }}</para>
</td>
</tr>
<tr>
<td><para style="invoice_titles">PO Number</para></td>
<td>
<para style="invoice_titles">PO Number</para>
</td>
<td>
<para style="invoice_numbers">{{ object.purchase_order|default_if_none:"" }}</para>
</td>
@@ -48,94 +58,100 @@
</blockTable>
</td>
</tr>
</blockTable>
</tr>
</blockTable>
{% endif %}
<spacer length="15"/>
<blockTable style="eventSpecifics" colWidths="165,165,165">
<tr>
<td leftPadding="0">
<h2>Hirer</h2>
<h3>{{ object.person.name }}</h3>
<h3>{{ object.organisation.name|default_if_none:"" }}</h3>
{% if invoice %}
<keepInFrame>
{% if object.organisation.address %}
<para style="specific_description">{{ object.organisation.address|default_if_none:""|linebreaksbr }}</para>
{% elif object.person.address %}
<para style="specific_description">{{ object.person.address|default_if_none:""|linebreaksbr }}</para>
{% endif %}
</keepInFrame>
{% endif %}
<keepInFrame>
{% if object.person.phone %}
<para style="specific_description">{{ object.person.phone }}</para>
{% elif object.organisation.phone %}
<para style="specific_description">{{ object.organisation.phone }}</para>
{% endif %}
</keepInFrame>
<keepInFrame>
{% if invoice %}
{% if object.organisation.email %}
<para style="specific_description">{{ object.organisation.email }}</para>
{% elif object.person.email %}
<para style="specific_description">{{ object.person.email }}</para>
{% endif %}
{% else %}
{% if object.person.email %}
<para style="specific_description">{{ object.person.email }}</para>
{% elif object.organisation.email %}
<para style="specific_description">{{ object.organisation.email }}</para>
{% endif %}
{% endif %}
</keepInFrame>
</td>
<td>
<h2>Venue</h2>
<h3>{{ object.venue.name }}</h3>
{% if not invoice %}
<tr>
<td leftPadding="0">
<h2>Hirer</h2>
<h3>{{ object.person.name }}</h3>
<h3>{{ object.organisation.name|default_if_none:"" }}</h3>
{% if invoice %}
<keepInFrame>
<para style="specific_description">{{ object.venue.address|default_if_none:""|linebreaksbr }}</para>
{% if object.organisation.address %}
<para style="specific_description">{{ object.organisation.address|default_if_none:""|linebreaksbr }}</para>
{% elif object.person.address %}
<para style="specific_description">{{ object.person.address|default_if_none:""|linebreaksbr }}</para>
{% endif %}
</keepInFrame>
{% endif %}
<keepInFrame>
{% if object.person.phone %}
<para style="specific_description">{{ object.person.phone }}</para>
{% elif object.organisation.phone %}
<para style="specific_description">{{ object.organisation.phone }}</para>
{% endif %}
</td>
<td rightPadding="0">
<h2>Timings</h2>
<blockTable style="eventDetails" colWidths="55,75">
</keepInFrame>
<keepInFrame>
{% if invoice %}
{% if object.organisation.email %}
<para style="specific_description">{{ object.organisation.email }}</para>
{% elif object.person.email %}
<para style="specific_description">{{ object.person.email }}</para>
{% endif %}
{% else %}
{% if object.person.email %}
<para style="specific_description">{{ object.person.email }}</para>
{% elif object.organisation.email %}
<para style="specific_description">{{ object.organisation.email }}</para>
{% endif %}
{% endif %}
</keepInFrame>
</td>
<td>
<h2>Venue</h2>
<h3>{{ object.venue.name }}</h3>
{% if not invoice %}
<keepInFrame>
<para style="specific_description">{{ object.venue.address|default_if_none:""|linebreaksbr }}</para>
</keepInFrame>
{% endif %}
</td>
<td rightPadding="0">
<h2>Timings</h2>
<blockTable style="eventDetails" colWidths="55,75">
<tr>
<td leftPadding="0" topPadding="0">
<h3>Start</h3>
</td>
<td>
<para style="times">{{ object.start_time|time:"H:i" }}
{{ object.start_date|date:"d/m/Y" }}
</para>
</td>
</tr>
<tr>
<td leftPadding="0">
<h3>End</h3>
</td>
<td>
<para style="times">{{ object.end_time|default_if_none:""|time:"H:i" }}
{{ object.end_date|date:"d/m/Y" }}
</para>
</td>
</tr>
{% if object.access_at and not invoice %}
<tr>
<td leftPadding="0" topPadding="0"><h3>Start</h3></td>
<td>
<para style="times">{{ object.start_time|time:"H:i" }}
{{ object.start_date|date:"d/m/Y" }}
</para>
<td leftPadding="0">
<h3>Access</h3>
</td>
</tr>
<tr>
<td leftPadding="0"><h3>End</h3></td>
<td>
<para style="times">{{ object.end_time|default_if_none:""|time:"H:i" }}
{{ object.end_date|date:"d/m/Y" }}
</para>
</td>
</tr>
{% if object.access_at and not invoice%}
<tr>
<td leftPadding="0"><h3>Access</h3></td>
<td>
<para style="times">{{ object.access_at|time:"H:i" }}
{{ object.access_at|date:"d/m/Y" }}
</para>
</td>
</tr>
{% endif %}
</blockTable>
</td>
{% endif %}
</blockTable>
</td>
</tr>
</tr>
</blockTable>
<spacer length="15"/>
@@ -174,11 +190,11 @@
{% if item.description %}
</para>
<para style="item_description">
<em>{{ item.description|linebreaksbr }}</em>
<em>{{ item.description|linebreaksbr }}</em>
</para>
<para>
{% endif %}
</para>
</para>
</td>
<td>£ {{ item.cost|floatformat:2 }}</td>
<td>{{ item.quantity }}</td>
@@ -187,7 +203,7 @@
{% endfor %}
</blockTable>
<keepTogether>
<blockTable style="totalTable" colWidths="300,115,80">
<blockTable style="totalTable" colWidths="300,115,80">
<tr>
<td>{% if not invoice %}VAT Registration Number: 170734807{% endif %}</td>
<td>Total (ex. VAT)</td>
@@ -196,9 +212,9 @@
<tr>
<td>
{% if not invoice %}
<para>
<b>The full hire fee is payable at least 10 days before the event.</b>
</para>
<para>
<b>The full hire fee is payable at least 10 days before the event.</b>
</para>
{% endif %}
</td>
<td>VAT @ {{ object.vat_rate.as_percent|floatformat:2 }}%</td>
@@ -206,7 +222,7 @@
</tr>
<tr>
<td>
<para>
{% if invoice %}
VAT Registration Number: 170734807
@@ -214,7 +230,7 @@
<b>This contract is not an invoice.</b>
{% endif %}
</para>
</td>
<td>
<para>
@@ -227,92 +243,100 @@
</para>
</td>
</tr>
</blockTable>
</blockTable>
</keepTogether>
{% if not invoice %}
<keepTogether>
<blockTable style="infoTable">
<tr>
<td>
<para>Bookings will
<b>not</b>
be confirmed until payment is received and the contract is signed.
</para>
</td>
</tr>
<tr>
<td>24 Hour Emergency Contacts: 07825 065681 and 07825 065678</td>
</tr>
<tr>
<td>
<para>Bookings will
<b>not</b>
be confirmed until payment is received and the contract is signed.
</para>
</td>
</tr>
<tr>
<td>24 Hour Emergency Contacts: 07825 065681 and 07825 065678</td>
</tr>
</blockTable>
</keepTogether>
<spacer length="15"/>
<keepTogether>
<para style="blockPara">
<b>To be signed on booking:</b>
<b>To be signed on booking:</b>
</para>
{% if object.organisation.union_account %}
<para style="blockPara">
<i>
I agree that am authorised to sign this invoice. I agree that I am the President/Treasurer of the hirer, or
that I have provided written permission from either the President or Treasurer of the hirer stating that I can
sign for this invoice.
</i>
<i>
I agree that am authorised to sign this invoice. I agree that I am the President/Treasurer of the hirer,
or
that I have provided written permission from either the President or Treasurer of the hirer stating that
I can
sign for this invoice.
</i>
</para>
<para style="blockPara">
<i>
I have read, understood and fully accepted the current conditions of hire. I agree to return any dry hire
items to TEC PA &amp; Lighting in the same condition at the end of the hire period.
</i>
<i>
I have read, understood and fully accepted the current conditions of hire. I agree to return any dry
hire
items to TEC PA &amp; Lighting in the same condition at the end of the hire period.
</i>
</para>
<para style="blockPara">
<b>
Conditions of hire attached and available on the TEC PA &amp; Lighting website. E&amp;OE
</b>
<b>
Conditions of hire attached and available on the TEC PA &amp; Lighting website. E&amp;OE
</b>
</para>
<para style="blockPara">
Please return this form directly to TEC PA &amp; Lighting and not the Students' Union Finance Department.
Please return this form directly to TEC PA &amp; Lighting and not the Students' Union Finance Department.
</para>
<blockTable style="signatureTable" colWidths="70,100,325">
<tr>
<td>Account Code</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Account Code</td>
<td></td>
<td></td>
</tr>
</blockTable>
{% else %}
<para style="blockPara">
<i>
I, the hirer, have read, understand and fully accept the current conditions of hire. This document forms a
binding contract between TEC PA &amp; Lighting and the hirer, the aforementioned conditions of hire forming
an integral part of it.
</i>
<i>
I, the hirer, have read, understand and fully accept the current conditions of hire. This document forms
a
binding contract between TEC PA &amp; Lighting and the hirer, the aforementioned conditions of hire
forming
an integral part of it.
</i>
</para>
<para style="blockPara">
<b>
Conditions of hire attached and available on the TEC PA &amp; Lighting website. E&amp;OE
</b>
<b>
Conditions of hire attached and available on the TEC PA &amp; Lighting website. E&amp;OE
</b>
</para>
{% include "RIGS/event_print_signature.xml" %}
<spacer length="10"/>
<para style="blockPara">
<b>To be signed on the day of the event/hire:</b>
<b>To be signed on the day of the event/hire:</b>
</para>
<para style="blockPara">
<i>
I, the hirer, have received the goods/services as requested and in good order. I agree to return any dry hire
items to TEC PA &amp; Lighting in a similar condition at the end of the hire period.
</i>
<i>
I, the hirer, have received the goods/services as requested and in good order. I agree to return any dry
hire
items to TEC PA &amp; Lighting in a similar condition at the end of the hire period.
</i>
</para>
{% endif %}
{% include "RIGS/event_print_signature.xml" %}
</keepTogether>
{% endif %}
<namedString id="lastPage"><pageNumber/></namedString>
{% endif %}
<namedString id="lastPage">
<pageNumber/>
</namedString>

View File

@@ -27,7 +27,7 @@
<td>
<div><strong>{{ event.start_date|date:"D d/m/Y" }}</strong></div>
{% if event.end_date and event.end_date != event.start_date %}
<div><strong>{{ event.end_date|date:"D d/m/Y" }}</strong></div>
<div><strong>{{ event.end_date|date:"D d/m/Y" }}</strong></div>
{% endif %}
<span class="text-muted">{{ event.get_status_display }}</span>
</td>
@@ -69,13 +69,14 @@
{{ event.start_date|date:"(Y-m-d)" }}<br/>
</dd>
{% endif %}
{% if event.has_end_time%}{% if event.start_date != event.end_date or event.start_time != event.end_time %}
<dt>Event ends</dt>
<dd>
{{ event.end_time|date:"H:i" }}<br/>
{{ event.end_date|date:"(Y-m-d)" }}
</dd>
{% endif %}{% endif %}
{% if event.has_end_time %}
{% if event.start_date != event.end_date or event.start_time != event.end_time %}
<dt>Event ends</dt>
<dd>
{{ event.end_time|date:"H:i" }}<br/>
{{ event.end_date|date:"(Y-m-d)" }}
</dd>
{% endif %}{% endif %}
</dl>
{% endif %}
</td>
@@ -99,4 +100,4 @@
{% endfor %}
</tbody>
</table>
</div>
</div>

View File

@@ -3,76 +3,94 @@
{% block content %}
<div class="col-sm-12">
<h1>R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></h1>
<h1>R
<small>ig</small>
I
<small>nformation</small>
G
<small>athering</small>
S
<small>ystem</small>
</h1>
</div>
<div class="col-sm-12">
<p><h4 class="list-group-item-heading" style="margin:0;">Welcome back {{ user.get_full_name }}, there are {{ rig_count }} rigs coming up.</h4>
<p><h4 class="list-group-item-heading" style="margin:0;">Welcome back {{ user.get_full_name }}, there
are {{ rig_count }} rigs coming up.</h4>
</p>
</div>
<div class="row">
<div class="col-sm-{% if perms.RIGS.view_event %}6{% else %}12{% endif %}">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="list-group-item-heading">Quick Links</h4>
</div>
<div class="list-group">
<a class="list-group-item" href="{% url 'rigboard' %}"><span class="glyphicon glyphicon-list"></span> Rigboard</a>
<a class="list-group-item" href="{% url 'web_calendar' %}"><span class="glyphicon glyphicon-calendar"></span> Calendar</a>
{% if perms.RIGS.add_event %}<a class="list-group-item" href="{% url 'event_create' %}"><span class="glyphicon glyphicon-plus"></span> New Event</a>{% endif %}
<div class="col-sm-{% if perms.RIGS.view_event %}6{% else %}12{% endif %}">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="list-group-item-heading">Quick Links</h4>
</div>
<div class="list-group">
<a class="list-group-item" href="{% url 'rigboard' %}"><span
class="glyphicon glyphicon-list"></span> Rigboard</a>
<a class="list-group-item" href="{% url 'web_calendar' %}"><span
class="glyphicon glyphicon-calendar"></span> Calendar</a>
{% if perms.RIGS.add_event %}<a class="list-group-item" href="{% url 'event_create' %}"><span
class="glyphicon glyphicon-plus"></span> New Event</a>{% endif %}
<div class="list-group-item default"></div>
<a class="list-group-item" href="//members.nottinghamtec.co.uk/forum" target="_blank"><span class="glyphicon glyphicon-link"></span> TEC Forum</a>
<a class="list-group-item" href="//members.nottinghamtec.co.uk/wiki" target="_blank"><span class="glyphicon glyphicon-link"></span> TEC Wiki</a>
<a class="list-group-item" href="//members.nottinghamtec.co.uk/price" target="_blank"><span class="glyphicon glyphicon-link"></span> Price List</a>
</div>
</div>
<a class="list-group-item" href="//members.nottinghamtec.co.uk/forum" target="_blank"><span
class="glyphicon glyphicon-link"></span> TEC Forum</a>
<a class="list-group-item" href="//members.nottinghamtec.co.uk/wiki" target="_blank"><span
class="glyphicon glyphicon-link"></span> TEC Wiki</a>
<a class="list-group-item" href="//members.nottinghamtec.co.uk/price" target="_blank"><span
class="glyphicon glyphicon-link"></span> Price List</a>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Search Rigboard</h4>
</div>
</div>
<div class="list-group">
<div class="list-group-item">
<form class="form" role="form" action="{% url 'person_list' %}" method="GET">
<div class="input-group">
<input type="search" name="q" class="form-control" placeholder="Search People" />
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</form>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Search Rigboard</h4>
</div>
<div class="list-group-item">
<form class="form" role="form" action="{% url 'organisation_list' %}" method="GET">
<div class="input-group">
<input type="search" name="q" class="form-control" placeholder="Search Organisations" />
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-search"></span></button>
<div class="list-group">
<div class="list-group-item">
<form class="form" role="form" action="{% url 'person_list' %}" method="GET">
<div class="input-group">
<input type="search" name="q" class="form-control" placeholder="Search People"/>
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><span
class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</form>
</div>
<div class="list-group-item">
<form class="form" role="form" action="{% url 'venue_list' %}" method="GET">
<div class="input-group">
<input type="search" name="q" class="form-control" placeholder="Search Venues" />
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-search"></span></button>
</div>
</form>
</div>
<div class="list-group-item">
<form class="form" role="form" action="{% url 'organisation_list' %}" method="GET">
<div class="input-group">
<input type="search" name="q" class="form-control" placeholder="Search Organisations"/>
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><span
class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</form>
</div>
</form>
</div>
<div class="list-group-item">
<form class="form" role="form" action="{% url 'venue_list' %}" method="GET">
<div class="input-group">
<input type="search" name="q" class="form-control" placeholder="Search Venues"/>
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><span
class="glyphicon glyphicon-search"></span></button>
</span>
</div>
</form>
</div>
</div>
</div>
</div>
{% if perms.RIGS.view_event %}
<div class="col-sm-6">
{% include 'RIGS/activity_feed.html' %}
</div>
{% endif %}
</div>
{% if perms.RIGS.view_event %}
<div class="col-sm-6" >
{% include 'RIGS/activity_feed.html' %}
</div>
{% endif %}
</div>
{% endblock %}
{% endblock %}

View File

@@ -6,20 +6,20 @@
<div class="col-sm-12">
<div class="row">
<div class="col-sm-8">
<h2>Invoice {{ object.pk }} ({{ object.invoice_date|date:"d/m/Y"}})</h2>
<h2>Invoice {{ object.pk }} ({{ object.invoice_date|date:"d/m/Y" }})</h2>
</div>
<div class="col-sm-4 text-right">
<div class="btn-group btn-page">
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-default" title="Void Invoice">
<span class="glyphicon glyphicon-remove"></span> <span
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-default" title="Void Invoice">
<span class="glyphicon glyphicon-remove"></span> <span
class="hidden-xs">Delete</span>
</a>
<a href="{% url 'invoice_void' object.pk %}" class="btn btn-default" title="Void Invoice">
<span class="glyphicon glyphicon-ban-circle"></span> <span
</a>
<a href="{% url 'invoice_void' object.pk %}" class="btn btn-default" title="Void Invoice">
<span class="glyphicon glyphicon-ban-circle"></span> <span
class="hidden-xs">Void</span>
</a>
<a href="{% url 'invoice_print' object.pk %}" target="_blank" class="btn btn-default"><span
</a>
<a href="{% url 'invoice_print' object.pk %}" target="_blank" class="btn btn-default"><span
class="glyphicon glyphicon-print"></span> <span
class="hidden-xs">Print</span></a>
</div>
@@ -139,4 +139,4 @@
</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -29,7 +29,8 @@
{% for object in object_list %}
<tr>
<td class="{% if object.is_closed %}success{% else %}warning{% endif %}">{{ object.pk }}<br>
<span class="text-muted">{% if object.void %}(VOID){% elif object.is_closed %}(PAID){% else %}(O/S){% endif %}</span></td>
<span class="text-muted">{% if object.void %}(VOID){% elif object.is_closed %}
(PAID){% else %}(O/S){% endif %}</span></td>
<td class="
{% if object.event.cancelled %}
active text-muted
@@ -43,19 +44,22 @@
{% else %}
danger
{% endif %}
"><a href="{% url 'event_detail' object.event.pk %}">N{{ object.event.pk|stringformat:"05d" }}</a>: {{ object.event.name }} <br>
<span class="text-muted">{{ object.event.get_status_display }}{% if not object.event.mic %}, No MIC{% endif %}
"><a
href="{% url 'event_detail' object.event.pk %}">N{{ object.event.pk|stringformat:"05d" }}</a>: {{ object.event.name }}
<br>
<span class="text-muted">{{ object.event.get_status_display }}{% if not object.event.mic %},
No MIC{% endif %}
</span></td>
</td>
<td>{% if object.event.organisation %}
{{ object.event.organisation.name }}
<br>
<span class="text-muted">{{ object.event.organisation.union_account|yesno:'Internal,External' }}</span>
{% else %}
{{ object.event.person.name }}
<br>
<span class="text-muted">External</span>
{% endif %}
{{ object.event.organisation.name }}
<br>
<span class="text-muted">{{ object.event.organisation.union_account|yesno:'Internal,External' }}</span>
{% else %}
{{ object.event.person.name }}
<br>
<span class="text-muted">External</span>
{% endif %}
</td>
<td>{{ object.event.start_date }}</td>
<td>{{ object.invoice_date }}</td>
@@ -76,4 +80,4 @@
</div>
{% endif %}
</div>
{% endblock %}
{% endblock %}

View File

@@ -1,13 +1,13 @@
{% extends 'RIGS/invoice_list.html' %}
{% block title %}
Outstanding Invoices
Outstanding Invoices
{% endblock %}
{% block heading %}
Outstanding Invoices ({{ count }} Events, £ {{ total|floatformat:2 }})
Outstanding Invoices ({{ count }} Events, £ {{ total|floatformat:2 }})
{% endblock %}
{% block description %}
<p>Paperwork for these events has been sent to treasury, but the full balance has not yet appeared on a ledger</p>
{% endblock %}
<p>Paperwork for these events has been sent to treasury, but the full balance has not yet appeared on a ledger</p>
{% endblock %}

View File

@@ -1,13 +1,13 @@
{% extends 'RIGS/invoice_list.html' %}
{% block title %}
Invoice Archive
Invoice Archive
{% endblock %}
{% block heading %}
All Invoices
All Invoices
{% endblock %}
{% block description %}
<p>This page displays all invoices: outstanding, paid, and void</p>
{% endblock %}
<p>This page displays all invoices: outstanding, paid, and void</p>
{% endblock %}

View File

@@ -1,25 +1,25 @@
<tr id="item-{{item.pk}}" data-pk="{{item.pk}}" class="item_row">
<td>
<span class="name">{{ item.name }}</span>
<tr id="item-{{ item.pk }}" data-pk="{{ item.pk }}" class="item_row">
<td>
<span class="name">{{ item.name }}</span>
<div class="item-description">
<em class="description">{{item.description|linebreaksbr}}</em>
<em class="description">{{ item.description|linebreaksbr }}</em>
</div>
</td>
<td>£&nbsp;<span class="cost">{{item.cost|floatformat:2}}</span></td>
<td class="quantity">{{item.quantity}}</td>
<td>£&nbsp;<span class="sub-total" data-subtotal="{{item.total_cost}}">{{item.total_cost|floatformat:2}}</span></td>
{% if edit %}
<td class="vert-align text-right">
<button type="button" class="item-edit btn btn-xs btn-default"
data-pk="{{item.pk}}"
data-toggle="modal" data-target="#itemModal">
<span class="glyphicon glyphicon-edit"></span>
</button>
<button type="button" class="item-delete btn btn-xs btn-danger"
data-pk="{{item.pk}}">
<span class="glyphicon glyphicon-remove"></span>
</button>
</td>
{% endif %}
</td>
<td>£&nbsp;<span class="cost">{{ item.cost|floatformat:2 }}</span></td>
<td class="quantity">{{ item.quantity }}</td>
<td>£&nbsp;<span class="sub-total" data-subtotal="{{ item.total_cost }}">{{ item.total_cost|floatformat:2 }}</span>
</td>
{% if edit %}
<td class="vert-align text-right">
<button type="button" class="item-edit btn btn-xs btn-default"
data-pk="{{ item.pk }}"
data-toggle="modal" data-target="#itemModal">
<span class="glyphicon glyphicon-edit"></span>
</button>
<button type="button" class="item-delete btn btn-xs btn-danger"
data-pk="{{ item.pk }}">
<span class="glyphicon glyphicon-remove"></span>
</button>
</td>
{% endif %}
</tr>

View File

@@ -1,71 +1,73 @@
<div class="panel table-responsive">
<table class="table table-hover" id="item-table" {% if edit %}data-orderurl="{#% url 'item_order' %#}"{% endif %}>
<thead>
<tr>
<td>Item</td>
<td>Price</td>
<td>Quantity</td>
<td>Sub-total</td>
{% if edit %}
<td class="text-right">
<button type="button" class="btn btn-default btn-xs item-add"
data-url="{#% url eventitem_add object.pk %#}" data-toggle="modal"
data-target="#itemModal">
<span class="glyphicon glyphicon-plus"></span>
</button>
</td>
{% endif %}
</tr>
</thead>
<tbody id="item-table-body">
{% for item in object.items.all %}
{% include 'RIGS/item_row.html' %}
{% endfor %}
</tbody>
<tfoot>
<tr>
<td rowspan="3" colspan="2"></td>
<td>Total (ex. VAT)</td>
<td colspan="2">£ <span id="sumtotal">{{object.sum_total|default:0|floatformat:2}}</span></td>
</tr>
<tr>
{% if not object.pk %}
<td id="vat-rate" data-rate="{{currentVAT.rate}}">VAT @
{{currentVAT.as_percent|floatformat}}% (TBC)</td>
{% else %}
<td id="vat-rate" data-rate="{{object.vat_rate.rate}}">VAT @
{{object.vat_rate.as_percent|floatformat|default:"TBD"}}%</td>
{% endif %}
<td colspan="2">£ <span id="vat">{{object.vat|default:0|floatformat:2}}</span></td>
</tr>
<tr>
<td>Total</td>
<td colspan="2">£ <span id="total">{{object.total|default:0|floatformat:2}}</span></td>
</tr>
</tfoot>
</table>
<table class="table table-hover" id="item-table" {% if edit %}data-orderurl="{#% url 'item_order' %#}"{% endif %}>
<thead>
<tr>
<td>Item</td>
<td>Price</td>
<td>Quantity</td>
<td>Sub-total</td>
{% if edit %}
<td class="text-right">
<button type="button" class="btn btn-default btn-xs item-add"
data-url="{#% url eventitem_add object.pk %#}" data-toggle="modal"
data-target="#itemModal">
<span class="glyphicon glyphicon-plus"></span>
</button>
</td>
{% endif %}
</tr>
</thead>
<tbody id="item-table-body">
{% for item in object.items.all %}
{% include 'RIGS/item_row.html' %}
{% endfor %}
</tbody>
<tfoot>
<tr>
<td rowspan="3" colspan="2"></td>
<td>Total (ex. VAT)</td>
<td colspan="2">£ <span id="sumtotal">{{ object.sum_total|default:0|floatformat:2 }}</span></td>
</tr>
<tr>
{% if not object.pk %}
<td id="vat-rate" data-rate="{{ currentVAT.rate }}">VAT @
{{ currentVAT.as_percent|floatformat }}% (TBC)
</td>
{% else %}
<td id="vat-rate" data-rate="{{ object.vat_rate.rate }}">VAT @
{{ object.vat_rate.as_percent|floatformat|default:"TBD" }}%
</td>
{% endif %}
<td colspan="2">£ <span id="vat">{{ object.vat|default:0|floatformat:2 }}</span></td>
</tr>
<tr>
<td>Total</td>
<td colspan="2">£ <span id="total">{{ object.total|default:0|floatformat:2 }}</span></td>
</tr>
</tfoot>
</table>
</div>
<table class="hidden invisible">
<tr id="new-item-row" class="item_row">
<td>
<span class="name"></span>
<div class="item-description">
<em class="description"></em>
</div>
<div class="item-description">
<em class="description"></em>
</div>
</td>
<td>£&nbsp;<span class="cost"></span></td>
<td class="quantity"></td>
<td>£&nbsp;<span class="sub-total"></span></td>
{% if edit %}
<td class="vert-align text-right">
<button type="button" class="item-edit btn btn-xs btn-default"
data-toggle="modal" data-target="#itemModal">
<span class="glyphicon glyphicon-edit"></span>
</button>
<button type="button" class="item-delete btn btn-xs btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</button>
</td>
<td class="vert-align text-right">
<button type="button" class="item-edit btn btn-xs btn-default"
data-toggle="modal" data-target="#itemModal">
<span class="glyphicon glyphicon-edit"></span>
</button>
<button type="button" class="item-delete btn btn-xs btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</button>
</td>
{% endif %}
</tr>
</table>

View File

@@ -1,4 +1,6 @@
{% load to_class_name from filters %}
{# pass in variable "object" to this template #}
<a title="{% if object.is_rig == False %}Non-rig{% elif object.dry_hire %}Dry Hire{% elif object.is_rig %}Rig{%else%}{{object|to_class_name}}{% endif %} | '{{object.name}}'" href="{{ object.get_absolute_url }}">{% if object.is_rig == False %}Non-rig{% elif object.dry_hire %}Dry Hire{% elif object.is_rig %}Rig{%else%}{{object|to_class_name}}{% endif %} | '{{object.name}}'</a>
<a title="{% if object.is_rig == False %}Non-rig{% elif object.dry_hire %}Dry Hire{% elif object.is_rig %}Rig{% else %}{{ object|to_class_name }}{% endif %} | '{{ object.name }}'"
href="{{ object.get_absolute_url }}">{% if object.is_rig == False %}Non-rig{% elif object.dry_hire %}Dry
Hire{% elif object.is_rig %}Rig{% else %}{{ object|to_class_name }}{% endif %} | '{{ object.name }}'</a>

View File

@@ -13,7 +13,7 @@
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
</div>
{% endif %}
@@ -29,7 +29,8 @@
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
<dt>Email</dt>
<dd><a href="mailto:{{ object.email }}"><span class="overflow-ellipsis">{{ object.email }}</span></a></dd>
<dd><a href="mailto:{{ object.email }}"><span
class="overflow-ellipsis">{{ object.email }}</span></a></dd>
<dt>Address</dt>
<dd>{{ object.address|linebreaksbr }}</dd>
@@ -39,7 +40,7 @@
<dt>Union Account</dt>
<dd>{{ object.union_account|yesno|capfirst }}</dd>
</dl>
</dl>
</div>
</div>
</div>
@@ -50,7 +51,10 @@
<div class="panel-body">
<div class="list-group">
{% for person,count in object.persons %}
<a class="list-group-item" href="{% url 'person_detail' person.pk %}">{{ person.pk|stringformat:"05d" }} | {{ person.name }} <span class="badge" title="{{count}} events with {{person.name}} for {{object.name}}">{{count}}</span></a>
<a class="list-group-item"
href="{% url 'person_detail' person.pk %}">{{ person.pk|stringformat:"05d" }}
| {{ person.name }} <span class="badge"
title="{{ count }} events with {{ person.name }} for {{ object.name }}">{{ count }}</span></a>
{% endfor %}
</div>
</div>
@@ -77,7 +81,7 @@
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
<div>
<a href="{% url 'organisation_history' object.pk %}" title="View Revision History">
@@ -96,9 +100,9 @@
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'organisation_detail' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-eye-open"></span> Open Page</a>
class="glyphicon glyphicon-eye-open"></span> Open Page</a>
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
<div>
<a href="{% url 'organisation_history' object.pk %}" title="View Revision History">
@@ -108,4 +112,4 @@
</div>
</div>
{% endblock %}
{% endif %}
{% endif %}

View File

@@ -24,7 +24,7 @@
</div>
</form>
</div>
<div class="text-right col-sm-12">{% paginator %}</div>
<div class="text-right col-sm-12">{% paginator %}</div>
</div>
<div class="table-responsive">
<table class="table table-striped">
@@ -67,4 +67,4 @@
</div>
<div class="align-right">{% paginator %}</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -13,7 +13,7 @@
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'person_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
</div>
{% endif %}
@@ -29,14 +29,15 @@
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
<dt>Email</dt>
<dd><a href="mailto:{{ object.email }}"><span class="overflow-ellipsis">{{ object.email }}</span></a></dd>
<dd><a href="mailto:{{ object.email }}"><span
class="overflow-ellipsis">{{ object.email }}</span></a></dd>
<dt>Address</dt>
<dd>{{ object.address|linebreaksbr }}</dd>
<dt>Notes</dt>
<dd>{{ object.notes|linebreaksbr }}</dd>
</dl>
</dl>
</div>
</div>
</div>
@@ -47,7 +48,10 @@
<div class="panel-body">
<div class="list-group">
{% for organisation,count in object.organisations %}
<a class="list-group-item" href="{% url 'organisation_detail' organisation.pk %}">{{ organisation.pk|stringformat:"05d" }} | {{ organisation.name }} <span class="badge" title="{{count}} events with {{object.name}} for {{organisation.name}}">{{count}}</span></a>
<a class="list-group-item"
href="{% url 'organisation_detail' organisation.pk %}">{{ organisation.pk|stringformat:"05d" }}
| {{ organisation.name }} <span class="badge"
title="{{ count }} events with {{ object.name }} for {{ organisation.name }}">{{ count }}</span></a>
{% endfor %}
</div>
</div>
@@ -74,7 +78,7 @@
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'person_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
<div>
<a href="{% url 'person_history' object.pk %}" title="View Revision History">
@@ -93,9 +97,9 @@
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'person_detail' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-eye-open"></span> Open Page</a>
class="glyphicon glyphicon-eye-open"></span> Open Page</a>
<a href="{% url 'person_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
<div>
<a href="{% url 'person_history' object.pk %}" title="View Revision History">
@@ -105,4 +109,4 @@
</div>
</div>
{% endblock %}
{% endif %}
{% endif %}

View File

@@ -1,12 +1,13 @@
{# pass in variable "profile" to this template #}
<button title="{{profile.name}}" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover focus' data-toggle="popover" data-content='
<img src="{{profile.profile_picture}}" class="img-responsive img-rounded center-block" style="max-width:4em" />
<button title="{{ profile.name }}" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true"
data-trigger='hover focus' data-toggle="popover" data-content='
<img src="{{ profile.profile_picture }}" class="img-responsive img-rounded center-block" style="max-width:4em" />
<dl class="dl-vertical">
<dt>Email</dt>
<dd>{{profile.email}}</dd>
<dd>{{ profile.email }}</dd>
<dt>Phone</dt>
<dd>{{profile.phone}}</dd>
<dd>{{ profile.phone }}</dd>
</dl>
'>{{profile.first_name}}</button>
'>{{ profile.first_name }}</button>

View File

@@ -1,157 +1,171 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %}
{% block title %}RIGS Profile {{object.pk}}{% endblock %}
{% block title %}RIGS Profile {{ object.pk }}{% endblock %}
{% block js %}
<script>
$(document).ready(function() {
$('#urlParamForm').change(function(){
url = "?";
$('#urlParamForm *').filter(':input').each(function(index, value){
defaultVal = $(value).data('default');
param = $(value).val();
val = $(value).prop('checked');
$(document).ready(function () {
$('#urlParamForm').change(function () {
url = "?";
$('#urlParamForm *').filter(':input').each(function (index, value) {
defaultVal = $(value).data('default');
param = $(value).val();
val = $(value).prop('checked');
if(val != defaultVal){
url = url+param+"="+val+"&";
}
});
ics_url = $('#cal-url').data('url') + url.substring(0, url.length - 1);
$('#cal-url').text(ics_url);
if (val != defaultVal) {
url = url + param + "=" + val + "&";
}
});
ics_url = $('#cal-url').data('url') + url.substring(0, url.length - 1);
$('#cal-url').text(ics_url);
gcal_url = $('#gcal-link').data('url') + encodeURIComponent(url.substring(0, url.length - 1));
$('#gcal-link').attr('href',gcal_url);
});
gcal_url = $('#gcal-link').data('url') + encodeURIComponent(url.substring(0, url.length - 1));
$('#gcal-link').attr('href', gcal_url);
});
$('#urlParamForm').change(); //Do the initial setting
});
</script>
$('#urlParamForm').change(); //Do the initial setting
});
</script>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="col-sm-6">
<h3>{{object.name}}</h3>
</div>
{% if not request.is_ajax %}
{% if object.pk == user.pk %}
<div class="col-sm-6 text-right">
<div class="btn-group btn-page">
<a href="{% url 'profile_update_self' %}" class="btn btn-default">
Edit Profile <span class="glyphicon glyphicon-pencil"></span>
</a>
<a href="{% url 'password_change' %}" class="btn btn-default">
Change Password <span class="glyphicon glyphicon-lock"></span>
</a>
</div>
</div>
{% endif %}
{% endif %}
<div class="col-sm-8 ">
<dl class="dl-horizontal">
<dt>First Name</dt>
<dd>{{object.first_name}}</dd>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<dt>Last Name</dt>
<dd>{{object.last_name}}</dd>
<div class="col-sm-6">
<h3>{{ object.name }}</h3>
</div>
{% if not request.is_ajax %}
{% if object.pk == user.pk %}
<div class="col-sm-6 text-right">
<div class="btn-group btn-page">
<a href="{% url 'profile_update_self' %}" class="btn btn-default">
Edit Profile <span class="glyphicon glyphicon-pencil"></span>
</a>
<a href="{% url 'password_change' %}" class="btn btn-default">
Change Password <span class="glyphicon glyphicon-lock"></span>
</a>
</div>
</div>
{% endif %}
{% endif %}
<div class="col-sm-8 ">
<dl class="dl-horizontal">
<dt>First Name</dt>
<dd>{{ object.first_name }}</dd>
<dt>Email</dt>
<dd>{{object.email}}</dd>
<dt>Last Name</dt>
<dd>{{ object.last_name }}</dd>
<dt>Last Login</dt>
<dd>{{object.last_login|date:"d/m/Y H:i"}}</dd>
<dt>Email</dt>
<dd>{{ object.email }}</dd>
<dt>Date Joined</dt>
<dd>{{object.date_joined|date:"d/m/Y H:i"}}</dd>
<dt>Last Login</dt>
<dd>{{ object.last_login|date:"d/m/Y H:i" }}</dd>
<dt>Initials</dt>
<dd>{{object.initials}}</dd>
<dt>Date Joined</dt>
<dd>{{ object.date_joined|date:"d/m/Y H:i" }}</dd>
<dt>Phone</dt>
<dd><a href="tel:{{ object.phone }}">{{object.phone}}</a></dd>
</dl>
{% if not request.is_ajax %}
{% if object.pk == user.pk %}
<div class="pull-right">
<a href="{% url 'reset_api_key' %}" class="btn btn-default">
{% if user.api_key %}Reset API Key{% else %}Generate API Key{% endif %}
<span class="glyphicon glyphicon-repeat"></span>
</a>
</div>
<h4>Personal iCal Details</h4>
<dt>Initials</dt>
<dd>{{ object.initials }}</dd>
<dl class="dl-horizontal">
<dt>API Key</dt>
<dd>
{% if user.api_key %}
{{user.api_key}}
{% else %}
No API Key Generated
{% endif %}
</dd>
<dt>Phone</dt>
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
</dl>
{% if not request.is_ajax %}
{% if object.pk == user.pk %}
<dt>Calendar Options</dt>
<dd>
<div class="well well-sm text-center">
<form class="form-inline" id="urlParamForm">
<div class="form-group">
<label class="checkbox-inline">
<input type="checkbox" value="rig" data-default="true" checked> Rigs
</label>
<label class="checkbox-inline">
<input type="checkbox" value="non-rig" data-default="true" checked> Non-Rigs
</label>
<label class="checkbox-inline">
<input type="checkbox" value="dry-hire" data-default="true" checked> Dry-Hires
</label>
<label class="checkbox-inline">
<input type="checkbox" value="cancelled" data-default="false" > Cancelled
</label>
<label class="checkbox-inline">
<input type="checkbox" value="provisional" data-default="true" checked> Provisional
</label>
<label class="checkbox-inline">
<input type="checkbox" value="confirmed" data-default="true" checked> Confirmed/Booked
</label>
</div>
</form>
</div>
</dd>
<dt>Calendar URL</dt>
<dd>
{% if user.api_key %}
<pre id="cal-url" data-url="http{{ request.is_secure|yesno:"s,"}}://{{ request.get_host }}{% url 'ics_calendar' api_pk=user.pk api_key=user.api_key %}"></pre>
<small><a id="gcal-link" data-url="https://support.google.com/calendar/answer/37100" href="">Click here</a> for instructions on adding to google calendar.<br/>
To sync from google calendar to mobile device, visit <a href="https://www.google.com/calendar/syncselect" target="_blank">this page</a> on your device and tick "RIGS Calendar".</small>
{% else %}
<pre>No API Key Generated</pre>
{% endif %}
</dd>
</dl>
{% endif %}
{% endif %}
</div>
<div class="col-sm-3">
<div class="center-block">
<img src="{{object.profile_picture}}" class="img-responsive img-rounded" />
</div>
<div class="pull-right">
<a href="{% url 'reset_api_key' %}" class="btn btn-default">
{% if user.api_key %}Reset API Key{% else %}Generate API Key{% endif %}
<span class="glyphicon glyphicon-repeat"></span>
</a>
</div>
</div>
<h4>Personal iCal Details</h4>
<div class="row">
<div class="col-sm-12">
<h4>Events</h4>
{% with object.latest_events as events %}
{% include 'RIGS/event_table.html' %}
{% endwith %}
</div>
</div>
</div>
</div>
{% endblock %}
<dl class="dl-horizontal">
<dt>API Key</dt>
<dd>
{% if user.api_key %}
{{ user.api_key }}
{% else %}
No API Key Generated
{% endif %}
</dd>
<dt>Calendar Options</dt>
<dd>
<div class="well well-sm text-center">
<form class="form-inline" id="urlParamForm">
<div class="form-group">
<label class="checkbox-inline">
<input type="checkbox" value="rig" data-default="true" checked> Rigs
</label>
<label class="checkbox-inline">
<input type="checkbox" value="non-rig" data-default="true" checked>
Non-Rigs
</label>
<label class="checkbox-inline">
<input type="checkbox" value="dry-hire" data-default="true" checked>
Dry-Hires
</label>
<label class="checkbox-inline">
<input type="checkbox" value="cancelled" data-default="false"> Cancelled
</label>
<label class="checkbox-inline">
<input type="checkbox" value="provisional" data-default="true" checked>
Provisional
</label>
<label class="checkbox-inline">
<input type="checkbox" value="confirmed" data-default="true" checked>
Confirmed/Booked
</label>
</div>
</form>
</div>
</dd>
<dt>Calendar URL</dt>
<dd>
{% if user.api_key %}
<pre id="cal-url" data-url="http{{ request.is_secure|yesno:"s," }}://
{{ request.get_host }}{% url 'ics_calendar' api_pk=user.pk api_key=user.api_key %}"></pre>
<small><a id="gcal-link" data-url="https://support.google.com/calendar/answer/37100"
href="">Click here</a> for instructions on adding to google calendar.<br/>
To sync from google calendar to mobile device, visit <a
href="https://www.google.com/calendar/syncselect" target="_blank">this
page</a> on your device and tick "RIGS Calendar".
</small>
{% else %}
<pre>No API Key Generated</pre>
{% endif %}
</dd>
</dl>
{% endif %}
{% endif %}
</div>
<div class="col-sm-3">
<div class="center-block">
<img src="{{ object.profile_picture }}" class="img-responsive img-rounded"/>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<h4>Events</h4>
{% with object.latest_events as events %}
{% include 'RIGS/event_table.html' %}
{% endwith %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,71 +1,77 @@
{% extends 'base.html' %}
{% load widget_tweaks %}
{% block title %}Update Profile {{object.name}}{% endblock %}
{% block title %}Update Profile {{ object.name }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
{% include 'form_errors.html' %}
<h3>Update Profile</h3>
<div class="col-sm-6">
<form action="{{form.action|default:request.path}}" method="post" class="form-horizontal">{% csrf_token %}
<div class="form-group">
<label for="{{form.first_name.id_for_label}}" class="col-sm-4 control-label">{{form.first_name.label}}</label>
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
{% include 'form_errors.html' %}
<h3>Update Profile</h3>
<div class="col-sm-6">
<form action="{{ form.action|default:request.path }}" method="post"
class="form-horizontal">{% csrf_token %}
<div class="form-group">
<label for="{{ form.first_name.id_for_label }}"
class="col-sm-4 control-label">{{ form.first_name.label }}</label>
<div class="col-sm-8">
{% render_field form.first_name class+="form-control" placeholder=form.first_name.label %}
</div>
</div>
<div class="col-sm-8">
{% render_field form.first_name class+="form-control" placeholder=form.first_name.label %}
</div>
</div>
<div class="form-group">
<label for="{{form.last_name.id_for_label}}" class="col-sm-4 control-label">{{form.last_name.label}}</label>
<div class="form-group">
<label for="{{ form.last_name.id_for_label }}"
class="col-sm-4 control-label">{{ form.last_name.label }}</label>
<div class="col-sm-8">
{% render_field form.last_name class+="form-control" placeholder=form.last_name.label %}
</div>
</div>
<div class="col-sm-8">
{% render_field form.last_name class+="form-control" placeholder=form.last_name.label %}
</div>
</div>
<div class="form-group">
<label for="{{form.email.id_for_label}}" class="col-sm-4 control-label">{{form.email.label}}</label>
<div class="form-group">
<label for="{{ form.email.id_for_label }}"
class="col-sm-4 control-label">{{ form.email.label }}</label>
<div class="col-sm-8">
{% render_field form.email type="email" class+="form-control" placeholder=form.email.label %}
</div>
</div>
<div class="col-sm-8">
{% render_field form.email type="email" class+="form-control" placeholder=form.email.label %}
</div>
</div>
<div class="form-group">
<label for="{{form.initials.id_for_label}}" class="col-sm-4 control-label">{{form.initials.label}}</label>
<div class="form-group">
<label for="{{ form.initials.id_for_label }}"
class="col-sm-4 control-label">{{ form.initials.label }}</label>
<div class="col-sm-8">
{% render_field form.initials class+="form-control" placeholder=form.initials.label %}
</div>
</div>
<div class="col-sm-8">
{% render_field form.initials class+="form-control" placeholder=form.initials.label %}
</div>
</div>
<div class="form-group">
<label for="{{form.phone.id_for_label}}" class="col-sm-4 control-label">{{form.phone.label}}</label>
<div class="form-group">
<label for="{{ form.phone.id_for_label }}"
class="col-sm-4 control-label">{{ form.phone.label }}</label>
<div class="col-sm-8">
{% render_field form.phone type="tel" class+="form-control" placeholder=form.phone.label %}
</div>
</div>
<div class="col-sm-8">
{% render_field form.phone type="tel" class+="form-control" placeholder=form.phone.label %}
</div>
</div>
<div class="form-group">
<input class="btn btn-primary pull-right" type="submit"/>
</div>
</form>
</div>
<div class="form-group">
<input class="btn btn-primary pull-right" type="submit"/>
</div>
</form>
</div>
<div class="col-sm-3 col-sm-offset-2">
<div class="center-block">
<a href="https://gravatar.com/">
<img src="{{object.profile_picture}}" class="img-responsive img-rounded" />
<div class="text-center">
Images hosted by Gravatar
</div>
</a>
</div>
</div>
</div>
</div>
{% endblock %}
<div class="col-sm-3 col-sm-offset-2">
<div class="center-block">
<a href="https://gravatar.com/">
<img src="{{ object.profile_picture }}" class="img-responsive img-rounded"/>
<div class="text-center">
Images hosted by Gravatar
</div>
</a>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -8,14 +8,14 @@
<div class="row">
<div class="col-sm-10">
<h3>Rigboard</h3>
</div>
{% if perms.RIGS.add_event %}
<div class="col-sm-2">
<a href="{% url 'event_create' %}" class="btn btn-default pull-right">New <span
class="glyphicon glyphicon-plus"></span></a>
</div>
{% if perms.RIGS.add_event %}
<div class="col-sm-2">
<a href="{% url 'event_create' %}" class="btn btn-default pull-right">New <span
class="glyphicon glyphicon-plus"></span></a>
</div>
{% endif %}
{% comment %}
{# Bring search back at a later date #}
<div class="col-sm-3 col-sm-offset-9">
@@ -33,4 +33,4 @@
{% include 'RIGS/event_table.html' %}
</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -13,7 +13,7 @@
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'venue_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
</div>
{% endif %}
@@ -29,7 +29,8 @@
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
<dt>Email</dt>
<dd><a href="mailto:{{ object.email }}"><span class="overflow-ellipsis">{{ object.email }}</span></a></dd>
<dd><a href="mailto:{{ object.email }}"><span
class="overflow-ellipsis">{{ object.email }}</span></a></dd>
<dt>Address</dt>
<dd>{{ object.address|linebreaksbr }}</dd>
@@ -39,7 +40,7 @@
<dt>Three Phase Available</dt>
<dd>{{ object.three_phase_available|yesno|capfirst }}</dd>
</dl>
</dl>
</div>
</div>
</div>
@@ -63,7 +64,7 @@
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'venue_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
<div>
<a href="{% url 'venue_history' object.pk %}" title="View Revision History">
@@ -83,9 +84,9 @@
<div class="col-sm-12 text-right">
<div class="btn-group btn-page">
<a href="{% url 'venue_detail' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-eye-open"></span> Open Page</a>
class="glyphicon glyphicon-eye-open"></span> Open Page</a>
<a href="{% url 'venue_update' object.pk %}" class="btn btn-default"><span
class="glyphicon glyphicon-pencil"></span> Edit</a>
class="glyphicon glyphicon-pencil"></span> Edit</a>
</div>
<div>
<a href="{% url 'venue_history' object.pk %}" title="View Revision History">
@@ -95,4 +96,4 @@
</div>
</div>
{% endblock %}
{% endif %}
{% endif %}

View File

@@ -69,14 +69,14 @@
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<input class="btn btn-primary pull-right" type="submit"/>
</div>
</form>
</div>
{% endblock %}
{% endblock %}

View File

@@ -26,7 +26,7 @@
</div>
<div class="text-right col-sm-12">{% paginator %}</div>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
@@ -66,4 +66,4 @@
</div>
<div class="align-right">{% paginator %}</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -1,13 +1,18 @@
{% for change in version.field_changes %}
<button title="Changes to {{ change.field.verbose_name }}" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='{% spaceless %}
<button title="Changes to {{ change.field.verbose_name }}" type="button" class="btn btn-default btn-xs"
data-container="body" data-html="true" data-trigger='hover' data-toggle="popover"
data-content='{% spaceless %}
{% include "RIGS/version_changes_change.html" %}
{% endspaceless %}'>{{ change.field.verbose_name }}</button>
{% endfor %}
{% for itemChange in version.item_changes %}
<button title="Changes to item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'" type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='{% spaceless %}
<button title="Changes to item '
{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'"
type="button" class="btn btn-default btn-xs" data-container="body" data-html="true" data-trigger='hover'
data-toggle="popover" data-content='{% spaceless %}
<ul class="list-group">
{% for change in itemChange.changes %}
<li class="list-group-item">
@@ -16,6 +21,8 @@
</li>
{% endfor %}
</ul>
{% endspaceless %}'>item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'</button>
{% endfor %}
{% endspaceless %}'>item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}
{{ itemChange.old.name }}{% endif %}'
</button>
{% endfor %}

View File

@@ -4,18 +4,18 @@
{% if diff.type == "insert" %}
<ins>{{ diff.text|linebreaksbr }}</ins>
{% elif diff.type == "delete" %}
<del>{{diff.text|linebreaksbr}}</del>
<del>{{ diff.text|linebreaksbr }}</del>
{% else %}
<span>{{diff.text|linebreaksbr}}</span>
<span>{{ diff.text|linebreaksbr }}</span>
{% endif %}
{% endfor %}
{% else %}
{% else %}
{% if change.old %}
<del {% if change.long %}class="overflow-ellipsis"{% endif %}>
{% if change.linebreaks %}
{{change.old|linebreaksbr}}
{{ change.old|linebreaksbr }}
{% else %}
{{change.old}}
{{ change.old }}
{% endif %}
</del>
{% endif %}
@@ -25,9 +25,9 @@
{% if change.new %}
<ins {% if change.long %}class="overflow-ellipsis"{% endif %}>
{% if change.linebreaks %}
{{change.new|linebreaksbr}}
{{ change.new|linebreaksbr }}
{% else %}
{{change.new}}
{{ change.new }}
{% endif %}
</ins>
{% endif %}

View File

@@ -3,32 +3,34 @@
{% load paginator from filters %}
{% load static %}
{% block title %}{{object|to_class_name}} {{ object.pk|stringformat:"05d" }} - Revision History{% endblock %}
{% block title %}{{ object|to_class_name }} {{ object.pk|stringformat:"05d" }} - Revision History{% endblock %}
{% block js %}
<script src="{% static "js/tooltip.js" %}"></script>
<script src="{% static "js/popover.js" %}"></script>
<script>
$(function () {
$('[data-toggle="popover"]').popover().click(function(){
if($(this).attr('href')){
window.location.href = $(this).attr('href');
}
});
$(function () {
$('[data-toggle="popover"]').popover().click(function () {
if ($(this).attr('href')) {
window.location.href = $(this).attr('href');
}
});
})
</script>
{% endblock %}
{% block content %}
<div class="col-sm-12">
<div class="col-sm-12">
<div class="row">
<div class="col-sm-12">
<h3><a href="{{ object.get_absolute_url }}">{{object|to_class_name}} {{ object.pk|stringformat:"05d" }}</a> - Revision History</h3>
<h3>
<a href="{{ object.get_absolute_url }}">{{ object|to_class_name }} {{ object.pk|stringformat:"05d" }}</a>
- Revision History</h3>
</div>
<div class="text-right col-sm-12">{% paginator %}</div>
</div>
<div class="table-responsive">
<table class="table table-striped">
<table class="table table-striped">
<thead>
<tr>
<td>Date</td>
@@ -39,29 +41,29 @@
</tr>
</thead>
<tbody>
{% for version in object_list %}
{% if version.item_changes or version.field_changes or version.old == None %}
<tr>
<td>{{ version.revision.date_created }}</td>
<td>{{ version.version.pk }}|{{ version.revision.pk }}</td>
<td>{{ version.revision.user.name }}</td>
<td>
{% if version.old == None %}
{{object|to_class_name}} Created
{% else %}
{% include 'RIGS/version_changes.html' %}
{% endif %}
</td>
<td>
{{ version.revision.comment }}
</td>
</tr>
{% endif %}
{% endfor %}
{% for version in object_list %}
{% if version.item_changes or version.field_changes or version.old == None %}
<tr>
<td>{{ version.revision.date_created }}</td>
<td>{{ version.version.pk }}|{{ version.revision.pk }}</td>
<td>{{ version.revision.user.name }}</td>
<td>
{% if version.old == None %}
{{ object|to_class_name }} Created
{% else %}
{% include 'RIGS/version_changes.html' %}
{% endif %}
</td>
<td>
{{ version.revision.comment }}
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
<div class="align-right">{% paginator %}</div>
</div>
{% endblock %}
</div>
<div class="align-right">{% paginator %}</div>
</div>
{% endblock %}

View File

@@ -1,18 +1,21 @@
from django import template
from django import forms
from django import template
from django.forms.forms import NON_FIELD_ERRORS
from django.forms.utils import ErrorDict
register = template.Library()
@register.filter
def multiply(value, arg):
return value*arg
return value * arg
@register.filter
def to_class_name(value):
return value.__class__.__name__
@register.filter
def nice_errors(form, non_field_msg='General form errors'):
nice_errors = ErrorDict()
@@ -25,6 +28,7 @@ def nice_errors(form, non_field_msg='General form errors'):
nice_errors[key] = errors
return nice_errors
def paginator(context, adjacent_pages=3):
"""
To be used in conjunction with the object_list generic view.
@@ -41,7 +45,7 @@ def paginator(context, adjacent_pages=3):
endPage = page.number + adjacent_pages + 1
if endPage >= paginator.num_pages - 1: endPage = paginator.num_pages + 1
page_numbers = [n for n in range(startPage, endPage) \
if n > 0 and n <= paginator.num_pages]
if n > 0 and n <= paginator.num_pages]
dict = {
'request': context['request'],
@@ -57,27 +61,29 @@ def paginator(context, adjacent_pages=3):
'has_next': page.has_next(),
'has_previous': page.has_previous(),
}
if page.has_next():
dict['next'] = page.next_page_number()
if page.has_previous():
dict['previous'] = page.previous_page_number()
return dict
register.inclusion_tag('pagination.html', takes_context=True)(paginator)
@register.simple_tag
def url_replace(request, field, value):
dict_ = request.GET.copy()
dict_[field] = value
return dict_.urlencode()
@register.simple_tag
def orderby(request, field, attr):
dict_ = request.GET.copy()
if dict_.__contains__(field) and dict_[field] == attr:
@@ -88,4 +94,4 @@ def orderby(request, field, attr):
else:
dict_[field] = attr
return dict_.urlencode()
return dict_.urlencode()

View File

@@ -17,10 +17,9 @@ from RIGS import models
class UserRegistrationTest(LiveServerTestCase):
def setUp(self):
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3) # Set implicit wait session wide
self.browser.implicitly_wait(3) # Set implicit wait session wide
os.environ['RECAPTCHA_TESTING'] = 'True'
def tearDown(self):
@@ -149,17 +148,17 @@ class UserRegistrationTest(LiveServerTestCase):
class EventTest(LiveServerTestCase):
def setUp(self):
self.profile = models.Profile(
username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True)
self.profile.set_password("EventTestPassword")
self.profile.save()
self.vatrate = models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1')
self.vatrate = models.VatRate.objects.create(
start_at='2014-03-05', rate=0.20, comment='test1')
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3) # Set implicit wait session wide
self.browser.implicitly_wait(3) # Set implicit wait session wide
self.browser.maximize_window()
os.environ['RECAPTCHA_TESTING'] = 'True'
@@ -203,7 +202,8 @@ class EventTest(LiveServerTestCase):
# Gets redirected to login and back
self.authenticate('/event/create/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
# setup WebDriverWait to use later (to wait for animations)
wait = WebDriverWait(self.browser, 10)
wait.until(animation_is_finished())
@@ -229,7 +229,9 @@ class EventTest(LiveServerTestCase):
modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
self.assertIn(
"Add Person",
modal.find_element_by_tag_name('h3').text)
# Fill person form out and submit
modal.find_element_by_xpath(
@@ -254,7 +256,9 @@ class EventTest(LiveServerTestCase):
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Person", modal.find_element_by_tag_name('h3').text)
self.assertIn(
"Add Person",
modal.find_element_by_tag_name('h3').text)
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2")
@@ -290,7 +294,9 @@ class EventTest(LiveServerTestCase):
'//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click()
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Edit Person", modal.find_element_by_tag_name('h3').text)
self.assertIn(
"Edit Person",
modal.find_element_by_tag_name('h3').text)
name = modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]')
self.assertEqual(person1.name, name.get_attribute('value'))
@@ -313,7 +319,8 @@ class EventTest(LiveServerTestCase):
modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Organisation", modal.find_element_by_tag_name('h3').text)
self.assertIn("Add Organisation",
modal.find_element_by_tag_name('h3').text)
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation")
modal.find_element_by_xpath(
@@ -340,7 +347,9 @@ class EventTest(LiveServerTestCase):
modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed())
self.assertIn("Add Venue", modal.find_element_by_tag_name('h3').text)
self.assertIn(
"Add Venue",
modal.find_element_by_tag_name('h3').text)
modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue")
modal.find_element_by_xpath(
@@ -366,51 +375,72 @@ class EventTest(LiveServerTestCase):
form.find_element_by_id('id_end_time').send_keys('07:00')
# Add item
form.find_element_by_xpath('//button[contains(@class, "item-add")]').click()
form.find_element_by_xpath(
'//button[contains(@class, "item-add")]').click()
wait.until(animation_is_finished())
modal = self.browser.find_element_by_id("itemModal")
modal.find_element_by_id("item_name").send_keys("Test Item 1")
modal.find_element_by_id("item_description").send_keys("This is an item description\nthat for reasons unkown spans two lines")
modal.find_element_by_id("item_description").send_keys(
"This is an item description\nthat for reasons unkown spans two lines")
e = modal.find_element_by_id("item_quantity")
e.click()
e.send_keys(Keys.UP)
e.send_keys(Keys.UP)
e = modal.find_element_by_id("item_cost")
e.send_keys("23.95")
e.send_keys(Keys.ENTER) # enter submit
e.send_keys(Keys.ENTER) # enter submit
# Confirm item has been saved to json field
objectitems = self.browser.execute_script("return objectitems;")
self.assertEqual(1, len(objectitems))
testitem = objectitems["-1"]['fields'] # as we are deliberately creating this we know the ID
# as we are deliberately creating this we know the ID
testitem = objectitems["-1"]['fields']
self.assertEqual("Test Item 1", testitem['name'])
self.assertEqual("2", testitem['quantity']) # test a couple of "worse case" fields
# test a couple of "worse case" fields
self.assertEqual("2", testitem['quantity'])
# See new item appear in table
row = self.browser.find_element_by_id('item--1') # ID number is known, see above
self.assertIn("Test Item 1", row.find_element_by_xpath('//span[@class="name"]').text)
self.assertIn("This is an item description", row.find_element_by_xpath('//div[@class="item-description"]').text)
self.assertEqual(u'£ 23.95', row.find_element_by_xpath('//tr[@id="item--1"]/td[2]').text)
self.assertEqual("2", row.find_element_by_xpath('//td[@class="quantity"]').text)
self.assertEqual(u'£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text)
row = self.browser.find_element_by_id(
'item--1') # ID number is known, see above
self.assertIn(
"Test Item 1",
row.find_element_by_xpath('//span[@class="name"]').text)
self.assertIn(
"This is an item description",
row.find_element_by_xpath('//div[@class="item-description"]').text)
self.assertEqual(u'£ 23.95', row.find_element_by_xpath(
'//tr[@id="item--1"]/td[2]').text)
self.assertEqual("2", row.find_element_by_xpath(
'//td[@class="quantity"]').text)
self.assertEqual(u'£ 47.90', row.find_element_by_xpath(
'//tr[@id="item--1"]/td[4]').text)
# Check totals
self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text)
self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text)
self.assertEqual("9.58", self.browser.find_element_by_id('vat').text)
self.assertEqual("57.48", self.browser.find_element_by_id('total').text)
self.assertEqual(
"47.90", self.browser.find_element_by_id('sumtotal').text)
self.assertIn(
"(TBC)",
self.browser.find_element_by_id('vat-rate').text)
self.assertEqual(
"9.58", self.browser.find_element_by_id('vat').text)
self.assertEqual(
"57.48", self.browser.find_element_by_id('total').text)
# Attempt to save - missing title
save.click()
# See error
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
error = self.browser.find_element_by_xpath(
'//div[contains(@class, "alert-danger")]')
self.assertTrue(error.is_displayed())
# Should only have one error message
self.assertEqual("Name", error.find_element_by_xpath('//dt[1]').text)
self.assertEqual("This field is required.", error.find_element_by_xpath('//dd[1]/ul/li').text)
self.assertEqual(
"Name", error.find_element_by_xpath('//dt[1]').text)
self.assertEqual("This field is required.",
error.find_element_by_xpath('//dd[1]/ul/li').text)
# don't need error so close it
error.find_element_by_xpath('//div[contains(@class, "alert-danger")]//button[@class="close"]').click()
error.find_element_by_xpath(
'//div[contains(@class, "alert-danger")]//button[@class="close"]').click()
try:
self.assertFalse(error.is_displayed())
except StaleElementReferenceException:
@@ -431,84 +461,102 @@ 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("N0000%d | 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
pass
def testEventDuplicate(self):
testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, start_date=date.today() + timedelta(days=6), description="start future no end")
testEvent = models.Event.objects.create(
name="TE E1",
status=models.Event.PROVISIONAL,
start_date=date.today() +
timedelta(
days=6),
description="start future no end")
item1 = models.EventItem(
event=testEvent,
name="Test Item 1",
cost="10.00",
quantity="1",
order=1
).save()
event=testEvent,
name="Test Item 1",
cost="10.00",
quantity="1",
order=1
).save()
item2 = models.EventItem(
event=testEvent,
name="Test Item 2",
description="Foo",
cost="9.72",
quantity="3",
order=2,
).save()
event=testEvent,
name="Test Item 2",
description="Foo",
cost="9.72",
quantity="3",
order=2,
).save()
self.browser.get(self.live_server_url + '/event/' + str(testEvent.pk) + '/duplicate/')
self.browser.get(self.live_server_url + '/event/' +
str(testEvent.pk) + '/duplicate/')
self.authenticate('/event/' + str(testEvent.pk) + '/duplicate/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
# setup WebDriverWait to use later (to wait for animations)
wait = WebDriverWait(self.browser, 10)
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
form = self.browser.find_element_by_tag_name('form')
# Check the items are visible
table = self.browser.find_element_by_id('item-table') # ID number is known, see above
table = self.browser.find_element_by_id(
'item-table') # ID number is known, see above
self.assertIn("Test Item 1", table.text)
self.assertIn("Test Item 2", table.text)
# Add item
form.find_element_by_xpath('//button[contains(@class, "item-add")]').click()
form.find_element_by_xpath(
'//button[contains(@class, "item-add")]').click()
wait.until(animation_is_finished())
modal = self.browser.find_element_by_id("itemModal")
modal.find_element_by_id("item_name").send_keys("Test Item 3")
modal.find_element_by_id("item_description").send_keys("This is an item description\nthat for reasons unkown spans two lines")
modal.find_element_by_id("item_description").send_keys(
"This is an item description\nthat for reasons unkown spans two lines")
e = modal.find_element_by_id("item_quantity")
e.click()
e.send_keys(Keys.UP)
e.send_keys(Keys.UP)
e = modal.find_element_by_id("item_cost")
e.send_keys("23.95")
e.send_keys(Keys.ENTER) # enter submit
e.send_keys(Keys.ENTER) # enter submit
# Attempt to save
save.click()
self.assertNotIn("N0000%d"%testEvent.pk, self.browser.find_element_by_xpath('//h1').text)
self.assertNotIn(
"N0000%d" %
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
table = self.browser.find_element_by_id(
'item-table') # ID number is known, see above
self.assertIn("Test Item 1", table.text)
self.assertIn("Test Item 2", table.text)
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)
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
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)
# 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)
# Check the items are as they were
table = self.browser.find_element_by_id('item-table') # ID number is known, see above
table = self.browser.find_element_by_id(
'item-table') # ID number is known, see above
self.assertIn("Test Item 1", table.text)
self.assertIn("Test Item 2", table.text)
self.assertNotIn("Test Item 3", table.text)
@@ -518,7 +566,8 @@ class EventTest(LiveServerTestCase):
# Gets redirected to login and back
self.authenticate('/event/create/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
# setup WebDriverWait to use later (to wait for animations)
wait = WebDriverWait(self.browser, 10)
wait.until(animation_is_finished())
@@ -526,7 +575,8 @@ class EventTest(LiveServerTestCase):
self.browser.find_element_by_xpath('//button[.="Rig"]').click()
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
# Set title
e = self.browser.find_element_by_id('id_name')
@@ -541,14 +591,16 @@ class EventTest(LiveServerTestCase):
# Attempt to save - should fail
save.click()
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
error = self.browser.find_element_by_xpath(
'//div[contains(@class, "alert-danger")]')
self.assertTrue(error.is_displayed())
self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text)
self.assertIn("can't finish before it has started",
error.find_element_by_xpath('//dd[1]/ul/li').text)
# Same date, end time before start time
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
@@ -563,14 +615,16 @@ class EventTest(LiveServerTestCase):
# Attempt to save - should fail
save.click()
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
error = self.browser.find_element_by_xpath(
'//div[contains(@class, "alert-danger")]')
self.assertTrue(error.is_displayed())
self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text)
self.assertIn("can't finish before it has started",
error.find_element_by_xpath('//dd[1]/ul/li').text)
# Same date, end time before start time
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
@@ -583,10 +637,10 @@ class EventTest(LiveServerTestCase):
form.find_element_by_id('id_end_time').clear()
form.find_element_by_id('id_end_time').send_keys('06:00')
# No end date, end time before start time
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
@@ -600,14 +654,16 @@ class EventTest(LiveServerTestCase):
# Attempt to save - should fail
save.click()
error = self.browser.find_element_by_xpath('//div[contains(@class, "alert-danger")]')
error = self.browser.find_element_by_xpath(
'//div[contains(@class, "alert-danger")]')
self.assertTrue(error.is_displayed())
self.assertIn("can't finish before it has started", error.find_element_by_xpath('//dd[1]/ul/li').text)
self.assertIn("can't finish before it has started",
error.find_element_by_xpath('//dd[1]/ul/li').text)
# 2 dates, end after start
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
form.find_element_by_id('id_start_date').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24')
@@ -615,24 +671,26 @@ class EventTest(LiveServerTestCase):
form.find_element_by_id('id_end_date').send_keys('3015-04-26')
form.find_element_by_id('id_start_time').clear()
form.find_element_by_id('id_end_time').clear()
# Attempt to save - should succeed
save.click()
# 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("N0000%d | Test Event Name" % event.pk, successTitle)
def testRigNonRig(self):
self.browser.get(self.live_server_url + '/event/create/')
# Gets redirected to login and back
self.authenticate('/event/create/')
wait = WebDriverWait(self.browser, 10) #setup WebDriverWait to use later (to wait for animations)
self.browser.implicitly_wait(3) #Set session-long wait (only works for non-existant DOM objects)
# setup WebDriverWait to use later (to wait for animations)
wait = WebDriverWait(self.browser, 10)
# Set session-long wait (only works for non-existant DOM objects)
self.browser.implicitly_wait(3)
wait.until(animation_is_finished())
@@ -643,7 +701,8 @@ class EventTest(LiveServerTestCase):
self.browser.find_element_by_xpath('//button[.="Rig"]').click()
form = self.browser.find_element_by_tag_name('form')
save = self.browser.find_element_by_xpath('(//button[@type="submit"])[3]')
save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]')
# Set title
e = self.browser.find_element_by_id('id_name')
@@ -655,16 +714,23 @@ class EventTest(LiveServerTestCase):
# Save the rig
save.click()
detail_panel = self.browser.find_element_by_xpath("//div[@id='content']/div/div[6]/div/div")
detail_panel = self.browser.find_element_by_xpath(
"//div[@id='content']/div/div[6]/div/div")
self.assertTrue(detail_panel.is_displayed())
self.assertIn("Event Detail", detail_panel.text)
def testEventDetail(self):
with transaction.atomic(), reversion.create_revision():
person = models.Person(name="Event Detail Person", email="eventdetail@person.tests.rigs", phone="123 123")
person = models.Person(
name="Event Detail Person",
email="eventdetail@person.tests.rigs",
phone="123 123")
person.save()
with transaction.atomic(), reversion.create_revision():
organisation = models.Organisation(name="Event Detail Organisation", email="eventdetail@organisation.tests.rigs", phone="123 456").save()
organisation = models.Organisation(
name="Event Detail Organisation",
email="eventdetail@organisation.tests.rigs",
phone="123 456").save()
with transaction.atomic(), reversion.create_revision():
venue = models.Venue(name="Event Detail Venue").save()
with transaction.atomic(), reversion.create_revision():
@@ -694,59 +760,182 @@ class EventTest(LiveServerTestCase):
order=2,
).save()
self.browser.get(self.live_server_url + '/event/%d' % event.pk)
self.authenticate('/event/%d/' % event.pk)
self.assertIn("N%05d | %s" % (event.pk, event.name),
self.browser.find_element_by_xpath('//h1').text)
self.browser.get(self.live_server_url + '/event/%d'%event.pk)
self.authenticate('/event/%d/'%event.pk)
self.assertIn("N%05d | %s"%(event.pk, event.name), self.browser.find_element_by_xpath('//h1').text)
personPanel = self.browser.find_element_by_xpath(
'//div[contains(text(), "Contact Details")]/..')
self.assertEqual(person.name, personPanel.find_element_by_xpath(
'//dt[text()="Person"]/following-sibling::dd[1]').text)
self.assertEqual(person.email, personPanel.find_element_by_xpath(
'//dt[text()="Email"]/following-sibling::dd[1]').text)
self.assertEqual(person.phone, personPanel.find_element_by_xpath(
'//dt[text()="Phone Number"]/following-sibling::dd[1]').text)
personPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Contact Details")]/..')
self.assertEqual(person.name, personPanel.find_element_by_xpath('//dt[text()="Person"]/following-sibling::dd[1]').text)
self.assertEqual(person.email, personPanel.find_element_by_xpath('//dt[text()="Email"]/following-sibling::dd[1]').text)
self.assertEqual(person.phone, personPanel.find_element_by_xpath('//dt[text()="Phone Number"]/following-sibling::dd[1]').text)
organisationPanel = self.browser.find_element_by_xpath(
'//div[contains(text(), "Contact Details")]/..')
organisationPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Contact Details")]/..')
class IcalTest(LiveServerTestCase):
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)
self.not_current_events = set(
self.all_events) - set(self.current_events)
self.vatrate = models.VatRate.objects.create(start_at='2014-03-05',rate=0.20,comment='test1')
self.vatrate = models.VatRate.objects.create(
start_at='2014-03-05', rate=0.20, comment='test1')
self.profile = models.Profile(
username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True)
self.profile.set_password("EventTestPassword")
self.profile.save()
# produce 7 normal events - 5 current - 1 last week - 1 two years ago - 2 provisional - 2 confirmed - 3 booked
models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, start_date=date.today() + timedelta(days=6), description="start future no end")
models.Event.objects.create(name="TE E2", status=models.Event.PROVISIONAL, start_date=date.today(), description="start today no end")
models.Event.objects.create(name="TE E3", status=models.Event.CONFIRMED, start_date=date.today(), end_date=date.today(), description="start today with end today")
models.Event.objects.create(name="TE E4", status=models.Event.CONFIRMED, start_date=date.today()-timedelta(weeks=104), description="start past 2 years no end")
models.Event.objects.create(name="TE E5", status=models.Event.BOOKED, start_date=date.today()-timedelta(days=7), end_date=date.today()-timedelta(days=1), description="start past 1 week with end past")
models.Event.objects.create(name="TE E6", status=models.Event.BOOKED, 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", status=models.Event.BOOKED, start_date=date.today()+timedelta(days=2), end_date=date.today()+timedelta(days=2), description="start + end in future")
# produce 7 normal events - 5 current - 1 last week - 1 two years ago -
# 2 provisional - 2 confirmed - 3 booked
models.Event.objects.create(
name="TE E1",
status=models.Event.PROVISIONAL,
start_date=date.today() +
timedelta(
days=6),
description="start future no end")
models.Event.objects.create(
name="TE E2",
status=models.Event.PROVISIONAL,
start_date=date.today(),
description="start today no end")
models.Event.objects.create(
name="TE E3",
status=models.Event.CONFIRMED,
start_date=date.today(),
end_date=date.today(),
description="start today with end today")
models.Event.objects.create(
name="TE E4",
status=models.Event.CONFIRMED,
start_date=date.today() -
timedelta(
weeks=104),
description="start past 2 years no end")
models.Event.objects.create(
name="TE E5",
status=models.Event.BOOKED,
start_date=date.today() -
timedelta(
days=7),
end_date=date.today() -
timedelta(
days=1),
description="start past 1 week with end past")
models.Event.objects.create(
name="TE E6",
status=models.Event.BOOKED,
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",
status=models.Event.BOOKED,
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")
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")
# 5 dry hire - 3 current - 1 cancelled
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")
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")
# 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")
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")
self.browser = webdriver.Firefox()
self.browser.implicitly_wait(3) # Set implicit wait session wide
self.browser.implicitly_wait(3) # Set implicit wait session wide
os.environ['RECAPTCHA_TESTING'] = 'True'
def tearDown(self):
@@ -777,24 +966,34 @@ class IcalTest(LiveServerTestCase):
# Completes and comes back to /user/
# Checks that no api key is displayed
self.assertEqual("No API Key Generated", self.browser.find_element_by_xpath("//div[@id='content']/div/div/div[3]/dl[2]/dd").text)
self.assertEqual("No API Key Generated", self.browser.find_element_by_css_selector("pre").text)
self.assertEqual("No API Key Generated", self.browser.find_element_by_xpath(
"//div[@id='content']/div/div/div[3]/dl[2]/dd").text)
self.assertEqual("No API Key Generated",
self.browser.find_element_by_css_selector("pre").text)
# Now creates an API key, and check a URL is displayed one
self.browser.find_element_by_link_text("Generate API Key").click()
self.assertIn("rigs.ics", self.browser.find_element_by_id("cal-url").text)
self.assertIn(
"rigs.ics",
self.browser.find_element_by_id("cal-url").text)
self.assertNotIn("?", self.browser.find_element_by_id("cal-url").text)
# Lets change everything so it's not the default value
self.browser.find_element_by_xpath("//input[@value='rig']").click()
self.browser.find_element_by_xpath("//input[@value='non-rig']").click()
self.browser.find_element_by_xpath("//input[@value='dry-hire']").click()
self.browser.find_element_by_xpath("//input[@value='cancelled']").click()
self.browser.find_element_by_xpath("//input[@value='provisional']").click()
self.browser.find_element_by_xpath("//input[@value='confirmed']").click()
self.browser.find_element_by_xpath(
"//input[@value='dry-hire']").click()
self.browser.find_element_by_xpath(
"//input[@value='cancelled']").click()
self.browser.find_element_by_xpath(
"//input[@value='provisional']").click()
self.browser.find_element_by_xpath(
"//input[@value='confirmed']").click()
# and then check the url is correct
self.assertIn("rigs.ics?rig=false&non-rig=false&dry-hire=false&cancelled=true&provisional=false&confirmed=false", self.browser.find_element_by_id("cal-url").text)
self.assertIn(
"rigs.ics?rig=false&non-rig=false&dry-hire=false&cancelled=true&provisional=false&confirmed=false",
self.browser.find_element_by_id("cal-url").text)
# Awesome - all seems to work
@@ -807,27 +1006,24 @@ class IcalTest(LiveServerTestCase):
# Now creates an API key, and check a URL is displayed one
self.browser.find_element_by_link_text("Generate API Key").click()
c = Client()
# Default settings - should have all non-cancelled events
# Get the ical file (can't do this in selanium because reasons)
icalUrl = self.browser.find_element_by_id("cal-url").text
response = c.get(icalUrl)
self.assertEqual(200, response.status_code)
#Check has entire file
# Check has entire file
self.assertIn("BEGIN:VCALENDAR", response.content)
self.assertIn("END:VCALENDAR", response.content)
expectedIn= [1,2,3,5,6,7,10,11,12,13,15,16,17]
for test in range(1,18):
expectedIn = [1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 15, 16, 17]
for test in range(1, 18):
if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content)
self.assertIn("TE E" + str(test) + " ", response.content)
else:
self.assertNotIn("TE E"+str(test)+" ", response.content)
self.assertNotIn("TE E" + str(test) + " ", response.content)
# Only dry hires
self.browser.find_element_by_xpath("//input[@value='rig']").click()
@@ -837,66 +1033,72 @@ class IcalTest(LiveServerTestCase):
response = c.get(icalUrl)
self.assertEqual(200, response.status_code)
expectedIn= [10,11,12,13]
for test in range(1,18):
expectedIn = [10, 11, 12, 13]
for test in range(1, 18):
if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content)
self.assertIn("TE E" + str(test) + " ", response.content)
else:
self.assertNotIn("TE E"+str(test)+" ", response.content)
self.assertNotIn("TE E" + str(test) + " ", response.content)
# Only provisional rigs
self.browser.find_element_by_xpath("//input[@value='rig']").click()
self.browser.find_element_by_xpath("//input[@value='dry-hire']").click()
self.browser.find_element_by_xpath("//input[@value='confirmed']").click()
self.browser.find_element_by_xpath(
"//input[@value='dry-hire']").click()
self.browser.find_element_by_xpath(
"//input[@value='confirmed']").click()
icalUrl = self.browser.find_element_by_id("cal-url").text
response = c.get(icalUrl)
self.assertEqual(200, response.status_code)
expectedIn= [1,2]
for test in range(1,18):
expectedIn = [1, 2]
for test in range(1, 18):
if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content)
self.assertIn("TE E" + str(test) + " ", response.content)
else:
self.assertNotIn("TE E"+str(test)+" ", response.content)
self.assertNotIn("TE E" + str(test) + " ", response.content)
# Only cancelled non-rigs
self.browser.find_element_by_xpath("//input[@value='rig']").click()
self.browser.find_element_by_xpath("//input[@value='non-rig']").click()
self.browser.find_element_by_xpath("//input[@value='provisional']").click()
self.browser.find_element_by_xpath("//input[@value='cancelled']").click()
self.browser.find_element_by_xpath(
"//input[@value='provisional']").click()
self.browser.find_element_by_xpath(
"//input[@value='cancelled']").click()
icalUrl = self.browser.find_element_by_id("cal-url").text
response = c.get(icalUrl)
self.assertEqual(200, response.status_code)
expectedIn= [18]
for test in range(1,18):
expectedIn = [18]
for test in range(1, 18):
if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content)
self.assertIn("TE E" + str(test) + " ", response.content)
else:
self.assertNotIn("TE E"+str(test)+" ", response.content)
self.assertNotIn("TE E" + str(test) + " ", response.content)
# Nothing selected
self.browser.find_element_by_xpath("//input[@value='non-rig']").click()
self.browser.find_element_by_xpath("//input[@value='cancelled']").click()
self.browser.find_element_by_xpath(
"//input[@value='cancelled']").click()
icalUrl = self.browser.find_element_by_id("cal-url").text
response = c.get(icalUrl)
self.assertEqual(200, response.status_code)
expectedIn= []
for test in range(1,18):
expectedIn = []
for test in range(1, 18):
if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content)
self.assertIn("TE E" + str(test) + " ", response.content)
else:
self.assertNotIn("TE E"+str(test)+" ", response.content)
# Wow - that was a lot of tests
self.assertNotIn("TE E" + str(test) + " ", response.content)
# Wow - that was a lot of tests
class animation_is_finished(object):
""" Checks if animation is done """
def __init__(self):
pass

View File

@@ -1,9 +1,11 @@
from datetime import date, timedelta, datetime, time
from decimal import *
import pytz
from django.conf import settings
from django.test import TestCase
from RIGS import models
from datetime import date, timedelta, datetime, time
from decimal import *
class ProfileTestCase(TestCase):
@@ -16,8 +18,10 @@ 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')
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')
def test_find_correct(self):
r = models.VatRate.objects.find_rate('2015-03-01')
@@ -34,18 +38,27 @@ 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)
self.not_current_events = set(
self.all_events) - set(self.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")
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")
# 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 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 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),
@@ -62,7 +75,11 @@ class EventTestCase(TestCase):
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 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,
@@ -73,7 +90,11 @@ class EventTestCase(TestCase):
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 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,
@@ -83,7 +104,8 @@ class EventTestCase(TestCase):
def test_count(self):
# Santiy check we have the expected events created
self.assertEqual(models.Event.objects.count(), 18, "Incorrect number of events, check setup")
self.assertEqual(models.Event.objects.count(), 18,
"Incorrect number of events, check setup")
def test_rig_count(self):
# by my count this is 7
@@ -93,10 +115,16 @@ class EventTestCase(TestCase):
current_events = models.Event.objects.current_events()
self.assertEqual(len(current_events), len(self.current_events))
for eid in self.current_events:
self.assertIn(models.Event.objects.get(name="TE E%d" % eid), current_events)
self.assertIn(
models.Event.objects.get(
name="TE E%d" %
eid), current_events)
for eid in self.not_current_events:
self.assertNotIn(models.Event.objects.get(name="TE E%d" % eid), current_events)
self.assertNotIn(
models.Event.objects.get(
name="TE E%d" %
eid), current_events)
def test_related_venue(self):
v1 = models.Venue.objects.create(name="TE V1")
@@ -204,29 +232,43 @@ class EventTestCase(TestCase):
event.save()
def test_earliest_time(self):
event = models.Event(name="TE ET", start_date=date(2016, 01, 01))
event = models.Event(name="TE ET", start_date=date(2016, 0o1, 0o1))
# Just a start date
self.assertEqual(event.earliest_time, date(2016, 01, 01))
self.assertEqual(event.earliest_time, date(2016, 0o1, 0o1))
# With start time
event.start_time = time(9, 00)
self.assertEqual(event.earliest_time, self.create_datetime(2016, 1, 1, 9, 00))
self.assertEqual(
event.earliest_time,
self.create_datetime(
2016,
1,
1,
9,
00))
# With access time
event.access_at = self.create_datetime(2015, 12, 03, 9, 57)
event.access_at = self.create_datetime(2015, 12, 0o3, 9, 57)
self.assertEqual(event.earliest_time, event.access_at)
# With meet time
event.meet_at = self.create_datetime(2015, 12, 03, 9, 55)
event.meet_at = self.create_datetime(2015, 12, 0o3, 9, 55)
self.assertEqual(event.earliest_time, event.meet_at)
# Check order isn't important
event.start_date = date(2015, 12, 03)
self.assertEqual(event.earliest_time, self.create_datetime(2015, 12, 03, 9, 00))
event.start_date = date(2015, 12, 0o3)
self.assertEqual(
event.earliest_time,
self.create_datetime(
2015,
12,
0o3,
9,
00))
def test_latest_time(self):
event = models.Event(name="TE LT", start_date=date(2016, 01, 01))
event = models.Event(name="TE LT", start_date=date(2016, 0o1, 0o1))
# Just start date
self.assertEqual(event.latest_time, event.start_date)
@@ -237,25 +279,48 @@ class EventTestCase(TestCase):
# With end time
event.end_time = time(23, 00)
self.assertEqual(event.latest_time, self.create_datetime(2016, 1, 2, 23, 00))
self.assertEqual(
event.latest_time, self.create_datetime(
2016, 1, 2, 23, 00))
def test_in_bounds(self):
manager = models.Event.objects
events = [
manager.create(name="TE IB0", start_date='2016-01-02'), # yes no
manager.create(name="TE IB1", start_date='2015-12-31', end_date='2016-01-04'),
manager.create(
name="TE IB1",
start_date='2015-12-31',
end_date='2016-01-04'),
# 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 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'),
# negative check
manager.create(name='TE IB6', start_date='2015-12-31', end_date='2016-01-01'),
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(
datetime(
2016, 1, 2), datetime(
2016, 1, 3))
self.assertIn(events[0], in_bounds)
self.assertIn(events[1], in_bounds)
self.assertIn(events[2], in_bounds)
@@ -272,11 +337,14 @@ class EventTestCase(TestCase):
class EventItemTestCase(TestCase):
def setUp(self):
self.e1 = models.Event.objects.create(name="TI E1", start_date=date.today())
self.e2 = models.Event.objects.create(name="TI E2", start_date=date.today())
self.e1 = models.Event.objects.create(
name="TI E1", start_date=date.today())
self.e2 = models.Event.objects.create(
name="TI E2", start_date=date.today())
def test_item_cost(self):
item = models.EventItem.objects.create(event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
item = models.EventItem.objects.create(
event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
self.assertEqual(item.total_cost, 1.00)
item.cost = 2.50
@@ -289,8 +357,10 @@ class EventItemTestCase(TestCase):
item.delete()
def test_item_order(self):
i1 = models.EventItem.objects.create(event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
i2 = models.EventItem.objects.create(event=self.e1, name="TI I2", quantity=1, cost=1.00, order=2)
i1 = models.EventItem.objects.create(
event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
i2 = models.EventItem.objects.create(
event=self.e1, name="TI I2", quantity=1, cost=1.00, order=2)
items = self.e1.items.all()
self.assertListEqual([i1, i2], list(items))
@@ -298,17 +368,29 @@ class EventItemTestCase(TestCase):
class EventPricingTestCase(TestCase):
def setUp(self):
models.VatRate.objects.create(rate=0.20, comment="TP V1", start_at='2013-01-01')
models.VatRate.objects.create(rate=0.10, comment="TP V2", start_at=date.today() - timedelta(days=1))
self.e1 = models.Event.objects.create(name="TP E1", start_date=date.today() - timedelta(days=2))
self.e2 = models.Event.objects.create(name="TP E2", start_date=date.today())
models.VatRate.objects.create(
rate=0.20, comment="TP V1", start_at='2013-01-01')
models.VatRate.objects.create(
rate=0.10,
comment="TP V2",
start_at=date.today() -
timedelta(
days=1))
self.e1 = models.Event.objects.create(
name="TP E1", start_date=date.today() - timedelta(days=2))
self.e2 = models.Event.objects.create(
name="TP E2", start_date=date.today())
# Create some items E1, total 70.40
# Create some items E2, total 381.20
self.i1 = models.EventItem.objects.create(event=self.e1, name="TP I1", quantity=1, cost=50.00, order=1)
self.i2 = models.EventItem.objects.create(event=self.e1, name="TP I2", quantity=2, cost=3.20, order=2)
self.i3 = models.EventItem.objects.create(event=self.e1, name="TP I3", quantity=7, cost=2.00, order=3)
self.i4 = models.EventItem.objects.create(event=self.e2, name="TP I4", quantity=2, cost=190.60, order=1)
self.i1 = models.EventItem.objects.create(
event=self.e1, name="TP I1", quantity=1, cost=50.00, order=1)
self.i2 = models.EventItem.objects.create(
event=self.e1, name="TP I2", quantity=2, cost=3.20, order=2)
self.i3 = models.EventItem.objects.create(
event=self.e1, name="TP I3", quantity=7, cost=2.00, order=3)
self.i4 = models.EventItem.objects.create(
event=self.e2, name="TP I4", quantity=2, cost=190.60, order=1)
# Decimal type is needed here as that is what is returned from the model.
# Using anything else results in a failure due to floating point arritmetic

View File

@@ -47,7 +47,10 @@ class TestAdminMergeObjects(TestCase):
def setUp(self):
self.profile.set_password('testuser')
self.profile.save()
self.assertTrue(self.client.login(username=self.profile.username, password='testuser'))
self.assertTrue(
self.client.login(
username=self.profile.username,
password='testuser'))
def test_merge_confirmation(self):
change_url = reverse('admin:RIGS_venue_changelist')
@@ -89,10 +92,16 @@ class TestAdminMergeObjects(TestCase):
self.assertTrue(models.Venue.objects.get(pk=self.venues[1].pk))
# Check the un-needed venue has been disposed of
self.assertRaises(ObjectDoesNotExist, models.Venue.objects.get, pk=self.venues[2].pk)
self.assertRaises(
ObjectDoesNotExist,
models.Venue.objects.get,
pk=self.venues[2].pk)
# Check the one we didn't delete is still there
self.assertEqual(models.Venue.objects.get(pk=self.venues[3].pk), self.venues[3])
self.assertEqual(
models.Venue.objects.get(
pk=self.venues[3].pk),
self.venues[3])
# Check the events have been moved to the master venue
for key, event in self.events.iteritems():
@@ -118,10 +127,16 @@ class TestAdminMergeObjects(TestCase):
self.assertTrue(models.Person.objects.get(pk=self.persons[1].pk))
# Check the un-needed people have been disposed of
self.assertRaises(ObjectDoesNotExist, models.Person.objects.get, pk=self.persons[2].pk)
self.assertRaises(
ObjectDoesNotExist,
models.Person.objects.get,
pk=self.persons[2].pk)
# Check the one we didn't delete is still there
self.assertEqual(models.Person.objects.get(pk=self.persons[3].pk), self.persons[3])
self.assertEqual(
models.Person.objects.get(
pk=self.persons[3].pk),
self.persons[3])
# Check the events have been moved to the master person
for key, event in self.events.iteritems():
@@ -144,25 +159,40 @@ class TestAdminMergeObjects(TestCase):
self.assertContains(response, self.organisations[1].name)
# Check the master copy still exists
self.assertTrue(models.Organisation.objects.get(pk=self.organisations[1].pk))
self.assertTrue(
models.Organisation.objects.get(
pk=self.organisations[1].pk))
# Check the un-needed organisations have been disposed of
self.assertRaises(ObjectDoesNotExist, models.Organisation.objects.get, pk=self.organisations[2].pk)
self.assertRaises(
ObjectDoesNotExist,
models.Organisation.objects.get,
pk=self.organisations[2].pk)
# Check the one we didn't delete is still there
self.assertEqual(models.Organisation.objects.get(pk=self.organisations[3].pk), self.organisations[3])
self.assertEqual(
models.Organisation.objects.get(
pk=self.organisations[3].pk),
self.organisations[3])
# Check the events have been moved to the master organisation
for key, event in self.events.iteritems():
updatedEvent = models.Event.objects.get(pk=event.pk)
if event.organisation == self.organisations[3]: # The one we left in place
if event.organisation == self.organisations[
3]: # The one we left in place
continue
self.assertEqual(updatedEvent.organisation, self.organisations[1])
class TestInvoiceDelete(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.profile = models.Profile.objects.create(
username="testuser1",
email="1@test.com",
is_superuser=True,
is_active=True,
is_staff=True)
cls.events = {
1: models.Event.objects.create(name="TE E1", start_date=date.today()),
@@ -175,16 +205,22 @@ class TestInvoiceDelete(TestCase):
}
cls.payments = {
1: models.Payment.objects.create(invoice=cls.invoices[1], date=date.today(), amount=12.34, method=models.Payment.CASH)
1: models.Payment.objects.create(invoice=cls.invoices[1], date=date.today(), amount=12.34,
method=models.Payment.CASH)
}
def setUp(self):
self.profile.set_password('testuser')
self.profile.save()
self.assertTrue(self.client.login(username=self.profile.username, password='testuser'))
self.assertTrue(
self.client.login(
username=self.profile.username,
password='testuser'))
def test_invoice_delete_allowed(self):
request_url = reverse('invoice_delete', kwargs={'pk':self.invoices[2].pk})
request_url = reverse(
'invoice_delete', kwargs={
'pk': self.invoices[2].pk})
response = self.client.get(request_url, follow=True)
self.assertContains(response, "Are you sure")
@@ -196,13 +232,20 @@ class TestInvoiceDelete(TestCase):
response = self.client.post(request_url, follow=True)
# Check the invoice is deleted
self.assertRaises(ObjectDoesNotExist, models.Invoice.objects.get, pk=self.invoices[2].pk)
self.assertRaises(
ObjectDoesNotExist,
models.Invoice.objects.get,
pk=self.invoices[2].pk)
def test_invoice_delete_not_allowed(self):
request_url = reverse('invoice_delete', kwargs={'pk':self.invoices[1].pk})
request_url = reverse(
'invoice_delete', kwargs={
'pk': self.invoices[1].pk})
response = self.client.get(request_url, follow=True)
self.assertContains(response, "To delete an invoice, delete the payments first.")
self.assertContains(
response,
"To delete an invoice, delete the payments first.")
# Check the invoice still exists
self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk))
@@ -213,6 +256,7 @@ class TestInvoiceDelete(TestCase):
# Check this didn't work
self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk))
class TestSampleDataGenerator(TestCase):
@override_settings(DEBUG=True)
def test_generate_sample_data(self):
@@ -225,4 +269,8 @@ class TestSampleDataGenerator(TestCase):
def test_production_exception(self):
from django.core.management.base import CommandError
self.assertRaisesRegexp(CommandError, ".*production", call_command, 'generateSampleData')
self.assertRaisesRegexp(
CommandError,
".*production",
call_command,
'generateSampleData')

View File

@@ -1,161 +1,225 @@
from django.conf.urls import patterns, include, url
from django.conf.urls import patterns, url
from django.contrib.auth.decorators import login_required
from RIGS import models, views, rigboard, finance, ical, versioning, forms
from django.views.generic import RedirectView
from PyRIGS.decorators import permission_required_with_403
from PyRIGS.decorators import api_key_required
from PyRIGS.decorators import permission_required_with_403
from RIGS import models, views, rigboard, finance, ical, versioning, forms
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'),
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(r'^user/password_reset/$',
'django.contrib.auth.views.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()),
permission_required_with_403('RIGS.add_person')(
views.PersonCreate.as_view()),
name='person_create'),
url(r'^people/(?P<pk>\d+)/$',
permission_required_with_403('RIGS.view_person')(views.PersonDetail.as_view()),
permission_required_with_403('RIGS.view_person')(
views.PersonDetail.as_view()),
name='person_detail'),
url(r'^people/(?P<pk>\d+)/history/$',
permission_required_with_403('RIGS.view_person')(versioning.VersionHistory.as_view()),
permission_required_with_403('RIGS.view_person')(
versioning.VersionHistory.as_view()),
name='person_history', kwargs={'model': models.Person}),
url(r'^people/(?P<pk>\d+)/edit/$',
permission_required_with_403('RIGS.change_person')(views.PersonUpdate.as_view()),
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()),
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()),
permission_required_with_403('RIGS.add_organisation')(
views.OrganisationCreate.as_view()),
name='organisation_create'),
url(r'^organisations/(?P<pk>\d+)/$',
permission_required_with_403('RIGS.view_organisation')(views.OrganisationDetail.as_view()),
permission_required_with_403('RIGS.view_organisation')(
views.OrganisationDetail.as_view()),
name='organisation_detail'),
url(r'^organisations/(?P<pk>\d+)/history/$',
permission_required_with_403('RIGS.view_organisation')(versioning.VersionHistory.as_view()),
permission_required_with_403('RIGS.view_organisation')(
versioning.VersionHistory.as_view()),
name='organisation_history', kwargs={'model': models.Organisation}),
url(r'^organisations/(?P<pk>\d+)/edit/$',
permission_required_with_403('RIGS.change_organisation')(views.OrganisationUpdate.as_view()),
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()),
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()),
permission_required_with_403('RIGS.add_venue')(
views.VenueCreate.as_view()),
name='venue_create'),
url(r'^venues/(?P<pk>\d+)/$',
permission_required_with_403('RIGS.view_venue')(views.VenueDetail.as_view()),
permission_required_with_403('RIGS.view_venue')(
views.VenueDetail.as_view()),
name='venue_detail'),
url(r'^venues/(?P<pk>\d+)/history/$',
permission_required_with_403('RIGS.view_venue')(versioning.VersionHistory.as_view()),
permission_required_with_403('RIGS.view_venue')(
versioning.VersionHistory.as_view()),
name='venue_history', kwargs={'model': models.Venue}),
url(r'^venues/(?P<pk>\d+)/edit/$',
permission_required_with_403('RIGS.change_venue')(views.VenueUpdate.as_view()),
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<view>(month|week|day))/$', login_required()(rigboard.WebCalendar.as_view()), name='web_calendar'),
url(r'^rigboard/calendar/(?P<view>(month|week|day))/(?P<date>(\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/$',
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<view>(month|week|day))/$',
login_required()(rigboard.WebCalendar.as_view()),
name='web_calendar'),
url(
r'^rigboard/calendar/(?P<view>(month|week|day))/(?P<date>(\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()),
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()),
permission_required_with_403('RIGS.view_event')(
versioning.ActivityFeed.as_view()),
name='activity_feed'),
url(r'^event/(?P<pk>\d+)/$',
permission_required_with_403('RIGS.view_event')(rigboard.EventDetail.as_view()),
permission_required_with_403('RIGS.view_event')(
rigboard.EventDetail.as_view()),
name='event_detail'),
url(r'^event/(?P<pk>\d+)/print/$',
permission_required_with_403('RIGS.view_event')(rigboard.EventPrint.as_view()),
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()),
permission_required_with_403('RIGS.add_event')(
rigboard.EventCreate.as_view()),
name='event_create'),
url(r'^event/(?P<pk>\d+)/edit/$',
permission_required_with_403('RIGS.change_event')(rigboard.EventUpdate.as_view()),
permission_required_with_403('RIGS.change_event')(
rigboard.EventUpdate.as_view()),
name='event_update'),
url(r'^event/(?P<pk>\d+)/duplicate/$',
permission_required_with_403('RIGS.add_event')(rigboard.EventDuplicate.as_view()),
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<pk>\d+)/history/$',
permission_required_with_403('RIGS.view_event')(versioning.VersionHistory.as_view()),
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()),
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()),
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()),
permission_required_with_403('RIGS.add_invoice')(
finance.InvoiceWaiting.as_view()),
name='invoice_waiting'),
url(r'^event/(?P<pk>\d+)/invoice/$',
permission_required_with_403('RIGS.add_invoice')(finance.InvoiceEvent.as_view()),
permission_required_with_403('RIGS.add_invoice')(
finance.InvoiceEvent.as_view()),
name='invoice_event'),
url(r'^invoice/(?P<pk>\d+)/$',
permission_required_with_403('RIGS.view_invoice')(finance.InvoiceDetail.as_view()),
permission_required_with_403('RIGS.view_invoice')(
finance.InvoiceDetail.as_view()),
name='invoice_detail'),
url(r'^invoice/(?P<pk>\d+)/print/$',
permission_required_with_403('RIGS.view_invoice')(finance.InvoicePrint.as_view()),
permission_required_with_403('RIGS.view_invoice')(
finance.InvoicePrint.as_view()),
name='invoice_print'),
url(r'^invoice/(?P<pk>\d+)/void/$',
permission_required_with_403('RIGS.change_invoice')(finance.InvoiceVoid.as_view()),
permission_required_with_403('RIGS.change_invoice')(
finance.InvoiceVoid.as_view()),
name='invoice_void'),
url(r'^invoice/(?P<pk>\d+)/delete/$',
permission_required_with_403('RIGS.change_invoice')(finance.InvoiceDelete.as_view()),
permission_required_with_403('RIGS.change_invoice')(
finance.InvoiceDelete.as_view()),
name='invoice_delete'),
url(r'^payment/create/$',
permission_required_with_403('RIGS.add_payment')(finance.PaymentCreate.as_view()),
permission_required_with_403('RIGS.add_payment')(
finance.PaymentCreate.as_view()),
name='payment_create'),
url(r'^payment/(?P<pk>\d+)/delete/$',
permission_required_with_403('RIGS.add_payment')(finance.PaymentDelete.as_view()),
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/$',
login_required(views.ProfileDetail.as_view()),
name='profile_detail'),
url(r'^user/(?P<pk>\d+)/$',
permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()),
name='profile_detail'),
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'),
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<api_pk>\d+)/(?P<api_key>\w+)/rigs.ics$', api_key_required(ical.CalendarICS()), name="ics_calendar"),
url(r'^ical/(?P<api_pk>\d+)/(?P<api_key>\w+)/rigs.ics$',
api_key_required(ical.CalendarICS()), name="ics_calendar"),
# API
url(r'^api/(?P<model>\w+)/$', login_required(views.SecureAPIRequest.as_view()), name="api_secure"),
url(r'^api/(?P<model>\w+)/(?P<pk>\d+)/$', login_required(views.SecureAPIRequest.as_view()), name="api_secure"),
url(r'^api/(?P<model>\w+)/$',
login_required(views.SecureAPIRequest.as_view()),
name="api_secure"),
url(r'^api/(?P<model>\w+)/(?P<pk>\d+)/$',
login_required(views.SecureAPIRequest.as_view()),
name="api_secure"),
# Legacy URL's
url(r'^rig/show/(?P<pk>\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')),
)
url(r'^rig/show/(?P<pk>\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')),
)

View File

@@ -1,37 +1,39 @@
import datetime
import logging
import reversion
from diff_match_patch import diff_match_patch
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import IntegerField, EmailField, TextField
from django.shortcuts import get_object_or_404
from django.views import generic
# Versioning
import reversion
from reversion.models import Version
from django.contrib.contenttypes.models import ContentType # Used to lookup the content_type
from django.db.models import IntegerField, EmailField, TextField
from diff_match_patch import diff_match_patch
from RIGS import models
import datetime
logger = logging.getLogger('tec.pyrigs')
def model_compare(oldObj, newObj, excluded_keys=[]):
# recieves two objects of the same model, and compares them. Returns an array of FieldCompare objects
# recieves two objects of the same model, and compares them. Returns an
# array of FieldCompare objects
try:
theFields = oldObj._meta.fields # This becomes deprecated in Django 1.8!!!!!!!!!!!!! (but an alternative becomes available)
# This becomes deprecated in Django 1.8!!!!!!!!!!!!! (but an
# alternative becomes available)
theFields = oldObj._meta.fields
except AttributeError:
theFields = newObj._meta.fields
class FieldCompare(object):
def __init__(self, field=None, old=None, new=None):
self.field = field
self._old = old
self._new = new
def display_value(self, value):
if isinstance(self.field, IntegerField) and len(self.field.choices) > 0:
if isinstance(self.field, IntegerField) and len(
self.field.choices) > 0:
return [x[1] for x in self.field.choices if x[0] == value][0]
return value
@@ -104,13 +106,15 @@ def model_compare(oldObj, newObj, excluded_keys=[]):
def compare_event_items(old, new):
# Recieves two event version objects and compares their items, returns an array of ItemCompare objects
# Recieves two event version objects and compares their items, returns an
# array of ItemCompare objects
item_type = ContentType.objects.get_for_model(models.EventItem)
old_item_versions = old.revision.version_set.filter(content_type=item_type)
new_item_versions = new.revision.version_set.filter(content_type=item_type)
class ItemCompare(object):
def __init__(self, old=None, new=None, changes=None):
self.old = old
self.new = new
@@ -126,18 +130,25 @@ def compare_event_items(old, new):
for version in new_item_versions: # go through the new versions
if version.field_dict["event"] == new.object_id_int:
try:
compare = item_dict[version.object_id] # see if there's a matching old version
compare.new = version.object_version.object # then add the new version to the dictionary
# see if there's a matching old version
compare = item_dict[version.object_id]
# then add the new version to the dictionary
compare.new = version.object_version.object
except KeyError: # there's no matching old version, so add this item to the dictionary by itself
compare = ItemCompare(new=version.object_version.object)
item_dict[version.object_id] = compare # update the dictionary with the changes
# update the dictionary with the changes
item_dict[version.object_id] = compare
changes = []
for (_, compare) in item_dict.items():
compare.changes = model_compare(compare.old, compare.new, ['id', 'event', 'order']) # see what's changed
compare.changes = model_compare(
compare.old, compare.new, [
'id', 'event', 'order']) # see what's changed
if len(compare.changes) >= 1:
changes.append(compare) # transfer into a sequential array to make it easier to deal with later
# transfer into a sequential array to make it easier to deal with
# later
changes.append(compare)
return changes
@@ -158,7 +169,8 @@ 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.get_for_object_reference(
version.content_type.model_class(), thisId)
try:
previousVersions = versions.filter(revision_id__lt=version.revision_id).latest(
@@ -173,7 +185,7 @@ def get_changes_for_version(newVersion, oldVersion=None):
# Pass in a previous version if you already know it (for efficiancy)
# if not provided then it will be looked up in the database
if oldVersion == None:
if oldVersion is None:
oldVersion = get_previous_version(newVersion)
modelClass = newVersion.content_type.model_class()
@@ -192,7 +204,8 @@ def get_changes_for_version(newVersion, oldVersion=None):
if oldVersion:
compare['old'] = oldVersion.object_version.object
compare['field_changes'] = model_compare(compare['old'], compare['new'])
compare['field_changes'] = model_compare(
compare['old'], compare['new'])
compare['item_changes'] = compare_event_items(oldVersion, newVersion)
return compare
@@ -207,7 +220,8 @@ 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.get_for_object_reference(
thisModel, self.kwargs['pk'])
return versions
@@ -225,7 +239,8 @@ class VersionHistory(generic.ListView):
if versionNo >= len(versions) - 1:
thisItem = get_changes_for_version(thisVersion, None)
else:
thisItem = get_changes_for_version(thisVersion, versions[versionNo + 1])
thisItem = get_changes_for_version(
thisVersion, versions[versionNo + 1])
items.append(thisItem)
@@ -241,7 +256,8 @@ class ActivityTable(generic.ListView):
paginate_by = 25
def get_queryset(self):
versions = get_versions_for_model([models.Event, models.Venue, models.Person, models.Organisation])
versions = get_versions_for_model(
[models.Event, models.Venue, models.Person, models.Organisation])
return versions
def get_context_data(self, **kwargs):
@@ -265,14 +281,17 @@ class ActivityFeed(generic.ListView):
paginate_by = 25
def get_queryset(self):
versions = get_versions_for_model([models.Event, models.Venue, models.Person, models.Organisation])
versions = get_versions_for_model(
[models.Event, models.Venue, models.Person, models.Organisation])
return versions
def get_context_data(self, **kwargs):
maxTimeDelta = []
maxTimeDelta.append({'maxAge': datetime.timedelta(days=1), 'group': datetime.timedelta(hours=1)})
maxTimeDelta.append({'maxAge': None, 'group': datetime.timedelta(days=1)})
maxTimeDelta.append({'maxAge': datetime.timedelta(
days=1), 'group': datetime.timedelta(hours=1)})
maxTimeDelta.append(
{'maxAge': None, 'group': datetime.timedelta(days=1)})
# Call the base implementation first to get a context
context = super(ActivityFeed, self).get_context_data(**kwargs)
@@ -281,19 +300,23 @@ class ActivityFeed(generic.ListView):
for thisVersion in context['object_list']:
thisItem = get_changes_for_version(thisVersion, None)
if thisItem['item_changes'] or thisItem['field_changes'] or thisItem['old'] == None:
if thisItem['item_changes'] or thisItem[
'field_changes'] or thisItem['old'] is None:
thisItem['withPrevious'] = False
if len(items) >= 1:
timeAgo = datetime.datetime.now(thisItem['revision'].date_created.tzinfo) - thisItem[
'revision'].date_created
timeDiff = items[-1]['revision'].date_created - thisItem['revision'].date_created
timeDiff = items[-1]['revision'].date_created - \
thisItem['revision'].date_created
timeTogether = False
for params in maxTimeDelta:
if params['maxAge'] is None or timeAgo <= params['maxAge']:
if params['maxAge'] is None or timeAgo <= params[
'maxAge']:
timeTogether = timeDiff < params['group']
break
sameUser = thisItem['revision'].user == items[-1]['revision'].user
sameUser = thisItem[
'revision'].user == items[-1]['revision'].user
thisItem['withPrevious'] = timeTogether & sameUser
items.append(thisItem)

View File

@@ -1,23 +1,25 @@
from django.core.exceptions import PermissionDenied
from django.http.response import HttpResponseRedirect
from django.http import HttpResponse
from django.core.urlresolvers import reverse_lazy, reverse, NoReverseMatch
from django.views import generic
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django.core import serializers
from django.conf import settings
import datetime
import operator
from functools import reduce
import simplejson
from django.contrib import messages
import datetime, pytz
import operator
from registration.views import RegistrationView
from django.core import serializers
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse_lazy, reverse, NoReverseMatch
from django.db.models import Q
from django.http import HttpResponse
from django.http.response import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.views import generic
from RIGS import models, forms
from RIGS import models
"""
Displays the current rig count along with a few other bits and pieces
"""
class Index(generic.TemplateView):
template_name = 'RIGS/index.html'
@@ -26,6 +28,7 @@ class Index(generic.TemplateView):
context['rig_count'] = models.Event.objects.rig_count()
return context
def login(request, **kwargs):
if request.user.is_authenticated():
next = request.REQUEST.get('next', '/')
@@ -35,11 +38,14 @@ def login(request, **kwargs):
return login(request)
"""
Called from a modal window (e.g. when an item is submitted to an event/invoice).
May optionally also include some javascript in a success message to cause a load of
the new information onto the page.
"""
class CloseModal(generic.TemplateView):
template_name = 'closemodal.html'
@@ -54,7 +60,8 @@ class PersonList(generic.ListView):
def get_queryset(self):
q = self.request.GET.get('q', "")
if len(q) >= 3:
object_list = self.model.objects.filter(Q(name__icontains=q) | Q(email__icontains=q))
object_list = self.model.objects.filter(
Q(name__icontains=q) | Q(email__icontains=q))
else:
object_list = self.model.objects.all()
orderBy = self.request.GET.get('orderBy', None)
@@ -69,16 +76,24 @@ class PersonDetail(generic.DetailView):
class PersonCreate(generic.CreateView):
model = models.Person
fields = ['name','phone','email','address','notes']
fields = ['name', 'phone', 'email', 'address', 'notes']
def get_success_url(self):
if self.request.is_ajax():
url = reverse_lazy('closemodal')
update_url = str(reverse_lazy('person_update',kwargs={'pk':self.object.pk}))
messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object]))
messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'")
update_url = str(
reverse_lazy(
'person_update', kwargs={
'pk': self.object.pk}))
messages.info(self.request, "modalobject=" +
serializers.serialize("json", [self.object]))
messages.info(
self.request,
"modalobject[0]['update_url']='" +
update_url +
"'")
else:
url = reverse_lazy('person_detail', kwargs={
url = reverse_lazy('person_detail', kwargs={
'pk': self.object.pk,
})
return url
@@ -86,16 +101,24 @@ class PersonCreate(generic.CreateView):
class PersonUpdate(generic.UpdateView):
model = models.Person
fields = ['name','phone','email','address','notes']
fields = ['name', 'phone', 'email', 'address', 'notes']
def get_success_url(self):
if self.request.is_ajax():
url = reverse_lazy('closemodal')
update_url = str(reverse_lazy('person_update',kwargs={'pk':self.object.pk}))
messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object]))
messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'")
update_url = str(
reverse_lazy(
'person_update', kwargs={
'pk': self.object.pk}))
messages.info(self.request, "modalobject=" +
serializers.serialize("json", [self.object]))
messages.info(
self.request,
"modalobject[0]['update_url']='" +
update_url +
"'")
else:
url = reverse_lazy('person_detail', kwargs={
url = reverse_lazy('person_detail', kwargs={
'pk': self.object.pk,
})
return url
@@ -108,7 +131,8 @@ class OrganisationList(generic.ListView):
def get_queryset(self):
q = self.request.GET.get('q', "")
if len(q) >= 3:
object_list = self.model.objects.filter(Q(name__icontains=q) | Q(address__icontains=q))
object_list = self.model.objects.filter(
Q(name__icontains=q) | Q(address__icontains=q))
else:
object_list = self.model.objects.all()
orderBy = self.request.GET.get('orderBy', "")
@@ -123,16 +147,25 @@ class OrganisationDetail(generic.DetailView):
class OrganisationCreate(generic.CreateView):
model = models.Organisation
fields = ['name','phone','email','address','notes','union_account']
fields = ['name', 'phone', 'email', 'address', 'notes', 'union_account']
def get_success_url(self):
if self.request.is_ajax():
url = reverse_lazy('closemodal')
update_url = str(reverse_lazy('organisation_update',kwargs={'pk':self.object.pk}))
messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object]))
messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'")
update_url = str(
reverse_lazy(
'organisation_update',
kwargs={
'pk': self.object.pk}))
messages.info(self.request, "modalobject=" +
serializers.serialize("json", [self.object]))
messages.info(
self.request,
"modalobject[0]['update_url']='" +
update_url +
"'")
else:
url = reverse_lazy('organisation_detail', kwargs={
url = reverse_lazy('organisation_detail', kwargs={
'pk': self.object.pk,
})
return url
@@ -140,16 +173,25 @@ class OrganisationCreate(generic.CreateView):
class OrganisationUpdate(generic.UpdateView):
model = models.Organisation
fields = ['name','phone','email','address','notes','union_account']
fields = ['name', 'phone', 'email', 'address', 'notes', 'union_account']
def get_success_url(self):
if self.request.is_ajax():
url = reverse_lazy('closemodal')
update_url = str(reverse_lazy('organisation_update',kwargs={'pk':self.object.pk}))
messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object]))
messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'")
update_url = str(
reverse_lazy(
'organisation_update',
kwargs={
'pk': self.object.pk}))
messages.info(self.request, "modalobject=" +
serializers.serialize("json", [self.object]))
messages.info(
self.request,
"modalobject[0]['update_url']='" +
update_url +
"'")
else:
url = reverse_lazy('organisation_detail', kwargs={
url = reverse_lazy('organisation_detail', kwargs={
'pk': self.object.pk,
})
return url
@@ -162,7 +204,8 @@ class VenueList(generic.ListView):
def get_queryset(self):
q = self.request.GET.get('q', "")
if len(q) >= 3:
object_list = self.model.objects.filter(Q(name__icontains=q) | Q(address__icontains=q))
object_list = self.model.objects.filter(
Q(name__icontains=q) | Q(address__icontains=q))
else:
object_list = self.model.objects.all()
orderBy = self.request.GET.get('orderBy', "")
@@ -177,16 +220,30 @@ class VenueDetail(generic.DetailView):
class VenueCreate(generic.CreateView):
model = models.Venue
fields = ['name','phone','email','address','notes','three_phase_available']
fields = [
'name',
'phone',
'email',
'address',
'notes',
'three_phase_available']
def get_success_url(self):
if self.request.is_ajax():
url = reverse_lazy('closemodal')
update_url = str(reverse_lazy('venue_update',kwargs={'pk':self.object.pk}))
messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object]))
messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'")
update_url = str(
reverse_lazy(
'venue_update', kwargs={
'pk': self.object.pk}))
messages.info(self.request, "modalobject=" +
serializers.serialize("json", [self.object]))
messages.info(
self.request,
"modalobject[0]['update_url']='" +
update_url +
"'")
else:
url = reverse_lazy('venue_detail', kwargs={
url = reverse_lazy('venue_detail', kwargs={
'pk': self.object.pk,
})
return url
@@ -194,16 +251,30 @@ class VenueCreate(generic.CreateView):
class VenueUpdate(generic.UpdateView):
model = models.Venue
fields = ['name','phone','email','address','notes','three_phase_available']
fields = [
'name',
'phone',
'email',
'address',
'notes',
'three_phase_available']
def get_success_url(self):
if self.request.is_ajax():
url = reverse_lazy('closemodal')
update_url = str(reverse_lazy('venue_update',kwargs={'pk':self.object.pk}))
messages.info(self.request, "modalobject="+serializers.serialize("json", [self.object]))
messages.info(self.request, "modalobject[0]['update_url']='"+update_url+"'")
update_url = str(
reverse_lazy(
'venue_update', kwargs={
'pk': self.object.pk}))
messages.info(self.request, "modalobject=" +
serializers.serialize("json", [self.object]))
messages.info(
self.request,
"modalobject[0]['update_url']='" +
update_url +
"'")
else:
url = reverse_lazy('venue_detail', kwargs={
url = reverse_lazy('venue_detail', kwargs={
'pk': self.object.pk,
})
return url
@@ -261,9 +332,9 @@ class SecureAPIRequest(generic.View):
# Supply data for autocomplete ajax request in json form
term = request.GET.get('term', None)
if term:
if fields is None: # Default to just name
if fields is None: # Default to just name
fields = ['name']
# Build a list of Q objects for use later
queries = []
for part in term.split(" "):
@@ -273,7 +344,6 @@ class SecureAPIRequest(generic.View):
qs.append(q)
queries.append(reduce(operator.or_, qs))
# Build the data response list
results = []
query = reduce(operator.and_, queries)
@@ -284,25 +354,31 @@ class SecureAPIRequest(generic.View):
'value': o.pk,
'label': o.name,
}
try: # See if there is a valid update URL
data['update'] = reverse("%s_update" % model, kwargs={'pk': o.pk})
try: # See if there is a valid update URL
data['update'] = reverse(
"%s_update" %
model, kwargs={
'pk': o.pk})
except NoReverseMatch:
pass
results.append(data)
# return a data response
json = simplejson.dumps(results)
return HttpResponse(json, content_type="application/json") # Always json
return HttpResponse(
json, content_type="application/json") # Always json
start = request.GET.get('start', None)
end = request.GET.get('end', None)
if model == "event" and start and end:
# Probably a calendar request
start_datetime = datetime.datetime.strptime( start, "%Y-%m-%dT%H:%M:%S" )
end_datetime = datetime.datetime.strptime( end, "%Y-%m-%dT%H:%M:%S" )
start_datetime = datetime.datetime.strptime(
start, "%Y-%m-%dT%H:%M:%S")
end_datetime = datetime.datetime.strptime(end, "%Y-%m-%dT%H:%M:%S")
objects = self.models[model].objects.events_in_bounds(start_datetime,end_datetime)
objects = self.models[model].objects.events_in_bounds(
start_datetime, end_datetime)
results = []
for item in objects:
@@ -318,10 +394,12 @@ class SecureAPIRequest(generic.View):
results.append(data)
json = simplejson.dumps(results)
return HttpResponse(json, content_type="application/json") # Always json
return HttpResponse(
json, content_type="application/json") # Always json
return HttpResponse(model)
class ProfileDetail(generic.DetailView):
model = models.Profile
@@ -334,6 +412,7 @@ class ProfileDetail(generic.DetailView):
return self.model.objects.filter(pk=pk)
class ProfileUpdateSelf(generic.UpdateView):
model = models.Profile
fields = ['first_name', 'last_name', 'email', 'initials', 'phone']
@@ -345,13 +424,14 @@ class ProfileUpdateSelf(generic.UpdateView):
return self.model.objects.filter(pk=pk)
def get_success_url(self):
url = reverse_lazy('profile_detail')
url = reverse_lazy('profile_detail')
return url
class ResetApiKey(generic.RedirectView):
def get_redirect_url(self, *args, **kwargs):
self.request.user.api_key = self.request.user.make_api_key()
self.request.user.save()
return reverse_lazy('profile_detail')

View File

@@ -14,11 +14,11 @@ from RIGS import models
import reversion
import datetime
import uuid
from multiprocessing import Process
# Slight fix for needing to restablish the connection
connection.close()
def fix_email(email):
if not (email is None or email is "") and ("@" not in email):
email += "@nottingham.ac.uk"
@@ -110,7 +110,8 @@ def import_organisations(delete=False):
notes = row[5]
object, created = models.Organisation.objects.get_or_create(pk=row[0], name=row[1], phone=row[2],
address=row[3],
address=row[
3],
union_account=row[4], notes=notes)
if created:
print("Created: " + object.__str__())
@@ -325,13 +326,15 @@ def import_invoices(delete=False):
payment.save()
print(payment)
if invoice.invoice_date < (datetime.date.today() - datetime.timedelta(days=365)) and invoice.balance:
if invoice.invoice_date < (
datetime.date.today() - datetime.timedelta(days=365)) and invoice.balance:
p2 = models.Payment(amount=invoice.balance)
p2.invoice = invoice
p2.method = payment.ADJUSTMENT
p2.date = datetime.date.today()
p2.save()
@transaction.atomic
def main():
# processs = []
@@ -340,7 +343,7 @@ def main():
# processs.append(Process(target=import_organisations, args=(True,)))
# processs.append(Process(target=import_vat_rates, args=(True,)))
# processs.append(Process(target=import_venues, args=(True,)))
# # Start all processs
# [x.start() for x in processs]
# # Wait for all processs to finish
@@ -351,22 +354,34 @@ def main():
import_organisations(True)
import_vat_rates(True)
import_venues(True)
import_rigs(True)
import_eventitem(True)
import_invoices(True)
# Do this before doing non rigs else it gets ugly
sql = "SELECT setval(\'\"RIGS_%s_id_seq\"\', (SELECT MAX(id) FROM \"RIGS_%s\"));" % ('event', 'event')
sql = "SELECT setval(\'\"RIGS_%s_id_seq\"\', (SELECT MAX(id) FROM \"RIGS_%s\"));" % (
'event', 'event')
cursor = connections['default'].cursor()
cursor.execute(sql)
import_nonrigs(False)
sequences = ['profile', 'person', 'organisation', 'vatrate', 'venue', 'event', 'eventitem', 'invoice', 'payment']
sequences = [
'profile',
'person',
'organisation',
'vatrate',
'venue',
'event',
'eventitem',
'invoice',
'payment']
for seq in sequences:
sql = "SELECT setval(\'\"RIGS_%s_id_seq\"\', (SELECT MAX(id) FROM \"RIGS_%s\"));" % (seq, seq)
sql = "SELECT setval(\'\"RIGS_%s_id_seq\"\', (SELECT MAX(id) FROM \"RIGS_%s\"));" % (
seq, seq)
cursor = connections['default'].cursor()
cursor.execute(sql)
if __name__ == "__main__":
main()