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
119 changed files with 9371 additions and 3218 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

1
.gitignore vendored
View File

@@ -25,7 +25,6 @@ var/
*.egg-info/ *.egg-info/
.installed.cfg .installed.cfg
*.egg *.egg
node_modules/
# Continer extras # Continer extras
.vagrant .vagrant

2
.idea/modules.xml generated
View File

@@ -2,7 +2,7 @@
<project version="4"> <project version="4">
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <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> </modules>
</component> </component>
</project> </project>

View File

@@ -1,6 +1,4 @@
language: node_js language: python
node_js:
"6"
python: python:
"2.7" "2.7"
@@ -9,10 +7,8 @@ before_install:
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16" - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
install: install:
- pip install --user -r requirements.txt - pip install -r requirements.txt
- pip install --user coveralls codeclimate-test-reporter - pip install coveralls codeclimate-test-reporter
- npm install
- npm run build
before_script: before_script:
- python manage.py collectstatic --noinput - python manage.py collectstatic --noinput

View File

@@ -1,20 +1,15 @@
from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.template import RequestContext from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from RIGS import models
def user_passes_test_with_403(test_func, login_url=None, oembed_view=None): def user_passes_test_with_403(test_func, login_url=None):
""" """
Decorator for views that checks that the user passes the given test. Decorator for views that checks that the user passes the given test.
Anonymous users will be redirected to login_url, while users that fail Anonymous users will be redirected to login_url, while users that fail
the test will be given a 403 error. the test will be given a 403 error.
If embed_view is set, then a JS redirect will be used, and a application/json+oembed
meta tag set with the url of oembed_view
(oembed_view will be passed the kwargs from the main function)
""" """
if not login_url: if not login_url:
from django.conf import settings from django.conf import settings
@@ -25,30 +20,31 @@ def user_passes_test_with_403(test_func, login_url=None, oembed_view=None):
if test_func(request.user): if test_func(request.user):
return view_func(request, *args, **kwargs) return view_func(request, *args, **kwargs)
elif not request.user.is_authenticated(): elif not request.user.is_authenticated():
if oembed_view is not None: return HttpResponseRedirect('%s?%s=%s' % (
extra_context = {} login_url, REDIRECT_FIELD_NAME, request.get_full_path()))
extra_context['oembed_url'] = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], reverse(oembed_view, kwargs=kwargs))
extra_context['login_url'] = "{0}?{1}={2}".format(login_url, REDIRECT_FIELD_NAME, request.get_full_path())
resp = render_to_response('login_redirect.html', extra_context, context_instance=RequestContext(request))
return resp
else:
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, request.get_full_path()))
else: 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 resp.status_code = 403
return resp return resp
_checklogin.__doc__ = view_func.__doc__ _checklogin.__doc__ = view_func.__doc__
_checklogin.__dict__ = view_func.__dict__ _checklogin.__dict__ = view_func.__dict__
return _checklogin return _checklogin
return _dec return _dec
def permission_required_with_403(perm, login_url=None, oembed_view=None): def permission_required_with_403(perm, login_url=None):
""" """
Decorator for views that checks whether a user has a particular permission 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. 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, oembed_view=oembed_view) 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): def api_key_required(function):
@@ -57,12 +53,14 @@ def api_key_required(function):
Failed users will be given a 403 error. Failed users will be given a 403 error.
Should only be used for urls which include <api_pk> and <api_key> kwargs Should only be used for urls which include <api_pk> and <api_key> kwargs
""" """
def wrap(request, *args, **kwargs): def wrap(request, *args, **kwargs):
userid = kwargs.get('api_pk') userid = kwargs.get('api_pk')
key = kwargs.get('api_key') 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 error_resp.status_code = 403
if key is None: if key is None:
@@ -78,4 +76,5 @@ def api_key_required(function):
if user_object.api_key != key: if user_object.api_key != key:
return error_resp return error_resp
return function(request, *args, **kwargs) return function(request, *args, **kwargs)
return wrap return wrap

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, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # 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! # 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 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 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: if STAGING:
ALLOWED_HOSTS.append('.herokuapp.com') ALLOWED_HOSTS.append('.herokuapp.com')
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
if not DEBUG: 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'] INTERNAL_IPS = ['127.0.0.1']
@@ -40,7 +46,6 @@ ADMINS = (
('Tom Price', 'tomtom5152@gmail.com') ('Tom Price', 'tomtom5152@gmail.com')
) )
# Application definition # Application definition
INSTALLED_APPS = ( INSTALLED_APPS = (
@@ -77,7 +82,6 @@ ROOT_URLCONF = 'PyRIGS.urls'
WSGI_APPLICATION = 'PyRIGS.wsgi.application' WSGI_APPLICATION = 'PyRIGS.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases # https://docs.djangoproject.com/en/1.7/ref/settings/#databases
DATABASES = { DATABASES = {
@@ -89,6 +93,7 @@ DATABASES = {
if not DEBUG: if not DEBUG:
import dj_database_url import dj_database_url
DATABASES['default'] = dj_database_url.config() DATABASES['default'] = dj_database_url.config()
# Logging # Logging
@@ -119,12 +124,12 @@ LOGGING = {
'mail_admins': { 'mail_admins': {
'class': 'django.utils.log.AdminEmailHandler', 'class': 'django.utils.log.AdminEmailHandler',
'level': 'ERROR', '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, 'include_html': True,
}, },
}, },
'loggers': { 'loggers': {
# Again, default Django configuration to email unhandled exceptions # Again, default Django configuration to email unhandled exceptions
'django.request': { 'django.request': {
'handlers': ['mail_admins'], 'handlers': ['mail_admins'],
'level': 'ERROR', 'level': 'ERROR',
@@ -146,6 +151,7 @@ RAVEN_CONFIG = {
# If you are using git, you can also automatically configure the # If you are using git, you can also automatically configure the
# release based on the git info. # release based on the git info.
# 'release': raven.fetch_git_sha(os.path.dirname(os.path.dirname(__file__))), # 'release': raven.fetch_git_sha(os.path.dirname(os.path.dirname(__file__))),
'debug': DEBUG,
} }
# User system # User system
@@ -191,7 +197,7 @@ USE_L10N = True
USE_TZ = 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 = ( TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth",
@@ -204,20 +210,19 @@ TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.messages.context_processors.messages", "django.contrib.messages.context_processors.messages",
) )
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/ # https://docs.djangoproject.com/en/1.7/howto/static-files/
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/') STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
STATICFILES_DIRS = ( STATIC_DIRS = (
os.path.join(BASE_DIR, 'dist/'), os.path.join(BASE_DIR, 'static/')
) )
TEMPLATE_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" 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.conf.urls import patterns, include, url
from django.contrib import admin from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.conf import settings
from registration.backends.default.views import RegistrationView from registration.backends.default.views import RegistrationView
import RIGS import RIGS
from RIGS import regbackend
urlpatterns = patterns('', urlpatterns = patterns('',
# Examples: # Examples:
# url(r'^$', 'PyRIGS.views.home', name='home'), # url(r'^$', 'PyRIGS.views.home', name='home'),
# url(r'^blog/', include('blog.urls')), # url(r'^blog/', include('blog.urls')),
url(r'^', include('RIGS.urls')), url(r'^', include('RIGS.urls')),
url('^user/register/$', RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail), url('^user/register/$',
name="registration_register"), RegistrationView.as_view(form_class=RIGS.forms.ProfileRegistrationFormUniqueEmail),
url('^user/', include('django.contrib.auth.urls')), name="registration_register"),
url('^user/', include('registration.backends.default.urls')), 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: 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/ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
""" """
import os import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "PyRIGS.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "PyRIGS.settings")
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application

View File

@@ -85,7 +85,6 @@ Then load the sample data using the command:
python manage.py generateSampleData python manage.py generateSampleData
``` ```
4 user accounts are created for convenience: 4 user accounts are created for convenience:
|Username |Password | |Username |Password |
|---------|---------| |---------|---------|
|superuser|superuser| |superuser|superuser|

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 import reversion
from django.contrib import admin
from django.contrib.admin import helpers
from django.template.response import TemplateResponse
from django.contrib import messages 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.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.db.models import Count from django.db.models import Count
from django.forms import ModelForm 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. # Register your models here.
admin.site.register(models.VatRate, reversion.VersionAdmin) admin.site.register(models.VatRate, reversion.VersionAdmin)
@@ -50,7 +50,8 @@ class AssociateAdmin(reversion.VersionAdmin):
merge_fields = ['name'] merge_fields = ['name']
def get_queryset(self, request): 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): def number_of_events(self, obj):
return obj.latest_events.count() return obj.latest_events.count()
@@ -58,12 +59,16 @@ class AssociateAdmin(reversion.VersionAdmin):
number_of_events.admin_order_field = 'event_count' number_of_events.admin_order_field = 'event_count'
def merge(self, request, queryset): 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: try:
masterObjectPk = request.POST.get('master') masterObjectPk = request.POST.get('master')
masterObject = queryset.get(pk=masterObjectPk) masterObject = queryset.get(pk=masterObjectPk)
except ObjectDoesNotExist: 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 return
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@@ -79,6 +84,7 @@ class AssociateAdmin(reversion.VersionAdmin):
else: # Present the confirmation screen else: # Present the confirmation screen
class TempForm(ModelForm): class TempForm(ModelForm):
class Meta: class Meta:
model = queryset.model model = queryset.model
fields = self.merge_fields fields = self.merge_fields
@@ -106,10 +112,22 @@ class PersonAdmin(AssociateAdmin):
@admin.register(models.Venue) @admin.register(models.Venue)
class VenueAdmin(AssociateAdmin): class VenueAdmin(AssociateAdmin):
list_display = ('id', 'name', 'phone', 'email', 'number_of_events') 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) @admin.register(models.Organisation)
class OrganisationAdmin(AssociateAdmin): class OrganisationAdmin(AssociateAdmin):
list_display = ('id', 'name', 'phone', 'email', 'number_of_events') 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.contrib import messages
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from django.db.models import Q
from django.http import Http404, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.template import RequestContext from django.template import RequestContext
from django.template.loader import get_template from django.template.loader import get_template
from django.views import generic from django.views import generic
from django.db.models import Q
from z3c.rml import rml2pdf from z3c.rml import rml2pdf
from RIGS import models from RIGS import models
@@ -30,7 +30,8 @@ class InvoiceIndex(generic.ListView):
return context return context
def get_queryset(self): 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 " \ sql = "SELECT * FROM " \
"(SELECT " \ "(SELECT " \
"(SELECT COUNT(p.amount) FROM \"RIGS_payment\" AS p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payment_count\", " \ "(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) escapedEventName = re.sub('[^a-zA-Z0-9 \n\.]', '', object.name)
response = HttpResponse(content_type='application/pdf') response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = "filename=Invoice %05d - N%05d | %s.pdf" % (invoice.pk, invoice.event.pk, escapedEventName) response[
'Content-Disposition'] = "filename=Invoice %05d | %s.pdf" % (invoice.pk, escapedEventName)
response.write(pdfData) response.write(pdfData)
return response return response
@@ -92,7 +94,9 @@ class InvoiceVoid(generic.View):
if object.void: if object.void:
return HttpResponseRedirect(reverse_lazy('invoice_list')) 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): class InvoiceDelete(generic.DeleteView):
model = models.Invoice model = models.Invoice
@@ -100,20 +104,27 @@ class InvoiceDelete(generic.DeleteView):
def get(self, request, pk): def get(self, request, pk):
obj = self.get_object() obj = self.get_object()
if obj.payment_set.all().count() > 0: if obj.payment_set.all().count() > 0:
messages.info(self.request, 'To delete an invoice, delete the payments first.') messages.info(
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': obj.pk})) 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) return super(InvoiceDelete, self).get(pk)
def post(self, request, pk): def post(self, request, pk):
obj = self.get_object() obj = self.get_object()
if obj.payment_set.all().count() > 0: if obj.payment_set.all().count() > 0:
messages.info(self.request, 'To delete an invoice, delete the payments first.') messages.info(
return HttpResponseRedirect(reverse_lazy('invoice_detail', kwargs={'pk': obj.pk})) 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) return super(InvoiceDelete, self).post(pk)
def get_success_url(self): def get_success_url(self):
return self.request.POST.get('next') return self.request.POST.get('next')
class InvoiceArchive(generic.ListView): class InvoiceArchive(generic.ListView):
model = models.Invoice model = models.Invoice
template_name = 'RIGS/invoice_list_archive.html' template_name = 'RIGS/invoice_list_archive.html'
@@ -122,7 +133,7 @@ class InvoiceArchive(generic.ListView):
class InvoiceWaiting(generic.ListView): class InvoiceWaiting(generic.ListView):
model = models.Event model = models.Event
paginate_by = 25 # paginate_by = 25
template_name = 'RIGS/event_invoice.html' template_name = 'RIGS/event_invoice.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@@ -141,12 +152,14 @@ class InvoiceWaiting(generic.ListView):
# @todo find a way to select items # @todo find a way to select items
events = self.model.objects.filter( events = self.model.objects.filter(
( (
Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end # Starts before with no end
Q(end_date__lte=datetime.date.today()) # Has end date, finishes before Q(start_date__lte=datetime.date.today(), end_date__isnull=True) |
) & Q(invoice__isnull=True) # Has not already been invoiced # Has end date, finishes before
& Q(is_rig=True) # Is a rig (not non-rig) 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') \ ).order_by('start_date') \
.select_related('person', .select_related('person',
'organisation', 'organisation',
'venue', 'mic') \ 'venue', 'mic') \
@@ -165,7 +178,8 @@ class InvoiceEvent(generic.View):
invoice.invoice_date = datetime.date.today() invoice.invoice_date = datetime.date.today()
messages.success(self.request, 'Invoice created successfully') 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): class PaymentCreate(generic.CreateView):
@@ -174,8 +188,10 @@ class PaymentCreate(generic.CreateView):
def get_initial(self): def get_initial(self):
initial = super(generic.CreateView, self).get_initial() initial = super(generic.CreateView, self).get_initial()
invoicepk = self.request.GET.get('invoice', self.request.POST.get('invoice', None)) invoicepk = self.request.GET.get(
if invoicepk == None: 'invoice', self.request.POST.get(
'invoice', None))
if invoicepk is None:
raise Http404() raise Http404()
invoice = get_object_or_404(models.Invoice, pk=invoicepk) invoice = get_object_or_404(models.Invoice, pk=invoicepk)
initial.update({'invoice': invoice}) initial.update({'invoice': invoice})

View File

@@ -1,12 +1,12 @@
__author__ = 'Ghost' __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 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 from RIGS import models
@@ -17,14 +17,22 @@ class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
class Meta: class Meta:
model = models.Profile 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): def clean_initials(self):
""" """
Validate that the supplied initials are unique. Validate that the supplied initials are unique.
""" """
if models.Profile.objects.filter(initials__iexact=self.cleaned_data['initials']): if models.Profile.objects.filter(
raise forms.ValidationError("These initials are already in use. Please supply different initials.") initials__iexact=self.cleaned_data['initials']):
raise forms.ValidationError(
"These initials are already in use. Please supply different initials.")
return self.cleaned_data['initials'] return self.cleaned_data['initials']
@@ -45,9 +53,14 @@ class ProfileChangeForm(UserChangeForm):
# Events Shit # Events Shit
class EventForm(forms.ModelForm): class EventForm(forms.ModelForm):
datetime_input_formats = formats.get_format_lazy("DATETIME_INPUT_FORMATS") + settings.DATETIME_INPUT_FORMATS datetime_input_formats = formats.get_format_lazy(
meet_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False) "DATETIME_INPUT_FORMATS") + settings.DATETIME_INPUT_FORMATS
access_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False) 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() items_json = forms.CharField()
@@ -89,7 +102,8 @@ class EventForm(forms.ModelForm):
items = {} items = {}
for key in data: for key in data:
pk = int(key) 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 return items

View File

@@ -1,17 +1,18 @@
from RIGS import models, forms import datetime
from django_ical.views import ICalFeed
from django.db.models import Q import pytz
from django.core.urlresolvers import reverse_lazy, reverse, NoReverseMatch from django.conf import settings
from django.utils import timezone from django.db.models import Q
from django.conf import settings from django_ical.views import ICalFeed
from RIGS import models
import datetime, pytz
class CalendarICS(ICalFeed): class CalendarICS(ICalFeed):
""" """
A simple event calender A simple event calender
""" """
#Metadata which is passed on to clients # Metadata which is passed on to clients
product_id = 'RIGS' product_id = 'RIGS'
title = 'RIGS Calendar' title = 'RIGS Calendar'
timezone = settings.TIME_ZONE timezone = settings.TIME_ZONE
@@ -27,28 +28,32 @@ class CalendarICS(ICalFeed):
def get_object(self, request, *args, **kwargs): def get_object(self, request, *args, **kwargs):
params = {} params = {}
params['dry-hire'] = request.GET.get('dry-hire','true') == 'true' params['dry-hire'] = request.GET.get('dry-hire', 'true') == 'true'
params['non-rig'] = request.GET.get('non-rig','true') == 'true' params['non-rig'] = request.GET.get('non-rig', 'true') == 'true'
params['rig'] = request.GET.get('rig','true') == 'true' params['rig'] = request.GET.get('rig', 'true') == 'true'
params['cancelled'] = request.GET.get('cancelled','false') == 'true' params['cancelled'] = request.GET.get('cancelled', 'false') == 'true'
params['provisional'] = request.GET.get('provisional','true') == 'true' params['provisional'] = request.GET.get(
params['confirmed'] = request.GET.get('confirmed','true') == 'true' 'provisional', 'true') == 'true'
params['confirmed'] = request.GET.get('confirmed', 'true') == 'true'
return params return params
def description(self,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 = "Calendar generated by RIGS system. This includes event types: " + ('Rig, ' if params['rig'] else '') + (
desc = desc + "Includes events with status: " + ('Cancelled, ' if params['cancelled'] else '') + ('Provisional, ' if params['provisional'] else '') + ('Confirmed/Booked, ' if params['confirmed'] 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 return desc
def items(self, params): 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) start = datetime.datetime.now() - datetime.timedelta(days=365)
filter = Q(start_date__gte=start) 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']: if params['dry-hire']:
typeFilters = typeFilters | Q(dry_hire=True, is_rig=True) typeFilters = typeFilters | Q(dry_hire=True, is_rig=True)
@@ -59,18 +64,22 @@ class CalendarICS(ICalFeed):
if params['rig']: if params['rig']:
typeFilters = typeFilters | Q(is_rig=True, dry_hire=False) 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']: if params['cancelled']:
statusFilters = statusFilters | Q(status=models.Event.CANCELLED) statusFilters = statusFilters | Q(status=models.Event.CANCELLED)
if params['provisional']: if params['provisional']:
statusFilters = statusFilters | Q(status=models.Event.PROVISIONAL) statusFilters = statusFilters | Q(status=models.Event.PROVISIONAL)
if params['confirmed']: 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 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): def item_title(self, item):
title = '' title = ''
@@ -89,7 +98,7 @@ class CalendarICS(ICalFeed):
title += item.name title += item.name
# Add the status # Add the status
title += ' ('+str(item.get_status_display())+')' title += ' (' + str(item.get_status_display()) + ')'
return title return title
@@ -97,12 +106,13 @@ class CalendarICS(ICalFeed):
return item.earliest_time return item.earliest_time
def item_end_datetime(self, item): 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 + datetime.timedelta(days=1)
return item.latest_time return item.latest_time
def item_location(self,item): def item_location(self, item):
return item.venue return item.venue
def item_description(self, item): def item_description(self, item):
@@ -111,33 +121,39 @@ class CalendarICS(ICalFeed):
tz = pytz.timezone(self.timezone) 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 += 'Event = ' + item.name + '\n'
desc += 'Venue = ' + (item.venue.name if item.venue else '---') + '\n' desc += 'Venue = ' + (item.venue.name if item.venue else '---') + '\n'
if item.is_rig and item.person: 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 += 'Status = ' + str(item.get_status_display()) + '\n'
desc += 'MIC = ' + (item.mic.name if item.mic else '---') + '\n' desc += 'MIC = ' + (item.mic.name if item.mic else '---') + '\n'
desc += '\n' desc += '\n'
if item.meet_at: 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: 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: 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: 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' desc += '\n'
if item.description: 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 # 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" 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 return desc
@@ -149,8 +165,8 @@ class CalendarICS(ICalFeed):
# def item_created(self, item): #TODO - Implement created date-time (using django-reversion?) - not really necessary though # def item_created(self, item): #TODO - Implement created date-time (using django-reversion?) - not really necessary though
# return '' # 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 return item.last_edited_at
def item_guid(self, item): # use the rig-id as the ical unique event identifier def item_guid(self, item): # use the rig-id as the ical unique event identifier
return item.pk 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 datetime
import random 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 from RIGS import models
class Command(BaseCommand): class Command(BaseCommand):
help = 'Adds sample data to use for testing' help = 'Adds sample data to use for testing'
can_import_settings = True can_import_settings = True
@@ -19,17 +21,17 @@ class Command(BaseCommand):
keyholder_group = None keyholder_group = None
finance_group = None finance_group = None
def handle(self, *args, **options): def handle(self, *args, **options):
from django.conf import settings from django.conf import settings
if not (settings.DEBUG or settings.STAGING): if not (settings.DEBUG or settings.STAGING):
raise CommandError('You cannot run this command in production') 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(): 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.setupGenericProfiles()
@@ -44,7 +46,18 @@ class Command(BaseCommand):
self.setupUsefulProfiles() self.setupUsefulProfiles()
def setupPeople(self): 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): for i, name in enumerate(names):
with reversion.create_revision(): with reversion.create_revision():
reversion.set_user(random.choice(self.profiles)) reversion.set_user(random.choice(self.profiles))
@@ -66,7 +79,32 @@ class Command(BaseCommand):
self.people.append(newPerson) self.people.append(newPerson)
def setupOrganisations(self): 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): for i, name in enumerate(names):
with reversion.create_revision(): with reversion.create_revision():
reversion.set_user(random.choice(self.profiles)) reversion.set_user(random.choice(self.profiles))
@@ -90,7 +128,17 @@ class Command(BaseCommand):
self.organisations.append(newOrganisation) self.organisations.append(newOrganisation)
def setupVenues(self): 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): for i, name in enumerate(names):
with reversion.create_revision(): with reversion.create_revision():
reversion.set_user(random.choice(self.profiles)) reversion.set_user(random.choice(self.profiles))
@@ -117,8 +165,14 @@ class Command(BaseCommand):
self.keyholder_group = Group.objects.create(name='Keyholders') self.keyholder_group = Group.objects.create(name='Keyholders')
self.finance_group = Group.objects.create(name='Finance') 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"] keyholderPerms = ["add_event", "change_event", "view_event", "add_eventitem", "change_eventitem",
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"] "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: for permId in keyholderPerms:
self.keyholder_group.permissions.add(Permission.objects.get(codename=permId)) 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)) self.finance_group.permissions.add(Permission.objects.get(codename=permId))
def setupGenericProfiles(self): 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): for i, name in enumerate(names):
newProfile = models.Profile.objects.create(username=name.replace(" ",""), first_name=name.split(" ")[0], last_name=name.split(" ")[-1], newProfile = models.Profile.objects.create(username=name.replace(" ", ""), first_name=name.split(" ")[0],
email=name.replace(" ","")+"@example.com", last_name=name.split(" ")[-1],
initials="".join([ j[0].upper() for j in name.split() ])) email=name.replace(" ", "") + "@example.com",
initials="".join([j[0].upper() for j in name.split()]))
if i % 2 == 0: if i % 2 == 0:
newProfile.phone = "01234 567894" newProfile.phone = "01234 567894"
@@ -139,110 +195,127 @@ class Command(BaseCommand):
self.profiles.append(newProfile) self.profiles.append(newProfile)
def setupUsefulProfiles(self): def setupUsefulProfiles(self):
superUser = models.Profile.objects.create(username="superuser", first_name="Super", last_name="User", initials="SU", superUser = models.Profile.objects.create(username="superuser", first_name="Super", last_name="User",
email="superuser@example.com", is_superuser=True, is_active=True, is_staff=True) initials="SU",
email="superuser@example.com", is_superuser=True, is_active=True,
is_staff=True)
superUser.set_password('superuser') superUser.set_password('superuser')
superUser.save() superUser.save()
financeUser = models.Profile.objects.create(username="finance", first_name="Finance", last_name="User", initials="FU", financeUser = models.Profile.objects.create(username="finance", first_name="Finance", last_name="User",
email="financeuser@example.com", is_active=True) initials="FU",
email="financeuser@example.com", is_active=True)
financeUser.groups.add(self.finance_group) financeUser.groups.add(self.finance_group)
financeUser.groups.add(self.keyholder_group) financeUser.groups.add(self.keyholder_group)
financeUser.set_password('finance') financeUser.set_password('finance')
financeUser.save() financeUser.save()
keyholderUser = models.Profile.objects.create(username="keyholder", first_name="Keyholder", last_name="User", initials="KU", keyholderUser = models.Profile.objects.create(username="keyholder", first_name="Keyholder", last_name="User",
email="keyholderuser@example.com", is_active=True) initials="KU",
email="keyholderuser@example.com", is_active=True)
keyholderUser.groups.add(self.keyholder_group) keyholderUser.groups.add(self.keyholder_group)
keyholderUser.set_password('keyholder') keyholderUser.set_password('keyholder')
keyholderUser.save() keyholderUser.save()
basicUser = models.Profile.objects.create(username="basic", first_name="Basic", last_name="User", initials="BU", basicUser = models.Profile.objects.create(username="basic", first_name="Basic", last_name="User", initials="BU",
email="basicuser@example.com", is_active=True) email="basicuser@example.com", is_active=True)
basicUser.set_password('basic') basicUser.set_password('basic')
basicUser.save() basicUser.save()
def setupEvents(self): 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"] names = ["Outdoor Concert", "Hall Open Mic Night", "Festival", "Weekend Event", "Magic Show", "Society Ball",
descriptions = ["A brief desciption of the event","This event is boring","Probably wont happen","Warning: this has lots of kit"] "Evening Show", "Talent Show", "Acoustic Evening", "Hire of Things", "SU Event", "End of Term Show",
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!"] "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}, itemOptions = [
{'name': 'Projector', 'description': 'Some kind of video thinamejig, probably with unnecessary processing for free', 'quantity': 1, 'cost': 500.00}, {'name': 'Speakers', 'description': 'Some really really big speakers \n these are very loud', 'quantity': 2,
{'name': 'Lighting Desk', 'description': 'Cannot provide guarentee that it will work', 'quantity': 1, 'cost': 200.52}, 'cost': 200.00},
{'name': 'Moving lights', 'description': 'Flashy lights, with the copper', 'quantity': 8, 'cost': 50.00}, {'name': 'Projector',
{'name': 'Microphones', 'description': 'Make loud noise \n you will want speakers with this', 'quantity': 5, 'cost': 0.50}, 'description': 'Some kind of video thinamejig, probably with unnecessary processing for free',
{'name': 'Sound Mixer Thing', 'description': 'Might be analogue, might be digital', 'quantity': 1, 'cost': 100.00}, 'quantity': 1, 'cost': 500.00},
{'name': 'Electricity', 'description': 'You need this', 'quantity': 1, 'cost': 200.00}, {'name': 'Lighting Desk', 'description': 'Cannot provide guarentee that it will work', 'quantity': 1,
{'name': 'Crew', 'description': 'Costs nothing, because reasons', 'quantity': 1, 'cost': 0.00}, 'cost': 200.52},
{'name': 'Loyalty Discount', 'description': 'Have some negative moneys', 'quantity': 1, 'cost': -50.00}] {'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(): with reversion.create_revision():
reversion.set_user(random.choice(self.profiles)) 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) 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) newEvent = models.Event.objects.create(name=name, start_date=startDate)
if random.randint(0,2) > 1: # 1 in 3 have a start time if random.randint(0, 2) > 1: # 1 in 3 have a start time
newEvent.start_time = datetime.time(random.randint(15,20)) 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 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)) newEvent.end_time = datetime.time(random.randint(21, 23))
elif random.randint(0,1)>0: # half of the others finish early the next day 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_date = newEvent.start_date + datetime.timedelta(days=1)
newEvent.end_time = datetime.time(random.randint(0,5)) 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 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_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) 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) 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) 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) newEvent.venue = random.choice(self.venues)
# Could have any status, equally weighted # 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) 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.notes = random.choice(notes)
newEvent.save() newEvent.save()
# Now add some items # Now add some items
for j in range(random.randint(1,5)): for j in range(random.randint(1, 5)):
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 = models.EventItem.objects.create(event=newEvent, order=j, **itemData)
newItem.save() newItem.save()
while newEvent.sum_total < 0: 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 = models.EventItem.objects.create(event=newEvent, order=j, **itemData)
newItem.save() newItem.save()
with reversion.create_revision(): with reversion.create_revision():
reversion.set_user(random.choice(self.profiles)) reversion.set_user(random.choice(self.profiles))
if newEvent.start_date < datetime.date.today(): # think about adding an invoice 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 random.randint(0, 2) > 0: # 2 in 3 have had paperwork sent to treasury
newInvoice = models.Invoice.objects.create(event=newEvent) 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 newInvoice.void = True
elif random.randint(0,2)>1: # 1 in 3 have been paid 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()) models.Payment.objects.create(invoice=newInvoice, amount=newInvoice.balance,
date=datetime.date.today())

View File

@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations
import django.core.validators import django.core.validators
import django.utils.timezone import django.utils.timezone
from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('auth', '0001_initial'), ('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)), ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)),
('password', models.CharField(max_length=128, verbose_name='password')), ('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')), ('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')), ('is_superuser', models.BooleanField(default=False,
('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')])), 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')), ('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')), ('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')), ('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_staff', models.BooleanField(default=False,
('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')), 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')), ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('initials', models.CharField(max_length=5, unique=True)), ('initials', models.CharField(max_length=5, unique=True)),
('phone', models.CharField(max_length=13, blank=True, null=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')), ('groups', models.ManyToManyField(blank=True,
('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')), 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={ options={
'verbose_name_plural': 'users', 'verbose_name_plural': 'users',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings from django.conf import settings
from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('RIGS', '0015_auto_20141208_0233'), ('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)), ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('date', models.DateField()), ('date', models.DateField()),
('amount', models.DecimalField(help_text=b'Please use ex. VAT', max_digits=10, decimal_places=2)), ('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')), ('invoice', models.ForeignKey(to='RIGS.Invoice')),
], ],
options={ options={
@@ -40,7 +41,8 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='event', model_name='event',
name='mic', 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, preserve_default=True,
), ),
] ]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('RIGS', '0018_auto_20150130_0016'), ('RIGS', '0018_auto_20150130_0016'),
] ]
@@ -14,7 +13,9 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='payment', model_name='payment',
name='method', 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, preserve_default=True,
), ),
] ]

View File

@@ -5,7 +5,6 @@ from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('RIGS', '0019_auto_20150131_1919'), ('RIGS', '0019_auto_20150131_1919'),
] ]
@@ -14,7 +13,9 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='payment', model_name='payment',
name='method', 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, preserve_default=True,
), ),
] ]

View File

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

View File

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

View File

@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations
import django.core.validators
import django.contrib.auth.models import django.contrib.auth.models
import django.core.validators
from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('RIGS', '0022_auto_20150424_2104'), ('RIGS', '0022_auto_20150424_2104'),
] ]
@@ -46,7 +45,10 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='profile', model_name='profile',
name='groups', 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( migrations.AlterField(
model_name='profile', model_name='profile',
@@ -56,7 +58,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='profile', model_name='profile',
name='username', 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( migrations.AlterField(
model_name='venue', model_name='venue',

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations
import django.db.models.deletion import django.db.models.deletion
from django.db import models, migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('RIGS', '0023_auto_20150529_0048'), ('RIGS', '0023_auto_20150529_0048'),
] ]
@@ -15,6 +14,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='event', model_name='event',
name='based_on', 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 datetime
import hashlib import hashlib
import pytz
import random import random
import string import string
from collections import Counter from collections import Counter
from decimal import Decimal from decimal import Decimal
import pytz
import reversion import reversion
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
@@ -19,22 +19,31 @@ from django.utils.functional import cached_property
# Create your models here. # Create your models here.
@python_2_unicode_compatible @python_2_unicode_compatible
class Profile(AbstractUser): 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) 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 @classmethod
def make_api_key(cls): def make_api_key(cls):
size = 20 size = 20
chars = string.ascii_letters + string.digits chars = string.ascii_letters + string.digits
new_api_key = ''.join(random.choice(chars) for x in range(size)) new_api_key = ''.join(random.choice(chars) for x in range(size))
return new_api_key; return new_api_key
@property @property
def profile_picture(self): def profile_picture(self):
url = "" url = ""
if settings.USE_GRAVATAR or settings.USE_GRAVATAR is None: 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 return url
@property @property
@@ -46,7 +55,8 @@ class Profile(AbstractUser):
@property @property
def latest_events(self): 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): def __str__(self):
return self.name return self.name
@@ -107,7 +117,8 @@ class Person(models.Model, RevisionMixin):
@property @property
def organisations(self): def organisations(self):
o = [] 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: if e.organisation:
o.append(e.organisation) o.append(e.organisation)
@@ -118,7 +129,8 @@ class Person(models.Model, RevisionMixin):
@property @property
def latest_events(self): 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): def get_absolute_url(self):
return reverse_lazy('person_detail', kwargs={'pk': self.pk}) return reverse_lazy('person_detail', kwargs={'pk': self.pk})
@@ -151,7 +163,8 @@ class Organisation(models.Model, RevisionMixin):
@property @property
def persons(self): def persons(self):
p = [] 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: if e.person:
p.append(e.person) p.append(e.person)
@@ -162,7 +175,8 @@ class Organisation(models.Model, RevisionMixin):
@property @property
def latest_events(self): 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): def get_absolute_url(self):
return reverse_lazy('organisation_detail', kwargs={'pk': self.pk}) return reverse_lazy('organisation_detail', kwargs={'pk': self.pk})
@@ -205,7 +219,8 @@ class VatRate(models.Model, RevisionMixin):
get_latest_by = 'start_at' get_latest_by = 'start_at'
def __str__(self): 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 @reversion.register
@@ -227,7 +242,8 @@ class Venue(models.Model, RevisionMixin):
@property @property
def latest_events(self): 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): def get_absolute_url(self):
return reverse_lazy('venue_detail', kwargs={'pk': self.pk}) return reverse_lazy('venue_detail', kwargs={'pk': self.pk})
@@ -249,7 +265,10 @@ class EventManager(models.Manager):
status=Event.CANCELLED)) | # Active dry hire status=Event.CANCELLED)) | # Active dry hire
(models.Q(dry_hire=True, checked_in_by__isnull=True) & ( (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.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', ).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person',
'organisation', 'organisation',
'venue', 'mic') 'venue', 'mic')
@@ -257,16 +276,24 @@ class EventManager(models.Manager):
def events_in_bounds(self, start, end): def events_in_bounds(self, start, end):
events = self.filter( events = self.filter(
(models.Q(start_date__gte=start.date(), start_date__lte=end.date())) | # Start date in bounds # Start date in bounds
(models.Q(end_date__gte=start.date(), end_date__lte=end.date())) | # End date in bounds (models.Q(start_date__gte=start.date(), start_date__lte=end.date())) |
(models.Q(access_at__gte=start, access_at__lte=end)) | # Access at in bounds # 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(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 # Start before, end after
(models.Q(access_at__lte=start, start_date__gte=end)) | # Access before, start after (models.Q(start_date__lte=start, end_date__gte=end)) |
(models.Q(access_at__lte=start, end_date__gte=end)) | # Access before, end after # Access before, start after
(models.Q(meet_at__lte=start, start_date__gte=end)) | # Meet before, start after (models.Q(access_at__lte=start, start_date__gte=end)) |
(models.Q(meet_at__lte=start, end_date__gte=end)) # Meet before, end after # 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', ).order_by('start_date', 'end_date', 'start_time', 'end_time', 'meet_at').select_related('person',
'organisation', 'organisation',
@@ -309,7 +336,9 @@ class Event(models.Model, RevisionMixin):
venue = models.ForeignKey('Venue', blank=True, null=True) venue = models.ForeignKey('Venue', blank=True, null=True)
description = models.TextField(blank=True, null=True) description = models.TextField(blank=True, null=True)
notes = 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) dry_hire = models.BooleanField(default=False)
is_rig = models.BooleanField(default=True) is_rig = models.BooleanField(default=True)
based_on = models.ForeignKey('Event', on_delete=models.SET_NULL, related_name='future_events', blank=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) meet_info = models.CharField(max_length=255, blank=True, null=True)
# Crew management # 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, mic = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_mic', blank=True, null=True,
verbose_name="MIC") verbose_name="MIC")
# Monies # Monies
payment_method = models.CharField(max_length=255, blank=True, null=True) payment_method = models.CharField(max_length=255, blank=True, null=True)
payment_received = 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') purchase_order = models.CharField(
collector = models.CharField(max_length=255, blank=True, null=True, verbose_name='collected by') 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 # Calculated values
""" """
@@ -409,9 +450,11 @@ class Event(models.Model, RevisionMixin):
# If there is no start time defined, pretend it's midnight # If there is no start time defined, pretend it's midnight
startTimeFaked = False startTimeFaked = False
if self.has_start_time: 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: 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 startTimeFaked = True
# timezoneIssues - apply the default timezone to the naiive datetime # timezoneIssues - apply the default timezone to the naiive datetime
@@ -419,7 +462,8 @@ class Event(models.Model, RevisionMixin):
startDateTime = tz.localize(startDateTime) startDateTime = tz.localize(startDateTime)
datetime_list.append(startDateTime) # then add it to the list 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 we faked it & it's the earliest, better own up
if startTimeFaked and earliest == startDateTime: if startTimeFaked and earliest == startDateTime:
@@ -455,12 +499,14 @@ class Event(models.Model, RevisionMixin):
def clean(self): def clean(self):
if self.end_date and self.start_date > self.end_date: 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 startEndSameDay = not self.end_date or self.end_date == self.start_date
hasStartAndEnd = self.has_start_time and self.has_end_time hasStartAndEnd = self.has_start_time and self.has_end_time
if startEndSameDay and hasStartAndEnd and self.start_time > self.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): def save(self, *args, **kwargs):
"""Call :meth:`full_clean` before saving.""" """Call :meth:`full_clean` before saving."""
@@ -489,7 +535,8 @@ class EventItem(models.Model):
ordering = ['order'] ordering = ['order']
def __str__(self): 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): class EventCrew(models.Model):
@@ -557,8 +604,15 @@ class Payment(models.Model):
invoice = models.ForeignKey('Invoice') invoice = models.ForeignKey('Invoice')
date = models.DateField() date = models.DateField()
amount = models.DecimalField(max_digits=10, decimal_places=2, help_text='Please use ex. VAT') amount = models.DecimalField(
method = models.CharField(max_length=2, choices=METHODS, null=True, blank=True) 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): def __str__(self):
return "%s: %d" % (self.get_method_display(), self.amount) 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 from RIGS.forms import ProfileRegistrationFormUniqueEmail
def user_created(sender, user, request, **kwargs): def user_created(sender, user, request, **kwargs):
form = ProfileRegistrationFormUniqueEmail(request.POST) form = ProfileRegistrationFormUniqueEmail(request.POST)
user.first_name = form.data['first_name'] user.first_name = form.data['first_name']
user.last_name = form.data['last_name'] user.last_name = form.data['last_name']
user.initials = form.data['initials'] user.initials = form.data['initials']
user.phone = form.data['phone'] user.phone = form.data['phone']
user.save() user.save()
from registration.signals import user_registered from registration.signals import user_registered
user_registered.connect(user_created) user_registered.connect(user_created)

View File

@@ -1,26 +1,23 @@
import os
import cStringIO as StringIO import cStringIO as StringIO
from io import BytesIO import copy
import datetime
import re
import urllib2 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.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.shortcuts import get_object_or_404
from django.template import RequestContext from django.template import RequestContext
from django.template.loader import get_template from django.template.loader import get_template
from django.conf import settings from django.views import generic
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from django.db.models import Q
from django.contrib import messages
from z3c.rml import rml2pdf from z3c.rml import rml2pdf
from PyPDF2 import PdfFileMerger, PdfFileReader
import simplejson
from RIGS import models, forms from RIGS import models, forms
import datetime
import re
import copy
__author__ = 'ghost' __author__ = 'ghost'
@@ -36,42 +33,21 @@ class RigboardIndex(generic.TemplateView):
context['events'] = models.Event.objects.current_events() context['events'] = models.Event.objects.current_events()
return context return context
class WebCalendar(generic.TemplateView): class WebCalendar(generic.TemplateView):
template_name = 'RIGS/calendar.html' template_name = 'RIGS/calendar.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(WebCalendar, self).get_context_data(**kwargs) context = super(WebCalendar, self).get_context_data(**kwargs)
context['view'] = kwargs.get('view','') context['view'] = kwargs.get('view', '')
context['date'] = kwargs.get('date','') context['date'] = kwargs.get('date', '')
return context return context
class EventDetail(generic.DetailView): class EventDetail(generic.DetailView):
model = models.Event model = models.Event
class EventOembed(generic.View):
model = models.Event
def get(self, request, pk=None):
embed_url = reverse('event_embed', args=[pk])
full_url = "{0}://{1}{2}".format(request.scheme, request.META['HTTP_HOST'], embed_url)
data = {
'html': '<iframe src="{0}" frameborder="0" width="100%" height="250"></iframe>'.format(full_url),
'version': '1.0',
'type': 'rich',
'height': '250'
}
json = simplejson.JSONEncoderForHTML().encode(data)
return HttpResponse(json, content_type="application/json")
class EventEmbed(EventDetail):
template_name = 'RIGS/event_embed.html'
class EventCreate(generic.CreateView): class EventCreate(generic.CreateView):
model = models.Event model = models.Event
form_class = forms.EventForm form_class = forms.EventForm
@@ -83,10 +59,12 @@ class EventCreate(generic.CreateView):
form = context['form'] form = context['form']
if re.search('"-\d+"', form['items_json'].value()): 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
# Get some other objects to include in the form. Used when there are errors but also nice and quick. # errors but also nice and quick.
for field, model in form.related_models.iteritems(): for field, model in form.related_models.iteritems():
value = form[field].value() value = form[field].value()
if value is not None and value != '': if value is not None and value != '':
@@ -107,7 +85,8 @@ class EventUpdate(generic.UpdateView):
form = context['form'] 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(): for field, model in form.related_models.iteritems():
value = form[field].value() value = form[field].value()
if value is not None and value != '': if value is not None and value != '':
@@ -117,17 +96,21 @@ class EventUpdate(generic.UpdateView):
def get_success_url(self): def get_success_url(self):
return reverse_lazy('event_detail', kwargs={'pk': self.object.pk}) return reverse_lazy('event_detail', kwargs={'pk': self.object.pk})
class EventDuplicate(EventUpdate): class EventDuplicate(EventUpdate):
def get_object(self, queryset=None): def get_object(self, queryset=None):
old = super(EventDuplicate, self).get_object(queryset) # Get the object (the event you're duplicating) # Get the object (the event you're duplicating)
new = copy.copy(old) # Make a copy of the object in memory old = super(EventDuplicate, self).get_object(queryset)
new.based_on = old # Make the new event based on the old event new = copy.copy(old) # Make a copy of the object in memory
new.purchase_order = None 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) if self.request.method in (
new.pk = None # This means a new event will be created on save, and all items will be re-created 'POST', 'PUT'): # This only happens on save (otherwise items won't display in editor)
else: 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 return new
@@ -136,6 +119,7 @@ class EventDuplicate(EventUpdate):
context["duplicate"] = True context["duplicate"] = True
return context return context
class EventPrint(generic.View): class EventPrint(generic.View):
def get(self, request, pk): def get(self, request, pk):
object = get_object_or_404(models.Event, pk=pk) object = get_object_or_404(models.Event, pk=pk)
@@ -145,8 +129,7 @@ class EventPrint(generic.View):
merger = PdfFileMerger() merger = PdfFileMerger()
for copy in copies: 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, 'object': object,
'fonts': { 'fonts': {
'opensans': { 'opensans': {
@@ -154,11 +137,12 @@ class EventPrint(generic.View):
'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF', 'bold': 'RIGS/static/fonts/OPENSANS-BOLD.TTF',
} }
}, },
'copy':copy, 'copy': copy,
'current_user':request.user, '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) rml = template.render(context)
buffer = StringIO.StringIO() buffer = StringIO.StringIO()
@@ -179,10 +163,12 @@ class EventPrint(generic.View):
escapedEventName = re.sub('[^a-zA-Z0-9 \n\.]', '', object.name) 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()) response.write(merged.getvalue())
return response return response
class EventArchive(generic.ArchiveIndexView): class EventArchive(generic.ArchiveIndexView):
model = models.Event model = models.Event
date_field = "start_date" date_field = "start_date"
@@ -216,6 +202,9 @@ class EventArchive(generic.ArchiveIndexView):
qs.select_related('person', 'organisation', 'venue', 'mic') qs.select_related('person', 'organisation', 'venue', 'mic')
if len(qs) == 0: 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

File diff suppressed because one or more lines are too long

162
RIGS/static/js/affix.js Normal file
View File

@@ -0,0 +1,162 @@
/* ========================================================================
* Bootstrap: affix.js v3.3.2
* http://getbootstrap.com/javascript/#affix
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// AFFIX CLASS DEFINITION
// ======================
var Affix = function (element, options) {
this.options = $.extend({}, Affix.DEFAULTS, options)
this.$target = $(this.options.target)
.on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
.on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
this.$element = $(element)
this.affixed =
this.unpin =
this.pinnedOffset = null
this.checkPosition()
}
Affix.VERSION = '3.3.2'
Affix.RESET = 'affix affix-top affix-bottom'
Affix.DEFAULTS = {
offset: 0,
target: window
}
Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
var scrollTop = this.$target.scrollTop()
var position = this.$element.offset()
var targetHeight = this.$target.height()
if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
if (this.affixed == 'bottom') {
if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
}
var initializing = this.affixed == null
var colliderTop = initializing ? scrollTop : position.top
var colliderHeight = initializing ? targetHeight : height
if (offsetTop != null && scrollTop <= offsetTop) return 'top'
if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
return false
}
Affix.prototype.getPinnedOffset = function () {
if (this.pinnedOffset) return this.pinnedOffset
this.$element.removeClass(Affix.RESET).addClass('affix')
var scrollTop = this.$target.scrollTop()
var position = this.$element.offset()
return (this.pinnedOffset = position.top - scrollTop)
}
Affix.prototype.checkPositionWithEventLoop = function () {
setTimeout($.proxy(this.checkPosition, this), 1)
}
Affix.prototype.checkPosition = function () {
if (!this.$element.is(':visible')) return
var height = this.$element.height()
var offset = this.options.offset
var offsetTop = offset.top
var offsetBottom = offset.bottom
var scrollHeight = $('body').height()
if (typeof offset != 'object') offsetBottom = offsetTop = offset
if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
if (this.affixed != affix) {
if (this.unpin != null) this.$element.css('top', '')
var affixType = 'affix' + (affix ? '-' + affix : '')
var e = $.Event(affixType + '.bs.affix')
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
this.affixed = affix
this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
this.$element
.removeClass(Affix.RESET)
.addClass(affixType)
.trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
}
if (affix == 'bottom') {
this.$element.offset({
top: scrollHeight - height - offsetBottom
})
}
}
// AFFIX PLUGIN DEFINITION
// =======================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.affix')
var options = typeof option == 'object' && option
if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.affix
$.fn.affix = Plugin
$.fn.affix.Constructor = Affix
// AFFIX NO CONFLICT
// =================
$.fn.affix.noConflict = function () {
$.fn.affix = old
return this
}
// AFFIX DATA-API
// ==============
$(window).on('load', function () {
$('[data-spy="affix"]').each(function () {
var $spy = $(this)
var data = $spy.data()
data.offset = data.offset || {}
if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
if (data.offsetTop != null) data.offset.top = data.offsetTop
Plugin.call($spy, data)
})
})
}(jQuery);

94
RIGS/static/js/alert.js Normal file
View File

@@ -0,0 +1,94 @@
/* ========================================================================
* Bootstrap: alert.js v3.3.2
* http://getbootstrap.com/javascript/#alerts
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// ALERT CLASS DEFINITION
// ======================
var dismiss = '[data-dismiss="alert"]'
var Alert = function (el) {
$(el).on('click', dismiss, this.close)
}
Alert.VERSION = '3.3.2'
Alert.TRANSITION_DURATION = 150
Alert.prototype.close = function (e) {
var $this = $(this)
var selector = $this.attr('data-target')
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
var $parent = $(selector)
if (e) e.preventDefault()
if (!$parent.length) {
$parent = $this.closest('.alert')
}
$parent.trigger(e = $.Event('close.bs.alert'))
if (e.isDefaultPrevented()) return
$parent.removeClass('in')
function removeElement() {
// detach from parent, fire event then clean up data
$parent.detach().trigger('closed.bs.alert').remove()
}
$.support.transition && $parent.hasClass('fade') ?
$parent
.one('bsTransitionEnd', removeElement)
.emulateTransitionEnd(Alert.TRANSITION_DURATION) :
removeElement()
}
// ALERT PLUGIN DEFINITION
// =======================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.alert')
if (!data) $this.data('bs.alert', (data = new Alert(this)))
if (typeof option == 'string') data[option].call($this)
})
}
var old = $.fn.alert
$.fn.alert = Plugin
$.fn.alert.Constructor = Alert
// ALERT NO CONFLICT
// =================
$.fn.alert.noConflict = function () {
$.fn.alert = old
return this
}
// ALERT DATA-API
// ==============
$(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
}(jQuery);

View File

@@ -1,104 +1,104 @@
$(document).ready(function() { $(document).ready(function () {
clearSelectionLabel = '(no selection)'; 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'); //console.log('Changing selected value');
obj.find('option').remove(); //Remove all the available options obj.find('option').remove(); //Remove all the available options
obj.append( //Add the new option obj.append( //Add the new option
$("<option></option>") $("<option></option>")
.attr("value",pk) .attr("value", pk)
.text(text) .text(text)
.data('update_url',update_url) .data('update_url', update_url)
); );
obj.selectpicker('render'); //Re-render the UI obj.selectpicker('render'); //Re-render the UI
obj.selectpicker('refresh'); //Re-render the UI obj.selectpicker('refresh'); //Re-render the UI
obj.selectpicker('val', pk); //Set the new value to be selected obj.selectpicker('val', pk); //Set the new value to be selected
obj.change(); //Trigger the change function manually obj.change(); //Trigger the change function manually
} }
function refreshUpdateHref(obj) { function refreshUpdateHref(obj) {
//console.log('Refreshing Update URL'); //console.log('Refreshing Update URL');
targetObject = $('#'+obj.attr('id')+'-update'); targetObject = $('#' + obj.attr('id') + '-update');
update_url = $('option:selected', obj).data('update_url'); update_url = $('option:selected', obj).data('update_url');
if (update_url=="") { //Probably "clear selection" has been chosen if (update_url == "") { //Probably "clear selection" has been chosen
// console.log('Trying to disable'); // console.log('Trying to disable');
targetObject.attr('disabled', true); targetObject.attr('disabled', true);
} else { } else {
targetObject.attr('href', update_url); targetObject.attr('href', update_url);
targetObject.attr('disabled', false); targetObject.attr('disabled', false);
} }
} }
$(".selectpicker").each(function() { $(".selectpicker").each(function () {
var options = { var options = {
ajax: { ajax: {
url: $(this).data('sourceurl'), url: $(this).data('sourceurl'),
type: 'GET', type: 'GET',
dataType: 'json', dataType: 'json',
// Use "{{{q}}}" as a placeholder and Ajax Bootstrap Select will // Use "{{{q}}}" as a placeholder and Ajax Bootstrap Select will
// automatically replace it with the value of the search query. // automatically replace it with the value of the search query.
data: { data: {
term: '{{{q}}}' 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;
} }
},
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>") $(this).prepend($("<option></option>")
.attr("value",'') .attr("value", '')
.text(clearSelectionLabel) .text(clearSelectionLabel)
.data('update_url','')); //Add "clear selection" option .data('update_url', '')); //Add "clear selection" option
$(this).selectpicker().ajaxSelectPicker(options); //Initiaise selectPicker $(this).selectpicker().ajaxSelectPicker(options); //Initiaise selectPicker
$(this).change(function(){ //on change, update the edit button href $(this).change(function () { //on change, update the edit button href
// console.log('Selectbox Changed'); // console.log('Selectbox Changed');
refreshUpdateHref($(this)); 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']);
}
}); });
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']);
}
});
}); });

116
RIGS/static/js/button.js Normal file
View File

@@ -0,0 +1,116 @@
/* ========================================================================
* Bootstrap: button.js v3.3.2
* http://getbootstrap.com/javascript/#buttons
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// BUTTON PUBLIC CLASS DEFINITION
// ==============================
var Button = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, Button.DEFAULTS, options)
this.isLoading = false
}
Button.VERSION = '3.3.2'
Button.DEFAULTS = {
loadingText: 'loading...'
}
Button.prototype.setState = function (state) {
var d = 'disabled'
var $el = this.$element
var val = $el.is('input') ? 'val' : 'html'
var data = $el.data()
state = state + 'Text'
if (data.resetText == null) $el.data('resetText', $el[val]())
// push to event loop to allow forms to submit
setTimeout($.proxy(function () {
$el[val](data[state] == null ? this.options[state] : data[state])
if (state == 'loadingText') {
this.isLoading = true
$el.addClass(d).attr(d, d)
} else if (this.isLoading) {
this.isLoading = false
$el.removeClass(d).removeAttr(d)
}
}, this), 0)
}
Button.prototype.toggle = function () {
var changed = true
var $parent = this.$element.closest('[data-toggle="buttons"]')
if ($parent.length) {
var $input = this.$element.find('input')
if ($input.prop('type') == 'radio') {
if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
else $parent.find('.active').removeClass('active')
}
if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
} else {
this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
}
if (changed) this.$element.toggleClass('active')
}
// BUTTON PLUGIN DEFINITION
// ========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.button')
var options = typeof option == 'object' && option
if (!data) $this.data('bs.button', (data = new Button(this, options)))
if (option == 'toggle') data.toggle()
else if (option) data.setState(option)
})
}
var old = $.fn.button
$.fn.button = Plugin
$.fn.button.Constructor = Button
// BUTTON NO CONFLICT
// ==================
$.fn.button.noConflict = function () {
$.fn.button = old
return this
}
// BUTTON DATA-API
// ===============
$(document)
.on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
var $btn = $(e.target)
if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
Plugin.call($btn, 'toggle')
e.preventDefault()
})
.on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
$(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
})
}(jQuery);

237
RIGS/static/js/carousel.js Normal file
View File

@@ -0,0 +1,237 @@
/* ========================================================================
* Bootstrap: carousel.js v3.3.2
* http://getbootstrap.com/javascript/#carousel
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// CAROUSEL CLASS DEFINITION
// =========================
var Carousel = function (element, options) {
this.$element = $(element)
this.$indicators = this.$element.find('.carousel-indicators')
this.options = options
this.paused =
this.sliding =
this.interval =
this.$active =
this.$items = null
this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
.on('mouseenter.bs.carousel', $.proxy(this.pause, this))
.on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
}
Carousel.VERSION = '3.3.2'
Carousel.TRANSITION_DURATION = 600
Carousel.DEFAULTS = {
interval: 5000,
pause: 'hover',
wrap: true,
keyboard: true
}
Carousel.prototype.keydown = function (e) {
if (/input|textarea/i.test(e.target.tagName)) return
switch (e.which) {
case 37: this.prev(); break
case 39: this.next(); break
default: return
}
e.preventDefault()
}
Carousel.prototype.cycle = function (e) {
e || (this.paused = false)
this.interval && clearInterval(this.interval)
this.options.interval
&& !this.paused
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
return this
}
Carousel.prototype.getItemIndex = function (item) {
this.$items = item.parent().children('.item')
return this.$items.index(item || this.$active)
}
Carousel.prototype.getItemForDirection = function (direction, active) {
var activeIndex = this.getItemIndex(active)
var willWrap = (direction == 'prev' && activeIndex === 0)
|| (direction == 'next' && activeIndex == (this.$items.length - 1))
if (willWrap && !this.options.wrap) return active
var delta = direction == 'prev' ? -1 : 1
var itemIndex = (activeIndex + delta) % this.$items.length
return this.$items.eq(itemIndex)
}
Carousel.prototype.to = function (pos) {
var that = this
var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
if (pos > (this.$items.length - 1) || pos < 0) return
if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
if (activeIndex == pos) return this.pause().cycle()
return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
}
Carousel.prototype.pause = function (e) {
e || (this.paused = true)
if (this.$element.find('.next, .prev').length && $.support.transition) {
this.$element.trigger($.support.transition.end)
this.cycle(true)
}
this.interval = clearInterval(this.interval)
return this
}
Carousel.prototype.next = function () {
if (this.sliding) return
return this.slide('next')
}
Carousel.prototype.prev = function () {
if (this.sliding) return
return this.slide('prev')
}
Carousel.prototype.slide = function (type, next) {
var $active = this.$element.find('.item.active')
var $next = next || this.getItemForDirection(type, $active)
var isCycling = this.interval
var direction = type == 'next' ? 'left' : 'right'
var that = this
if ($next.hasClass('active')) return (this.sliding = false)
var relatedTarget = $next[0]
var slideEvent = $.Event('slide.bs.carousel', {
relatedTarget: relatedTarget,
direction: direction
})
this.$element.trigger(slideEvent)
if (slideEvent.isDefaultPrevented()) return
this.sliding = true
isCycling && this.pause()
if (this.$indicators.length) {
this.$indicators.find('.active').removeClass('active')
var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
$nextIndicator && $nextIndicator.addClass('active')
}
var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
if ($.support.transition && this.$element.hasClass('slide')) {
$next.addClass(type)
$next[0].offsetWidth // force reflow
$active.addClass(direction)
$next.addClass(direction)
$active
.one('bsTransitionEnd', function () {
$next.removeClass([type, direction].join(' ')).addClass('active')
$active.removeClass(['active', direction].join(' '))
that.sliding = false
setTimeout(function () {
that.$element.trigger(slidEvent)
}, 0)
})
.emulateTransitionEnd(Carousel.TRANSITION_DURATION)
} else {
$active.removeClass('active')
$next.addClass('active')
this.sliding = false
this.$element.trigger(slidEvent)
}
isCycling && this.cycle()
return this
}
// CAROUSEL PLUGIN DEFINITION
// ==========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.carousel')
var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
var action = typeof option == 'string' ? option : options.slide
if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (action) data[action]()
else if (options.interval) data.pause().cycle()
})
}
var old = $.fn.carousel
$.fn.carousel = Plugin
$.fn.carousel.Constructor = Carousel
// CAROUSEL NO CONFLICT
// ====================
$.fn.carousel.noConflict = function () {
$.fn.carousel = old
return this
}
// CAROUSEL DATA-API
// =================
var clickHandler = function (e) {
var href
var $this = $(this)
var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
if (!$target.hasClass('carousel')) return
var options = $.extend({}, $target.data(), $this.data())
var slideIndex = $this.attr('data-slide-to')
if (slideIndex) options.interval = false
Plugin.call($target, options)
if (slideIndex) {
$target.data('bs.carousel').to(slideIndex)
}
e.preventDefault()
}
$(document)
.on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
.on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
$(window).on('load', function () {
$('[data-ride="carousel"]').each(function () {
var $carousel = $(this)
Plugin.call($carousel, $carousel.data())
})
})
}(jQuery);

211
RIGS/static/js/collapse.js Normal file
View File

@@ -0,0 +1,211 @@
/* ========================================================================
* Bootstrap: collapse.js v3.3.2
* http://getbootstrap.com/javascript/#collapse
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// COLLAPSE PUBLIC CLASS DEFINITION
// ================================
var Collapse = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, Collapse.DEFAULTS, options)
this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]')
this.transitioning = null
if (this.options.parent) {
this.$parent = this.getParent()
} else {
this.addAriaAndCollapsedClass(this.$element, this.$trigger)
}
if (this.options.toggle) this.toggle()
}
Collapse.VERSION = '3.3.2'
Collapse.TRANSITION_DURATION = 350
Collapse.DEFAULTS = {
toggle: true,
trigger: '[data-toggle="collapse"]'
}
Collapse.prototype.dimension = function () {
var hasWidth = this.$element.hasClass('width')
return hasWidth ? 'width' : 'height'
}
Collapse.prototype.show = function () {
if (this.transitioning || this.$element.hasClass('in')) return
var activesData
var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
if (actives && actives.length) {
activesData = actives.data('bs.collapse')
if (activesData && activesData.transitioning) return
}
var startEvent = $.Event('show.bs.collapse')
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
if (actives && actives.length) {
Plugin.call(actives, 'hide')
activesData || actives.data('bs.collapse', null)
}
var dimension = this.dimension()
this.$element
.removeClass('collapse')
.addClass('collapsing')[dimension](0)
.attr('aria-expanded', true)
this.$trigger
.removeClass('collapsed')
.attr('aria-expanded', true)
this.transitioning = 1
var complete = function () {
this.$element
.removeClass('collapsing')
.addClass('collapse in')[dimension]('')
this.transitioning = 0
this.$element
.trigger('shown.bs.collapse')
}
if (!$.support.transition) return complete.call(this)
var scrollSize = $.camelCase(['scroll', dimension].join('-'))
this.$element
.one('bsTransitionEnd', $.proxy(complete, this))
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
}
Collapse.prototype.hide = function () {
if (this.transitioning || !this.$element.hasClass('in')) return
var startEvent = $.Event('hide.bs.collapse')
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
var dimension = this.dimension()
this.$element[dimension](this.$element[dimension]())[0].offsetHeight
this.$element
.addClass('collapsing')
.removeClass('collapse in')
.attr('aria-expanded', false)
this.$trigger
.addClass('collapsed')
.attr('aria-expanded', false)
this.transitioning = 1
var complete = function () {
this.transitioning = 0
this.$element
.removeClass('collapsing')
.addClass('collapse')
.trigger('hidden.bs.collapse')
}
if (!$.support.transition) return complete.call(this)
this.$element
[dimension](0)
.one('bsTransitionEnd', $.proxy(complete, this))
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)
}
Collapse.prototype.toggle = function () {
this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
Collapse.prototype.getParent = function () {
return $(this.options.parent)
.find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
.each($.proxy(function (i, element) {
var $element = $(element)
this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
}, this))
.end()
}
Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
var isOpen = $element.hasClass('in')
$element.attr('aria-expanded', isOpen)
$trigger
.toggleClass('collapsed', !isOpen)
.attr('aria-expanded', isOpen)
}
function getTargetFromTrigger($trigger) {
var href
var target = $trigger.attr('data-target')
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
return $(target)
}
// COLLAPSE PLUGIN DEFINITION
// ==========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.collapse')
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data && options.toggle && option == 'show') options.toggle = false
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.collapse
$.fn.collapse = Plugin
$.fn.collapse.Constructor = Collapse
// COLLAPSE NO CONFLICT
// ====================
$.fn.collapse.noConflict = function () {
$.fn.collapse = old
return this
}
// COLLAPSE DATA-API
// =================
$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
var $this = $(this)
if (!$this.attr('data-target')) e.preventDefault()
var $target = getTargetFromTrigger($this)
var data = $target.data('bs.collapse')
var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this })
Plugin.call($target, option)
})
}(jQuery);

161
RIGS/static/js/dropdown.js Normal file
View File

@@ -0,0 +1,161 @@
/* ========================================================================
* Bootstrap: dropdown.js v3.3.2
* http://getbootstrap.com/javascript/#dropdowns
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// DROPDOWN CLASS DEFINITION
// =========================
var backdrop = '.dropdown-backdrop'
var toggle = '[data-toggle="dropdown"]'
var Dropdown = function (element) {
$(element).on('click.bs.dropdown', this.toggle)
}
Dropdown.VERSION = '3.3.2'
Dropdown.prototype.toggle = function (e) {
var $this = $(this)
if ($this.is('.disabled, :disabled')) return
var $parent = getParent($this)
var isActive = $parent.hasClass('open')
clearMenus()
if (!isActive) {
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
// if mobile we use a backdrop because click events don't delegate
$('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
}
var relatedTarget = { relatedTarget: this }
$parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
if (e.isDefaultPrevented()) return
$this
.trigger('focus')
.attr('aria-expanded', 'true')
$parent
.toggleClass('open')
.trigger('shown.bs.dropdown', relatedTarget)
}
return false
}
Dropdown.prototype.keydown = function (e) {
if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
var $this = $(this)
e.preventDefault()
e.stopPropagation()
if ($this.is('.disabled, :disabled')) return
var $parent = getParent($this)
var isActive = $parent.hasClass('open')
if ((!isActive && e.which != 27) || (isActive && e.which == 27)) {
if (e.which == 27) $parent.find(toggle).trigger('focus')
return $this.trigger('click')
}
var desc = ' li:not(.divider):visible a'
var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc)
if (!$items.length) return
var index = $items.index(e.target)
if (e.which == 38 && index > 0) index-- // up
if (e.which == 40 && index < $items.length - 1) index++ // down
if (!~index) index = 0
$items.eq(index).trigger('focus')
}
function clearMenus(e) {
if (e && e.which === 3) return
$(backdrop).remove()
$(toggle).each(function () {
var $this = $(this)
var $parent = getParent($this)
var relatedTarget = { relatedTarget: this }
if (!$parent.hasClass('open')) return
$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
if (e.isDefaultPrevented()) return
$this.attr('aria-expanded', 'false')
$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
})
}
function getParent($this) {
var selector = $this.attr('data-target')
if (!selector) {
selector = $this.attr('href')
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
var $parent = selector && $(selector)
return $parent && $parent.length ? $parent : $this.parent()
}
// DROPDOWN PLUGIN DEFINITION
// ==========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.dropdown')
if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
if (typeof option == 'string') data[option].call($this)
})
}
var old = $.fn.dropdown
$.fn.dropdown = Plugin
$.fn.dropdown.Constructor = Dropdown
// DROPDOWN NO CONFLICT
// ====================
$.fn.dropdown.noConflict = function () {
$.fn.dropdown = old
return this
}
// APPLY TO STANDARD DROPDOWN ELEMENTS
// ===================================
$(document)
.on('click.bs.dropdown.data-api', clearMenus)
.on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
.on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
.on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
.on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown)
.on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown)
}(jQuery);

View File

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

337
RIGS/static/js/modal.js Normal file
View File

@@ -0,0 +1,337 @@
/* ========================================================================
* Bootstrap: modal.js v3.3.5
* http://getbootstrap.com/javascript/#modals
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// MODAL CLASS DEFINITION
// ======================
var Modal = function (element, options) {
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$dialog = this.$element.find('.modal-dialog')
this.$backdrop = null
this.isShown = null
this.originalBodyPad = null
this.scrollbarWidth = 0
this.ignoreBackdropClick = false
if (this.options.remote) {
this.$element
.find('.modal-content')
.load(this.options.remote, $.proxy(function () {
this.$element.trigger('loaded.bs.modal')
}, this))
}
}
Modal.VERSION = '3.3.5'
Modal.TRANSITION_DURATION = 300
Modal.BACKDROP_TRANSITION_DURATION = 150
Modal.DEFAULTS = {
backdrop: true,
keyboard: true,
show: true
}
Modal.prototype.toggle = function (_relatedTarget) {
return this.isShown ? this.hide() : this.show(_relatedTarget)
}
Modal.prototype.show = function (_relatedTarget) {
var that = this
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
this.isShown = true
this.checkScrollbar()
this.setScrollbar()
this.$body.addClass('modal-open')
this.escape()
this.resize()
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
this.$dialog.on('mousedown.dismiss.bs.modal', function () {
that.$element.one('mouseup.dismiss.bs.modal', function (e) {
if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
})
})
this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade')
if (!that.$element.parent().length) {
that.$element.appendTo(that.$body) // don't move modals dom position
}
that.$element
.show()
.scrollTop(0)
that.adjustDialog()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
that.enforceFocus()
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
transition ?
that.$dialog // wait for modal to slide in
.one('bsTransitionEnd', function () {
that.$element.trigger('focus').trigger(e)
})
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
that.$element.trigger('focus').trigger(e)
})
}
Modal.prototype.hide = function (e) {
if (e) e.preventDefault()
e = $.Event('hide.bs.modal')
this.$element.trigger(e)
if (!this.isShown || e.isDefaultPrevented()) return
this.isShown = false
this.escape()
this.resize()
$(document).off('focusin.bs.modal')
this.$element
.removeClass('in')
.off('click.dismiss.bs.modal')
.off('mouseup.dismiss.bs.modal')
this.$dialog.off('mousedown.dismiss.bs.modal')
$.support.transition && this.$element.hasClass('fade') ?
this.$element
.one('bsTransitionEnd', $.proxy(this.hideModal, this))
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
this.hideModal()
}
Modal.prototype.enforceFocus = function () {
$(document)
.off('focusin.bs.modal') // guard against infinite focus loop
.on('focusin.bs.modal', $.proxy(function (e) {
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
this.$element.trigger('focus')
}
}, this))
}
Modal.prototype.escape = function () {
if (this.isShown && this.options.keyboard) {
this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
e.which == 27 && this.hide()
}, this))
} else if (!this.isShown) {
this.$element.off('keydown.dismiss.bs.modal')
}
}
Modal.prototype.resize = function () {
if (this.isShown) {
$(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
} else {
$(window).off('resize.bs.modal')
}
}
Modal.prototype.hideModal = function () {
var that = this
this.$element.hide()
this.backdrop(function () {
that.$body.removeClass('modal-open')
that.resetAdjustments()
that.resetScrollbar()
that.$element.trigger('hidden.bs.modal')
})
}
Modal.prototype.removeBackdrop = function () {
this.$backdrop && this.$backdrop.remove()
this.$backdrop = null
}
Modal.prototype.backdrop = function (callback) {
var that = this
var animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $(document.createElement('div'))
.addClass('modal-backdrop ' + animate)
.appendTo(this.$body)
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
if (this.ignoreBackdropClick) {
this.ignoreBackdropClick = false
return
}
if (e.target !== e.currentTarget) return
this.options.backdrop == 'static'
? this.$element[0].focus()
: this.hide()
}, this))
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
if (!callback) return
doAnimate ?
this.$backdrop
.one('bsTransitionEnd', callback)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
var callbackRemove = function () {
that.removeBackdrop()
callback && callback()
}
$.support.transition && this.$element.hasClass('fade') ?
this.$backdrop
.one('bsTransitionEnd', callbackRemove)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callbackRemove()
} else if (callback) {
callback()
}
}
// these following methods are used to handle overflowing modals
Modal.prototype.handleUpdate = function () {
this.adjustDialog()
}
Modal.prototype.adjustDialog = function () {
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
this.$element.css({
paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
})
}
Modal.prototype.resetAdjustments = function () {
this.$element.css({
paddingLeft: '',
paddingRight: ''
})
}
Modal.prototype.checkScrollbar = function () {
var fullWindowWidth = window.innerWidth
if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
var documentElementRect = document.documentElement.getBoundingClientRect()
fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
}
this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
this.scrollbarWidth = this.measureScrollbar()
}
Modal.prototype.setScrollbar = function () {
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
this.originalBodyPad = document.body.style.paddingRight || ''
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
}
Modal.prototype.resetScrollbar = function () {
this.$body.css('padding-right', this.originalBodyPad)
}
Modal.prototype.measureScrollbar = function () { // thx walsh
var scrollDiv = document.createElement('div')
scrollDiv.className = 'modal-scrollbar-measure'
this.$body.append(scrollDiv)
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
this.$body[0].removeChild(scrollDiv)
return scrollbarWidth
}
// MODAL PLUGIN DEFINITION
// =======================
function Plugin(option, _relatedTarget) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.modal')
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option](_relatedTarget)
else if (options.show) data.show(_relatedTarget)
})
}
var old = $.fn.modal
$.fn.modal = Plugin
$.fn.modal.Constructor = Modal
// MODAL NO CONFLICT
// =================
$.fn.modal.noConflict = function () {
$.fn.modal = old
return this
}
// MODAL DATA-API
// ==============
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
var $this = $(this)
var href = $this.attr('href')
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
if ($this.is('a')) e.preventDefault()
$target.one('show.bs.modal', function (showEvent) {
if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
$target.one('hidden.bs.modal', function () {
$this.is(':visible') && $this.trigger('focus')
})
})
Plugin.call($target, option, this)
})
}(jQuery);

113
RIGS/static/js/popover.js Normal file
View File

@@ -0,0 +1,113 @@
/* ========================================================================
* Bootstrap: popover.js v3.3.2
* http://getbootstrap.com/javascript/#popovers
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// POPOVER PUBLIC CLASS DEFINITION
// ===============================
var Popover = function (element, options) {
this.init('popover', element, options)
}
if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
Popover.VERSION = '3.3.2'
Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
placement: 'right',
trigger: 'click',
content: '',
template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
})
// NOTE: POPOVER EXTENDS tooltip.js
// ================================
Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
Popover.prototype.constructor = Popover
Popover.prototype.getDefaults = function () {
return Popover.DEFAULTS
}
Popover.prototype.setContent = function () {
var $tip = this.tip()
var title = this.getTitle()
var content = this.getContent()
$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
$tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
](content)
$tip.removeClass('fade top bottom left right in')
// IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
// this manually by checking the contents.
if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
}
Popover.prototype.hasContent = function () {
return this.getTitle() || this.getContent()
}
Popover.prototype.getContent = function () {
var $e = this.$element
var o = this.options
return $e.attr('data-content')
|| (typeof o.content == 'function' ?
o.content.call($e[0]) :
o.content)
}
Popover.prototype.arrow = function () {
return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
}
Popover.prototype.tip = function () {
if (!this.$tip) this.$tip = $(this.options.template)
return this.$tip
}
// POPOVER PLUGIN DEFINITION
// =========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.popover')
var options = typeof option == 'object' && option
if (!data && option == 'destroy') return
if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.popover
$.fn.popover = Plugin
$.fn.popover.Constructor = Popover
// POPOVER NO CONFLICT
// ===================
$.fn.popover.noConflict = function () {
$.fn.popover = old
return this
}
}(jQuery);

175
RIGS/static/js/scrollspy.js Normal file
View File

@@ -0,0 +1,175 @@
/* ========================================================================
* Bootstrap: scrollspy.js v3.3.2
* http://getbootstrap.com/javascript/#scrollspy
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// SCROLLSPY CLASS DEFINITION
// ==========================
function ScrollSpy(element, options) {
var process = $.proxy(this.process, this)
this.$body = $('body')
this.$scrollElement = $(element).is('body') ? $(window) : $(element)
this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
this.selector = (this.options.target || '') + ' .nav li > a'
this.offsets = []
this.targets = []
this.activeTarget = null
this.scrollHeight = 0
this.$scrollElement.on('scroll.bs.scrollspy', process)
this.refresh()
this.process()
}
ScrollSpy.VERSION = '3.3.2'
ScrollSpy.DEFAULTS = {
offset: 10
}
ScrollSpy.prototype.getScrollHeight = function () {
return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
}
ScrollSpy.prototype.refresh = function () {
var offsetMethod = 'offset'
var offsetBase = 0
if (!$.isWindow(this.$scrollElement[0])) {
offsetMethod = 'position'
offsetBase = this.$scrollElement.scrollTop()
}
this.offsets = []
this.targets = []
this.scrollHeight = this.getScrollHeight()
var self = this
this.$body
.find(this.selector)
.map(function () {
var $el = $(this)
var href = $el.data('target') || $el.attr('href')
var $href = /^#./.test(href) && $(href)
return ($href
&& $href.length
&& $href.is(':visible')
&& [[$href[offsetMethod]().top + offsetBase, href]]) || null
})
.sort(function (a, b) { return a[0] - b[0] })
.each(function () {
self.offsets.push(this[0])
self.targets.push(this[1])
})
}
ScrollSpy.prototype.process = function () {
var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
var scrollHeight = this.getScrollHeight()
var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
var offsets = this.offsets
var targets = this.targets
var activeTarget = this.activeTarget
var i
if (this.scrollHeight != scrollHeight) {
this.refresh()
}
if (scrollTop >= maxScroll) {
return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
}
if (activeTarget && scrollTop < offsets[0]) {
this.activeTarget = null
return this.clear()
}
for (i = offsets.length; i--;) {
activeTarget != targets[i]
&& scrollTop >= offsets[i]
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])
&& this.activate(targets[i])
}
}
ScrollSpy.prototype.activate = function (target) {
this.activeTarget = target
this.clear()
var selector = this.selector +
'[data-target="' + target + '"],' +
this.selector + '[href="' + target + '"]'
var active = $(selector)
.parents('li')
.addClass('active')
if (active.parent('.dropdown-menu').length) {
active = active
.closest('li.dropdown')
.addClass('active')
}
active.trigger('activate.bs.scrollspy')
}
ScrollSpy.prototype.clear = function () {
$(this.selector)
.parentsUntil(this.options.target, '.active')
.removeClass('active')
}
// SCROLLSPY PLUGIN DEFINITION
// ===========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.scrollspy')
var options = typeof option == 'object' && option
if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.scrollspy
$.fn.scrollspy = Plugin
$.fn.scrollspy.Constructor = ScrollSpy
// SCROLLSPY NO CONFLICT
// =====================
$.fn.scrollspy.noConflict = function () {
$.fn.scrollspy = old
return this
}
// SCROLLSPY DATA-API
// ==================
$(window).on('load.bs.scrollspy.data-api', function () {
$('[data-spy="scroll"]').each(function () {
var $spy = $(this)
Plugin.call($spy, $spy.data())
})
})
}(jQuery);

153
RIGS/static/js/tab.js Normal file
View File

@@ -0,0 +1,153 @@
/* ========================================================================
* Bootstrap: tab.js v3.3.2
* http://getbootstrap.com/javascript/#tabs
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// TAB CLASS DEFINITION
// ====================
var Tab = function (element) {
this.element = $(element)
}
Tab.VERSION = '3.3.2'
Tab.TRANSITION_DURATION = 150
Tab.prototype.show = function () {
var $this = this.element
var $ul = $this.closest('ul:not(.dropdown-menu)')
var selector = $this.data('target')
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
}
if ($this.parent('li').hasClass('active')) return
var $previous = $ul.find('.active:last a')
var hideEvent = $.Event('hide.bs.tab', {
relatedTarget: $this[0]
})
var showEvent = $.Event('show.bs.tab', {
relatedTarget: $previous[0]
})
$previous.trigger(hideEvent)
$this.trigger(showEvent)
if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
var $target = $(selector)
this.activate($this.closest('li'), $ul)
this.activate($target, $target.parent(), function () {
$previous.trigger({
type: 'hidden.bs.tab',
relatedTarget: $this[0]
})
$this.trigger({
type: 'shown.bs.tab',
relatedTarget: $previous[0]
})
})
}
Tab.prototype.activate = function (element, container, callback) {
var $active = container.find('> .active')
var transition = callback
&& $.support.transition
&& (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length)
function next() {
$active
.removeClass('active')
.find('> .dropdown-menu > .active')
.removeClass('active')
.end()
.find('[data-toggle="tab"]')
.attr('aria-expanded', false)
element
.addClass('active')
.find('[data-toggle="tab"]')
.attr('aria-expanded', true)
if (transition) {
element[0].offsetWidth // reflow for transition
element.addClass('in')
} else {
element.removeClass('fade')
}
if (element.parent('.dropdown-menu')) {
element
.closest('li.dropdown')
.addClass('active')
.end()
.find('[data-toggle="tab"]')
.attr('aria-expanded', true)
}
callback && callback()
}
$active.length && transition ?
$active
.one('bsTransitionEnd', next)
.emulateTransitionEnd(Tab.TRANSITION_DURATION) :
next()
$active.removeClass('in')
}
// TAB PLUGIN DEFINITION
// =====================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.tab')
if (!data) $this.data('bs.tab', (data = new Tab(this)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.tab
$.fn.tab = Plugin
$.fn.tab.Constructor = Tab
// TAB NO CONFLICT
// ===============
$.fn.tab.noConflict = function () {
$.fn.tab = old
return this
}
// TAB DATA-API
// ============
var clickHandler = function (e) {
e.preventDefault()
Plugin.call($(this), 'show')
}
$(document)
.on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
.on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
}(jQuery);

View File

@@ -0,0 +1,52 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bootstrap Plugin Test Suite</title>
<!-- jquery -->
<!--<script src="http://code.jquery.com/jquery-1.7.min.js"></script>-->
<script src="vendor/jquery.js"></script>
<!-- qunit -->
<link rel="stylesheet" href="vendor/qunit.css" media="screen">
<script src="vendor/qunit.js"></script>
<!-- plugin sources -->
<script src="../transition.js"></script>
<script src="../alert.js"></script>
<script src="../button.js"></script>
<script src="../carousel.js"></script>
<script src="../collapse.js"></script>
<script src="../dropdown.js"></script>
<script src="../modal.js"></script>
<script src="../scrollspy.js"></script>
<script src="../tab.js"></script>
<script src="../tooltip.js"></script>
<script src="../popover.js"></script>
<script src="../affix.js"></script>
<!-- unit tests -->
<script src="unit/transition.js"></script>
<script src="unit/alert.js"></script>
<script src="unit/button.js"></script>
<script src="unit/carousel.js"></script>
<script src="unit/collapse.js"></script>
<script src="unit/dropdown.js"></script>
<script src="unit/modal.js"></script>
<script src="unit/scrollspy.js"></script>
<script src="unit/tab.js"></script>
<script src="unit/tooltip.js"></script>
<script src="unit/popover.js"></script>
<script src="unit/affix.js"></script>
</head>
<body>
<div>
<h1 id="qunit-header">Bootstrap Plugin Test Suite</h1>
<h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"></div>
</div>
</body>
</html>

View File

@@ -0,0 +1,25 @@
$(function () {
module("affix")
test("should provide no conflict", function () {
var affix = $.fn.affix.noConflict()
ok(!$.fn.affix, 'affix was set back to undefined (org value)')
$.fn.affix = affix
})
test("should be defined on jquery object", function () {
ok($(document.body).affix, 'affix method is defined')
})
test("should return element", function () {
ok($(document.body).affix()[0] == document.body, 'document.body returned')
})
test("should exit early if element is not visible", function () {
var $affix = $('<div style="display: none"></div>').affix()
$affix.data('bs.affix').checkPosition()
ok(!$affix.hasClass('affix'), 'affix class was not added')
})
})

View File

@@ -0,0 +1,62 @@
$(function () {
module("alert")
test("should provide no conflict", function () {
var alert = $.fn.alert.noConflict()
ok(!$.fn.alert, 'alert was set back to undefined (org value)')
$.fn.alert = alert
})
test("should be defined on jquery object", function () {
ok($(document.body).alert, 'alert method is defined')
})
test("should return element", function () {
ok($(document.body).alert()[0] == document.body, 'document.body returned')
})
test("should fade element out on clicking .close", function () {
var alertHTML = '<div class="alert-message warning fade in">'
+ '<a class="close" href="#" data-dismiss="alert">×</a>'
+ '<p><strong>Holy guacamole!</strong> Best check yo self, you\'re not looking too good.</p>'
+ '</div>'
, alert = $(alertHTML).alert()
alert.find('.close').click()
ok(!alert.hasClass('in'), 'remove .in class on .close click')
})
test("should remove element when clicking .close", function () {
$.support.transition = false
var alertHTML = '<div class="alert-message warning fade in">'
+ '<a class="close" href="#" data-dismiss="alert">×</a>'
+ '<p><strong>Holy guacamole!</strong> Best check yo self, you\'re not looking too good.</p>'
+ '</div>'
, alert = $(alertHTML).appendTo('#qunit-fixture').alert()
ok($('#qunit-fixture').find('.alert-message').length, 'element added to dom')
alert.find('.close').click()
ok(!$('#qunit-fixture').find('.alert-message').length, 'element removed from dom')
})
test("should not fire closed when close is prevented", function () {
$.support.transition = false
stop();
$('<div class="alert"/>')
.on('close.bs.alert', function (e) {
e.preventDefault();
ok(true);
start();
})
.on('closed.bs.alert', function () {
ok(false);
})
.alert('close')
})
})

View File

@@ -0,0 +1,116 @@
$(function () {
module("button")
test("should provide no conflict", function () {
var button = $.fn.button.noConflict()
ok(!$.fn.button, 'button was set back to undefined (org value)')
$.fn.button = button
})
test("should be defined on jquery object", function () {
ok($(document.body).button, 'button method is defined')
})
test("should return element", function () {
ok($(document.body).button()[0] == document.body, 'document.body returned')
})
test("should return set state to loading", function () {
var btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
equal(btn.html(), 'mdo', 'btn text equals mdo')
btn.button('loading')
equal(btn.html(), 'fat', 'btn text equals fat')
stop()
setTimeout(function () {
ok(btn.attr('disabled'), 'btn is disabled')
ok(btn.hasClass('disabled'), 'btn has disabled class')
start()
}, 0)
})
test("should return reset state", function () {
var btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
equal(btn.html(), 'mdo', 'btn text equals mdo')
btn.button('loading')
equal(btn.html(), 'fat', 'btn text equals fat')
stop()
setTimeout(function () {
ok(btn.attr('disabled'), 'btn is disabled')
ok(btn.hasClass('disabled'), 'btn has disabled class')
start()
stop()
btn.button('reset')
equal(btn.html(), 'mdo', 'btn text equals mdo')
setTimeout(function () {
ok(!btn.attr('disabled'), 'btn is not disabled')
ok(!btn.hasClass('disabled'), 'btn does not have disabled class')
start()
}, 0)
}, 0)
})
test("should toggle active", function () {
var btn = $('<button class="btn">mdo</button>')
ok(!btn.hasClass('active'), 'btn does not have active class')
btn.button('toggle')
ok(btn.hasClass('active'), 'btn has class active')
})
test("should toggle active when btn children are clicked", function () {
var btn = $('<button class="btn" data-toggle="button">mdo</button>')
, inner = $('<i></i>')
btn
.append(inner)
.appendTo($('#qunit-fixture'))
ok(!btn.hasClass('active'), 'btn does not have active class')
inner.click()
ok(btn.hasClass('active'), 'btn has class active')
})
test("should toggle active when btn children are clicked within btn-group", function () {
var btngroup = $('<div class="btn-group" data-toggle="buttons"></div>')
, btn = $('<button class="btn">fat</button>')
, inner = $('<i></i>')
btngroup
.append(btn.append(inner))
.appendTo($('#qunit-fixture'))
ok(!btn.hasClass('active'), 'btn does not have active class')
inner.click()
ok(btn.hasClass('active'), 'btn has class active')
})
test("should check for closest matching toggle", function () {
var group = '<div class="btn-group" data-toggle="buttons">' +
'<label class="btn btn-primary active">' +
'<input type="radio" name="options" id="option1" checked="true"> Option 1' +
'</label>' +
'<label class="btn btn-primary">' +
'<input type="radio" name="options" id="option2"> Option 2' +
'</label>' +
'<label class="btn btn-primary">' +
'<input type="radio" name="options" id="option3"> Option 3' +
'</label>' +
'</div>'
group = $(group)
var btn1 = $(group.children()[0])
var btn2 = $(group.children()[1])
var btn3 = $(group.children()[2])
group.appendTo($('#qunit-fixture'))
ok(btn1.hasClass('active'), 'btn1 has active class')
ok(btn1.find('input').prop('checked'), 'btn1 is checked')
ok(!btn2.hasClass('active'), 'btn2 does not have active class')
ok(!btn2.find('input').prop('checked'), 'btn2 is not checked')
btn2.find('input').click()
ok(!btn1.hasClass('active'), 'btn1 does not have active class')
ok(!btn1.find('input').prop('checked'), 'btn1 is checked')
ok(btn2.hasClass('active'), 'btn2 has active class')
ok(btn2.find('input').prop('checked'), 'btn2 is checked')
})
})

View File

@@ -0,0 +1,87 @@
$(function () {
module("carousel")
test("should provide no conflict", function () {
var carousel = $.fn.carousel.noConflict()
ok(!$.fn.carousel, 'carousel was set back to undefined (org value)')
$.fn.carousel = carousel
})
test("should be defined on jquery object", function () {
ok($(document.body).carousel, 'carousel method is defined')
})
test("should return element", function () {
ok($(document.body).carousel()[0] == document.body, 'document.body returned')
})
test("should not fire sliden when slide is prevented", function () {
$.support.transition = false
stop()
$('<div class="carousel"/>')
.on('slide.bs.carousel', function (e) {
e.preventDefault();
ok(true);
start();
})
.on('slid.bs.carousel', function () {
ok(false);
})
.carousel('next')
})
test("should fire slide event with direction", function () {
var template = '<div id="myCarousel" class="carousel slide"><div class="carousel-inner"><div class="item active"><img alt=""><div class="carousel-caption"><h4>{{_i}}First Thumbnail label{{/i}}</h4><p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p></div></div><div class="item"><img alt=""><div class="carousel-caption"><h4>{{_i}}Second Thumbnail label{{/i}}</h4><p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p></div></div><div class="item"><img alt=""><div class="carousel-caption"><h4>{{_i}}Third Thumbnail label{{/i}}</h4><p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p></div></div></div><a class="left carousel-control" href="#myCarousel" data-slide="prev">&lsaquo;</a><a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a></div>'
$.support.transition = false
stop()
$(template).on('slide.bs.carousel', function (e) {
e.preventDefault()
ok(e.direction)
ok(e.direction === 'right' || e.direction === 'left')
start()
}).carousel('next')
})
test("should fire slide event with relatedTarget", function () {
var template = '<div id="myCarousel" class="carousel slide"><div class="carousel-inner"><div class="item active"><img alt=""><div class="carousel-caption"><h4>{{_i}}First Thumbnail label{{/i}}</h4><p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p></div></div><div class="item"><img alt=""><div class="carousel-caption"><h4>{{_i}}Second Thumbnail label{{/i}}</h4><p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p></div></div><div class="item"><img alt=""><div class="carousel-caption"><h4>{{_i}}Third Thumbnail label{{/i}}</h4><p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p></div></div></div><a class="left carousel-control" href="#myCarousel" data-slide="prev">&lsaquo;</a><a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a></div>'
$.support.transition = false
stop()
$(template)
.on('slide.bs.carousel', function (e) {
e.preventDefault();
ok(e.relatedTarget);
ok($(e.relatedTarget).hasClass('item'));
start();
})
.carousel('next')
})
test("should set interval from data attribute", 4, function () {
var template = $('<div id="myCarousel" class="carousel slide"> <div class="carousel-inner"> <div class="item active"> <img alt=""> <div class="carousel-caption"> <h4>{{_i}}First Thumbnail label{{/i}}</h4> <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p> </div> </div> <div class="item"> <img alt=""> <div class="carousel-caption"> <h4>{{_i}}Second Thumbnail label{{/i}}</h4> <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p> </div> </div> <div class="item"> <img alt=""> <div class="carousel-caption"> <h4>{{_i}}Third Thumbnail label{{/i}}</h4> <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p> </div> </div> </div> <a class="left carousel-control" href="#myCarousel" data-slide="prev">&lsaquo;</a> <a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a> </div>');
template.attr("data-interval", 1814);
template.appendTo("body");
$('[data-slide]').first().click();
ok($('#myCarousel').data('bs.carousel').options.interval == 1814);
$('#myCarousel').remove();
template.appendTo("body").attr("data-modal", "foobar");
$('[data-slide]').first().click();
ok($('#myCarousel').data('bs.carousel').options.interval == 1814, "even if there is an data-modal attribute set");
$('#myCarousel').remove();
template.appendTo("body");
$('[data-slide]').first().click();
$('#myCarousel').attr('data-interval', 1860);
$('[data-slide]').first().click();
ok($('#myCarousel').data('bs.carousel').options.interval == 1814, "attributes should be read only on intitialization");
$('#myCarousel').remove();
template.attr("data-interval", false);
template.appendTo("body");
$('#myCarousel').carousel(1);
ok($('#myCarousel').data('bs.carousel').options.interval === false, "data attribute has higher priority than default options");
$('#myCarousel').remove();
})
})

View File

@@ -0,0 +1,164 @@
$(function () {
module("collapse")
test("should provide no conflict", function () {
var collapse = $.fn.collapse.noConflict()
ok(!$.fn.collapse, 'collapse was set back to undefined (org value)')
$.fn.collapse = collapse
})
test("should be defined on jquery object", function () {
ok($(document.body).collapse, 'collapse method is defined')
})
test("should return element", function () {
ok($(document.body).collapse()[0] == document.body, 'document.body returned')
})
test("should show a collapsed element", function () {
var el = $('<div class="collapse"></div>').collapse('show')
ok(el.hasClass('in'), 'has class in')
ok(/height/.test(el.attr('style')), 'has height set')
})
test("should hide a collapsed element", function () {
var el = $('<div class="collapse"></div>').collapse('hide')
ok(!el.hasClass('in'), 'does not have class in')
ok(/height/.test(el.attr('style')), 'has height set')
})
test("should not fire shown when show is prevented", function () {
$.support.transition = false
stop()
$('<div class="collapse"/>')
.on('show.bs.collapse', function (e) {
e.preventDefault();
ok(true);
start();
})
.on('shown.bs.collapse', function () {
ok(false);
})
.collapse('show')
})
test("should reset style to auto after finishing opening collapse", function () {
$.support.transition = false
stop()
$('<div class="collapse" style="height: 0px"/>')
.on('show.bs.collapse', function () {
ok(this.style.height == '0px')
})
.on('shown.bs.collapse', function () {
ok(this.style.height == 'auto')
start()
})
.collapse('show')
})
test("should add active class to target when collapse shown", function () {
$.support.transition = false
stop()
var target = $('<a data-toggle="collapse" href="#test1"></a>')
.appendTo($('#qunit-fixture'))
var collapsible = $('<div id="test1"></div>')
.appendTo($('#qunit-fixture'))
.on('show.bs.collapse', function () {
ok(!target.hasClass('collapsed'))
start()
})
target.click()
})
test("should remove active class to target when collapse hidden", function () {
$.support.transition = false
stop()
var target = $('<a data-toggle="collapse" href="#test1"></a>')
.appendTo($('#qunit-fixture'))
var collapsible = $('<div id="test1" class="in"></div>')
.appendTo($('#qunit-fixture'))
.on('hide.bs.collapse', function () {
ok(target.hasClass('collapsed'))
start()
})
target.click()
})
test("should remove active class from inactive accordion targets", function () {
$.support.transition = false
stop()
var accordion = $('<div id="accordion"><div class="accordion-group"></div><div class="accordion-group"></div><div class="accordion-group"></div></div>')
.appendTo($('#qunit-fixture'))
var target1 = $('<a data-toggle="collapse" href="#body1" data-parent="#accordion"></a>')
.appendTo(accordion.find('.accordion-group').eq(0))
var collapsible1 = $('<div id="body1" class="in"></div>')
.appendTo(accordion.find('.accordion-group').eq(0))
var target2 = $('<a class="collapsed" data-toggle="collapse" href="#body2" data-parent="#accordion"></a>')
.appendTo(accordion.find('.accordion-group').eq(1))
var collapsible2 = $('<div id="body2"></div>')
.appendTo(accordion.find('.accordion-group').eq(1))
var target3 = $('<a class="collapsed" data-toggle="collapse" href="#body3" data-parent="#accordion"></a>')
.appendTo(accordion.find('.accordion-group').eq(2))
var collapsible3 = $('<div id="body3"></div>')
.appendTo(accordion.find('.accordion-group').eq(2))
.on('show.bs.collapse', function () {
ok(target1.hasClass('collapsed'))
ok(target2.hasClass('collapsed'))
ok(!target3.hasClass('collapsed'))
start()
})
target3.click()
})
test("should allow dots in data-parent", function () {
$.support.transition = false
stop()
var accordion = $('<div class="accordion"><div class="accordion-group"></div><div class="accordion-group"></div><div class="accordion-group"></div></div>')
.appendTo($('#qunit-fixture'))
var target1 = $('<a data-toggle="collapse" href="#body1" data-parent=".accordion"></a>')
.appendTo(accordion.find('.accordion-group').eq(0))
var collapsible1 = $('<div id="body1" class="in"></div>')
.appendTo(accordion.find('.accordion-group').eq(0))
var target2 = $('<a class="collapsed" data-toggle="collapse" href="#body2" data-parent=".accordion"></a>')
.appendTo(accordion.find('.accordion-group').eq(1))
var collapsible2 = $('<div id="body2"></div>')
.appendTo(accordion.find('.accordion-group').eq(1))
var target3 = $('<a class="collapsed" data-toggle="collapse" href="#body3" data-parent=".accordion"></a>')
.appendTo(accordion.find('.accordion-group').eq(2))
var collapsible3 = $('<div id="body3"></div>')
.appendTo(accordion.find('.accordion-group').eq(2))
.on('show.bs.collapse', function () {
ok(target1.hasClass('collapsed'))
ok(target2.hasClass('collapsed'))
ok(!target3.hasClass('collapsed'))
start()
})
target3.click()
})
})

View File

@@ -0,0 +1,219 @@
$(function () {
module("dropdowns")
test("should provide no conflict", function () {
var dropdown = $.fn.dropdown.noConflict()
ok(!$.fn.dropdown, 'dropdown was set back to undefined (org value)')
$.fn.dropdown = dropdown
})
test("should be defined on jquery object", function () {
ok($(document.body).dropdown, 'dropdown method is defined')
})
test("should return element", function () {
var el = $("<div />")
ok(el.dropdown()[0] === el[0], 'same element returned')
})
test("should not open dropdown if target is disabled", function () {
var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">'
+ '<button disabled href="#" class="btn dropdown-toggle" data-toggle="dropdown">Dropdown</button>'
+ '<ul class="dropdown-menu">'
+ '<li><a href="#">Secondary link</a></li>'
+ '<li><a href="#">Something else here</a></li>'
+ '<li class="divider"></li>'
+ '<li><a href="#">Another link</a></li>'
+ '</ul>'
+ '</li>'
+ '</ul>'
, dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').dropdown().click()
ok(!dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
})
test("should not open dropdown if target is disabled", function () {
var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">'
+ '<button href="#" class="btn dropdown-toggle disabled" data-toggle="dropdown">Dropdown</button>'
+ '<ul class="dropdown-menu">'
+ '<li><a href="#">Secondary link</a></li>'
+ '<li><a href="#">Something else here</a></li>'
+ '<li class="divider"></li>'
+ '<li><a href="#">Another link</a></li>'
+ '</ul>'
+ '</li>'
+ '</ul>'
, dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').dropdown().click()
ok(!dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
})
test("should add class open to menu if clicked", function () {
var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">'
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
+ '<ul class="dropdown-menu">'
+ '<li><a href="#">Secondary link</a></li>'
+ '<li><a href="#">Something else here</a></li>'
+ '<li class="divider"></li>'
+ '<li><a href="#">Another link</a></li>'
+ '</ul>'
+ '</li>'
+ '</ul>'
, dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').dropdown().click()
ok(dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
})
test("should test if element has a # before assuming it's a selector", function () {
var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">'
+ '<a href="/foo/" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
+ '<ul class="dropdown-menu">'
+ '<li><a href="#">Secondary link</a></li>'
+ '<li><a href="#">Something else here</a></li>'
+ '<li class="divider"></li>'
+ '<li><a href="#">Another link</a></li>'
+ '</ul>'
+ '</li>'
+ '</ul>'
, dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').dropdown().click()
ok(dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
})
test("should remove open class if body clicked", function () {
var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">'
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
+ '<ul class="dropdown-menu">'
+ '<li><a href="#">Secondary link</a></li>'
+ '<li><a href="#">Something else here</a></li>'
+ '<li class="divider"></li>'
+ '<li><a href="#">Another link</a></li>'
+ '</ul>'
+ '</li>'
+ '</ul>'
, dropdown = $(dropdownHTML)
.appendTo('#qunit-fixture')
.find('[data-toggle="dropdown"]')
.dropdown()
.click()
ok(dropdown.parent('.dropdown').hasClass('open'), 'open class added on click')
$('body').click()
ok(!dropdown.parent('.dropdown').hasClass('open'), 'open class removed')
dropdown.remove()
})
test("should remove open class if body clicked, with multiple drop downs", function () {
var dropdownHTML =
'<ul class="nav">'
+ ' <li><a href="#menu1">Menu 1</a></li>'
+ ' <li class="dropdown" id="testmenu">'
+ ' <a class="dropdown-toggle" data-toggle="dropdown" href="#testmenu">Test menu <b class="caret"></b></a>'
+ ' <ul class="dropdown-menu" role="menu">'
+ ' <li><a href="#sub1">Submenu 1</a></li>'
+ ' </ul>'
+ ' </li>'
+ '</ul>'
+ '<div class="btn-group">'
+ ' <button class="btn">Actions</button>'
+ ' <button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>'
+ ' <ul class="dropdown-menu">'
+ ' <li><a href="#">Action 1</a></li>'
+ ' </ul>'
+ '</div>'
, dropdowns = $(dropdownHTML).appendTo('#qunit-fixture').find('[data-toggle="dropdown"]')
, first = dropdowns.first()
, last = dropdowns.last()
ok(dropdowns.length == 2, "Should be two dropdowns")
first.click()
ok(first.parents('.open').length == 1, 'open class added on click')
ok($('#qunit-fixture .open').length == 1, 'only one object is open')
$('body').click()
ok($("#qunit-fixture .open").length === 0, 'open class removed')
last.click()
ok(last.parent('.open').length == 1, 'open class added on click')
ok($('#qunit-fixture .open').length == 1, 'only one object is open')
$('body').click()
ok($("#qunit-fixture .open").length === 0, 'open class removed')
$("#qunit-fixture").html("")
})
test("should fire show and hide event", function () {
var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">'
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
+ '<ul class="dropdown-menu">'
+ '<li><a href="#">Secondary link</a></li>'
+ '<li><a href="#">Something else here</a></li>'
+ '<li class="divider"></li>'
+ '<li><a href="#">Another link</a></li>'
+ '</ul>'
+ '</li>'
+ '</ul>'
, dropdown = $(dropdownHTML)
.appendTo('#qunit-fixture')
.find('[data-toggle="dropdown"]')
.dropdown()
stop()
dropdown
.parent('.dropdown')
.bind('show.bs.dropdown', function () {
ok(true, 'show was called')
})
.bind('hide.bs.dropdown', function () {
ok(true, 'hide was called')
start()
})
dropdown.click()
$(document.body).click()
})
test("should fire shown and hiden event", function () {
var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">'
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
+ '<ul class="dropdown-menu">'
+ '<li><a href="#">Secondary link</a></li>'
+ '<li><a href="#">Something else here</a></li>'
+ '<li class="divider"></li>'
+ '<li><a href="#">Another link</a></li>'
+ '</ul>'
+ '</li>'
+ '</ul>'
, dropdown = $(dropdownHTML)
.appendTo('#qunit-fixture')
.find('[data-toggle="dropdown"]')
.dropdown()
stop()
dropdown
.parent('.dropdown')
.bind('shown.bs.dropdown', function () {
ok(true, 'show was called')
})
.bind('hidden.bs.dropdown', function () {
ok(true, 'hide was called')
start()
})
dropdown.click()
$(document.body).click()
})
})

View File

@@ -0,0 +1,196 @@
$(function () {
module("modal")
test("should provide no conflict", function () {
var modal = $.fn.modal.noConflict()
ok(!$.fn.modal, 'modal was set back to undefined (org value)')
$.fn.modal = modal
})
test("should be defined on jquery object", function () {
var div = $("<div id='modal-test'></div>")
ok(div.modal, 'modal method is defined')
})
test("should return element", function () {
var div = $("<div id='modal-test'></div>")
ok(div.modal() == div, 'document.body returned')
$('#modal-test').remove()
})
test("should expose defaults var for settings", function () {
ok($.fn.modal.Constructor.DEFAULTS, 'default object exposed')
})
test("should insert into dom when show method is called", function () {
stop()
$.support.transition = false
$("<div id='modal-test'></div>")
.on("shown.bs.modal", function () {
ok($('#modal-test').length, 'modal inserted into dom')
$(this).remove()
start()
})
.modal("show")
})
test("should fire show event", function () {
stop()
$.support.transition = false
$("<div id='modal-test'></div>")
.on("show.bs.modal", function () {
ok(true, "show was called")
})
.on("shown.bs.modal", function () {
$(this).remove()
start()
})
.modal("show")
})
test("should not fire shown when default prevented", function () {
stop()
$.support.transition = false
$("<div id='modal-test'></div>")
.on("show.bs.modal", function (e) {
e.preventDefault()
ok(true, "show was called")
start()
})
.on("shown.bs.modal", function () {
ok(false, "shown was called")
})
.modal("show")
})
test("should hide modal when hide is called", function () {
stop()
$.support.transition = false
$("<div id='modal-test'></div>")
.on("shown.bs.modal", function () {
ok($('#modal-test').is(":visible"), 'modal visible')
ok($('#modal-test').length, 'modal inserted into dom')
$(this).modal("hide")
})
.on("hidden.bs.modal", function() {
ok(!$('#modal-test').is(":visible"), 'modal hidden')
$('#modal-test').remove()
start()
})
.modal("show")
})
test("should toggle when toggle is called", function () {
stop()
$.support.transition = false
var div = $("<div id='modal-test'></div>")
div
.on("shown.bs.modal", function () {
ok($('#modal-test').is(":visible"), 'modal visible')
ok($('#modal-test').length, 'modal inserted into dom')
div.modal("toggle")
})
.on("hidden.bs.modal", function() {
ok(!$('#modal-test').is(":visible"), 'modal hidden')
div.remove()
start()
})
.modal("toggle")
})
test("should remove from dom when click [data-dismiss=modal]", function () {
stop()
$.support.transition = false
var div = $("<div id='modal-test'><span class='close' data-dismiss='modal'></span></div>")
div
.on("shown.bs.modal", function () {
ok($('#modal-test').is(":visible"), 'modal visible')
ok($('#modal-test').length, 'modal inserted into dom')
div.find('.close').click()
})
.on("hidden.bs.modal", function() {
ok(!$('#modal-test').is(":visible"), 'modal hidden')
div.remove()
start()
})
.modal("toggle")
})
test("should allow modal close with 'backdrop:false'", function () {
stop()
$.support.transition = false
var div = $("<div>", { id: 'modal-test', "data-backdrop": false })
div
.on("shown.bs.modal", function () {
ok($('#modal-test').is(":visible"), 'modal visible')
div.modal("hide")
})
.on("hidden.bs.modal", function() {
ok(!$('#modal-test').is(":visible"), 'modal hidden')
div.remove()
start()
})
.modal("show")
})
test("should close modal when clicking outside of modal-content", function () {
stop()
$.support.transition = false
var div = $("<div id='modal-test'><div class='contents'></div></div>")
div
.bind("shown.bs.modal", function () {
ok($('#modal-test').length, 'modal insterted into dom')
$('.contents').click()
ok($('#modal-test').is(":visible"), 'modal visible')
$('#modal-test').click()
})
.bind("hidden.bs.modal", function() {
ok(!$('#modal-test').is(":visible"), 'modal hidden')
div.remove()
start()
})
.modal("show")
})
test("should trigger hide event once when clicking outside of modal-content", function () {
stop()
$.support.transition = false
var div = $("<div id='modal-test'><div class='contents'></div></div>")
var triggered
div
.bind("shown.bs.modal", function () {
triggered = 0
$('#modal-test').click()
})
.one("hidden.bs.modal", function() {
div.modal("show")
})
.bind("hide.bs.modal", function () {
triggered += 1
ok(triggered === 1, 'modal hide triggered once')
start()
})
.modal("show")
})
test("should close reopened modal with [data-dismiss=modal] click", function () {
stop()
$.support.transition = false
var div = $("<div id='modal-test'><div class='contents'><div id='close' data-dismiss='modal'></div></div></div>")
div
.bind("shown.bs.modal", function () {
$('#close').click()
ok(!$('#modal-test').is(":visible"), 'modal hidden')
})
.one("hidden.bs.modal", function() {
div.one('hidden.bs.modal', function () {
start()
}).modal("show")
})
.modal("show")
div.remove()
})
})

View File

@@ -0,0 +1,69 @@
/*
* grunt-contrib-qunit
* http://gruntjs.com/
*
* Copyright (c) 2013 "Cowboy" Ben Alman, contributors
* Licensed under the MIT license.
*/
/*global QUnit:true, alert:true*/
(function () {
'use strict';
// Don't re-order tests.
QUnit.config.reorder = false
// Run tests serially, not in parallel.
QUnit.config.autorun = false
// Send messages to the parent PhantomJS process via alert! Good times!!
function sendMessage() {
var args = [].slice.call(arguments)
alert(JSON.stringify(args))
}
// These methods connect QUnit to PhantomJS.
QUnit.log = function(obj) {
// What is this I dont even
if (obj.message === '[object Object], undefined:undefined') { return }
// Parse some stuff before sending it.
var actual = QUnit.jsDump.parse(obj.actual)
var expected = QUnit.jsDump.parse(obj.expected)
// Send it.
sendMessage('qunit.log', obj.result, actual, expected, obj.message, obj.source)
}
QUnit.testStart = function(obj) {
sendMessage('qunit.testStart', obj.name)
}
QUnit.testDone = function(obj) {
sendMessage('qunit.testDone', obj.name, obj.failed, obj.passed, obj.total)
}
QUnit.moduleStart = function(obj) {
sendMessage('qunit.moduleStart', obj.name)
}
QUnit.begin = function () {
sendMessage('qunit.begin')
console.log("Starting test suite")
console.log("================================================\n")
}
QUnit.moduleDone = function (opts) {
if (opts.failed === 0) {
console.log("\r\u2714 All tests passed in '" + opts.name + "' module")
} else {
console.log("\u2716 " + opts.failed + " tests failed in '" + opts.name + "' module")
}
sendMessage('qunit.moduleDone', opts.name, opts.failed, opts.passed, opts.total)
}
QUnit.done = function (opts) {
console.log("\n================================================")
console.log("Tests completed in " + opts.runtime + " milliseconds")
console.log(opts.passed + " tests of " + opts.total + " passed, " + opts.failed + " failed.")
sendMessage('qunit.done', opts.failed, opts.passed, opts.total, opts.runtime)
}
}())

View File

@@ -0,0 +1,133 @@
$(function () {
module("popover")
test("should provide no conflict", function () {
var popover = $.fn.popover.noConflict()
ok(!$.fn.popover, 'popover was set back to undefined (org value)')
$.fn.popover = popover
})
test("should be defined on jquery object", function () {
var div = $('<div></div>')
ok(div.popover, 'popover method is defined')
})
test("should return element", function () {
var div = $('<div></div>')
ok(div.popover() == div, 'document.body returned')
})
test("should render popover element", function () {
$.support.transition = false
var popover = $('<a href="#" title="mdo" data-content="http://twitter.com/mdo">@mdo</a>')
.appendTo('#qunit-fixture')
.popover('show')
ok($('.popover').length, 'popover was inserted')
popover.popover('hide')
ok(!$(".popover").length, 'popover removed')
})
test("should store popover instance in popover data object", function () {
$.support.transition = false
var popover = $('<a href="#" title="mdo" data-content="http://twitter.com/mdo">@mdo</a>')
.popover()
ok(!!popover.data('bs.popover'), 'popover instance exists')
})
test("should get title and content from options", function () {
$.support.transition = false
var popover = $('<a href="#">@fat</a>')
.appendTo('#qunit-fixture')
.popover({
title: function () {
return '@fat'
}
, content: function () {
return 'loves writing tests (╯°□°)╯︵ ┻━┻'
}
})
popover.popover('show')
ok($('.popover').length, 'popover was inserted')
equal($('.popover .popover-title').text(), '@fat', 'title correctly inserted')
equal($('.popover .popover-content').text(), 'loves writing tests (╯°□°)╯︵ ┻━┻', 'content correctly inserted')
popover.popover('hide')
ok(!$('.popover').length, 'popover was removed')
$('#qunit-fixture').empty()
})
test("should get title and content from attributes", function () {
$.support.transition = false
var popover = $('<a href="#" title="@mdo" data-content="loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻" >@mdo</a>')
.appendTo('#qunit-fixture')
.popover()
.popover('show')
ok($('.popover').length, 'popover was inserted')
equal($('.popover .popover-title').text(), '@mdo', 'title correctly inserted')
equal($('.popover .popover-content').text(), "loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻", 'content correctly inserted')
popover.popover('hide')
ok(!$('.popover').length, 'popover was removed')
$('#qunit-fixture').empty()
})
test("should get title and content from attributes #2", function () {
$.support.transition = false
var popover = $('<a href="#" title="@mdo" data-content="loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻" >@mdo</a>')
.appendTo('#qunit-fixture')
.popover({
title: 'ignored title option',
content: 'ignored content option'
})
.popover('show')
ok($('.popover').length, 'popover was inserted')
equal($('.popover .popover-title').text(), '@mdo', 'title correctly inserted')
equal($('.popover .popover-content').text(), "loves data attributes (づ。◕‿‿◕。)づ ︵ ┻━┻", 'content correctly inserted')
popover.popover('hide')
ok(!$('.popover').length, 'popover was removed')
$('#qunit-fixture').empty()
})
test("should respect custom classes", function() {
$.support.transition = false
var popover = $('<a href="#">@fat</a>')
.appendTo('#qunit-fixture')
.popover({
title: 'Test'
, content: 'Test'
, template: '<div class="popover foobar"><div class="arrow"></div><div class="inner"><h3 class="title"></h3><div class="content"><p></p></div></div></div>'
})
popover.popover('show')
ok($('.popover').length, 'popover was inserted')
ok($('.popover').hasClass('foobar'), 'custom class is present')
popover.popover('hide')
ok(!$('.popover').length, 'popover was removed')
$('#qunit-fixture').empty()
})
test("should destroy popover", function () {
var popover = $('<div/>').popover({trigger: 'hover'}).on('click.foo', function(){})
ok(popover.data('bs.popover'), 'popover has data')
ok($._data(popover[0], 'events').mouseover && $._data(popover[0], 'events').mouseout, 'popover has hover event')
ok($._data(popover[0], 'events').click[0].namespace == 'foo', 'popover has extra click.foo event')
popover.popover('show')
popover.popover('destroy')
ok(!popover.hasClass('in'), 'popover is hidden')
ok(!popover.data('popover'), 'popover does not have data')
ok($._data(popover[0],'events').click[0].namespace == 'foo', 'popover still has click.foo')
ok(!$._data(popover[0], 'events').mouseover && !$._data(popover[0], 'events').mouseout, 'popover does not have any events')
})
})

View File

@@ -0,0 +1,37 @@
$(function () {
module("scrollspy")
test("should provide no conflict", function () {
var scrollspy = $.fn.scrollspy.noConflict()
ok(!$.fn.scrollspy, 'scrollspy was set back to undefined (org value)')
$.fn.scrollspy = scrollspy
})
test("should be defined on jquery object", function () {
ok($(document.body).scrollspy, 'scrollspy method is defined')
})
test("should return element", function () {
ok($(document.body).scrollspy()[0] == document.body, 'document.body returned')
})
test("should switch active class on scroll", function () {
var sectionHTML = '<div id="masthead"></div>'
, $section = $(sectionHTML).append('#qunit-fixture')
, topbarHTML ='<div class="topbar">'
+ '<div class="topbar-inner">'
+ '<div class="container">'
+ '<h3><a href="#">Bootstrap</a></h3>'
+ '<ul class="nav">'
+ '<li><a href="#masthead">Overview</a></li>'
+ '</ul>'
+ '</div>'
+ '</div>'
+ '</div>'
, $topbar = $(topbarHTML).scrollspy()
ok($topbar.find('.active', true))
})
})

View File

@@ -0,0 +1,86 @@
$(function () {
module("tabs")
test("should provide no conflict", function () {
var tab = $.fn.tab.noConflict()
ok(!$.fn.tab, 'tab was set back to undefined (org value)')
$.fn.tab = tab
})
test("should be defined on jquery object", function () {
ok($(document.body).tab, 'tabs method is defined')
})
test("should return element", function () {
ok($(document.body).tab()[0] == document.body, 'document.body returned')
})
test("should activate element by tab id", function () {
var tabsHTML =
'<ul class="tabs">'
+ '<li><a href="#home">Home</a></li>'
+ '<li><a href="#profile">Profile</a></li>'
+ '</ul>'
$('<ul><li id="home"></li><li id="profile"></li></ul>').appendTo("#qunit-fixture")
$(tabsHTML).find('li:last a').tab('show')
equal($("#qunit-fixture").find('.active').attr('id'), "profile")
$(tabsHTML).find('li:first a').tab('show')
equal($("#qunit-fixture").find('.active').attr('id'), "home")
})
test("should activate element by tab id", function () {
var pillsHTML =
'<ul class="pills">'
+ '<li><a href="#home">Home</a></li>'
+ '<li><a href="#profile">Profile</a></li>'
+ '</ul>'
$('<ul><li id="home"></li><li id="profile"></li></ul>').appendTo("#qunit-fixture")
$(pillsHTML).find('li:last a').tab('show')
equal($("#qunit-fixture").find('.active').attr('id'), "profile")
$(pillsHTML).find('li:first a').tab('show')
equal($("#qunit-fixture").find('.active').attr('id'), "home")
})
test("should not fire closed when close is prevented", function () {
$.support.transition = false
stop();
$('<div class="tab"/>')
.on('show.bs.tab', function (e) {
e.preventDefault();
ok(true);
start();
})
.on('shown.bs.tab', function () {
ok(false);
})
.tab('show')
})
test("show and shown events should reference correct relatedTarget", function () {
var dropHTML =
'<ul class="drop">'
+ '<li class="dropdown"><a data-toggle="dropdown" href="#">1</a>'
+ '<ul class="dropdown-menu">'
+ '<li><a href="#1-1" data-toggle="tab">1-1</a></li>'
+ '<li><a href="#1-2" data-toggle="tab">1-2</a></li>'
+ '</ul>'
+ '</li>'
+ '</ul>'
$(dropHTML).find('ul>li:first a').tab('show').end()
.find('ul>li:last a').on('show', function(event){
equal(event.relatedTarget.hash, "#1-1")
}).on('shown', function(event){
equal(event.relatedTarget.hash, "#1-1")
}).tab('show')
})
})

View File

@@ -0,0 +1,437 @@
$(function () {
module("tooltip")
test("should provide no conflict", function () {
var tooltip = $.fn.tooltip.noConflict()
ok(!$.fn.tooltip, 'tooltip was set back to undefined (org value)')
$.fn.tooltip = tooltip
})
test("should be defined on jquery object", function () {
var div = $("<div></div>")
ok(div.tooltip, 'popover method is defined')
})
test("should return element", function () {
var div = $("<div></div>")
ok(div.tooltip() == div, 'document.body returned')
})
test("should expose default settings", function () {
ok(!!$.fn.tooltip.Constructor.DEFAULTS, 'defaults is defined')
})
test("should empty title attribute", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>').tooltip()
ok(tooltip.attr('title') === '', 'title attribute was emptied')
})
test("should add data attribute for referencing original title", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>').tooltip()
equal(tooltip.attr('data-original-title'), 'Another tooltip', 'original title preserved in data attribute')
})
test("should place tooltips relative to placement option", function () {
$.support.transition = false
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
.appendTo('#qunit-fixture')
.tooltip({placement: 'bottom'})
.tooltip('show')
ok($(".tooltip").is('.fade.bottom.in'), 'has correct classes applied')
tooltip.tooltip('hide')
})
test("should allow html entities", function () {
$.support.transition = false
var tooltip = $('<a href="#" rel="tooltip" title="<b>@fat</b>"></a>')
.appendTo('#qunit-fixture')
.tooltip({html: true})
.tooltip('show')
ok($('.tooltip b').length, 'b tag was inserted')
tooltip.tooltip('hide')
ok(!$(".tooltip").length, 'tooltip removed')
})
test("should respect custom classes", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
.appendTo('#qunit-fixture')
.tooltip({ template: '<div class="tooltip some-class"><div class="tooltip-arrow"/><div class="tooltip-inner"/></div>'})
.tooltip('show')
ok($('.tooltip').hasClass('some-class'), 'custom class is present')
tooltip.tooltip('hide')
ok(!$(".tooltip").length, 'tooltip removed')
})
test("should fire show event", function () {
stop()
var tooltip = $('<div title="tooltip title"></div>')
.on("show.bs.tooltip", function() {
ok(true, "show was called")
start()
})
.tooltip('show')
})
test("should fire shown event", function () {
stop()
var tooltip = $('<div title="tooltip title"></div>')
.on("shown.bs.tooltip", function() {
ok(true, "shown was called")
start()
})
.tooltip('show')
})
test("should not fire shown event when default prevented", function () {
stop()
var tooltip = $('<div title="tooltip title"></div>')
.on("show.bs.tooltip", function(e) {
e.preventDefault()
ok(true, "show was called")
start()
})
.on("shown.bs.tooltip", function() {
ok(false, "shown was called")
})
.tooltip('show')
})
test("should fire hide event", function () {
stop()
var tooltip = $('<div title="tooltip title"></div>')
.on("shown.bs.tooltip", function() {
$(this).tooltip('hide')
})
.on("hide.bs.tooltip", function() {
ok(true, "hide was called")
start()
})
.tooltip('show')
})
test("should fire hidden event", function () {
stop()
var tooltip = $('<div title="tooltip title"></div>')
.on("shown.bs.tooltip", function() {
$(this).tooltip('hide')
})
.on("hidden.bs.tooltip", function() {
ok(true, "hidden was called")
start()
})
.tooltip('show')
})
test("should not fire hidden event when default prevented", function () {
stop()
var tooltip = $('<div title="tooltip title"></div>')
.on("shown.bs.tooltip", function() {
$(this).tooltip('hide')
})
.on("hide.bs.tooltip", function(e) {
e.preventDefault()
ok(true, "hide was called")
start()
})
.on("hidden.bs.tooltip", function() {
ok(false, "hidden was called")
})
.tooltip('show')
})
test("should not show tooltip if leave event occurs before delay expires", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
.appendTo('#qunit-fixture')
.tooltip({ delay: 200 })
stop()
tooltip.trigger('mouseenter')
setTimeout(function () {
ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
tooltip.trigger('mouseout')
setTimeout(function () {
ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
start()
}, 200)
}, 100)
})
test("should not show tooltip if leave event occurs before delay expires, even if hide delay is 0", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
.appendTo('#qunit-fixture')
.tooltip({ delay: { show: 200, hide: 0} })
stop()
tooltip.trigger('mouseenter')
setTimeout(function () {
ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
tooltip.trigger('mouseout')
setTimeout(function () {
ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
start()
}, 200)
}, 100)
})
test("should wait 200 ms before hiding the tooltip", 3, function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
.appendTo('#qunit-fixture')
.tooltip({ delay: { show: 0, hide: 200} })
stop()
tooltip.trigger('mouseenter')
setTimeout(function () {
ok($(".tooltip").is('.fade.in'), 'tooltip is faded in')
tooltip.trigger('mouseout')
setTimeout(function () {
ok($(".tooltip").is('.fade.in'), '100ms:tooltip is still faded in')
setTimeout(function () {
ok(!$(".tooltip").is('.in'), 'tooltip removed')
start()
}, 150)
}, 100)
}, 1)
})
test("should not hide tooltip if leave event occurs, then tooltip is show immediately again", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
.appendTo('#qunit-fixture')
.tooltip({ delay: { show: 0, hide: 200} })
stop()
tooltip.trigger('mouseenter')
setTimeout(function () {
ok($(".tooltip").is('.fade.in'), 'tooltip is faded in')
tooltip.trigger('mouseout')
setTimeout(function () {
ok($(".tooltip").is('.fade.in'), '100ms:tooltip is still faded in')
tooltip.trigger('mouseenter')
setTimeout(function () {
ok($(".tooltip").is('.in'), 'tooltip removed')
start()
}, 150)
}, 100)
}, 1)
})
test("should not show tooltip if leave event occurs before delay expires", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
.appendTo('#qunit-fixture')
.tooltip({ delay: 100 })
stop()
tooltip.trigger('mouseenter')
setTimeout(function () {
ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
tooltip.trigger('mouseout')
setTimeout(function () {
ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
start()
}, 100)
}, 50)
})
test("should show tooltip if leave event hasn't occured before delay expires", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
.appendTo('#qunit-fixture')
.tooltip({ delay: 150 })
stop()
tooltip.trigger('mouseenter')
setTimeout(function () {
ok(!$(".tooltip").is('.fade.in'), 'tooltip is not faded in')
}, 100)
setTimeout(function () {
ok($(".tooltip").is('.fade.in'), 'tooltip has faded in')
start()
}, 200)
})
test("should destroy tooltip", function () {
var tooltip = $('<div/>').tooltip().on('click.foo', function(){})
ok(tooltip.data('bs.tooltip'), 'tooltip has data')
ok($._data(tooltip[0], 'events').mouseover && $._data(tooltip[0], 'events').mouseout, 'tooltip has hover event')
ok($._data(tooltip[0], 'events').click[0].namespace == 'foo', 'tooltip has extra click.foo event')
tooltip.tooltip('show')
tooltip.tooltip('destroy')
ok(!tooltip.hasClass('in'), 'tooltip is hidden')
ok(!$._data(tooltip[0], 'bs.tooltip'), 'tooltip does not have data')
ok($._data(tooltip[0], 'events').click[0].namespace == 'foo', 'tooltip still has click.foo')
ok(!$._data(tooltip[0], 'events').mouseover && !$._data(tooltip[0], 'events').mouseout, 'tooltip does not have any events')
})
test("should show tooltip with delegate selector on click", function () {
var div = $('<div><a href="#" rel="tooltip" title="Another tooltip"></a></div>')
var tooltip = div.appendTo('#qunit-fixture')
.tooltip({ selector: 'a[rel=tooltip]',
trigger: 'click' })
div.find('a').trigger('click')
ok($(".tooltip").is('.fade.in'), 'tooltip is faded in')
})
test("should show tooltip when toggle is called", function () {
var tooltip = $('<a href="#" rel="tooltip" title="tooltip on toggle"></a>')
.appendTo('#qunit-fixture')
.tooltip({trigger: 'manual'})
.tooltip('toggle')
ok($(".tooltip").is('.fade.in'), 'tooltip should be toggled in')
})
test("should place tooltips inside the body", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"></a>')
.appendTo('#qunit-fixture')
.tooltip({container:'body'})
.tooltip('show')
ok($("body > .tooltip").length, 'inside the body')
ok(!$("#qunit-fixture > .tooltip").length, 'not found in parent')
tooltip.tooltip('hide')
})
test("should place tooltip inside window", function(){
var container = $("<div />").appendTo("body")
.css({position: "absolute", width: 200, height: 200, bottom: 0, left: 0})
, tooltip = $("<a href='#' title='Very very very very very very very very long tooltip'>Hover me</a>")
.css({position: "absolute", top:0, left: 0})
.appendTo(container)
.tooltip({placement: "top", animate: false})
.tooltip("show")
stop()
setTimeout(function(){
ok($(".tooltip").offset().left >= 0)
start()
container.remove()
}, 100)
})
test("should place tooltip on top of element", function(){
var container = $("<div />").appendTo("body")
.css({position: "absolute", bottom: 0, left: 0, textAlign: "right", width: 300, height: 300})
, p = $("<p style='margin-top:200px' />").appendTo(container)
, tooltiped = $("<a href='#' title='very very very very very very very long tooltip'>Hover me</a>")
.css({marginTop: 200})
.appendTo(p)
.tooltip({placement: "top", animate: false})
.tooltip("show")
stop()
setTimeout(function(){
var tooltip = container.find(".tooltip")
start()
ok(tooltip.offset().top + tooltip.outerHeight() <= tooltiped.offset().top)
container.remove()
}, 100)
})
test("should add position class before positioning so that position-specific styles are taken into account", function(){
$("head").append('<style> .tooltip.right { white-space: nowrap; } .tooltip.right .tooltip-inner { max-width: none; } </style>')
var container = $("<div />").appendTo("body")
, target = $('<a href="#" rel="tooltip" title="very very very very very very very very long tooltip in one line"></a>')
.appendTo(container)
.tooltip({placement: 'right'})
.tooltip('show')
, tooltip = container.find(".tooltip")
ok( Math.round(target.offset().top + target[0].offsetHeight/2 - tooltip[0].offsetHeight/2) === Math.round(tooltip.offset().top) )
target.tooltip('hide')
})
test("tooltip title test #1", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip" style="display: inline-block; position: absolute; top: 0; left: 0;"></a>')
.appendTo('#qunit-fixture')
.tooltip({
})
.tooltip('show')
equal($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title from title attribute is set')
tooltip.tooltip('hide')
ok(!$(".tooltip").length, 'tooltip removed')
})
test("tooltip title test #2", function () {
var tooltip = $('<a href="#" rel="tooltip" title="Simple tooltip" style="display: inline-block; position: absolute; top: 0; left: 0;"></a>')
.appendTo('#qunit-fixture')
.tooltip({
title: 'This is a tooltip with some content'
})
.tooltip('show')
equal($('.tooltip').children('.tooltip-inner').text(), 'Simple tooltip', 'title is set from title attribute while prefered over title option')
tooltip.tooltip('hide')
ok(!$(".tooltip").length, 'tooltip removed')
})
test("tooltip title test #3", function () {
var tooltip = $('<a href="#" rel="tooltip" style="display: inline-block; position: absolute; top: 0; left: 0;"></a>')
.appendTo('#qunit-fixture')
.tooltip({
title: 'This is a tooltip with some content'
})
.tooltip('show')
equal($('.tooltip').children('.tooltip-inner').text(), 'This is a tooltip with some content', 'title from title option is set')
tooltip.tooltip('hide')
ok(!$(".tooltip").length, 'tooltip removed')
})
test("tooltips should be placed dynamically, with the dynamic placement option", function () {
$.support.transition = false
var ttContainer = $('<div id="dynamic-tt-test"/>').css({
'height' : 400
, 'overflow' : 'hidden'
, 'position' : 'absolute'
, 'top' : 0
, 'left' : 0
, 'width' : 600})
.appendTo('body')
var topTooltip = $('<div style="display: inline-block; position: absolute; left: 0; top: 0;" rel="tooltip" title="Top tooltip">Top Dynamic Tooltip</div>')
.appendTo('#dynamic-tt-test')
.tooltip({placement: 'auto'})
.tooltip('show')
ok($(".tooltip").is('.bottom'), 'top positioned tooltip is dynamically positioned bottom')
topTooltip.tooltip('hide')
var rightTooltip = $('<div style="display: inline-block; position: absolute; right: 0;" rel="tooltip" title="Right tooltip">Right Dynamic Tooltip</div>')
.appendTo('#dynamic-tt-test')
.tooltip({placement: 'right auto'})
.tooltip('show')
ok($(".tooltip").is('.left'), 'right positioned tooltip is dynamically positioned left')
rightTooltip.tooltip('hide')
var bottomTooltip = $('<div style="display: inline-block; position: absolute; bottom: 0;" rel="tooltip" title="Bottom tooltip">Bottom Dynamic Tooltip</div>')
.appendTo('#dynamic-tt-test')
.tooltip({placement: 'auto bottom'})
.tooltip('show')
ok($(".tooltip").is('.top'), 'bottom positioned tooltip is dynamically positioned top')
bottomTooltip.tooltip('hide')
var leftTooltip = $('<div style="display: inline-block; position: absolute; left: 0;" rel="tooltip" title="Left tooltip">Left Dynamic Tooltip</div>')
.appendTo('#dynamic-tt-test')
.tooltip({placement: 'auto left'})
.tooltip('show')
ok($(".tooltip").is('.right'), 'left positioned tooltip is dynamically positioned right')
leftTooltip.tooltip('hide')
ttContainer.remove()
})
})

View File

@@ -0,0 +1,13 @@
$(function () {
module("transition")
test("should be defined on jquery support object", function () {
ok($.support.transition !== undefined, 'transition object is defined')
})
test("should provide an end object", function () {
ok($.support.transition ? $.support.transition.end : true, 'end string is defined')
})
})

6
RIGS/static/js/tests/vendor/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

232
RIGS/static/js/tests/vendor/qunit.css vendored Normal file
View File

@@ -0,0 +1,232 @@
/**
* QUnit - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
*
* Copyright (c) 2012 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 15px 15px 0 0;
-moz-border-radius: 15px 15px 0 0;
-webkit-border-top-right-radius: 15px;
-webkit-border-top-left-radius: 15px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests ol {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
margin: 0.5em;
padding: 0.4em 0.5em 0.4em 0.5em;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #5E740B;
background-color: #fff;
border-left: 26px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 26px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 15px 15px;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
}
/** Runoff */
#qunit-fixture {
display:none;
}

1510
RIGS/static/js/tests/vendor/qunit.js vendored Normal file

File diff suppressed because it is too large Load Diff

472
RIGS/static/js/tooltip.js Normal file
View File

@@ -0,0 +1,472 @@
/* ========================================================================
* Bootstrap: tooltip.js v3.3.2
* http://getbootstrap.com/javascript/#tooltip
* Inspired by the original jQuery.tipsy by Jason Frame
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// TOOLTIP PUBLIC CLASS DEFINITION
// ===============================
var Tooltip = function (element, options) {
this.type =
this.options =
this.enabled =
this.timeout =
this.hoverState =
this.$element = null
this.init('tooltip', element, options)
}
Tooltip.VERSION = '3.3.2'
Tooltip.TRANSITION_DURATION = 150
Tooltip.DEFAULTS = {
animation: true,
placement: 'top',
selector: false,
template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
trigger: 'hover focus',
title: '',
delay: 0,
html: false,
container: false,
viewport: {
selector: 'body',
padding: 0
}
}
Tooltip.prototype.init = function (type, element, options) {
this.enabled = true
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
var triggers = this.options.trigger.split(' ')
for (var i = triggers.length; i--;) {
var trigger = triggers[i]
if (trigger == 'click') {
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
} else if (trigger != 'manual') {
var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
}
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
Tooltip.prototype.getDefaults = function () {
return Tooltip.DEFAULTS
}
Tooltip.prototype.getOptions = function (options) {
options = $.extend({}, this.getDefaults(), this.$element.data(), options)
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay,
hide: options.delay
}
}
return options
}
Tooltip.prototype.getDelegateOptions = function () {
var options = {}
var defaults = this.getDefaults()
this._options && $.each(this._options, function (key, value) {
if (defaults[key] != value) options[key] = value
})
return options
}
Tooltip.prototype.enter = function (obj) {
var self = obj instanceof this.constructor ?
obj : $(obj.currentTarget).data('bs.' + this.type)
if (self && self.$tip && self.$tip.is(':visible')) {
self.hoverState = 'in'
return
}
if (!self) {
self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
$(obj.currentTarget).data('bs.' + this.type, self)
}
clearTimeout(self.timeout)
self.hoverState = 'in'
if (!self.options.delay || !self.options.delay.show) return self.show()
self.timeout = setTimeout(function () {
if (self.hoverState == 'in') self.show()
}, self.options.delay.show)
}
Tooltip.prototype.leave = function (obj) {
var self = obj instanceof this.constructor ?
obj : $(obj.currentTarget).data('bs.' + this.type)
if (!self) {
self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
$(obj.currentTarget).data('bs.' + this.type, self)
}
clearTimeout(self.timeout)
self.hoverState = 'out'
if (!self.options.delay || !self.options.delay.hide) return self.hide()
self.timeout = setTimeout(function () {
if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide)
}
Tooltip.prototype.show = function () {
var e = $.Event('show.bs.' + this.type)
if (this.hasContent() && this.enabled) {
this.$element.trigger(e)
var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
if (e.isDefaultPrevented() || !inDom) return
var that = this
var $tip = this.tip()
var tipId = this.getUID(this.type)
this.setContent()
$tip.attr('id', tipId)
this.$element.attr('aria-describedby', tipId)
if (this.options.animation) $tip.addClass('fade')
var placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
var autoToken = /\s?auto?\s?/i
var autoPlace = autoToken.test(placement)
if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
$tip
.detach()
.css({ top: 0, left: 0, display: 'block' })
.addClass(placement)
.data('bs.' + this.type, this)
this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
var pos = this.getPosition()
var actualWidth = $tip[0].offsetWidth
var actualHeight = $tip[0].offsetHeight
if (autoPlace) {
var orgPlacement = placement
var $container = this.options.container ? $(this.options.container) : this.$element.parent()
var containerDim = this.getPosition($container)
placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' :
placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' :
placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' :
placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' :
placement
$tip
.removeClass(orgPlacement)
.addClass(placement)
}
var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
this.applyPlacement(calculatedOffset, placement)
var complete = function () {
var prevHoverState = that.hoverState
that.$element.trigger('shown.bs.' + that.type)
that.hoverState = null
if (prevHoverState == 'out') that.leave(that)
}
$.support.transition && this.$tip.hasClass('fade') ?
$tip
.one('bsTransitionEnd', complete)
.emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
complete()
}
}
Tooltip.prototype.applyPlacement = function (offset, placement) {
var $tip = this.tip()
var width = $tip[0].offsetWidth
var height = $tip[0].offsetHeight
// manually read margins because getBoundingClientRect includes difference
var marginTop = parseInt($tip.css('margin-top'), 10)
var marginLeft = parseInt($tip.css('margin-left'), 10)
// we must check for NaN for ie 8/9
if (isNaN(marginTop)) marginTop = 0
if (isNaN(marginLeft)) marginLeft = 0
offset.top = offset.top + marginTop
offset.left = offset.left + marginLeft
// $.fn.offset doesn't round pixel values
// so we use setOffset directly with our own function B-0
$.offset.setOffset($tip[0], $.extend({
using: function (props) {
$tip.css({
top: Math.round(props.top),
left: Math.round(props.left)
})
}
}, offset), 0)
$tip.addClass('in')
// check to see if placing tip in new offset caused the tip to resize itself
var actualWidth = $tip[0].offsetWidth
var actualHeight = $tip[0].offsetHeight
if (placement == 'top' && actualHeight != height) {
offset.top = offset.top + height - actualHeight
}
var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
if (delta.left) offset.left += delta.left
else offset.top += delta.top
var isVertical = /top|bottom/.test(placement)
var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
$tip.offset(offset)
this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
}
Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) {
this.arrow()
.css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
.css(isHorizontal ? 'top' : 'left', '')
}
Tooltip.prototype.setContent = function () {
var $tip = this.tip()
var title = this.getTitle()
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
$tip.removeClass('fade in top bottom left right')
}
Tooltip.prototype.hide = function (callback) {
var that = this
var $tip = this.tip()
var e = $.Event('hide.bs.' + this.type)
function complete() {
if (that.hoverState != 'in') $tip.detach()
that.$element
.removeAttr('aria-describedby')
.trigger('hidden.bs.' + that.type)
callback && callback()
}
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
$tip.removeClass('in')
$.support.transition && this.$tip.hasClass('fade') ?
$tip
.one('bsTransitionEnd', complete)
.emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
complete()
this.hoverState = null
return this
}
Tooltip.prototype.fixTitle = function () {
var $e = this.$element
if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
}
}
Tooltip.prototype.hasContent = function () {
return this.getTitle()
}
Tooltip.prototype.getPosition = function ($element) {
$element = $element || this.$element
var el = $element[0]
var isBody = el.tagName == 'BODY'
var elRect = el.getBoundingClientRect()
if (elRect.width == null) {
// width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
}
var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
return $.extend({}, elRect, scroll, outerDims, elOffset)
}
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
/* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
}
Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
var delta = { top: 0, left: 0 }
if (!this.$viewport) return delta
var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
var viewportDimensions = this.getPosition(this.$viewport)
if (/right|left/.test(placement)) {
var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
if (topEdgeOffset < viewportDimensions.top) { // top overflow
delta.top = viewportDimensions.top - topEdgeOffset
} else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
}
} else {
var leftEdgeOffset = pos.left - viewportPadding
var rightEdgeOffset = pos.left + viewportPadding + actualWidth
if (leftEdgeOffset < viewportDimensions.left) { // left overflow
delta.left = viewportDimensions.left - leftEdgeOffset
} else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
}
}
return delta
}
Tooltip.prototype.getTitle = function () {
var title
var $e = this.$element
var o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
return title
}
Tooltip.prototype.getUID = function (prefix) {
do prefix += ~~(Math.random() * 1000000)
while (document.getElementById(prefix))
return prefix
}
Tooltip.prototype.tip = function () {
return (this.$tip = this.$tip || $(this.options.template))
}
Tooltip.prototype.arrow = function () {
return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
}
Tooltip.prototype.enable = function () {
this.enabled = true
}
Tooltip.prototype.disable = function () {
this.enabled = false
}
Tooltip.prototype.toggleEnabled = function () {
this.enabled = !this.enabled
}
Tooltip.prototype.toggle = function (e) {
var self = this
if (e) {
self = $(e.currentTarget).data('bs.' + this.type)
if (!self) {
self = new this.constructor(e.currentTarget, this.getDelegateOptions())
$(e.currentTarget).data('bs.' + this.type, self)
}
}
self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
}
Tooltip.prototype.destroy = function () {
var that = this
clearTimeout(this.timeout)
this.hide(function () {
that.$element.off('.' + that.type).removeData('bs.' + that.type)
})
}
// TOOLTIP PLUGIN DEFINITION
// =========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.tooltip')
var options = typeof option == 'object' && option
if (!data && option == 'destroy') return
if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.tooltip
$.fn.tooltip = Plugin
$.fn.tooltip.Constructor = Tooltip
// TOOLTIP NO CONFLICT
// ===================
$.fn.tooltip.noConflict = function () {
$.fn.tooltip = old
return this
}
}(jQuery);

View File

@@ -0,0 +1,59 @@
/* ========================================================================
* Bootstrap: transition.js v3.3.2
* http://getbootstrap.com/javascript/#transitions
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
// ============================================================
function transitionEnd() {
var el = document.createElement('bootstrap')
var transEndEventNames = {
WebkitTransition : 'webkitTransitionEnd',
MozTransition : 'transitionend',
OTransition : 'oTransitionEnd otransitionend',
transition : 'transitionend'
}
for (var name in transEndEventNames) {
if (el.style[name] !== undefined) {
return { end: transEndEventNames[name] }
}
}
return false // explicit for ie8 ( ._.)
}
// http://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function (duration) {
var called = false
var $el = this
$(this).one('bsTransitionEnd', function () { called = true })
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
setTimeout(callback, duration)
return this
}
$(function () {
$.support.transition = transitionEnd()
if (!$.support.transition) return
$.event.special.bsTransitionEnd = {
bindType: $.support.transition.end,
delegateType: $.support.transition.end,
handle: function (e) {
if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
}
}
})
}(jQuery);

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/_autocomplete";
@import "jq-ui-bootstrap/_menu"; @import "jq-ui-bootstrap/_menu";
@import "jq-ui-bootstrap/_tooltip"; @import "jq-ui-bootstrap/_tooltip";
@import "compass/css3/animation"; @import "compass/css3/animation";
@import "compass/css3/transform"; @import "compass/css3/transform";
@@ -90,102 +89,62 @@ ins {
margin: 30px auto 0; margin: 30px auto 0;
.circle { .circle {
background-color: rgba(0,0,0,0); background-color: rgba(0, 0, 0, 0);
border: 5px solid rgba(0,183,229,0.9); border: 5px solid rgba(0, 183, 229, 0.9);
opacity: .9; opacity: .9;
border-right: 5px solid rgba(0,0,0,0); border-right: 5px solid rgba(0, 0, 0, 0);
border-left: 5px solid rgba(0,0,0,0); border-left: 5px solid rgba(0, 0, 0, 0);
border-radius: 50px; border-radius: 50px;
box-shadow: 0 0 35px #2187e7; box-shadow: 0 0 35px #2187e7;
width: 50px; width: 50px;
height: 50px; height: 50px;
margin: 0 auto; margin: 0 auto;
@include animation(spinPulse 1s infinite ease-in-out); @include animation(spinPulse 1s infinite ease-in-out);
} }
.circle1 { .circle1 {
background-color: rgba(0,0,0,0); background-color: rgba(0, 0, 0, 0);
border: 5px solid rgba(0,183,229,0.9); border: 5px solid rgba(0, 183, 229, 0.9);
opacity: .9; opacity: .9;
border-left: 5px solid rgba(0,0,0,0); border-left: 5px solid rgba(0, 0, 0, 0);
border-right: 5px solid rgba(0,0,0,0); border-right: 5px solid rgba(0, 0, 0, 0);
border-radius: 50px; border-radius: 50px;
box-shadow: 0 0 15px #2187e7; box-shadow: 0 0 15px #2187e7;
width: 30px; width: 30px;
height: 30px; height: 30px;
margin: 0 auto; margin: 0 auto;
position: relative; position: relative;
top: -40px; top: -40px;
@include animation(spinoffPulse 1s infinite linear); @include animation(spinoffPulse 1s infinite linear);
} }
@include keyframes(spinPulse) { @include keyframes(spinPulse) {
0% { 0% {
@include rotate(160deg); @include rotate(160deg);
opacity: 0; opacity: 0;
box-shadow: 0 0 1px #2187e7; box-shadow: 0 0 1px #2187e7;
} }
50% { 50% {
@include rotate(145deg); @include rotate(145deg);
opacity: 1; opacity: 1;
} }
100% { 100% {
@include rotate(-320deg); @include rotate(-320deg);
opacity: 0; opacity: 0;
}; }
;
} }
@include keyframes(spinoffPulse) { @include keyframes(spinoffPulse) {
0% { 0% {
@include rotate(0deg); @include rotate(0deg);
} }
100% { 100% {
@include rotate(360deg); @include rotate(360deg);
}; }
} ;
}
html.embedded{
min-height:100%;
display: table;
width: 100%;
body{
padding:0;
display: table-cell;
vertical-align: middle;
width:100%;
background:none;
}
.embed_container{
border:5px solid #e9e9e9;
padding:12px 0px;
min-height:100%;
width:100%;
}
.source{
background: url('/static/imgs/pyrigs-avatar.png') no-repeat;
background-size: 16px 16px;
padding-left: 20px;
color: #000;
}
h3{
margin-top:10px;
margin-bottom:5px;
}
p{
margin-bottom:2px;
font-size: 11px;
}
.event-mic-photo{
max-width: 3em;
} }
} }

View File

@@ -1,18 +1,66 @@
{% load static %} {% load static %}
<div class="panel panel-default"> {% block js %}
<div class="panel-heading"> <script src="{% static "js/tooltip.js" %}"></script>
<h4 class="panel-title">Recent Changes</h4> <script src="{% static "js/popover.js" %}"></script>
</div> <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');
}
});
<div class="list-group"> // 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);
});
});
});
});
</script>
{% endblock %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">Recent Changes</h4>
</div>
<div class="list-group">
<div id="activity_loading" class="list-group-item loading-animation"> <div id="activity_loading" class="list-group-item loading-animation">
<div class="circle"></div> <div class="circle"></div>
<div class="circle1"></div> <div class="circle1"></div>
</div> </div>
<div id="activity"> <div id="activity">
</div> </div>
</div>
</div> </div>
</div>

View File

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

View File

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

View File

@@ -2,39 +2,40 @@
{% load i18n l10n %} {% load i18n l10n %}
{% block content %} {% block content %}
<form action="" method="post">{% csrf_token %} <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> <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> <table>
{% for form in forms %} {% for form in forms %}
{% if forloop.first %} {% if forloop.first %}
<tr> <tr>
<th></th> <th></th>
<th> ID </th> <th> ID</th>
{% for field in form %} {% for field in form %}
<th>{{ field.label }}</th> <th>{{ field.label }}</th>
{% endfor %} {% endfor %}
</tr> </tr>
{% endif %} {% endif %}
<tr> <tr>
<td><input type="radio" name="master" value="{{form.instance.pk|unlocalize}}"></td> <td><input type="radio" name="master" value="{{ form.instance.pk|unlocalize }}"></td>
<td>{{form.instance.pk}}</td> <td>{{ form.instance.pk }}</td>
{% for field in form %} {% for field in form %}
<td> {{ field.value }} </td> <td> {{ field.value }} </td>
{% endfor %}
</tr>
{% endfor %} {% endfor %}
</tr> </table>
{% endfor %}
</table>
<div> <div>
{% for obj in queryset %} {% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" /> <input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}"/>
{% endfor %} {% endfor %}
<input type="hidden" name="action" value="merge" /> <input type="hidden" name="action" value="merge"/>
<input type="hidden" name="post" value="yes" /> <input type="hidden" name="post" value="yes"/>
<input type="submit" value="Merge them" /> <input type="submit" value="Merge them"/>
</div> </div>
</form> </form>
{% endblock %} {% endblock %}

View File

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

View File

@@ -11,14 +11,17 @@
<form class="form-inline"> <form class="form-inline">
<div class="form-group"> <div class="form-group">
<label for="start">Start</label> <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>
<div class="form-group"> <div class="form-group">
<label for="end">End</label> <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>
<div class="form-group"> <div class="form-group">
<input type="submit" class="btn btn-primary" /> <input type="submit" class="btn btn-primary"/>
</div> </div>
</form> </form>
</div> </div>

View File

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

View File

@@ -1,106 +0,0 @@
{% extends 'base_embed.html' %}
{% load static from staticfiles %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<a href="/">
<span class="source"> R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></span>
</a>
</div>
<div class="col-sm-12">
<span class="pull-right">
{% if object.mic %}
<div class="text-center">
<img src="{{ object.mic.profile_picture }}" class="event-mic-photo img-rounded"/>
</div>
{% elif object.is_rig %}
<span class="glyphicon glyphicon-exclamation-sign"></span>
{% endif %}
</span>
<h3>
<a {% if perms.RIGS.view_event %}href="{% url 'event_detail' object.pk %}"{% endif %}>
{% if object.is_rig %}N{{ object.pk|stringformat:"05d" }}{% else %}{{ object.pk }}{% endif %}
| {{ object.name }} </a>
{% if object.venue %}
<small>at {{ object.venue }}</small>
{% endif %}
<br/><small>
{{ object.start_date|date:"D d/m/Y" }}
{% if object.has_start_time %}
{{ object.start_time|date:"H:i" }}
{% endif %}
{% if object.end_date or object.has_end_time %}
&ndash;
{% endif %}
{% if object.end_date and object.end_date != object.start_date %}
{{ object.end_date|date:"D d/m/Y" }}
{% endif %}
{% if object.has_end_time %}
{{ object.end_time|date:"H:i" }}
{% endif %}
</small>
</h3>
<div class="row">
<div class="col-xs-6">
<p>
<strong>Status:</strong>
{{ object.get_status_display }}
</p>
<p>
{% if object.is_rig %}
<strong>Client:</strong> {{ object.person.name }}
{% if object.organisation %}
for {{ object.organisation.name }}
{% endif %}
{% if object.dry_hire %}(Dry Hire){% endif %}
{% else %}
<strong>Non-Rig</strong>
{% endif %}
</p>
<p>
<strong>MIC:</strong>
{% if object.mic %}
{{object.mic.name}}
{% else %}
None
{% endif %}
</p>
</div>
<div class="col-xs-6">
{% if object.meet_at %}
<p>
<strong>Crew meet:</strong>
{{ object.meet_at|date:"H:i" }} {{ object.meet_at|date:"(Y-m-d)" }}
</p>
{% endif %}
{% if object.access_at %}
<p>
<strong>Access at:</strong>
{{ object.access_at|date:"H:i" }} {{ object.access_at|date:"(Y-m-d)" }}
</p>
{% endif %}
<p>
<strong>Last updated:</strong>
{{ object.last_edited_at }} by "{{ object.last_edited_by.initials }}"
</p>
</div>
</div>
{% if object.description %}
<p>
<strong>Description: </strong>
{{ object.description|linebreaksbr }}
</p>
{% endif %}
</table>
</div>
</div>
{% endblock %}

View File

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

View File

@@ -5,17 +5,17 @@
{% block title %}Events for Invoice{% endblock %} {% block title %}Events for Invoice{% endblock %}
{% block js %} {% block js %}
<script src="{% static "js/tooltip.js" %}"></script> <script src="{% static "js/tooltip.js" %}"></script>
<script> <script>
$(function () { $(function () {
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="col-sm-12"> <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> <p>These events have happened, but paperwork has not yet been sent to treasury</p>
{% if is_paginated %} {% if is_paginated %}
<div class="col-md-6 col-md-offset-6 col-sm-12 text-right"> <div class="col-md-6 col-md-offset-6 col-sm-12 text-right">
@@ -77,7 +77,8 @@
{% endif %} {% endif %}
</td> </td>
<td class="text-right"> <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> <span class="glyphicon glyphicon-gbp"></span>
</a> </a>
</td> </td>

View File

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

View File

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

View File

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

View File

@@ -1,126 +1,96 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %}
{% block title %}RIGS{% endblock %} {% block title %}RIGS{% endblock %}
{% block content %} {% block content %}
<div class="col-sm-12"> <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>
<div class="col-sm-12"> <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> </p>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-{% if perms.RIGS.view_event %}6{% else %}12{% endif %}"> <div class="col-sm-{% if perms.RIGS.view_event %}6{% else %}12{% endif %}">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h4 class="list-group-item-heading">Quick Links</h4> <h4 class="list-group-item-heading">Quick Links</h4>
</div> </div>
<div class="list-group"> <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 'rigboard' %}"><span
<a class="list-group-item" href="{% url 'web_calendar' %}"><span class="glyphicon glyphicon-calendar"></span> Calendar</a> class="glyphicon glyphicon-list"></span> Rigboard</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 %} <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> <div class="list-group-item default"></div>
<a class="list-group-item" href="https://forum.nottinghamtec.co.uk" target="_blank"><span class="glyphicon glyphicon-link"></span> TEC Forum</a> <a class="list-group-item" href="//members.nottinghamtec.co.uk/forum" target="_blank"><span
<a class="list-group-item" href="//members.nottinghamtec.co.uk/wiki" target="_blank"><span class="glyphicon glyphicon-link"></span> TEC Wiki</a> class="glyphicon glyphicon-link"></span> TEC Forum</a>
<a class="list-group-item" href="http://members.nottinghamtec.co.uk/wiki/images/2/22/Event_Risk_Assesment.pdf" target="_blank"><span class="glyphicon glyphicon-link"></span> Pre-Event Risk Assessment</a> <a class="list-group-item" href="//members.nottinghamtec.co.uk/wiki" target="_blank"><span
<a class="list-group-item" href="//members.nottinghamtec.co.uk/price" target="_blank"><span class="glyphicon glyphicon-link"></span> Price List</a> class="glyphicon glyphicon-link"></span> TEC Wiki</a>
<a class="list-group-item" href="https://form.jotformeu.com/62203600438344" target="_blank"><span class="glyphicon glyphicon-link"></span> Subhire Insurance Form</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> </div>
</div>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h4 class="panel-title">Search Rigboard</h4> <h4 class="panel-title">Search Rigboard</h4>
</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> </div>
<div class="list-group-item"> <div class="list-group">
<form class="form" role="form" action="{% url 'organisation_list' %}" method="GET"> <div class="list-group-item">
<div class="input-group"> <form class="form" role="form" action="{% url 'person_list' %}" method="GET">
<input type="search" name="q" class="form-control" placeholder="Search Organisations" /> <div class="input-group">
<span class="input-group-btn"> <input type="search" name="q" class="form-control" placeholder="Search People"/>
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-search"></span></button> <span class="input-group-btn">
<button type="submit" class="btn btn-default"><span
class="glyphicon glyphicon-search"></span></button>
</span> </span>
</div> </div>
</form> </form>
</div> </div>
<div class="list-group-item"> <div class="list-group-item">
<form class="form" role="form" action="{% url 'venue_list' %}" method="GET"> <form class="form" role="form" action="{% url 'organisation_list' %}" method="GET">
<div class="input-group"> <div class="input-group">
<input type="search" name="q" class="form-control" placeholder="Search Venues" /> <input type="search" name="q" class="form-control" placeholder="Search Organisations"/>
<span class="input-group-btn"> <span class="input-group-btn">
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-search"></span></button> <button type="submit" class="btn btn-default"><span
class="glyphicon glyphicon-search"></span></button>
</span> </span>
</div> </div>
</form> </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> </div>
</div> </div>
{% if perms.RIGS.view_event %}
<div class="col-sm-6">
{% include 'RIGS/activity_feed.html' %}
</div>
{% endif %}
</div> </div>
{% if perms.RIGS.view_event %}
<div class="col-sm-6" >
{% include 'RIGS/activity_feed.html' %}
</div>
{% endif %}
</div>
{% endblock %}
{% block js %}
<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);
});
});
});
});
</script>
{% endblock %} {% endblock %}

View File

@@ -6,20 +6,20 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="row"> <div class="row">
<div class="col-sm-8"> <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>
<div class="col-sm-4 text-right"> <div class="col-sm-4 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-default" title="Void Invoice"> <a href="{% url 'invoice_delete' object.pk %}" class="btn btn-default" title="Void Invoice">
<span class="glyphicon glyphicon-remove"></span> <span <span class="glyphicon glyphicon-remove"></span> <span
class="hidden-xs">Delete</span> class="hidden-xs">Delete</span>
</a> </a>
<a href="{% url 'invoice_void' object.pk %}" class="btn btn-default" title="Void Invoice"> <a href="{% url 'invoice_void' object.pk %}" class="btn btn-default" title="Void Invoice">
<span class="glyphicon glyphicon-ban-circle"></span> <span <span class="glyphicon glyphicon-ban-circle"></span> <span
class="hidden-xs">Void</span> class="hidden-xs">Void</span>
</a> </a>
<a href="{% url 'invoice_print' object.pk %}" target="_blank" class="btn btn-default"><span <a href="{% url 'invoice_print' object.pk %}" target="_blank" class="btn btn-default"><span
class="glyphicon glyphicon-print"></span> <span class="glyphicon glyphicon-print"></span> <span
class="hidden-xs">Print</span></a> class="hidden-xs">Print</span></a>
</div> </div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
{% load to_class_name from filters %} {% load to_class_name from filters %}
{# pass in variable "object" to this template #} {# 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="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-default"><span <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>
</div> </div>
{% endif %} {% endif %}
@@ -29,7 +29,8 @@
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd> <dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
<dt>Email</dt> <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> <dt>Address</dt>
<dd>{{ object.address|linebreaksbr }}</dd> <dd>{{ object.address|linebreaksbr }}</dd>
@@ -50,7 +51,10 @@
<div class="panel-body"> <div class="panel-body">
<div class="list-group"> <div class="list-group">
{% for person,count in object.persons %} {% 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 %} {% endfor %}
</div> </div>
</div> </div>
@@ -77,7 +81,7 @@
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'organisation_update' object.pk %}" class="btn btn-default"><span <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>
<div> <div>
<a href="{% url 'organisation_history' object.pk %}" title="View Revision History"> <a href="{% url 'organisation_history' object.pk %}" title="View Revision History">
@@ -96,9 +100,9 @@
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'organisation_detail' object.pk %}" class="btn btn-default"><span <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 <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>
<div> <div>
<a href="{% url 'organisation_history' object.pk %}" title="View Revision History"> <a href="{% url 'organisation_history' object.pk %}" title="View Revision History">

View File

@@ -13,7 +13,7 @@
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'person_update' object.pk %}" class="btn btn-default"><span <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>
</div> </div>
{% endif %} {% endif %}
@@ -29,7 +29,8 @@
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd> <dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
<dt>Email</dt> <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> <dt>Address</dt>
<dd>{{ object.address|linebreaksbr }}</dd> <dd>{{ object.address|linebreaksbr }}</dd>
@@ -47,7 +48,10 @@
<div class="panel-body"> <div class="panel-body">
<div class="list-group"> <div class="list-group">
{% for organisation,count in object.organisations %} {% 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 %} {% endfor %}
</div> </div>
</div> </div>
@@ -74,7 +78,7 @@
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'person_update' object.pk %}" class="btn btn-default"><span <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>
<div> <div>
<a href="{% url 'person_history' object.pk %}" title="View Revision History"> <a href="{% url 'person_history' object.pk %}" title="View Revision History">
@@ -93,9 +97,9 @@
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'person_detail' object.pk %}" class="btn btn-default"><span <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 <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>
<div> <div>
<a href="{% url 'person_history' object.pk %}" title="View Revision History"> <a href="{% url 'person_history' object.pk %}" title="View Revision History">

View File

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

View File

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

View File

@@ -10,10 +10,10 @@
<h3>Rigboard</h3> <h3>Rigboard</h3>
</div> </div>
{% if perms.RIGS.add_event %} {% if perms.RIGS.add_event %}
<div class="col-sm-2"> <div class="col-sm-2">
<a href="{% url 'event_create' %}" class="btn btn-default pull-right">New <span <a href="{% url 'event_create' %}" class="btn btn-default pull-right">New <span
class="glyphicon glyphicon-plus"></span></a> class="glyphicon glyphicon-plus"></span></a>
</div> </div>
{% endif %} {% endif %}
{% comment %} {% comment %}

View File

@@ -13,7 +13,7 @@
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'venue_update' object.pk %}" class="btn btn-default"><span <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>
</div> </div>
{% endif %} {% endif %}
@@ -29,7 +29,8 @@
<dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd> <dd><a href="tel:{{ object.phone }}">{{ object.phone }}</a></dd>
<dt>Email</dt> <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> <dt>Address</dt>
<dd>{{ object.address|linebreaksbr }}</dd> <dd>{{ object.address|linebreaksbr }}</dd>
@@ -63,7 +64,7 @@
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'venue_update' object.pk %}" class="btn btn-default"><span <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>
<div> <div>
<a href="{% url 'venue_history' object.pk %}" title="View Revision History"> <a href="{% url 'venue_history' object.pk %}" title="View Revision History">
@@ -83,9 +84,9 @@
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
<div class="btn-group btn-page"> <div class="btn-group btn-page">
<a href="{% url 'venue_detail' object.pk %}" class="btn btn-default"><span <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 <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>
<div> <div>
<a href="{% url 'venue_history' object.pk %}" title="View Revision History"> <a href="{% url 'venue_history' object.pk %}" title="View Revision History">

View File

@@ -1,13 +1,18 @@
{% for change in version.field_changes %} {% 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" %} {% include "RIGS/version_changes_change.html" %}
{% endspaceless %}'>{{ change.field.verbose_name }}</button> {% endspaceless %}'>{{ change.field.verbose_name }}</button>
{% endfor %} {% endfor %}
{% for itemChange in version.item_changes %} {% 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"> <ul class="list-group">
{% for change in itemChange.changes %} {% for change in itemChange.changes %}
<li class="list-group-item"> <li class="list-group-item">
@@ -17,5 +22,7 @@
{% endfor %} {% endfor %}
</ul> </ul>
{% endspaceless %}'>item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}{{ itemChange.old.name }}{% endif %}'</button> {% endspaceless %}'>item '{% if itemChange.new %}{{ itemChange.new.name }}{% else %}
{{ itemChange.old.name }}{% endif %}'
</button>
{% endfor %} {% endfor %}

View File

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

View File

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

View File

@@ -1,18 +1,21 @@
from django import template
from django import forms from django import forms
from django import template
from django.forms.forms import NON_FIELD_ERRORS from django.forms.forms import NON_FIELD_ERRORS
from django.forms.utils import ErrorDict from django.forms.utils import ErrorDict
register = template.Library() register = template.Library()
@register.filter @register.filter
def multiply(value, arg): def multiply(value, arg):
return value*arg return value * arg
@register.filter @register.filter
def to_class_name(value): def to_class_name(value):
return value.__class__.__name__ return value.__class__.__name__
@register.filter @register.filter
def nice_errors(form, non_field_msg='General form errors'): def nice_errors(form, non_field_msg='General form errors'):
nice_errors = ErrorDict() nice_errors = ErrorDict()
@@ -25,6 +28,7 @@ def nice_errors(form, non_field_msg='General form errors'):
nice_errors[key] = errors nice_errors[key] = errors
return nice_errors return nice_errors
def paginator(context, adjacent_pages=3): def paginator(context, adjacent_pages=3):
""" """
To be used in conjunction with the object_list generic view. 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 endPage = page.number + adjacent_pages + 1
if endPage >= paginator.num_pages - 1: endPage = paginator.num_pages + 1 if endPage >= paginator.num_pages - 1: endPage = paginator.num_pages + 1
page_numbers = [n for n in range(startPage, endPage) \ 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 = { dict = {
'request': context['request'], 'request': context['request'],
@@ -64,20 +68,22 @@ def paginator(context, adjacent_pages=3):
dict['previous'] = page.previous_page_number() dict['previous'] = page.previous_page_number()
return dict return dict
register.inclusion_tag('pagination.html', takes_context=True)(paginator) register.inclusion_tag('pagination.html', takes_context=True)(paginator)
@register.simple_tag @register.simple_tag
def url_replace(request, field, value): def url_replace(request, field, value):
dict_ = request.GET.copy() dict_ = request.GET.copy()
dict_[field] = value dict_[field] = value
return dict_.urlencode() return dict_.urlencode()
@register.simple_tag @register.simple_tag
def orderby(request, field, attr): def orderby(request, field, attr):
dict_ = request.GET.copy() dict_ = request.GET.copy()
if dict_.__contains__(field) and dict_[field] == attr: if dict_.__contains__(field) and dict_[field] == attr:

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import re import re
import time
from datetime import date, timedelta from datetime import date, timedelta
import reversion import reversion
@@ -18,10 +17,9 @@ from RIGS import models
class UserRegistrationTest(LiveServerTestCase): class UserRegistrationTest(LiveServerTestCase):
def setUp(self): def setUp(self):
self.browser = webdriver.Firefox() 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' os.environ['RECAPTCHA_TESTING'] = 'True'
def tearDown(self): def tearDown(self):
@@ -150,17 +148,17 @@ class UserRegistrationTest(LiveServerTestCase):
class EventTest(LiveServerTestCase): class EventTest(LiveServerTestCase):
def setUp(self): def setUp(self):
self.profile = models.Profile( self.profile = models.Profile(
username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True) username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True)
self.profile.set_password("EventTestPassword") self.profile.set_password("EventTestPassword")
self.profile.save() 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 = 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() self.browser.maximize_window()
os.environ['RECAPTCHA_TESTING'] = 'True' os.environ['RECAPTCHA_TESTING'] = 'True'
@@ -204,7 +202,8 @@ class EventTest(LiveServerTestCase):
# Gets redirected to login and back # Gets redirected to login and back
self.authenticate('/event/create/') 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()) wait.until(animation_is_finished())
@@ -230,7 +229,9 @@ class EventTest(LiveServerTestCase):
modal = self.browser.find_element_by_id('modal') modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished()) wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed()) 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 # Fill person form out and submit
modal.find_element_by_xpath( modal.find_element_by_xpath(
@@ -255,7 +256,9 @@ class EventTest(LiveServerTestCase):
wait.until(animation_is_finished()) wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed()) 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( modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2") '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Person 2")
@@ -291,14 +294,15 @@ class EventTest(LiveServerTestCase):
'//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click() '//a[@data-target="#id_person" and contains(@href, "%s/edit/")]' % person1.pk).click()
wait.until(animation_is_finished()) wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed()) 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( name = modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]') '//div[@id="modal"]//input[@id="id_name"]')
self.assertEqual(person1.name, name.get_attribute('value')) self.assertEqual(person1.name, name.get_attribute('value'))
name.clear() name.clear()
name.send_keys('Rig ' + person1.name) name.send_keys('Rig ' + person1.name)
name.send_keys(Keys.ENTER) name.send_keys(Keys.ENTER)
time.sleep(0.1)
wait.until(animation_is_finished()) wait.until(animation_is_finished())
@@ -315,7 +319,8 @@ class EventTest(LiveServerTestCase):
modal = self.browser.find_element_by_id('modal') modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished()) wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed()) 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( modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation") '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Organisation")
modal.find_element_by_xpath( modal.find_element_by_xpath(
@@ -342,7 +347,9 @@ class EventTest(LiveServerTestCase):
modal = self.browser.find_element_by_id('modal') modal = self.browser.find_element_by_id('modal')
wait.until(animation_is_finished()) wait.until(animation_is_finished())
self.assertTrue(modal.is_displayed()) 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( modal.find_element_by_xpath(
'//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue") '//div[@id="modal"]//input[@id="id_name"]').send_keys("Test Venue")
modal.find_element_by_xpath( modal.find_element_by_xpath(
@@ -368,53 +375,72 @@ class EventTest(LiveServerTestCase):
form.find_element_by_id('id_end_time').send_keys('07:00') form.find_element_by_id('id_end_time').send_keys('07:00')
# Add item # 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()) wait.until(animation_is_finished())
modal = self.browser.find_element_by_id("itemModal") 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_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 = modal.find_element_by_id("item_quantity")
e.click() e.click()
e.send_keys(Keys.UP) e.send_keys(Keys.UP)
e.send_keys(Keys.UP) e.send_keys(Keys.UP)
e = modal.find_element_by_id("item_cost") e = modal.find_element_by_id("item_cost")
e.send_keys("23.95") e.send_keys("23.95")
e.send_keys(Keys.ENTER) # enter submit e.send_keys(Keys.ENTER) # enter submit
wait.until(animation_is_finished())
# Confirm item has been saved to json field # Confirm item has been saved to json field
objectitems = self.browser.execute_script("return objectitems;") objectitems = self.browser.execute_script("return objectitems;")
self.assertEqual(1, len(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("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 # See new item appear in table
row = self.browser.find_element_by_id('item--1') # ID number is known, see above row = self.browser.find_element_by_id(
self.assertIsNotNone(row.text) 'item--1') # ID number is known, see above
self.assertIn("Test Item 1", row.find_element_by_xpath('//span[@class="name"]').text) self.assertIn(
self.assertIn("This is an item description", row.find_element_by_xpath('//div[@class="item-description"]').text) "Test Item 1",
self.assertEqual(u'£ 23.95', row.find_element_by_xpath('//tr[@id="item--1"]/td[2]').text) row.find_element_by_xpath('//span[@class="name"]').text)
self.assertEqual("2", row.find_element_by_xpath('//td[@class="quantity"]').text) self.assertIn(
self.assertEqual(u'£ 47.90', row.find_element_by_xpath('//tr[@id="item--1"]/td[4]').text) "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 # Check totals
self.assertEqual("47.90", self.browser.find_element_by_id('sumtotal').text) self.assertEqual(
self.assertIn("(TBC)", self.browser.find_element_by_id('vat-rate').text) "47.90", self.browser.find_element_by_id('sumtotal').text)
self.assertEqual("9.58", self.browser.find_element_by_id('vat').text) self.assertIn(
self.assertEqual("57.48", self.browser.find_element_by_id('total').text) "(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 # Attempt to save - missing title
save.click() save.click()
# See error # 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()) self.assertTrue(error.is_displayed())
# Should only have one error message # Should only have one error message
self.assertEqual("Name", error.find_element_by_xpath('//dt[1]').text) self.assertEqual(
self.assertEqual("This field is required.", error.find_element_by_xpath('//dd[1]/ul/li').text) "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 # 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: try:
self.assertFalse(error.is_displayed()) self.assertFalse(error.is_displayed())
except StaleElementReferenceException: except StaleElementReferenceException:
@@ -435,93 +461,102 @@ class EventTest(LiveServerTestCase):
# See redirected to success page # See redirected to success page
successTitle = self.browser.find_element_by_xpath('//h1').text successTitle = self.browser.find_element_by_xpath('//h1').text
event = models.Event.objects.get(name='Test Event Name') 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: except WebDriverException:
# This is a dirty workaround for wercker being a bit funny and not running it correctly. # 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 # Waiting for wercker to get back to me about this
pass pass
def testEventDuplicate(self): 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", purchase_order="TESTPO") 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( item1 = models.EventItem(
event=testEvent, event=testEvent,
name="Test Item 1", name="Test Item 1",
cost="10.00", cost="10.00",
quantity="1", quantity="1",
order=1 order=1
).save() ).save()
item2 = models.EventItem( item2 = models.EventItem(
event=testEvent, event=testEvent,
name="Test Item 2", name="Test Item 2",
description="Foo", description="Foo",
cost="9.72", cost="9.72",
quantity="3", quantity="3",
order=2, order=2,
).save() ).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/') 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( save = self.browser.find_element_by_xpath(
'(//button[@type="submit"])[3]') '(//button[@type="submit"])[3]')
form = self.browser.find_element_by_tag_name('form') form = self.browser.find_element_by_tag_name('form')
# Check the items are visible # 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 1", table.text)
self.assertIn("Test Item 2", table.text) self.assertIn("Test Item 2", table.text)
# Check the info message is visible
self.assertIn("Event data duplicated but not yet saved",self.browser.find_element_by_id('content').text)
# Add item # 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()) wait.until(animation_is_finished())
modal = self.browser.find_element_by_id("itemModal") 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_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 = modal.find_element_by_id("item_quantity")
e.click() e.click()
e.send_keys(Keys.UP) e.send_keys(Keys.UP)
e.send_keys(Keys.UP) e.send_keys(Keys.UP)
e = modal.find_element_by_id("item_cost") e = modal.find_element_by_id("item_cost")
e.send_keys("23.95") e.send_keys("23.95")
e.send_keys(Keys.ENTER) # enter submit e.send_keys(Keys.ENTER) # enter submit
wait.until(animation_is_finished())
# Attempt to save # Attempt to save
save.click() save.click()
self.assertNotIn("N0000%d"%testEvent.pk, self.browser.find_element_by_xpath('//h1').text) self.assertNotIn(
self.assertNotIn("Event data duplicated but not yet saved", self.browser.find_element_by_id('content').text) # Check info message not visible "N0000%d" %
testEvent.pk,
self.browser.find_element_by_xpath('//h1').text)
# Check the new items are visible # 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 1", table.text)
self.assertIn("Test Item 2", table.text) self.assertIn("Test Item 2", table.text)
self.assertIn("Test Item 3", table.text) self.assertIn("Test Item 3", table.text)
infoPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Event Info")]/..') infoPanel = self.browser.find_element_by_xpath(
self.assertIn("N0000%d"%testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text) '//div[contains(text(), "Event Info")]/..')
# Check the PO hasn't carried through self.assertIn("N0000%d" % testEvent.pk, infoPanel.find_element_by_xpath(
self.assertNotIn("TESTPO", infoPanel.find_element_by_xpath('//dt[text()="PO"]/following-sibling::dd[1]').text) '//dt[text()="Based On"]/following-sibling::dd[1]').text)
self.browser.get(self.live_server_url + '/event/' +
str(testEvent.pk)) # Go back to the old event
# Check that based-on hasn't crept into the old event
self.browser.get(self.live_server_url + '/event/' + str(testEvent.pk)) #Go back to the old event infoPanel = self.browser.find_element_by_xpath(
'//div[contains(text(), "Event Info")]/..')
#Check that based-on hasn't crept into the old event self.assertNotIn("N0000%d" % testEvent.pk, infoPanel.find_element_by_xpath(
infoPanel = self.browser.find_element_by_xpath('//div[contains(text(), "Event Info")]/..') '//dt[text()="Based On"]/following-sibling::dd[1]').text)
self.assertNotIn("N0000%d"%testEvent.pk, infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text)
# Check the PO remains on the old event
self.assertIn("TESTPO", infoPanel.find_element_by_xpath('//dt[text()="PO"]/following-sibling::dd[1]').text)
# Check the items are as they were # 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 1", table.text)
self.assertIn("Test Item 2", table.text) self.assertIn("Test Item 2", table.text)
self.assertNotIn("Test Item 3", table.text) self.assertNotIn("Test Item 3", table.text)
@@ -531,7 +566,8 @@ class EventTest(LiveServerTestCase):
# Gets redirected to login and back # Gets redirected to login and back
self.authenticate('/event/create/') 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()) wait.until(animation_is_finished())
@@ -539,7 +575,8 @@ class EventTest(LiveServerTestCase):
self.browser.find_element_by_xpath('//button[.="Rig"]').click() self.browser.find_element_by_xpath('//button[.="Rig"]').click()
form = self.browser.find_element_by_tag_name('form') 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 # Set title
e = self.browser.find_element_by_id('id_name') e = self.browser.find_element_by_id('id_name')
@@ -554,14 +591,16 @@ class EventTest(LiveServerTestCase):
# Attempt to save - should fail # Attempt to save - should fail
save.click() 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.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 # Same date, end time before start time
form = self.browser.find_element_by_tag_name('form') 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').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24') form.find_element_by_id('id_start_date').send_keys('3015-04-24')
@@ -576,14 +615,16 @@ class EventTest(LiveServerTestCase):
# Attempt to save - should fail # Attempt to save - should fail
save.click() 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.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 # Same date, end time before start time
form = self.browser.find_element_by_tag_name('form') 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').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24') form.find_element_by_id('id_start_date').send_keys('3015-04-24')
@@ -596,10 +637,10 @@ class EventTest(LiveServerTestCase):
form.find_element_by_id('id_end_time').clear() form.find_element_by_id('id_end_time').clear()
form.find_element_by_id('id_end_time').send_keys('06:00') form.find_element_by_id('id_end_time').send_keys('06:00')
# No end date, end time before start time # No end date, end time before start time
form = self.browser.find_element_by_tag_name('form') 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').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24') form.find_element_by_id('id_start_date').send_keys('3015-04-24')
@@ -613,14 +654,16 @@ class EventTest(LiveServerTestCase):
# Attempt to save - should fail # Attempt to save - should fail
save.click() 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.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 # 2 dates, end after start
form = self.browser.find_element_by_tag_name('form') 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').clear()
form.find_element_by_id('id_start_date').send_keys('3015-04-24') form.find_element_by_id('id_start_date').send_keys('3015-04-24')
@@ -637,15 +680,17 @@ class EventTest(LiveServerTestCase):
# See redirected to success page # See redirected to success page
successTitle = self.browser.find_element_by_xpath('//h1').text successTitle = self.browser.find_element_by_xpath('//h1').text
event = models.Event.objects.get(name='Test Event Name') 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): def testRigNonRig(self):
self.browser.get(self.live_server_url + '/event/create/') self.browser.get(self.live_server_url + '/event/create/')
# Gets redirected to login and back # Gets redirected to login and back
self.authenticate('/event/create/') 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)
self.browser.implicitly_wait(3) #Set session-long wait (only works for non-existant DOM objects) 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()) wait.until(animation_is_finished())
@@ -656,7 +701,8 @@ class EventTest(LiveServerTestCase):
self.browser.find_element_by_xpath('//button[.="Rig"]').click() self.browser.find_element_by_xpath('//button[.="Rig"]').click()
form = self.browser.find_element_by_tag_name('form') 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 # Set title
e = self.browser.find_element_by_id('id_name') e = self.browser.find_element_by_id('id_name')
@@ -668,16 +714,23 @@ class EventTest(LiveServerTestCase):
# Save the rig # Save the rig
save.click() 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.assertTrue(detail_panel.is_displayed())
self.assertIn("Event Detail", detail_panel.text) self.assertIn("Event Detail", detail_panel.text)
def testEventDetail(self): def testEventDetail(self):
with transaction.atomic(), reversion.create_revision(): 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() person.save()
with transaction.atomic(), reversion.create_revision(): 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(): with transaction.atomic(), reversion.create_revision():
venue = models.Venue(name="Event Detail Venue").save() venue = models.Venue(name="Event Detail Venue").save()
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@@ -707,59 +760,182 @@ class EventTest(LiveServerTestCase):
order=2, order=2,
).save() ).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) personPanel = self.browser.find_element_by_xpath(
self.authenticate('/event/%d/'%event.pk) '//div[contains(text(), "Contact Details")]/..')
self.assertIn("N%05d | %s"%(event.pk, event.name), self.browser.find_element_by_xpath('//h1').text) 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")]/..') organisationPanel = self.browser.find_element_by_xpath(
self.assertEqual(person.name, personPanel.find_element_by_xpath('//dt[text()="Person"]/following-sibling::dd[1]').text) '//div[contains(text(), "Contact Details")]/..')
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")]/..')
class IcalTest(LiveServerTestCase): class IcalTest(LiveServerTestCase):
def setUp(self): def setUp(self):
self.all_events = set(range(1, 18)) self.all_events = set(range(1, 18))
self.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 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( self.profile = models.Profile(
username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True) username="EventTest", first_name="Event", last_name="Test", initials="ETU", is_superuser=True)
self.profile.set_password("EventTestPassword") self.profile.set_password("EventTestPassword")
self.profile.save() self.profile.save()
# produce 7 normal events - 5 current - 1 last week - 1 two years ago - 2 provisional - 2 confirmed - 3 booked # produce 7 normal events - 5 current - 1 last week - 1 two years ago -
models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL, start_date=date.today() + timedelta(days=6), description="start future no end") # 2 provisional - 2 confirmed - 3 booked
models.Event.objects.create(name="TE E2", status=models.Event.PROVISIONAL, start_date=date.today(), description="start today no end") models.Event.objects.create(
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") name="TE E1",
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") status=models.Event.PROVISIONAL,
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") start_date=date.today() +
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") timedelta(
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") 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 # 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(
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") 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 # 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(
models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile, description="dryhire today, checked in") name="TE E10",
models.Event.objects.create(name="TE E12", start_date=date.today()-timedelta(days=1), dry_hire=True, status=models.Event.BOOKED, description="dryhire past") start_date=date.today(),
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") dry_hire=True,
models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True, status=models.Event.CANCELLED, description="dryhire today cancelled") 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 # 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(
models.Event.objects.create(name="TE E16", start_date=date.today()+timedelta(days=1), is_rig=False, description="non rig tomorrow") name="TE E15",
models.Event.objects.create(name="TE E17", start_date=date.today()-timedelta(days=1), is_rig=False, description="non rig yesterday") start_date=date.today(),
models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED, description="non rig today cancelled") 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 = 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' os.environ['RECAPTCHA_TESTING'] = 'True'
def tearDown(self): def tearDown(self):
@@ -790,24 +966,34 @@ class IcalTest(LiveServerTestCase):
# Completes and comes back to /user/ # Completes and comes back to /user/
# Checks that no api key is displayed # 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_xpath(
self.assertEqual("No API Key Generated", self.browser.find_element_by_css_selector("pre").text) "//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 # Now creates an API key, and check a URL is displayed one
self.browser.find_element_by_link_text("Generate API Key").click() 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) self.assertNotIn("?", self.browser.find_element_by_id("cal-url").text)
# Lets change everything so it's not the default value # 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='rig']").click()
self.browser.find_element_by_xpath("//input[@value='non-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(
self.browser.find_element_by_xpath("//input[@value='cancelled']").click() "//input[@value='dry-hire']").click()
self.browser.find_element_by_xpath("//input[@value='provisional']").click() self.browser.find_element_by_xpath(
self.browser.find_element_by_xpath("//input[@value='confirmed']").click() "//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 # 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 # Awesome - all seems to work
@@ -820,8 +1006,6 @@ class IcalTest(LiveServerTestCase):
# Now creates an API key, and check a URL is displayed one # Now creates an API key, and check a URL is displayed one
self.browser.find_element_by_link_text("Generate API Key").click() self.browser.find_element_by_link_text("Generate API Key").click()
c = Client() c = Client()
# Default settings - should have all non-cancelled events # Default settings - should have all non-cancelled events
@@ -830,17 +1014,16 @@ class IcalTest(LiveServerTestCase):
response = c.get(icalUrl) response = c.get(icalUrl)
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
#Check has entire file # Check has entire file
self.assertIn("BEGIN:VCALENDAR", response.content) self.assertIn("BEGIN:VCALENDAR", response.content)
self.assertIn("END:VCALENDAR", response.content) self.assertIn("END:VCALENDAR", response.content)
expectedIn= [1,2,3,5,6,7,10,11,12,13,15,16,17] expectedIn = [1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 15, 16, 17]
for test in range(1,18): for test in range(1, 18):
if test in expectedIn: if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content) self.assertIn("TE E" + str(test) + " ", response.content)
else: else:
self.assertNotIn("TE E"+str(test)+" ", response.content) self.assertNotIn("TE E" + str(test) + " ", response.content)
# Only dry hires # Only dry hires
self.browser.find_element_by_xpath("//input[@value='rig']").click() self.browser.find_element_by_xpath("//input[@value='rig']").click()
@@ -850,73 +1033,79 @@ class IcalTest(LiveServerTestCase):
response = c.get(icalUrl) response = c.get(icalUrl)
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
expectedIn= [10,11,12,13] expectedIn = [10, 11, 12, 13]
for test in range(1,18): for test in range(1, 18):
if test in expectedIn: if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content) self.assertIn("TE E" + str(test) + " ", response.content)
else: else:
self.assertNotIn("TE E"+str(test)+" ", response.content) self.assertNotIn("TE E" + str(test) + " ", response.content)
# Only provisional rigs # Only provisional rigs
self.browser.find_element_by_xpath("//input[@value='rig']").click() 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(
self.browser.find_element_by_xpath("//input[@value='confirmed']").click() "//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 icalUrl = self.browser.find_element_by_id("cal-url").text
response = c.get(icalUrl) response = c.get(icalUrl)
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
expectedIn= [1,2] expectedIn = [1, 2]
for test in range(1,18): for test in range(1, 18):
if test in expectedIn: if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content) self.assertIn("TE E" + str(test) + " ", response.content)
else: else:
self.assertNotIn("TE E"+str(test)+" ", response.content) self.assertNotIn("TE E" + str(test) + " ", response.content)
# Only cancelled non-rigs # Only cancelled non-rigs
self.browser.find_element_by_xpath("//input[@value='rig']").click() 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='non-rig']").click()
self.browser.find_element_by_xpath("//input[@value='provisional']").click() self.browser.find_element_by_xpath(
self.browser.find_element_by_xpath("//input[@value='cancelled']").click() "//input[@value='provisional']").click()
self.browser.find_element_by_xpath(
"//input[@value='cancelled']").click()
icalUrl = self.browser.find_element_by_id("cal-url").text icalUrl = self.browser.find_element_by_id("cal-url").text
response = c.get(icalUrl) response = c.get(icalUrl)
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
expectedIn= [18] expectedIn = [18]
for test in range(1,18): for test in range(1, 18):
if test in expectedIn: if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content) self.assertIn("TE E" + str(test) + " ", response.content)
else: else:
self.assertNotIn("TE E"+str(test)+" ", response.content) self.assertNotIn("TE E" + str(test) + " ", response.content)
# Nothing selected # Nothing selected
self.browser.find_element_by_xpath("//input[@value='non-rig']").click() 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 icalUrl = self.browser.find_element_by_id("cal-url").text
response = c.get(icalUrl) response = c.get(icalUrl)
self.assertEqual(200, response.status_code) self.assertEqual(200, response.status_code)
expectedIn= [] expectedIn = []
for test in range(1,18): for test in range(1, 18):
if test in expectedIn: if test in expectedIn:
self.assertIn("TE E"+str(test)+" ", response.content) self.assertIn("TE E" + str(test) + " ", response.content)
else: else:
self.assertNotIn("TE E"+str(test)+" ", response.content) self.assertNotIn("TE E" + str(test) + " ", response.content)
# Wow - that was a lot of tests
# Wow - that was a lot of tests
class animation_is_finished(object): class animation_is_finished(object):
""" Checks if animation is done """ """ Checks if animation is done """
def __init__(self): def __init__(self):
pass pass
def __call__(self, driver): def __call__(self, driver):
time.sleep(0.1) # allow time for the animation to actually start
numberAnimating = driver.execute_script('return $(":animated").length') numberAnimating = driver.execute_script('return $(":animated").length')
finished = numberAnimating == 0 finished = numberAnimating == 0
if finished: if finished:
import time
time.sleep(0.1) time.sleep(0.1)
return finished return finished

View File

@@ -1,9 +1,11 @@
from datetime import date, timedelta, datetime, time
from decimal import *
import pytz import pytz
from django.conf import settings from django.conf import settings
from django.test import TestCase from django.test import TestCase
from RIGS import models from RIGS import models
from datetime import date, timedelta, datetime, time
from decimal import *
class ProfileTestCase(TestCase): class ProfileTestCase(TestCase):
@@ -16,8 +18,10 @@ class ProfileTestCase(TestCase):
class VatRateTestCase(TestCase): class VatRateTestCase(TestCase):
def setUp(self): def setUp(self):
models.VatRate.objects.create(start_at='2014-03-01', rate=0.20, comment='test1') models.VatRate.objects.create(
models.VatRate.objects.create(start_at='2016-03-01', rate=0.15, comment='test2') 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): def test_find_correct(self):
r = models.VatRate.objects.find_rate('2015-03-01') r = models.VatRate.objects.find_rate('2015-03-01')
@@ -34,18 +38,27 @@ class EventTestCase(TestCase):
def setUp(self): def setUp(self):
self.all_events = set(range(1, 18)) self.all_events = set(range(1, 18))
self.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 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(
self.profile = models.Profile.objects.create(username="testuser1", email="1@test.com") 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 # produce 7 normal events - 5 current
models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6), models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6),
description="start future no end") 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(), models.Event.objects.create(name="TE E3", start_date=date.today(), end_date=date.today(),
description="start today with end 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', models.Event.objects.create(name="TE E5", start_date='2014-03-20', end_date='2014-03-21',
description="start past with end past") description="start past with end past")
models.Event.objects.create(name="TE E6", start_date=date.today() - timedelta(days=2), 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") description="cancelled and started")
# 5 dry hire - 3 current # 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, models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile,
description="dryhire today, checked in") description="dryhire today, checked in")
models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True, 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") status=models.Event.CANCELLED, description="dryhire today cancelled")
# 4 non rig - 3 current # 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, models.Event.objects.create(name="TE E16", start_date=date.today() + timedelta(days=1), is_rig=False,
description="non rig tomorrow") description="non rig tomorrow")
models.Event.objects.create(name="TE E17", start_date=date.today() - timedelta(days=1), is_rig=False, 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): def test_count(self):
# Santiy check we have the expected events created # 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): def test_rig_count(self):
# by my count this is 7 # by my count this is 7
@@ -93,10 +115,16 @@ class EventTestCase(TestCase):
current_events = models.Event.objects.current_events() current_events = models.Event.objects.current_events()
self.assertEqual(len(current_events), len(self.current_events)) self.assertEqual(len(current_events), len(self.current_events))
for eid in 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: 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): def test_related_venue(self):
v1 = models.Venue.objects.create(name="TE V1") v1 = models.Venue.objects.create(name="TE V1")
@@ -204,29 +232,43 @@ class EventTestCase(TestCase):
event.save() event.save()
def test_earliest_time(self): 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 # 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 # With start time
event.start_time = time(9, 00) 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 # 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) self.assertEqual(event.earliest_time, event.access_at)
# With meet time # 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) self.assertEqual(event.earliest_time, event.meet_at)
# Check order isn't important # Check order isn't important
event.start_date = date(2015, 12, 03) event.start_date = date(2015, 12, 0o3)
self.assertEqual(event.earliest_time, self.create_datetime(2015, 12, 03, 9, 00)) self.assertEqual(
event.earliest_time,
self.create_datetime(
2015,
12,
0o3,
9,
00))
def test_latest_time(self): 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 # Just start date
self.assertEqual(event.latest_time, event.start_date) self.assertEqual(event.latest_time, event.start_date)
@@ -237,25 +279,48 @@ class EventTestCase(TestCase):
# With end time # With end time
event.end_time = time(23, 00) 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): def test_in_bounds(self):
manager = models.Event.objects manager = models.Event.objects
events = [ events = [
manager.create(name="TE IB0", start_date='2016-01-02'), # yes no 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 # basic checks
manager.create(name='TE IB2', start_date='2016-01-02', end_date='2016-01-04'), manager.create(
manager.create(name='TE IB3', start_date='2015-12-31', end_date='2016-01-03'), name='TE IB2',
manager.create(name='TE IB4', start_date='2016-01-04', access_at='2016-01-03'), start_date='2016-01-02',
manager.create(name='TE IB5', start_date='2016-01-04', meet_at='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 # 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[0], in_bounds)
self.assertIn(events[1], in_bounds) self.assertIn(events[1], in_bounds)
self.assertIn(events[2], in_bounds) self.assertIn(events[2], in_bounds)
@@ -272,11 +337,14 @@ class EventTestCase(TestCase):
class EventItemTestCase(TestCase): class EventItemTestCase(TestCase):
def setUp(self): def setUp(self):
self.e1 = models.Event.objects.create(name="TI E1", start_date=date.today()) self.e1 = models.Event.objects.create(
self.e2 = models.Event.objects.create(name="TI E2", start_date=date.today()) 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): 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) self.assertEqual(item.total_cost, 1.00)
item.cost = 2.50 item.cost = 2.50
@@ -289,8 +357,10 @@ class EventItemTestCase(TestCase):
item.delete() item.delete()
def test_item_order(self): def test_item_order(self):
i1 = models.EventItem.objects.create(event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1) i1 = models.EventItem.objects.create(
i2 = models.EventItem.objects.create(event=self.e1, name="TI I2", quantity=1, cost=1.00, order=2) 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() items = self.e1.items.all()
self.assertListEqual([i1, i2], list(items)) self.assertListEqual([i1, i2], list(items))
@@ -298,17 +368,29 @@ class EventItemTestCase(TestCase):
class EventPricingTestCase(TestCase): class EventPricingTestCase(TestCase):
def setUp(self): def setUp(self):
models.VatRate.objects.create(rate=0.20, comment="TP V1", start_at='2013-01-01') models.VatRate.objects.create(
models.VatRate.objects.create(rate=0.10, comment="TP V2", start_at=date.today() - timedelta(days=1)) rate=0.20, comment="TP V1", start_at='2013-01-01')
self.e1 = models.Event.objects.create(name="TP E1", start_date=date.today() - timedelta(days=2)) models.VatRate.objects.create(
self.e2 = models.Event.objects.create(name="TP E2", start_date=date.today()) 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 E1, total 70.40
# Create some items E2, total 381.20 # 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.i1 = models.EventItem.objects.create(
self.i2 = models.EventItem.objects.create(event=self.e1, name="TP I2", quantity=2, cost=3.20, order=2) event=self.e1, name="TP I1", quantity=1, cost=50.00, order=1)
self.i3 = models.EventItem.objects.create(event=self.e1, name="TP I3", quantity=7, cost=2.00, order=3) self.i2 = models.EventItem.objects.create(
self.i4 = models.EventItem.objects.create(event=self.e2, name="TP I4", quantity=2, cost=190.60, order=1) 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. # 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 # Using anything else results in a failure due to floating point arritmetic

Some files were not shown because too many files have changed in this diff Show More