Compare commits
31 Commits
imgbot
...
a9b034255e
| Author | SHA1 | Date | |
|---|---|---|---|
| a9b034255e | |||
| 6676183443 | |||
| 3f93cebf41 | |||
| 603e919ad0 | |||
| a0b70a3cac | |||
| a11e32252f | |||
| e48e016cb9 | |||
| ef1d9868da | |||
| 788fb3efe6 | |||
| 4f912932ca | |||
| 0598612c15 | |||
| 656f9fdd25 | |||
| ccda38918c | |||
| a1edf80dd0 | |||
| 83fe526cbd | |||
| 1d63bd940d | |||
| c090163f40 | |||
| baa3b2c9c6 | |||
| 462a16ec42 | |||
| 6cb3d1855a | |||
| 9279131edf | |||
| 3853ad0871 | |||
| 7eea868575 | |||
| fc6e66c7f5 | |||
| 01ed05ecd9 | |||
| 20e5d25130 | |||
| 11db880ac3 | |||
| 87caab6c8e | |||
| d79366d2e6 | |||
| 10f2152d8b | |||
| 0154ecb6d8 |
@@ -1,5 +1,3 @@
|
|||||||
[run]
|
[run]
|
||||||
omit = */migrations/*
|
plugins = django_coverage_plugin
|
||||||
*/tests/*
|
omit = */migrations/*, */tests/*
|
||||||
*/site-packages/*
|
|
||||||
*/distutils/*
|
|
||||||
|
|||||||
38
.github/workflows/django.yml
vendored
@@ -10,36 +10,38 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# strategy:
|
||||||
|
# matrix:
|
||||||
|
# browser: ['chrome']
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# BROWSER: ${{ matrix.browser }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: 3.9.1
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
id: pcache
|
|
||||||
with:
|
|
||||||
path: ~/.local/share/virtualenvs
|
|
||||||
key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pipenv-
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip pipenv
|
|
||||||
pipenv install -d
|
|
||||||
# if: steps.pcache.outputs.cache-hit != 'true'
|
|
||||||
- name: Cache Static Files
|
- name: Cache Static Files
|
||||||
id: static-cache
|
id: static-cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: 'pipeline/built_assets'
|
path: 'static/'
|
||||||
key: ${{ hashFiles('package-lock.json') }}-${{ hashFiles('pipeline/source_assets') }}
|
key: ${{ hashFiles('package-lock.json') }}-${{ hashFiles('pipeline/source_assets') }}
|
||||||
- uses: bahmutov/npm-install@v1
|
- uses: bahmutov/npm-install@v1
|
||||||
if: steps.static-cache.outputs.cache-hit != 'true'
|
if: steps.static-cache.outputs.cache-hit != 'true'
|
||||||
- run: node node_modules/gulp/bin/gulp build
|
- run: node node_modules/gulp/bin/gulp build
|
||||||
if: steps.static-cache.outputs.cache-hit != 'true'
|
if: steps.static-cache.outputs.cache-hit != 'true'
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
- name: Cache python deps
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ${{ env.pythonLocation }}
|
||||||
|
key: ${{ env.pythonLocation }}-${{ hashFiles('Pipfile.lock') }}
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install pipenv
|
||||||
|
pipenv install -d
|
||||||
- name: Basic Checks
|
- name: Basic Checks
|
||||||
run: |
|
run: |
|
||||||
pipenv run pycodestyle . --exclude=migrations,node_modules
|
pipenv run pycodestyle . --exclude=migrations,node_modules
|
||||||
@@ -47,7 +49,7 @@ jobs:
|
|||||||
pipenv run python manage.py makemigrations --check --dry-run
|
pipenv run python manage.py makemigrations --check --dry-run
|
||||||
pipenv run python manage.py collectstatic --noinput
|
pipenv run python manage.py collectstatic --noinput
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: pipenv run pytest -n auto -vv --cov
|
run: pipenv run pytest --cov -n 8
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -26,7 +26,6 @@ var/
|
|||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
node_modules/
|
node_modules/
|
||||||
data/
|
|
||||||
|
|
||||||
# Continer extras
|
# Continer extras
|
||||||
.vagrant
|
.vagrant
|
||||||
|
|||||||
166
Pipfile
@@ -4,101 +4,91 @@ verify_ssl = true
|
|||||||
name = "pypi"
|
name = "pypi"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
ansicolors = "~=1.1.8"
|
ansicolors = "==1.1.8"
|
||||||
asgiref = "~=3.3.1"
|
asgiref = "==3.3.1"
|
||||||
"backports.tempfile" = "~=1.0"
|
"backports.tempfile" = "==1.0"
|
||||||
"backports.weakref" = "~=1.0.post1"
|
"backports.weakref" = "==1.0.post1"
|
||||||
beautifulsoup4 = "~=4.9.3"
|
beautifulsoup4 = "==4.9.3"
|
||||||
Brotli = "~=1.0.9"
|
Brotli = "==1.0.9"
|
||||||
cachetools = "~=4.2.1"
|
cachetools = "==4.2.1"
|
||||||
certifi = "~=2020.12.5"
|
certifi = "==2020.12.5"
|
||||||
chardet = "~=4.0.0"
|
chardet = "==4.0.0"
|
||||||
configparser = "~=5.0.1"
|
configparser = "==5.0.1"
|
||||||
contextlib2 = "~=0.6.0.post1"
|
contextlib2 = "==0.6.0.post1"
|
||||||
cssselect = "~=1.1.0"
|
cssselect = "==1.1.0"
|
||||||
cssutils = "~=1.0.2"
|
cssutils = "==1.0.2"
|
||||||
dj-database-url = "~=0.5.0"
|
diff-match-patch = "==20200713"
|
||||||
dj-static = "~=0.0.6"
|
dj-database-url = "==0.5.0"
|
||||||
Django = "~=3.2"
|
dj-static = "==0.0.6"
|
||||||
django-debug-toolbar = "~=3.2"
|
Django = "==3.1.5"
|
||||||
django-filter = "~=2.4.0"
|
django-debug-toolbar = "==3.2"
|
||||||
django-ical = "~=1.7.1"
|
django-filter = "==2.4.0"
|
||||||
django-recurrence = "~=1.10.3"
|
django-gulp = "==4.1.0"
|
||||||
django-registration-redux = "~=2.9"
|
django-ical = "==1.7.1"
|
||||||
django-reversion = "~=3.0.9"
|
django-recaptcha = "==2.0.6"
|
||||||
django-toolbelt = "~=0.0.1"
|
django-recurrence = "==1.10.3"
|
||||||
django-widget-tweaks = "~=1.4.8"
|
django-registration-redux = "==2.9"
|
||||||
django-htmlmin = "~=0.11.0"
|
django-reversion = "==3.0.9"
|
||||||
envparse = "~=0.2.0"
|
django-toolbelt = "==0.0.1"
|
||||||
gunicorn = "~=20.0.4"
|
django-widget-tweaks = "==1.4.8"
|
||||||
icalendar = "~=4.0.7"
|
django-htmlmin = "==0.11.0"
|
||||||
idna = "~=2.10"
|
envparse = "==0.2.0"
|
||||||
lxml = "~=4.7.1"
|
gunicorn = "==20.0.4"
|
||||||
Markdown = "~=3.3.3"
|
icalendar = "==4.0.7"
|
||||||
msgpack = "~=1.0.2"
|
idna = "==2.10"
|
||||||
pep517 = "~=0.9.1"
|
importlib-metadata = "==3.4.0"
|
||||||
Pillow = "~=9.0.0"
|
lxml = "==4.6.2"
|
||||||
premailer = "~=3.7.0"
|
Markdown = "==3.3.3"
|
||||||
progress = "~=1.5"
|
msgpack = "==1.0.2"
|
||||||
psutil = "~=5.8.0"
|
pep517 = "==0.9.1"
|
||||||
psycopg2 = "~=2.8.6"
|
Pillow = "==8.1.0"
|
||||||
Pygments = "~=2.7.4"
|
pluggy = "==0.13.1"
|
||||||
pyparsing = "~=2.4.7"
|
premailer = "==3.7.0"
|
||||||
PyPDF2 = "~=1.26.0"
|
progress = "==1.5"
|
||||||
PyPOM = "~=2.2.0"
|
psutil = "==5.8.0"
|
||||||
python-dateutil = "~=2.8.1"
|
psycopg2 = "==2.8.6"
|
||||||
pytoml = "~=0.1.21"
|
Pygments = "==2.7.4"
|
||||||
pytz = "~=2020.5"
|
pyparsing = "==2.4.7"
|
||||||
reportlab = "~=3.5.59"
|
PyPDF2 = "==1.26.0"
|
||||||
requests = "~=2.25.1"
|
PyPOM = "==2.2.0"
|
||||||
retrying = "~=1.3.3"
|
python-dateutil = "==2.8.1"
|
||||||
simplejson = "~=3.17.2"
|
pytoml = "==0.1.21"
|
||||||
six = "~=1.15.0"
|
pytz = "==2020.5"
|
||||||
soupsieve = "~=2.1"
|
pytest-django = "==4.1.0"
|
||||||
sqlparse = "~=0.4.2"
|
pytest-xdist = "==2.2.0"
|
||||||
static3 = "~=0.7.0"
|
pytest-cov = "==2.11.1"
|
||||||
svg2rlg = "~=0.3"
|
reportlab = "==3.5.59"
|
||||||
tini = "~=3.0.1"
|
requests = "==2.25.1"
|
||||||
tornado = "~=6.1"
|
retrying = "==1.3.3"
|
||||||
urllib3 = "~=1.26.5"
|
selenium = "==3.141.0"
|
||||||
whitenoise = "~=5.2.0"
|
simplejson = "==3.17.2"
|
||||||
yolk = "~=0.4.3"
|
six = "==1.15.0"
|
||||||
"z3c.rml" = "~=4.1.2"
|
soupsieve = "==2.1"
|
||||||
zipp = "~=3.4.0"
|
sqlparse = "==0.4.1"
|
||||||
"zope.component" = "~=4.6.2"
|
static3 = "==0.7.0"
|
||||||
"zope.deferredimport" = "~=4.3.1"
|
svg2rlg = "==0.3"
|
||||||
"zope.deprecation" = "~=4.4.0"
|
tini = "==3.0.1"
|
||||||
"zope.event" = "~=4.5.0"
|
tornado = "==6.1"
|
||||||
"zope.hookable" = "~=5.0.1"
|
urllib3 = "==1.26.2"
|
||||||
"zope.interface" = "~=5.2.0"
|
whitenoise = "==5.2.0"
|
||||||
"zope.proxy" = "~=4.3.5"
|
yolk = "==0.4.3"
|
||||||
"zope.schema" = "~=6.0.1"
|
"z3c.rml" = "==4.1.2"
|
||||||
|
zipp = "==3.4.0"
|
||||||
|
"zope.component" = "==4.6.2"
|
||||||
|
"zope.deferredimport" = "==4.3.1"
|
||||||
|
"zope.deprecation" = "==4.4.0"
|
||||||
|
"zope.event" = "==4.5.0"
|
||||||
|
"zope.hookable" = "==5.0.1"
|
||||||
|
"zope.interface" = "==5.2.0"
|
||||||
|
"zope.proxy" = "==4.3.5"
|
||||||
|
"zope.schema" = "==6.0.1"
|
||||||
sentry-sdk = "*"
|
sentry-sdk = "*"
|
||||||
diff-match-patch = "*"
|
|
||||||
python-barcode = "*"
|
|
||||||
django-hCaptcha = "*"
|
|
||||||
importlib-metadata = "*"
|
|
||||||
django-hcaptcha = "*"
|
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
selenium = "~=3.141.0"
|
|
||||||
pycodestyle = "*"
|
pycodestyle = "*"
|
||||||
coveralls = "*"
|
coveralls = "*"
|
||||||
django-coverage-plugin = "*"
|
django-coverage-plugin = "*"
|
||||||
pytest-cov = "*"
|
pytest-cov = "*"
|
||||||
pytest-django = "*"
|
|
||||||
pluggy = "*"
|
|
||||||
pytest-splinter = "*"
|
|
||||||
pytest = "*"
|
|
||||||
pytest-reverse = "*"
|
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.9"
|
python_version = "3.9"
|
||||||
|
|
||||||
[dev-packages.pytest-xdist]
|
|
||||||
extras = [ "psutil",]
|
|
||||||
version = "*"
|
|
||||||
|
|
||||||
[dev-packages.PyPOM]
|
|
||||||
extras = [ "splinter",]
|
|
||||||
version = "*"
|
|
||||||
|
|||||||
1283
Pipfile.lock
generated
@@ -9,18 +9,23 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from pathlib import Path
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
|
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
from sentry_sdk.integrations.django import DjangoIntegration
|
from sentry_sdk.integrations.django import DjangoIntegration
|
||||||
from envparse import env
|
from envparse import env
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||||
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
SECRET_KEY = env('SECRET_KEY', default='gxhy(a#5mhp289_=6xx$7jh=eh$ymxg^ymc+di*0c*geiu3p_e')
|
SECRET_KEY = env('SECRET_KEY', default='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 = env('DEBUG', cast=bool, default=True)
|
DEBUG = env('DEBUG', cast=bool, default=True)
|
||||||
STAGING = env('STAGING', cast=bool, default=False)
|
STAGING = env('STAGING', cast=bool, default=False)
|
||||||
@@ -61,13 +66,12 @@ INSTALLED_APPS = (
|
|||||||
'users',
|
'users',
|
||||||
'RIGS',
|
'RIGS',
|
||||||
'assets',
|
'assets',
|
||||||
'training',
|
|
||||||
|
|
||||||
'debug_toolbar',
|
'debug_toolbar',
|
||||||
'registration',
|
'registration',
|
||||||
'reversion',
|
'reversion',
|
||||||
|
'captcha',
|
||||||
'widget_tweaks',
|
'widget_tweaks',
|
||||||
'hcaptcha',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
MIDDLEWARE = (
|
MIDDLEWARE = (
|
||||||
@@ -93,7 +97,7 @@ WSGI_APPLICATION = 'PyRIGS.wsgi.application'
|
|||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': str(BASE_DIR / 'db.sqlite3'),
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,9 +191,12 @@ LOGOUT_URL = '/user/logout/'
|
|||||||
|
|
||||||
ACCOUNT_ACTIVATION_DAYS = 7
|
ACCOUNT_ACTIVATION_DAYS = 7
|
||||||
|
|
||||||
# CAPTCHA settings
|
# reCAPTCHA settings
|
||||||
HCAPTCHA_SITEKEY = env('HCAPTCHA_SITEKEY', '10000000-ffff-ffff-ffff-000000000001')
|
RECAPTCHA_PUBLIC_KEY = env('RECAPTCHA_PUBLIC_KEY', default="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI") # If not set, use development key
|
||||||
HCAPTCHA_SECRET = env('HCAPTCHA_SECRET', '0x0000000000000000000000000000000000000000')
|
RECAPTCHA_PRIVATE_KEY = env('RECAPTCHA_PUBLIC_KEY', default="6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe") # If not set, use development key
|
||||||
|
NOCAPTCHA = True
|
||||||
|
|
||||||
|
SILENCED_SYSTEM_CHECKS = ['captcha.recaptcha_test_key_error']
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
EMAILER_TEST = False
|
EMAILER_TEST = False
|
||||||
@@ -228,16 +235,19 @@ DATETIME_INPUT_FORMATS = ('%Y-%m-%dT%H:%M', '%Y-%m-%dT%H:%M:%S')
|
|||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
STATIC_ROOT = str(BASE_DIR / 'static/')
|
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
|
||||||
|
STATIC_DIRS = [
|
||||||
|
os.path.join(BASE_DIR, 'static/'),
|
||||||
|
]
|
||||||
STATICFILES_DIRS = [
|
STATICFILES_DIRS = [
|
||||||
str(BASE_DIR / 'pipeline/built_assets'),
|
os.path.join(BASE_DIR, 'pipeline/built_assets'),
|
||||||
]
|
]
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [
|
'DIRS': [
|
||||||
BASE_DIR / 'templates'
|
os.path.join(BASE_DIR, 'templates')
|
||||||
],
|
],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
@@ -260,5 +270,3 @@ 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"
|
||||||
AUTHORISATION_NOTIFICATION_ADDRESS = 'productions@nottinghamtec.co.uk'
|
AUTHORISATION_NOTIFICATION_ADDRESS = 'productions@nottinghamtec.co.uk'
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from selenium.webdriver.support.wait import WebDriverWait
|
|||||||
|
|
||||||
from RIGS import models as rigsmodels
|
from RIGS import models as rigsmodels
|
||||||
from . import pages
|
from . import pages
|
||||||
|
from envparse import env
|
||||||
|
|
||||||
from pytest_django.asserts import assertContains
|
from pytest_django.asserts import assertContains
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ class BootstrapSelectElement(Region):
|
|||||||
self.find_element(*self._deselect_all_locator).click()
|
self.find_element(*self._deselect_all_locator).click()
|
||||||
|
|
||||||
def search(self, query):
|
def search(self, query):
|
||||||
# self.wait.until(expected_conditions.visibility_of_element_located(self._status_locator))
|
|
||||||
search_box = self.find_element(*self._search_locator)
|
search_box = self.find_element(*self._search_locator)
|
||||||
self.open()
|
self.open()
|
||||||
search_box.clear()
|
search_box.clear()
|
||||||
@@ -84,7 +83,7 @@ class BootstrapSelectElement(Region):
|
|||||||
return [self.BootstrapSelectOption(self, i) for i in options]
|
return [self.BootstrapSelectOption(self, i) for i in options]
|
||||||
|
|
||||||
def set_option(self, name, selected):
|
def set_option(self, name, selected):
|
||||||
options = [x for x in self.options if x.name == name]
|
options = list((x for x in self.options if x.name == name))
|
||||||
assert len(options) == 1
|
assert len(options) == 1
|
||||||
options[0].set_selected(selected)
|
options[0].set_selected(selected)
|
||||||
|
|
||||||
@@ -117,15 +116,6 @@ class TextBox(Region):
|
|||||||
self.root.send_keys(value)
|
self.root.send_keys(value)
|
||||||
|
|
||||||
|
|
||||||
class SimpleMDETextArea(Region):
|
|
||||||
@property
|
|
||||||
def value(self):
|
|
||||||
return self.driver.execute_script("return document.querySelector('#' + arguments[0]).nextSibling.nextSibling.CodeMirror.getDoc().getValue();", self.root.get_attribute("id"))
|
|
||||||
|
|
||||||
def set_value(self, value):
|
|
||||||
self.driver.execute_script("document.querySelector('#' + arguments[0]).nextSibling.nextSibling.CodeMirror.getDoc().setValue(arguments[1]);", self.root.get_attribute("id"), value)
|
|
||||||
|
|
||||||
|
|
||||||
class CheckBox(Region):
|
class CheckBox(Region):
|
||||||
def toggle(self):
|
def toggle(self):
|
||||||
self.root.click()
|
self.root.click()
|
||||||
|
|||||||
@@ -8,13 +8,18 @@ from pytest_django.asserts import assertRedirects, assertContains, assertNotCont
|
|||||||
from pytest_django.asserts import assertTemplateUsed, assertInHTML
|
from pytest_django.asserts import assertTemplateUsed, assertInHTML
|
||||||
|
|
||||||
from PyRIGS import urls
|
from PyRIGS import urls
|
||||||
from RIGS.models import Event, Profile
|
from RIGS.models import Event
|
||||||
from assets.models import Asset
|
from assets.models import Asset
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
import pytest
|
||||||
|
from django.core.management import call_command
|
||||||
from django.template.defaultfilters import striptags
|
from django.template.defaultfilters import striptags
|
||||||
from django.urls.exceptions import NoReverseMatch
|
from django.urls.exceptions import NoReverseMatch
|
||||||
|
|
||||||
from django.test import TestCase, TransactionTestCase
|
from RIGS.models import Event
|
||||||
|
from assets.models import Asset
|
||||||
|
from django.db import connection
|
||||||
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
|
|
||||||
@@ -44,7 +49,7 @@ def get_request_url(url):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("command", ['generateSampleAssetsData', 'generateSampleRIGSData', 'generateSampleUserData',
|
@pytest.mark.parametrize("command", ['generateSampleAssetsData', 'generateSampleRIGSData', 'generateSampleUserData',
|
||||||
'deleteSampleData', 'generateSampleTrainingData', 'generate_sample_training_users'])
|
'deleteSampleData'])
|
||||||
def test_production_exception(command):
|
def test_production_exception(command):
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
with pytest.raises(CommandError, match=".*production"):
|
with pytest.raises(CommandError, match=".*production"):
|
||||||
@@ -62,14 +67,16 @@ class TestSampleDataGenerator(TestCase):
|
|||||||
assert Event.objects.all().count() == 0
|
assert Event.objects.all().count() == 0
|
||||||
|
|
||||||
|
|
||||||
|
class TestSampleDataGenerator(TestCase):
|
||||||
@override_settings(DEBUG=True)
|
@override_settings(DEBUG=True)
|
||||||
@pytest.mark.skip(reason="broken")
|
def setUp(self):
|
||||||
def test_unauthenticated(client): # Nothing should be available to the unauthenticated
|
|
||||||
call_command('generateSampleData')
|
call_command('generateSampleData')
|
||||||
|
|
||||||
|
def test_unauthenticated(self): # Nothing should be available to the unauthenticated
|
||||||
for url in find_urls_recursive(urls.urlpatterns):
|
for url in find_urls_recursive(urls.urlpatterns):
|
||||||
request_url = get_request_url(url)
|
request_url = get_request_url(url)
|
||||||
if request_url and 'user' not in request_url: # User module is full of edge cases
|
if request_url and 'user' not in request_url: # User module is full of edge cases
|
||||||
response = client.get(request_url, follow=True, HTTP_HOST='example.com')
|
response = self.client.get(request_url, follow=True, HTTP_HOST='example.com')
|
||||||
assertContains(response, 'Login')
|
assertContains(response, 'Login')
|
||||||
if 'application/json+oembed' in response.content.decode():
|
if 'application/json+oembed' in response.content.decode():
|
||||||
assertTemplateUsed(response, 'login_redirect.html')
|
assertTemplateUsed(response, 'login_redirect.html')
|
||||||
@@ -79,59 +86,60 @@ def test_unauthenticated(client): # Nothing should be available to the unauthen
|
|||||||
else:
|
else:
|
||||||
expected_url = "{0}?next={1}".format(reverse('login'), request_url)
|
expected_url = "{0}?next={1}".format(reverse('login'), request_url)
|
||||||
assertRedirects(response, expected_url)
|
assertRedirects(response, expected_url)
|
||||||
call_command('deleteSampleData')
|
|
||||||
|
|
||||||
|
def test_page_titles(self):
|
||||||
|
assert self.client.login(username='superuser', password='superuser')
|
||||||
|
for url in filter((lambda u: "embed" not in u.name), find_urls_recursive(urls.urlpatterns)):
|
||||||
|
request_url = get_request_url(url)
|
||||||
|
response = self.client.get(request_url)
|
||||||
|
if hasattr(response, "context_data") and "page_title" in response.context_data:
|
||||||
|
expected_title = striptags(response.context_data["page_title"])
|
||||||
|
assertInHTML('<title>{} | Rig Information Gathering System'.format(expected_title),
|
||||||
|
response.content.decode())
|
||||||
|
print("{} | {}".format(request_url, expected_title)) # If test fails, tell me where!
|
||||||
|
self.client.logout()
|
||||||
|
|
||||||
@override_settings(DEBUG=True)
|
def test_basic_access(self):
|
||||||
@pytest.mark.skip(reason="broken")
|
assert self.client.login(username="basic", password="basic")
|
||||||
def test_basic_access(client):
|
|
||||||
call_command('generateSampleData')
|
|
||||||
assert client.login(username="basic", password="basic")
|
|
||||||
|
|
||||||
url = reverse('asset_list')
|
url = reverse('asset_list')
|
||||||
response = client.get(url)
|
response = self.client.get(url)
|
||||||
# Check edit and duplicate buttons NOT shown in list
|
# Check edit and duplicate buttons NOT shown in list
|
||||||
assertNotContains(response, 'Edit')
|
assertNotContains(response, 'Edit')
|
||||||
assertNotContains(response,
|
assertNotContains(response,
|
||||||
'Duplicate') # If this line is randomly failing, check the debug toolbar HTML hasn't crept in
|
'Duplicate') # If this line is randomly failing, check the debug toolbar HTML hasn't crept in
|
||||||
|
|
||||||
url = reverse('asset_detail', kwargs={'pk': Asset.objects.first().asset_id})
|
url = reverse('asset_detail', kwargs={'pk': Asset.objects.first().asset_id})
|
||||||
response = client.get(url)
|
response = self.client.get(url)
|
||||||
assertNotContains(response, 'Purchase Details')
|
assertNotContains(response, 'Purchase Details')
|
||||||
assertNotContains(response, 'View Revision History')
|
assertNotContains(response, 'View Revision History')
|
||||||
|
|
||||||
urlz = {'asset_history', 'asset_update', 'asset_duplicate'}
|
urlz = {'asset_history', 'asset_update', 'asset_duplicate'}
|
||||||
for url_name in urlz:
|
for url_name in urlz:
|
||||||
request_url = reverse(url_name, kwargs={'pk': Asset.objects.first().asset_id})
|
request_url = reverse(url_name, kwargs={'pk': Asset.objects.first().asset_id})
|
||||||
response = client.get(request_url, follow=True)
|
response = self.client.get(request_url, follow=True)
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
request_url = reverse('supplier_create')
|
request_url = reverse('supplier_create')
|
||||||
response = client.get(request_url, follow=True)
|
response = self.client.get(request_url, follow=True)
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
request_url = reverse('supplier_update', kwargs={'pk': 1})
|
request_url = reverse('supplier_update', kwargs={'pk': 1})
|
||||||
response = client.get(request_url, follow=True)
|
response = self.client.get(request_url, follow=True)
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
client.logout()
|
self.client.logout()
|
||||||
call_command('deleteSampleData')
|
|
||||||
|
|
||||||
|
def test_keyholder_access(self):
|
||||||
@override_settings(DEBUG=True)
|
assert self.client.login(username="keyholder", password="keyholder")
|
||||||
@pytest.mark.skip(reason="broken")
|
|
||||||
def test_keyholder_access(client):
|
|
||||||
call_command('generateSampleData')
|
|
||||||
assert client.login(username="keyholder", password="keyholder")
|
|
||||||
|
|
||||||
url = reverse('asset_list')
|
url = reverse('asset_list')
|
||||||
response = client.get(url)
|
response = self.client.get(url)
|
||||||
# Check edit and duplicate buttons shown in list
|
# Check edit and duplicate buttons shown in list
|
||||||
assertContains(response, 'Edit')
|
assertContains(response, 'Edit')
|
||||||
assertContains(response, 'Duplicate')
|
assertContains(response, 'Duplicate')
|
||||||
|
|
||||||
url = reverse('asset_detail', kwargs={'pk': Asset.objects.first().asset_id})
|
url = reverse('asset_detail', kwargs={'pk': Asset.objects.first().asset_id})
|
||||||
response = client.get(url)
|
response = self.client.get(url)
|
||||||
assertContains(response, 'Purchase Details')
|
assertContains(response, 'Purchase Details')
|
||||||
assertContains(response, 'View Revision History')
|
assertContains(response, 'View Revision History')
|
||||||
client.logout()
|
self.client.logout()
|
||||||
call_command('deleteSampleData')
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ urlpatterns = [
|
|||||||
path('', include('versioning.urls')),
|
path('', include('versioning.urls')),
|
||||||
path('', include('RIGS.urls')),
|
path('', include('RIGS.urls')),
|
||||||
path('assets/', include('assets.urls')),
|
path('assets/', include('assets.urls')),
|
||||||
path('training/', include('training.urls')),
|
|
||||||
|
|
||||||
path('', login_required(views.Index.as_view()), name='index'),
|
path('', login_required(views.Index.as_view()), name='index'),
|
||||||
|
|
||||||
@@ -28,14 +27,13 @@ urlpatterns = [
|
|||||||
path('', include('users.urls')),
|
path('', include('users.urls')),
|
||||||
|
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path("robots.txt", TemplateView.as_view(template_name="robots.txt", content_type="text/plain")),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
urlpatterns += staticfiles_urlpatterns()
|
urlpatterns += staticfiles_urlpatterns()
|
||||||
|
|
||||||
import debug_toolbar
|
import debug_toolbar
|
||||||
urlpatterns += [
|
urlpatterns = [
|
||||||
path('__debug__/', include(debug_toolbar.urls)),
|
path('__debug__/', include(debug_toolbar.urls)),
|
||||||
path('bootstrap/', TemplateView.as_view(template_name="bootstrap.html")),
|
path('bootstrap/', TemplateView.as_view(template_name="bootstrap.html")),
|
||||||
]
|
] + urlpatterns
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from django.views.decorators.clickjacking import xframe_options_exempt
|
|||||||
|
|
||||||
from RIGS import models
|
from RIGS import models
|
||||||
from assets import models as asset_models
|
from assets import models as asset_models
|
||||||
from training import models as training_models
|
|
||||||
|
|
||||||
|
|
||||||
def is_ajax(request):
|
def is_ajax(request):
|
||||||
@@ -39,8 +38,7 @@ class SecureAPIRequest(generic.View):
|
|||||||
'organisation': models.Organisation,
|
'organisation': models.Organisation,
|
||||||
'profile': models.Profile,
|
'profile': models.Profile,
|
||||||
'event': models.Event,
|
'event': models.Event,
|
||||||
'supplier': asset_models.Supplier,
|
'supplier': asset_models.Supplier
|
||||||
'training_item': training_models.TrainingItem,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
perms = {
|
perms = {
|
||||||
@@ -49,8 +47,7 @@ class SecureAPIRequest(generic.View):
|
|||||||
'organisation': 'RIGS.view_organisation',
|
'organisation': 'RIGS.view_organisation',
|
||||||
'profile': 'RIGS.view_profile',
|
'profile': 'RIGS.view_profile',
|
||||||
'event': None,
|
'event': None,
|
||||||
'supplier': None,
|
'supplier': None
|
||||||
'training_item': None, # TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -78,9 +75,6 @@ class SecureAPIRequest(generic.View):
|
|||||||
fields = request.GET.get('fields', None)
|
fields = request.GET.get('fields', None)
|
||||||
if fields:
|
if fields:
|
||||||
fields = fields.split(",")
|
fields = fields.split(",")
|
||||||
filters = request.GET.get('filters', [])
|
|
||||||
if filters:
|
|
||||||
filters = filters.split(",")
|
|
||||||
|
|
||||||
# Supply data for one record
|
# Supply data for one record
|
||||||
if pk:
|
if pk:
|
||||||
@@ -101,13 +95,8 @@ class SecureAPIRequest(generic.View):
|
|||||||
for field in fields:
|
for field in fields:
|
||||||
q = Q(**{field + "__icontains": part})
|
q = Q(**{field + "__icontains": part})
|
||||||
qs.append(q)
|
qs.append(q)
|
||||||
|
|
||||||
queries.append(reduce(operator.or_, qs))
|
queries.append(reduce(operator.or_, qs))
|
||||||
|
|
||||||
for f in filters:
|
|
||||||
q = Q(**{f: True})
|
|
||||||
queries.append(q)
|
|
||||||
|
|
||||||
# Build the data response list
|
# Build the data response list
|
||||||
results = []
|
results = []
|
||||||
query = reduce(operator.and_, queries)
|
query = reduce(operator.and_, queries)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
default_app_config = 'RIGS.apps.RIGSAppConfig'
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from reversion.admin import VersionAdmin
|
|||||||
from RIGS import models
|
from RIGS import models
|
||||||
from users import forms as user_forms
|
from users import forms as user_forms
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
admin.site.register(models.VatRate, VersionAdmin)
|
admin.site.register(models.VatRate, VersionAdmin)
|
||||||
admin.site.register(models.Event, VersionAdmin)
|
admin.site.register(models.Event, VersionAdmin)
|
||||||
admin.site.register(models.EventItem, VersionAdmin)
|
admin.site.register(models.EventItem, VersionAdmin)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class InvoiceIndex(generic.ListView):
|
|||||||
template_name = 'invoice_list.html'
|
template_name = 'invoice_list.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(InvoiceIndex, self).get_context_data(**kwargs)
|
||||||
total = 0
|
total = 0
|
||||||
for i in context['object_list']:
|
for i in context['object_list']:
|
||||||
total += i.balance
|
total += i.balance
|
||||||
@@ -33,7 +33,20 @@ class InvoiceIndex(generic.ListView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.model.objects.outstanding_invoices()
|
# Manual query is the only way I have found to do this efficiently. Not ideal but needs must
|
||||||
|
sql = "SELECT * FROM " \
|
||||||
|
"(SELECT " \
|
||||||
|
"(SELECT COUNT(p.amount) FROM \"RIGS_payment\" AS p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payment_count\", " \
|
||||||
|
"(SELECT SUM(ei.cost * ei.quantity) FROM \"RIGS_eventitem\" AS ei WHERE ei.event_id=\"RIGS_invoice\".event_id) AS \"cost\", " \
|
||||||
|
"(SELECT SUM(p.amount) FROM \"RIGS_payment\" AS p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payments\", " \
|
||||||
|
"\"RIGS_invoice\".\"id\", \"RIGS_invoice\".\"event_id\", \"RIGS_invoice\".\"invoice_date\", \"RIGS_invoice\".\"void\" FROM \"RIGS_invoice\") " \
|
||||||
|
"AS sub " \
|
||||||
|
"WHERE (((cost > 0.0) AND (payment_count=0)) OR (cost - payments) <> 0.0) AND void = '0'" \
|
||||||
|
"ORDER BY invoice_date"
|
||||||
|
|
||||||
|
query = self.model.objects.raw(sql)
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
class InvoiceDetail(generic.DetailView):
|
class InvoiceDetail(generic.DetailView):
|
||||||
@@ -41,15 +54,8 @@ class InvoiceDetail(generic.DetailView):
|
|||||||
template_name = 'invoice_detail.html'
|
template_name = 'invoice_detail.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(InvoiceDetail, self).get_context_data(**kwargs)
|
||||||
invoice_date = self.object.invoice_date.strftime("%d/%m/%Y")
|
context['page_title'] = "Invoice {} ({})".format(self.object.display_id, self.object.invoice_date.strftime("%d/%m/%Y"))
|
||||||
context['page_title'] = f"Invoice {self.object.display_id} ({invoice_date}) "
|
|
||||||
if self.object.void:
|
|
||||||
context['page_title'] += "<span class='badge badge-warning float-right'>VOID</span>"
|
|
||||||
elif self.object.is_closed:
|
|
||||||
context['page_title'] += "<span class='badge badge-success float-right'>PAID</span>"
|
|
||||||
else:
|
|
||||||
context['page_title'] += "<span class='badge badge-info float-right'>OUTSTANDING</span>"
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@@ -118,7 +124,7 @@ class InvoiceArchive(generic.ListView):
|
|||||||
paginate_by = 25
|
paginate_by = 25
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(InvoiceArchive, self).get_context_data(**kwargs)
|
||||||
context['page_title'] = "Invoice Archive"
|
context['page_title'] = "Invoice Archive"
|
||||||
context['description'] = "This page displays all invoices: outstanding, paid, and void"
|
context['description'] = "This page displays all invoices: outstanding, paid, and void"
|
||||||
return context
|
return context
|
||||||
@@ -167,7 +173,24 @@ class InvoiceWaiting(generic.ListView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.model.objects.waiting_invoices()
|
return self.get_objects()
|
||||||
|
|
||||||
|
def get_objects(self):
|
||||||
|
# TODO find a way to select items
|
||||||
|
events = self.model.objects.filter(
|
||||||
|
(
|
||||||
|
Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end
|
||||||
|
Q(end_date__lte=datetime.date.today()) # Has end date, finishes before
|
||||||
|
) & Q(invoice__isnull=True) & # Has not already been invoiced
|
||||||
|
Q(is_rig=True) # Is a rig (not non-rig)
|
||||||
|
|
||||||
|
).order_by('start_date') \
|
||||||
|
.select_related('person',
|
||||||
|
'organisation',
|
||||||
|
'venue', 'mic') \
|
||||||
|
.prefetch_related('items')
|
||||||
|
|
||||||
|
return events
|
||||||
|
|
||||||
|
|
||||||
class InvoiceEvent(generic.View):
|
class InvoiceEvent(generic.View):
|
||||||
@@ -197,7 +220,7 @@ class PaymentCreate(generic.CreateView):
|
|||||||
template_name = 'payment_form.html'
|
template_name = 'payment_form.html'
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
initial = super().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('invoice', self.request.POST.get('invoice', None))
|
||||||
if invoicepk is None:
|
if invoicepk is None:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from django.utils import timezone
|
|||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
from RIGS import models
|
from RIGS import models
|
||||||
from training.models import TrainingLevel
|
|
||||||
|
|
||||||
# Override the django form defaults to use the HTML date/time/datetime UI elements
|
# Override the django form defaults to use the HTML date/time/datetime UI elements
|
||||||
forms.DateField.widget = forms.DateInput(attrs={'type': 'date'})
|
forms.DateField.widget = forms.DateInput(attrs={'type': 'date'})
|
||||||
@@ -97,10 +96,10 @@ class EventForm(forms.ModelForm):
|
|||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
'You haven\'t provided any client contact details. Please add a person or organisation.',
|
'You haven\'t provided any client contact details. Please add a person or organisation.',
|
||||||
code='contact')
|
code='contact')
|
||||||
return super().clean()
|
return super(EventForm, self).clean()
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
m = super().save(commit=False)
|
m = super(EventForm, self).save(commit=False)
|
||||||
|
|
||||||
if (commit):
|
if (commit):
|
||||||
m.save()
|
m.save()
|
||||||
@@ -139,7 +138,7 @@ class BaseClientEventAuthorisationForm(forms.ModelForm):
|
|||||||
|
|
||||||
class InternalClientEventAuthorisationForm(BaseClientEventAuthorisationForm):
|
class InternalClientEventAuthorisationForm(BaseClientEventAuthorisationForm):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super(InternalClientEventAuthorisationForm, self).__init__(**kwargs)
|
||||||
self.fields['uni_id'].required = True
|
self.fields['uni_id'].required = True
|
||||||
self.fields['account_code'].required = True
|
self.fields['account_code'].required = True
|
||||||
|
|
||||||
@@ -154,7 +153,7 @@ class EventAuthorisationRequestForm(forms.Form):
|
|||||||
|
|
||||||
class EventRiskAssessmentForm(forms.ModelForm):
|
class EventRiskAssessmentForm(forms.ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(EventRiskAssessmentForm, self).__init__(*args, **kwargs)
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
if str(name) == 'supervisor_consulted':
|
if str(name) == 'supervisor_consulted':
|
||||||
field.widget = forms.CheckboxInput()
|
field.widget = forms.CheckboxInput()
|
||||||
@@ -165,9 +164,6 @@ class EventRiskAssessmentForm(forms.ModelForm):
|
|||||||
], attrs={'class': 'custom-control-input', 'required': 'true'})
|
], attrs={'class': 'custom-control-input', 'required': 'true'})
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.cleaned_data.get('big_power'):
|
|
||||||
if not self.cleaned_data.get('power_mic').level_qualifications.filter(level__department=TrainingLevel.POWER).exists():
|
|
||||||
self.add_error('power_mic', forms.ValidationError("Your Power MIC must be a Power Technician.", code="power_tech_required"))
|
|
||||||
# Check expected values
|
# Check expected values
|
||||||
unexpected_values = []
|
unexpected_values = []
|
||||||
for field, value in models.RiskAssessment.expected_values.items():
|
for field, value in models.RiskAssessment.expected_values.items():
|
||||||
@@ -185,7 +181,7 @@ class EventRiskAssessmentForm(forms.ModelForm):
|
|||||||
|
|
||||||
class EventChecklistForm(forms.ModelForm):
|
class EventChecklistForm(forms.ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(EventChecklistForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['date'].widget.format = '%Y-%m-%d'
|
self.fields['date'].widget.format = '%Y-%m-%d'
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
if field.__class__ == forms.NullBooleanField:
|
if field.__class__ == forms.NullBooleanField:
|
||||||
|
|||||||
14
RIGS/hs.py
@@ -70,11 +70,6 @@ class EventRiskAssessmentDetail(generic.DetailView):
|
|||||||
model = models.RiskAssessment
|
model = models.RiskAssessment
|
||||||
template_name = 'risk_assessment_detail.html'
|
template_name = 'risk_assessment_detail.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(EventRiskAssessmentDetail, self).get_context_data(**kwargs)
|
|
||||||
context['page_title'] = "Risk Assessment for Event <a href='{}'>{} {}</a>".format(self.object.event.get_absolute_url(), self.object.event.display_id, self.object.event.name)
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class EventRiskAssessmentList(generic.ListView):
|
class EventRiskAssessmentList(generic.ListView):
|
||||||
paginate_by = 20
|
paginate_by = 20
|
||||||
@@ -82,7 +77,7 @@ class EventRiskAssessmentList(generic.ListView):
|
|||||||
template_name = 'hs_object_list.html'
|
template_name = 'hs_object_list.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.model.objects.exclude(event__status=models.Event.CANCELLED).order_by('reviewed_at').select_related('event')
|
return self.model.objects.order_by('reviewed_at').select_related('event')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(EventRiskAssessmentList, self).get_context_data(**kwargs)
|
context = super(EventRiskAssessmentList, self).get_context_data(**kwargs)
|
||||||
@@ -112,7 +107,7 @@ class EventChecklistDetail(generic.DetailView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(EventChecklistDetail, self).get_context_data(**kwargs)
|
context = super(EventChecklistDetail, self).get_context_data(**kwargs)
|
||||||
context['page_title'] = "Event Checklist for Event <a href='{}'>{} {}</a>".format(self.object.event.get_absolute_url(), self.object.event.display_id, self.object.event.name)
|
context['page_title'] = "Event Checklist for Event {} {}".format(self.object.event.display_id, self.object.event.name)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@@ -187,9 +182,6 @@ class EventChecklistList(generic.ListView):
|
|||||||
model = models.EventChecklist
|
model = models.EventChecklist
|
||||||
template_name = 'hs_object_list.html'
|
template_name = 'hs_object_list.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return self.model.objects.exclude(event__status=models.Event.CANCELLED).order_by('reviewed_at').select_related('event')
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(EventChecklistList, self).get_context_data(**kwargs)
|
context = super(EventChecklistList, self).get_context_data(**kwargs)
|
||||||
context['title'] = 'Event Checklist'
|
context['title'] = 'Event Checklist'
|
||||||
@@ -218,7 +210,7 @@ class HSList(generic.ListView):
|
|||||||
template_name = 'hs_list.html'
|
template_name = 'hs_list.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return models.Event.objects.all().exclude(status=models.Event.CANCELLED).order_by('-start_date').select_related('riskassessment').prefetch_related('checklists')
|
return models.Event.objects.all().order_by('-start_date').select_related('riskassessment').prefetch_related('checklists')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(HSList, self).get_context_data(**kwargs)
|
context = super(HSList, self).get_context_data(**kwargs)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from django.core.management.base import BaseCommand, CommandError
|
|||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from assets import models
|
from assets import models
|
||||||
from RIGS import models as rigsmodels
|
from RIGS import models as rigsmodels
|
||||||
from training import models as tmodels
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@@ -32,11 +31,6 @@ class Command(BaseCommand):
|
|||||||
self.delete_objects(rigsmodels.Payment)
|
self.delete_objects(rigsmodels.Payment)
|
||||||
self.delete_objects(rigsmodels.RiskAssessment)
|
self.delete_objects(rigsmodels.RiskAssessment)
|
||||||
self.delete_objects(rigsmodels.EventChecklist)
|
self.delete_objects(rigsmodels.EventChecklist)
|
||||||
self.delete_objects(tmodels.TrainingCategory)
|
|
||||||
self.delete_objects(tmodels.TrainingItem)
|
|
||||||
self.delete_objects(tmodels.TrainingLevel)
|
|
||||||
self.delete_objects(tmodels.TrainingItemQualification)
|
|
||||||
self.delete_objects(tmodels.TrainingLevelRequirement)
|
|
||||||
|
|
||||||
def delete_objects(self, model):
|
def delete_objects(self, model):
|
||||||
for obj in model.objects.all():
|
for obj in model.objects.all():
|
||||||
|
|||||||
@@ -12,4 +12,3 @@ class Command(BaseCommand):
|
|||||||
call_command('generateSampleUserData')
|
call_command('generateSampleUserData')
|
||||||
call_command('generateSampleRIGSData')
|
call_command('generateSampleRIGSData')
|
||||||
call_command('generateSampleAssetsData')
|
call_command('generateSampleAssetsData')
|
||||||
call_command('generateSampleTrainingData')
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class Command(BaseCommand):
|
|||||||
profiles = models.Profile.objects.all()
|
profiles = models.Profile.objects.all()
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
print("Generating rigboard data")
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
if not (settings.DEBUG or settings.STAGING):
|
if not (settings.DEBUG or settings.STAGING):
|
||||||
@@ -36,7 +35,6 @@ class Command(BaseCommand):
|
|||||||
self.setup_organisations()
|
self.setup_organisations()
|
||||||
self.setup_venues()
|
self.setup_venues()
|
||||||
self.setup_events()
|
self.setup_events()
|
||||||
print("Done generating rigboard data")
|
|
||||||
|
|
||||||
def setup_people(self):
|
def setup_people(self):
|
||||||
names = ["Regulus Black", "Sirius Black", "Lavender Brown", "Cho Chang", "Vincent Crabbe", "Vincent Crabbe",
|
names = ["Regulus Black", "Sirius Black", "Lavender Brown", "Cho Chang", "Vincent Crabbe", "Vincent Crabbe",
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import RIGS.models
|
import RIGS.models
|
||||||
import versioning
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -26,6 +25,6 @@ class Migration(migrations.Migration):
|
|||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
},
|
},
|
||||||
bases=(models.Model, versioning.versioning.RevisionMixin),
|
bases=(models.Model, RIGS.models.RevisionMixin),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
import RIGS.models
|
import RIGS.models
|
||||||
import versioning
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -22,6 +21,6 @@ class Migration(migrations.Migration):
|
|||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
},
|
},
|
||||||
bases=(models.Model, versioning.versioning.RevisionMixin),
|
bases=(models.Model, RIGS.models.RevisionMixin),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
from django.db import models, migrations
|
from django.db import models, migrations
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import RIGS.models
|
import RIGS.models
|
||||||
import versioning
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -42,7 +41,7 @@ class Migration(migrations.Migration):
|
|||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
},
|
},
|
||||||
bases=(models.Model, versioning.versioning.RevisionMixin),
|
bases=(models.Model, RIGS.models.RevisionMixin),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='EventItem',
|
name='EventItem',
|
||||||
@@ -71,7 +70,7 @@ class Migration(migrations.Migration):
|
|||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
},
|
},
|
||||||
bases=(models.Model, versioning.versioning.RevisionMixin),
|
bases=(models.Model, RIGS.models.RevisionMixin),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import RIGS.models
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import versioning
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -59,7 +58,7 @@ class Migration(migrations.Migration):
|
|||||||
'ordering': ['event'],
|
'ordering': ['event'],
|
||||||
'permissions': [('review_eventchecklist', 'Can review Event Checklists')],
|
'permissions': [('review_eventchecklist', 'Can review Event Checklists')],
|
||||||
},
|
},
|
||||||
bases=(models.Model, versioning.versioning.RevisionMixin),
|
bases=(models.Model, RIGS.models.RevisionMixin),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='EventChecklistCrew',
|
name='EventChecklistCrew',
|
||||||
@@ -70,7 +69,7 @@ class Migration(migrations.Migration):
|
|||||||
('end', models.DateTimeField()),
|
('end', models.DateTimeField()),
|
||||||
('checklist', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='crew', to='RIGS.eventchecklist')),
|
('checklist', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='crew', to='RIGS.eventchecklist')),
|
||||||
],
|
],
|
||||||
bases=(models.Model, versioning.versioning.RevisionMixin),
|
bases=(models.Model, RIGS.models.RevisionMixin),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='EventChecklistVehicle',
|
name='EventChecklistVehicle',
|
||||||
@@ -79,7 +78,7 @@ class Migration(migrations.Migration):
|
|||||||
('vehicle', models.CharField(max_length=255)),
|
('vehicle', models.CharField(max_length=255)),
|
||||||
('checklist', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='vehicles', to='RIGS.eventchecklist')),
|
('checklist', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='vehicles', to='RIGS.eventchecklist')),
|
||||||
],
|
],
|
||||||
bases=(models.Model, versioning.versioning.RevisionMixin),
|
bases=(models.Model, RIGS.models.RevisionMixin),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='RiskAssessment',
|
name='RiskAssessment',
|
||||||
@@ -118,7 +117,7 @@ class Migration(migrations.Migration):
|
|||||||
'ordering': ['event'],
|
'ordering': ['event'],
|
||||||
'permissions': [('review_riskassessment', 'Can review Risk Assessments')],
|
'permissions': [('review_riskassessment', 'Can review Risk Assessments')],
|
||||||
},
|
},
|
||||||
bases=(models.Model, versioning.versioning.RevisionMixin),
|
bases=(models.Model, RIGS.models.RevisionMixin),
|
||||||
),
|
),
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
model_name='eventcrew',
|
model_name='eventcrew',
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
# Generated by Django 3.1.7 on 2021-03-02 11:48
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
def postgres_migration_prep(apps, schema_editor):
|
|
||||||
model = apps.get_model("RIGS", "Event")
|
|
||||||
for field in ["auth_request_to", "collector", "description", "notes", "purchase_order"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
model = apps.get_model("RIGS", "EventAuthorisation")
|
|
||||||
for field in ["account_code", "uni_id"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
model = apps.get_model("RIGS", "EventChecklist")
|
|
||||||
for field in ["extinguishers_location", "hs_location", "w1_description", "w2_description", "w3_description"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
model = apps.get_model("RIGS", "EventItem")
|
|
||||||
for field in ["description"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
model = apps.get_model("RIGS", "Organisation")
|
|
||||||
for field in ["address", "email", "notes", "phone"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
model = apps.get_model("RIGS", "Payment")
|
|
||||||
for field in ["method"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
model = apps.get_model("RIGS", "Person")
|
|
||||||
for field in ["address", "email", "notes", "phone"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
model = apps.get_model("RIGS", "Profile")
|
|
||||||
for field in ["phone"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
model = apps.get_model("RIGS", "RiskAssessment")
|
|
||||||
for field in ["general_notes", "persons_responsible_structures", "power_notes", "rigging_plan", "sound_notes"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
model = apps.get_model("RIGS", "Venue")
|
|
||||||
for field in ["address", "email", "notes", "phone"]:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('RIGS', '0039_auto_20210123_1910'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(postgres_migration_prep, migrations.RunPython.noop)
|
|
||||||
]
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 3.2.11 on 2022-01-09 14:56
|
# Generated by Django 3.1.5 on 2021-02-06 10:43
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
@@ -6,13 +6,13 @@ from django.db import migrations, models
|
|||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('RIGS', '0043_auto_20211027_1519'),
|
('RIGS', '0039_auto_20210123_1910'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='profile',
|
model_name='profile',
|
||||||
name='is_supervisor',
|
name='dark_theme',
|
||||||
field=models.BooleanField(default=False),
|
field=models.BooleanField(default=False),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 3.1.7 on 2021-03-02 12:04
|
# Generated by Django 3.1.5 on 2021-02-08 16:03
|
||||||
|
|
||||||
import RIGS.models
|
import RIGS.models
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
@@ -7,27 +7,10 @@ from django.db import migrations, models
|
|||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('RIGS', '0040_auto_20210302_1148'),
|
('RIGS', '0040_profile_dark_theme'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='event',
|
|
||||||
name='meet_info',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='event',
|
|
||||||
name='payment_method',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='event',
|
|
||||||
name='payment_received',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='profile',
|
|
||||||
name='dark_theme',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
name='auth_request_to',
|
name='auth_request_to',
|
||||||
@@ -43,11 +26,26 @@ class Migration(migrations.Migration):
|
|||||||
name='description',
|
name='description',
|
||||||
field=models.TextField(blank=True, default=''),
|
field=models.TextField(blank=True, default=''),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='event',
|
||||||
|
name='meet_info',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=255),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
name='notes',
|
name='notes',
|
||||||
field=models.TextField(blank=True, default=''),
|
field=models.TextField(blank=True, default=''),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='event',
|
||||||
|
name='payment_method',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='event',
|
||||||
|
name='payment_received',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=255),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='event',
|
model_name='event',
|
||||||
name='purchase_order',
|
name='purchase_order',
|
||||||
@@ -146,7 +144,7 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='profile',
|
model_name='profile',
|
||||||
name='phone',
|
name='phone',
|
||||||
field=models.CharField(blank=True, default='', max_length=13),
|
field=models.CharField(default='', max_length=13, null=True),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='riskassessment',
|
model_name='riskassessment',
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Generated by Django 3.1.13 on 2021-10-07 22:38
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('RIGS', '0041_auto_20210302_1204'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='fd_earth_fault',
|
|
||||||
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w1_earth_fault',
|
|
||||||
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w2_earth_fault',
|
|
||||||
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='eventchecklist',
|
|
||||||
name='w3_earth_fault',
|
|
||||||
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 3.1.13 on 2021-10-27 14:19
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('RIGS', '0042_auto_20211007_2338'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='profile',
|
|
||||||
name='initials',
|
|
||||||
field=models.CharField(max_length=5, null=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -20,16 +20,13 @@ from reversion.models import Version
|
|||||||
|
|
||||||
|
|
||||||
class Profile(AbstractUser):
|
class Profile(AbstractUser):
|
||||||
initials = models.CharField(max_length=5, null=True, blank=False)
|
initials = models.CharField(max_length=5, unique=True, null=True, blank=False)
|
||||||
phone = models.CharField(max_length=13, blank=True, default='')
|
phone = models.CharField(max_length=13, null=True, default='')
|
||||||
api_key = models.CharField(max_length=40, blank=True, editable=False, default='')
|
api_key = models.CharField(max_length=40, blank=True, editable=False, default='')
|
||||||
is_approved = models.BooleanField(default=False)
|
is_approved = models.BooleanField(default=False)
|
||||||
# Currently only populated by the admin approval email. TODO: Populate it each time we send any email, might need that...
|
# Currently only populated by the admin approval email. TODO: Populate it each time we send any email, might need that...
|
||||||
last_emailed = models.DateTimeField(blank=True, null=True)
|
last_emailed = models.DateTimeField(blank=True, null=True)
|
||||||
dark_theme = models.BooleanField(default=False)
|
dark_theme = models.BooleanField(default=False)
|
||||||
is_supervisor = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
reversion_hide = True
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_api_key(cls):
|
def make_api_key(cls):
|
||||||
@@ -68,8 +65,10 @@ class Profile(AbstractUser):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
# TODO move to versioning - currently get import errors with that
|
||||||
|
|
||||||
class RevisionMixin:
|
|
||||||
|
class RevisionMixin(object):
|
||||||
@property
|
@property
|
||||||
def is_first_version(self):
|
def is_first_version(self):
|
||||||
versions = Version.objects.get_for_object(self)
|
versions = Version.objects.get_for_object(self)
|
||||||
@@ -99,7 +98,7 @@ class RevisionMixin:
|
|||||||
version = self.current_version
|
version = self.current_version
|
||||||
if version is None:
|
if version is None:
|
||||||
return None
|
return None
|
||||||
return f"V{version.pk} | R{version.revision.pk}"
|
return "V{0} | R{1}".format(version.pk, version.revision.pk)
|
||||||
|
|
||||||
|
|
||||||
class Person(models.Model, RevisionMixin):
|
class Person(models.Model, RevisionMixin):
|
||||||
@@ -207,7 +206,7 @@ class VatRate(models.Model, RevisionMixin):
|
|||||||
get_latest_by = 'start_at'
|
get_latest_by = 'start_at'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.comment} {self.start_at} @ {self.as_percent}%"
|
return self.comment + " " + str(self.start_at) + " @ " + str(self.as_percent) + "%"
|
||||||
|
|
||||||
|
|
||||||
class Venue(models.Model, RevisionMixin):
|
class Venue(models.Model, RevisionMixin):
|
||||||
@@ -279,19 +278,6 @@ class EventManager(models.Manager):
|
|||||||
).count()
|
).count()
|
||||||
return event_count
|
return event_count
|
||||||
|
|
||||||
def waiting_invoices(self):
|
|
||||||
events = self.filter(
|
|
||||||
(
|
|
||||||
models.Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end
|
|
||||||
models.Q(end_date__lte=datetime.date.today()) # Or has end date, finishes before
|
|
||||||
) & models.Q(invoice__isnull=True) & # Has not already been invoiced
|
|
||||||
models.Q(is_rig=True) # Is a rig (not non-rig)
|
|
||||||
).order_by('start_date') \
|
|
||||||
.select_related('person', 'organisation', 'venue', 'mic') \
|
|
||||||
.prefetch_related('items')
|
|
||||||
|
|
||||||
return events
|
|
||||||
|
|
||||||
|
|
||||||
@reversion.register(follow=['items'])
|
@reversion.register(follow=['items'])
|
||||||
class Event(models.Model, RevisionMixin):
|
class Event(models.Model, RevisionMixin):
|
||||||
@@ -326,6 +312,7 @@ class Event(models.Model, RevisionMixin):
|
|||||||
end_time = models.TimeField(blank=True, null=True)
|
end_time = models.TimeField(blank=True, null=True)
|
||||||
access_at = models.DateTimeField(blank=True, null=True)
|
access_at = models.DateTimeField(blank=True, null=True)
|
||||||
meet_at = models.DateTimeField(blank=True, null=True)
|
meet_at = models.DateTimeField(blank=True, null=True)
|
||||||
|
meet_info = models.CharField(max_length=255, blank=True, default='')
|
||||||
|
|
||||||
# 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,
|
||||||
@@ -334,6 +321,8 @@ class Event(models.Model, RevisionMixin):
|
|||||||
verbose_name="MIC", on_delete=models.CASCADE)
|
verbose_name="MIC", on_delete=models.CASCADE)
|
||||||
|
|
||||||
# Monies
|
# Monies
|
||||||
|
payment_method = models.CharField(max_length=255, blank=True, default='')
|
||||||
|
payment_received = models.CharField(max_length=255, blank=True, default='')
|
||||||
purchase_order = models.CharField(max_length=255, blank=True, default='', verbose_name='PO')
|
purchase_order = models.CharField(max_length=255, blank=True, default='', verbose_name='PO')
|
||||||
collector = models.CharField(max_length=255, blank=True, default='', verbose_name='collected by')
|
collector = models.CharField(max_length=255, blank=True, default='', verbose_name='collected by')
|
||||||
|
|
||||||
@@ -344,14 +333,11 @@ class Event(models.Model, RevisionMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def display_id(self):
|
def display_id(self):
|
||||||
if self.pk:
|
|
||||||
if self.is_rig:
|
if self.is_rig:
|
||||||
return str("N%05d" % self.pk)
|
return str("N%05d" % self.pk)
|
||||||
|
else:
|
||||||
return self.pk
|
return self.pk
|
||||||
|
|
||||||
return "????"
|
|
||||||
|
|
||||||
# Calculated values
|
# Calculated values
|
||||||
"""
|
"""
|
||||||
EX Vat
|
EX Vat
|
||||||
@@ -373,9 +359,6 @@ class Event(models.Model, RevisionMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def vat(self):
|
def vat(self):
|
||||||
# No VAT is owed on internal transfers
|
|
||||||
if self.internal:
|
|
||||||
return 0
|
|
||||||
return Decimal(self.sum_total * self.vat_rate.rate).quantize(Decimal('.01'))
|
return Decimal(self.sum_total * self.vat_rate.rate).quantize(Decimal('.01'))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -475,7 +458,7 @@ class Event(models.Model, RevisionMixin):
|
|||||||
return reverse('event_detail', kwargs={'pk': self.pk})
|
return reverse('event_detail', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.display_id}: {self.name}"
|
return "{}: {}".format(self.display_id, self.name)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
errdict = {}
|
errdict = {}
|
||||||
@@ -521,11 +504,11 @@ class EventItem(models.Model, RevisionMixin):
|
|||||||
ordering = ['order']
|
ordering = ['order']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.event_id}.{self.order}: {self.event.name} | {self.name}"
|
return "{}.{}: {} | {}".format(self.event_id, self.order, self.event.name, self.name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def activity_feed_string(self):
|
def activity_feed_string(self):
|
||||||
return f"item {self.name}"
|
return str("item {}".format(self.name))
|
||||||
|
|
||||||
|
|
||||||
@reversion.register
|
@reversion.register
|
||||||
@@ -543,24 +526,7 @@ class EventAuthorisation(models.Model, RevisionMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def activity_feed_string(self):
|
def activity_feed_string(self):
|
||||||
return f"{self.event.display_id} (requested by {self.sent_by.initials})"
|
return "{} (requested by {})".format(self.event.display_id, self.sent_by.initials)
|
||||||
|
|
||||||
|
|
||||||
class InvoiceManager(models.Manager):
|
|
||||||
def outstanding_invoices(self):
|
|
||||||
# Manual query is the only way I have found to do this efficiently. Not ideal but needs must
|
|
||||||
sql = "SELECT * FROM " \
|
|
||||||
"(SELECT " \
|
|
||||||
"(SELECT COUNT(p.amount) FROM \"RIGS_payment\" AS p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payment_count\", " \
|
|
||||||
"(SELECT SUM(ei.cost * ei.quantity) FROM \"RIGS_eventitem\" AS ei WHERE ei.event_id=\"RIGS_invoice\".event_id) AS \"cost\", " \
|
|
||||||
"(SELECT SUM(p.amount) FROM \"RIGS_payment\" AS p WHERE p.invoice_id=\"RIGS_invoice\".id) AS \"payments\", " \
|
|
||||||
"\"RIGS_invoice\".\"id\", \"RIGS_invoice\".\"event_id\", \"RIGS_invoice\".\"invoice_date\", \"RIGS_invoice\".\"void\" FROM \"RIGS_invoice\") " \
|
|
||||||
"AS sub " \
|
|
||||||
"WHERE (((cost > 0.0) AND (payment_count=0)) OR (cost - payments) <> 0.0) AND void = '0'" \
|
|
||||||
"ORDER BY invoice_date"
|
|
||||||
|
|
||||||
query = self.raw(sql)
|
|
||||||
return query
|
|
||||||
|
|
||||||
|
|
||||||
@reversion.register(follow=['payment_set'])
|
@reversion.register(follow=['payment_set'])
|
||||||
@@ -571,8 +537,6 @@ class Invoice(models.Model, RevisionMixin):
|
|||||||
|
|
||||||
reversion_perm = 'RIGS.view_invoice'
|
reversion_perm = 'RIGS.view_invoice'
|
||||||
|
|
||||||
objects = InvoiceManager()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sum_total(self):
|
def sum_total(self):
|
||||||
return self.event.sum_total
|
return self.event.sum_total
|
||||||
@@ -671,6 +635,7 @@ class RiskAssessment(models.Model, RevisionMixin):
|
|||||||
|
|
||||||
# Power
|
# Power
|
||||||
big_power = models.BooleanField(help_text="Does the event require larger power supplies than 13A or 16A single phase wall sockets, or draw more than 20A total current?")
|
big_power = models.BooleanField(help_text="Does the event require larger power supplies than 13A or 16A single phase wall sockets, or draw more than 20A total current?")
|
||||||
|
# If yes to the above two, you must answer...
|
||||||
power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='power_mic', blank=True, null=True,
|
power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='power_mic', blank=True, null=True,
|
||||||
verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC? (if yes to the above question, this person <em>must</em> be a Power Technician or Power Supervisor)")
|
verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC? (if yes to the above question, this person <em>must</em> be a Power Technician or Power Supervisor)")
|
||||||
outside = models.BooleanField(help_text="Is the event outdoors?")
|
outside = models.BooleanField(help_text="Is the event outdoors?")
|
||||||
@@ -742,7 +707,7 @@ class RiskAssessment(models.Model, RevisionMixin):
|
|||||||
]
|
]
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def fieldz(self):
|
def fields(self):
|
||||||
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
|
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -802,21 +767,21 @@ class EventChecklist(models.Model, RevisionMixin):
|
|||||||
fd_voltage_l2 = models.IntegerField(blank=True, null=True, verbose_name="First Distro Voltage L2-N", help_text="L2 - N")
|
fd_voltage_l2 = models.IntegerField(blank=True, null=True, verbose_name="First Distro Voltage L2-N", help_text="L2 - N")
|
||||||
fd_voltage_l3 = models.IntegerField(blank=True, null=True, verbose_name="First Distro Voltage L3-N", help_text="L3 - N")
|
fd_voltage_l3 = models.IntegerField(blank=True, null=True, verbose_name="First Distro Voltage L3-N", help_text="L3 - N")
|
||||||
fd_phase_rotation = models.BooleanField(blank=True, null=True, verbose_name="Phase Rotation", help_text="Phase Rotation<br><small>(if required)</small>")
|
fd_phase_rotation = models.BooleanField(blank=True, null=True, verbose_name="Phase Rotation", help_text="Phase Rotation<br><small>(if required)</small>")
|
||||||
fd_earth_fault = models.DecimalField(blank=True, null=True, max_digits=5, decimal_places=2, verbose_name="Earth Fault Loop Impedance", help_text="Earth Fault Loop Impedance (Z<small>S</small>)")
|
fd_earth_fault = models.IntegerField(blank=True, null=True, verbose_name="Earth Fault Loop Impedance", help_text="Earth Fault Loop Impedance (Z<small>S</small>)")
|
||||||
fd_pssc = models.IntegerField(blank=True, null=True, verbose_name="PSCC", help_text="Prospective Short Circuit Current")
|
fd_pssc = models.IntegerField(blank=True, null=True, verbose_name="PSCC", help_text="Prospective Short Circuit Current")
|
||||||
# Worst case points
|
# Worst case points
|
||||||
w1_description = models.CharField(blank=True, default='', max_length=255, help_text="Description")
|
w1_description = models.CharField(blank=True, default='', max_length=255, help_text="Description")
|
||||||
w1_polarity = models.BooleanField(blank=True, null=True, help_text="Polarity Checked?")
|
w1_polarity = models.BooleanField(blank=True, null=True, help_text="Polarity Checked?")
|
||||||
w1_voltage = models.IntegerField(blank=True, null=True, help_text="Voltage")
|
w1_voltage = models.IntegerField(blank=True, null=True, help_text="Voltage")
|
||||||
w1_earth_fault = models.DecimalField(blank=True, null=True, max_digits=5, decimal_places=2, verbose_name="Earth Fault Loop Impedance", help_text="Earth Fault Loop Impedance (Z<small>S</small>)")
|
w1_earth_fault = models.IntegerField(blank=True, null=True, help_text="Earth Fault Loop Impedance (Z<small>S</small>)")
|
||||||
w2_description = models.CharField(blank=True, default='', max_length=255, help_text="Description")
|
w2_description = models.CharField(blank=True, default='', max_length=255, help_text="Description")
|
||||||
w2_polarity = models.BooleanField(blank=True, null=True, help_text="Polarity Checked?")
|
w2_polarity = models.BooleanField(blank=True, null=True, help_text="Polarity Checked?")
|
||||||
w2_voltage = models.IntegerField(blank=True, null=True, help_text="Voltage")
|
w2_voltage = models.IntegerField(blank=True, null=True, help_text="Voltage")
|
||||||
w2_earth_fault = models.DecimalField(blank=True, null=True, max_digits=5, decimal_places=2, verbose_name="Earth Fault Loop Impedance", help_text="Earth Fault Loop Impedance (Z<small>S</small>)")
|
w2_earth_fault = models.IntegerField(blank=True, null=True, help_text="Earth Fault Loop Impedance (Z<small>S</small>)")
|
||||||
w3_description = models.CharField(blank=True, default='', max_length=255, help_text="Description")
|
w3_description = models.CharField(blank=True, default='', max_length=255, help_text="Description")
|
||||||
w3_polarity = models.BooleanField(blank=True, null=True, help_text="Polarity Checked?")
|
w3_polarity = models.BooleanField(blank=True, null=True, help_text="Polarity Checked?")
|
||||||
w3_voltage = models.IntegerField(blank=True, null=True, help_text="Voltage")
|
w3_voltage = models.IntegerField(blank=True, null=True, help_text="Voltage")
|
||||||
w3_earth_fault = models.DecimalField(blank=True, null=True, max_digits=5, decimal_places=2, verbose_name="Earth Fault Loop Impedance", help_text="Earth Fault Loop Impedance (Z<small>S</small>)")
|
w3_earth_fault = models.IntegerField(blank=True, null=True, help_text="Earth Fault Loop Impedance (Z<small>S</small>)")
|
||||||
|
|
||||||
all_rcds_tested = models.BooleanField(blank=True, null=True, help_text="All circuit RCDs tested?<br><small>(using test button)</small>")
|
all_rcds_tested = models.BooleanField(blank=True, null=True, help_text="All circuit RCDs tested?<br><small>(using test button)</small>")
|
||||||
public_sockets_tested = models.BooleanField(blank=True, null=True, help_text="Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>")
|
public_sockets_tested = models.BooleanField(blank=True, null=True, help_text="Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>")
|
||||||
@@ -827,16 +792,16 @@ class EventChecklist(models.Model, RevisionMixin):
|
|||||||
|
|
||||||
inverted_fields = []
|
inverted_fields = []
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def fields(self):
|
||||||
|
return [n.name for n in list(self.model._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['event']
|
ordering = ['event']
|
||||||
permissions = [
|
permissions = [
|
||||||
('review_eventchecklist', 'Can review Event Checklists')
|
('review_eventchecklist', 'Can review Event Checklists')
|
||||||
]
|
]
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def fieldz(self):
|
|
||||||
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def activity_feed_string(self):
|
def activity_feed_string(self):
|
||||||
return str(self.event)
|
return str(self.event)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import simplejson
|
|||||||
from PyPDF2 import PdfFileMerger, PdfFileReader
|
from PyPDF2 import PdfFileMerger, PdfFileReader
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.staticfiles import finders
|
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||||
from django.core import signing
|
from django.core import signing
|
||||||
from django.core.exceptions import SuspiciousOperation
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
@@ -38,7 +38,7 @@ class RigboardIndex(generic.TemplateView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
# get super context
|
# get super context
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(RigboardIndex, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
# call out method to get current events
|
# call out method to get current events
|
||||||
context['events'] = models.Event.objects.current_events().select_related('riskassessment', 'invoice').prefetch_related('checklists')
|
context['events'] = models.Event.objects.current_events().select_related('riskassessment', 'invoice').prefetch_related('checklists')
|
||||||
@@ -50,7 +50,7 @@ class WebCalendar(generic.TemplateView):
|
|||||||
template_name = 'calendar.html'
|
template_name = 'calendar.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().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
|
||||||
@@ -61,8 +61,8 @@ class EventDetail(generic.DetailView):
|
|||||||
model = models.Event
|
model = models.Event
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(EventDetail, self).get_context_data(**kwargs)
|
||||||
title = f"{self.object.display_id} | {self.object.name}"
|
title = "{} | {}".format(self.object.display_id, self.object.name)
|
||||||
if self.object.dry_hire:
|
if self.object.dry_hire:
|
||||||
title += " <span class='badge badge-secondary'>Dry Hire</span>"
|
title += " <span class='badge badge-secondary'>Dry Hire</span>"
|
||||||
context['page_title'] = title
|
context['page_title'] = title
|
||||||
@@ -84,7 +84,7 @@ class EventCreate(generic.CreateView):
|
|||||||
template_name = 'event_form.html'
|
template_name = 'event_form.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(EventCreate, self).get_context_data(**kwargs)
|
||||||
context['page_title'] = "New Event"
|
context['page_title'] = "New Event"
|
||||||
context['edit'] = True
|
context['edit'] = True
|
||||||
context['currentVAT'] = models.VatRate.objects.current_rate()
|
context['currentVAT'] = models.VatRate.objects.current_rate()
|
||||||
@@ -110,8 +110,8 @@ class EventUpdate(generic.UpdateView):
|
|||||||
template_name = 'event_form.html'
|
template_name = 'event_form.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(EventUpdate, self).get_context_data(**kwargs)
|
||||||
context['page_title'] = f"Event {self.object.display_id}"
|
context['page_title'] = "Event {}".format(self.object.display_id)
|
||||||
context['edit'] = True
|
context['edit'] = True
|
||||||
|
|
||||||
form = context['form']
|
form = context['form']
|
||||||
@@ -134,7 +134,7 @@ class EventUpdate(generic.UpdateView):
|
|||||||
if hasattr(self.object, 'authorised'):
|
if hasattr(self.object, 'authorised'):
|
||||||
messages.warning(self.request,
|
messages.warning(self.request,
|
||||||
'This event has already been authorised by the client, any changes to the price will require reauthorisation.')
|
'This event has already been authorised by the client, any changes to the price will require reauthorisation.')
|
||||||
return super().render_to_response(context, **response_kwargs)
|
return super(EventUpdate, self).render_to_response(context, **response_kwargs)
|
||||||
|
|
||||||
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})
|
||||||
@@ -142,7 +142,7 @@ class EventUpdate(generic.UpdateView):
|
|||||||
|
|
||||||
class EventDuplicate(EventUpdate):
|
class EventDuplicate(EventUpdate):
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
old = super().get_object(queryset) # Get the object (the event you're duplicating)
|
old = super(EventDuplicate, self).get_object(queryset) # Get the object (the event you're duplicating)
|
||||||
new = copy.copy(old) # Make a copy of the object in memory
|
new = copy.copy(old) # Make a copy of the object in memory
|
||||||
new.based_on = old # Make the new event based on the old event
|
new.based_on = old # Make the new event based on the old event
|
||||||
new.purchase_order = None # Remove old PO
|
new.purchase_order = None # Remove old PO
|
||||||
@@ -151,7 +151,6 @@ class EventDuplicate(EventUpdate):
|
|||||||
# Clear checked in by if it's a dry hire
|
# Clear checked in by if it's a dry hire
|
||||||
if new.dry_hire is True:
|
if new.dry_hire is True:
|
||||||
new.checked_in_by = None
|
new.checked_in_by = None
|
||||||
new.collector = None
|
|
||||||
|
|
||||||
# Remove all the authorisation information from the new event
|
# Remove all the authorisation information from the new event
|
||||||
new.auth_request_to = ''
|
new.auth_request_to = ''
|
||||||
@@ -167,8 +166,8 @@ class EventDuplicate(EventUpdate):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(EventDuplicate, self).get_context_data(**kwargs)
|
||||||
context['page_title'] = f"Duplicate of Event {self.object.display_id}"
|
context['page_title'] = "Duplicate of Event {}".format(self.object.display_id)
|
||||||
context["duplicate"] = True
|
context["duplicate"] = True
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@@ -210,7 +209,8 @@ class EventArchive(generic.ListView):
|
|||||||
paginate_by = 25
|
paginate_by = 25
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
# get super context
|
||||||
|
context = super(EventArchive, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
context['start'] = self.request.GET.get('start', None)
|
context['start'] = self.request.GET.get('start', None)
|
||||||
context['end'] = self.request.GET.get('end', datetime.date.today().strftime('%Y-%m-%d'))
|
context['end'] = self.request.GET.get('end', datetime.date.today().strftime('%Y-%m-%d'))
|
||||||
@@ -265,7 +265,7 @@ class EventArchive(generic.ListView):
|
|||||||
# Preselect related for efficiency
|
# Preselect related for efficiency
|
||||||
qs.select_related('person', 'organisation', 'venue', 'mic')
|
qs.select_related('person', 'organisation', 'venue', 'mic')
|
||||||
|
|
||||||
if not qs.exists():
|
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
|
||||||
@@ -274,7 +274,6 @@ class EventArchive(generic.ListView):
|
|||||||
class EventAuthorise(generic.UpdateView):
|
class EventAuthorise(generic.UpdateView):
|
||||||
template_name = 'eventauthorisation_form.html'
|
template_name = 'eventauthorisation_form.html'
|
||||||
success_template = 'eventauthorisation_success.html'
|
success_template = 'eventauthorisation_success.html'
|
||||||
preview = False
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
self.object = form.save()
|
self.object = form.save()
|
||||||
@@ -282,7 +281,7 @@ class EventAuthorise(generic.UpdateView):
|
|||||||
self.template_name = self.success_template
|
self.template_name = self.success_template
|
||||||
messages.add_message(self.request, messages.SUCCESS,
|
messages.add_message(self.request, messages.SUCCESS,
|
||||||
'Success! Your event has been authorised. ' +
|
'Success! Your event has been authorised. ' +
|
||||||
f'You will also receive email confirmation to {self.object.email}.')
|
'You will also receive email confirmation to %s.' % self.object.email)
|
||||||
return self.render_to_response(self.get_context_data())
|
return self.render_to_response(self.get_context_data())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -296,13 +295,12 @@ class EventAuthorise(generic.UpdateView):
|
|||||||
return forms.InternalClientEventAuthorisationForm
|
return forms.InternalClientEventAuthorisationForm
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(EventAuthorise, self).get_context_data(**kwargs)
|
||||||
context['event'] = self.event
|
context['event'] = self.event
|
||||||
context['tos_url'] = settings.TERMS_OF_HIRE_URL
|
context['tos_url'] = settings.TERMS_OF_HIRE_URL
|
||||||
context['page_title'] = f"{self.event.display_id}: {self.event.name}"
|
context['page_title'] = "{}: {}".format(self.event.display_id, self.event.name)
|
||||||
if self.event.dry_hire:
|
if self.event.dry_hire:
|
||||||
context['page_title'] += ' <span class="badge badge-secondary align-top">Dry Hire</span>'
|
context['page_title'] += ' <span class="badge badge-secondary align-top">Dry Hire</span>'
|
||||||
context['preview'] = self.preview
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
@@ -318,7 +316,7 @@ class EventAuthorise(generic.UpdateView):
|
|||||||
return super(EventAuthorise, self).get(request, *args, **kwargs)
|
return super(EventAuthorise, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_form(self, **kwargs):
|
def get_form(self, **kwargs):
|
||||||
form = super().get_form(**kwargs)
|
form = super(EventAuthorise, self).get_form(**kwargs)
|
||||||
form.instance.event = self.event
|
form.instance.event = self.event
|
||||||
form.instance.email = self.request.email
|
form.instance.email = self.request.email
|
||||||
form.instance.sent_by = self.request.sent_by
|
form.instance.sent_by = self.request.sent_by
|
||||||
@@ -334,7 +332,7 @@ class EventAuthorise(generic.UpdateView):
|
|||||||
except (signing.BadSignature, AssertionError, KeyError, models.Profile.DoesNotExist):
|
except (signing.BadSignature, AssertionError, KeyError, models.Profile.DoesNotExist):
|
||||||
raise SuspiciousOperation(
|
raise SuspiciousOperation(
|
||||||
"This URL is invalid. Please ask your TEC contact for a new URL")
|
"This URL is invalid. Please ask your TEC contact for a new URL")
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super(EventAuthorise, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EventAuthorisationRequest(generic.FormView, generic.detail.SingleObjectMixin):
|
class EventAuthorisationRequest(generic.FormView, generic.detail.SingleObjectMixin):
|
||||||
@@ -344,7 +342,7 @@ class EventAuthorisationRequest(generic.FormView, generic.detail.SingleObjectMix
|
|||||||
|
|
||||||
@method_decorator(decorators.nottinghamtec_address_required)
|
@method_decorator(decorators.nottinghamtec_address_required)
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
return super().dispatch(*args, **kwargs)
|
return super(EventAuthorisationRequest, self).dispatch(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def object(self):
|
def object(self):
|
||||||
@@ -389,7 +387,7 @@ class EventAuthorisationRequest(generic.FormView, generic.detail.SingleObjectMix
|
|||||||
to=[email],
|
to=[email],
|
||||||
reply_to=[self.request.user.email],
|
reply_to=[self.request.user.email],
|
||||||
)
|
)
|
||||||
css = finders.find('css/email.css')
|
css = staticfiles_storage.path('css/email.css')
|
||||||
html = premailer.Premailer(get_template("eventauthorisation_client_request.html").render(context),
|
html = premailer.Premailer(get_template("eventauthorisation_client_request.html").render(context),
|
||||||
external_styles=css).transform()
|
external_styles=css).transform()
|
||||||
msg.attach_alternative(html, 'text/html')
|
msg.attach_alternative(html, 'text/html')
|
||||||
@@ -404,19 +402,19 @@ class EventAuthoriseRequestEmailPreview(generic.DetailView):
|
|||||||
model = models.Event
|
model = models.Event
|
||||||
|
|
||||||
def render_to_response(self, context, **response_kwargs):
|
def render_to_response(self, context, **response_kwargs):
|
||||||
css = finders.find('css/email.css')
|
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||||
response = super().render_to_response(context, **response_kwargs)
|
css = staticfiles_storage.path('css/email.css')
|
||||||
|
response = super(EventAuthoriseRequestEmailPreview, self).render_to_response(context, **response_kwargs)
|
||||||
assert isinstance(response, HttpResponse)
|
assert isinstance(response, HttpResponse)
|
||||||
response.content = premailer.Premailer(response.rendered_content, external_styles=css).transform()
|
response.content = premailer.Premailer(response.rendered_content, external_styles=css).transform()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(EventAuthoriseRequestEmailPreview, self).get_context_data(**kwargs)
|
||||||
context['hmac'] = signing.dumps({
|
context['hmac'] = signing.dumps({
|
||||||
'pk': self.object.pk,
|
'pk': self.object.pk,
|
||||||
'email': self.request.GET.get('email', 'hello@world.test'),
|
'email': self.request.GET.get('email', 'hello@world.test'),
|
||||||
'sent_by': self.request.user.pk,
|
'sent_by': self.request.user.pk,
|
||||||
})
|
})
|
||||||
context['to_name'] = self.request.GET.get('to_name', None)
|
context['to_name'] = self.request.GET.get('to_name', None)
|
||||||
context['target'] = 'event_authorise_form_preview'
|
|
||||||
return context
|
return context
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from io import BytesIO
|
|||||||
|
|
||||||
from PyPDF2 import PdfFileReader, PdfFileMerger
|
from PyPDF2 import PdfFileReader, PdfFileMerger
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.staticfiles import finders
|
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.mail import EmailMessage, EmailMultiAlternatives
|
from django.core.mail import EmailMessage, EmailMultiAlternatives
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
@@ -63,7 +63,7 @@ def send_eventauthorisation_success_email(instance):
|
|||||||
reply_to=[settings.AUTHORISATION_NOTIFICATION_ADDRESS],
|
reply_to=[settings.AUTHORISATION_NOTIFICATION_ADDRESS],
|
||||||
)
|
)
|
||||||
|
|
||||||
css = finders.find('css/email.css')
|
css = staticfiles_storage.path('css/email.css')
|
||||||
html = Premailer(get_template("eventauthorisation_client_success.html").render(context),
|
html = Premailer(get_template("eventauthorisation_client_success.html").render(context),
|
||||||
external_styles=css).transform()
|
external_styles=css).transform()
|
||||||
client_email.attach_alternative(html, 'text/html')
|
client_email.attach_alternative(html, 'text/html')
|
||||||
@@ -121,7 +121,7 @@ def send_admin_awaiting_approval_email(user, request, **kwargs):
|
|||||||
to=[admin.email],
|
to=[admin.email],
|
||||||
reply_to=[user.email],
|
reply_to=[user.email],
|
||||||
)
|
)
|
||||||
css = finders.find('css/email.css')
|
css = staticfiles_storage.path('css/email.css')
|
||||||
html = Premailer(get_template("admin_awaiting_approval.html").render(context),
|
html = Premailer(get_template("admin_awaiting_approval.html").render(context),
|
||||||
external_styles=css).transform()
|
external_styles=css).transform()
|
||||||
email.attach_alternative(html, 'text/html')
|
email.attach_alternative(html, 'text/html')
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 278 KiB |
|
Before Width: | Height: | Size: 5.4 MiB |
|
Before Width: | Height: | Size: 852 KiB |
|
Before Width: | Height: | Size: 164 KiB |
6
RIGS/static/js/marked.min.js
vendored
@@ -1,12 +1,9 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load invoices_waiting from filters %}
|
|
||||||
{% load invoices_outstanding from filters %}
|
|
||||||
{% load total_invoices_todo from filters %}
|
|
||||||
|
|
||||||
{% block titleheader %}
|
{% block titleheader %}
|
||||||
<a class="navbar-brand" style="margin-left: auto; margin-right: auto;" href="/">RIGS</a>
|
<a class="navbar-brand" href="/">RIGS</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block titleelements %}
|
{% block titleelements %}
|
||||||
@@ -47,17 +44,14 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.RIGS.view_invoice %}
|
{% if perms.RIGS.view_invoice %}
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
{% total_invoices_todo as todo %}
|
|
||||||
{% invoices_waiting as waiting %}
|
|
||||||
{% invoices_outstanding as outstanding %}
|
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownInvoices" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownInvoices" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
Invoices <span class="badge {% if todo == 0 %}badge-success{% else %}badge-danger{% endif %} badge-pill">{{ todo }}</span>
|
Invoices
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu" aria-labelledby="navbarDropdownInvoices">
|
<div class="dropdown-menu" aria-labelledby="navbarDropdownInvoices">
|
||||||
{% if perms.RIGS.add_invoice %}
|
{% if perms.RIGS.add_invoice %}
|
||||||
<a class="dropdown-item text-nowrap" href="{% url 'invoice_waiting' %}"><span class="fas fa-briefcase text-danger"></span> Waiting <span class="badge {% if waiting == 0 %}badge-success{% else %}badge-danger{% endif %} badge-pill">{{ waiting }}</span></a>
|
<a class="dropdown-item" href="{% url 'invoice_waiting' %}"><span class="fas fa-briefcase text-danger"></span> Waiting</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="dropdown-item" href="{% url 'invoice_list' %}"><span class="fas fa-pound-sign text-warning"></span> Outstanding <span class="badge {% if outstanding == 0 %}badge-success{% else %}badge-danger{% endif %} badge-pill">{{ outstanding }}</span></a>
|
<a class="dropdown-item" href="{% url 'invoice_list' %}"><span class="fas fa-pound-sign text-warning"></span> Outstanding</a>
|
||||||
<a class="dropdown-item" href="{% url 'invoice_archive' %}"><span class="fas fa-book"></span> Archive</a>
|
<a class="dropdown-item" href="{% url 'invoice_archive' %}"><span class="fas fa-book"></span> Archive</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -80,7 +74,6 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ block.super }}
|
|
||||||
<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>
|
||||||
|
|||||||
@@ -5,13 +5,11 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ block.super }}
|
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/>
|
||||||
<link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block preload_js %}
|
{% block preload_js %}
|
||||||
{{ block.super }}
|
<script src="{% static 'js/bootstrap-select.js' %}"></script>
|
||||||
<script src="{% static 'js/selects.js' %}" async></script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 text-right my-3">
|
<div class="col-12 text-right my-3">
|
||||||
{% button 'edit' url='ec_edit' pk=object.pk %}
|
{% button 'edit' url='ec_edit' pk=object.pk %}
|
||||||
{% button 'view' url='event_detail' pk=object.event.pk text="Event" %}
|
{% button 'view' url='event_detail' pk=object.pk text="Event" %}
|
||||||
{% include 'partials/review_status.html' with perm=perms.RIGS.review_eventchecklist review='ec_review' %}
|
{% include 'partials/review_status.html' with perm=perms.RIGS.review_eventchecklist review='ec_review' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,11 +32,7 @@
|
|||||||
</dd>
|
</dd>
|
||||||
<dt class="col-6">{{ object|help_text:'power_mic' }}</dt>
|
<dt class="col-6">{{ object|help_text:'power_mic' }}</dt>
|
||||||
<dd class="col-6">
|
<dd class="col-6">
|
||||||
{% if object.power_mic %}
|
|
||||||
<a href="{% url 'profile_detail' object.power_mic.pk %}">{{ object.power_mic.name }}</a>
|
<a href="{% url 'profile_detail' object.power_mic.pk %}">{{ object.power_mic.name }}</a>
|
||||||
{% else %}
|
|
||||||
None
|
|
||||||
{% endif %}
|
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<p>List vehicles and their drivers</p>
|
<p>List vehicles and their drivers</p>
|
||||||
@@ -102,10 +98,6 @@
|
|||||||
<td>{{crew.role}}</td>
|
<td>{{crew.role}}</td>
|
||||||
<td>{{crew.end}}</td>
|
<td>{{crew.end}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="4" class="text-center bg-warning">Apparently this event happened by magic...</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -113,27 +105,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header">Power {% include 'partials/event_size.html' with object=object.event.riskassessment %}</div>
|
<div class="card-header">Power {% include 'partials/event_size.html' with object=object.event.riskassessment %}</div>
|
||||||
|
{% if object.event.riskassessment.event_size != 2 %}
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{% if object.event.riskassessment.event_size == 0 %}
|
{% if object.event.riskassessment.event_size == 1 %}
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-10">{{ object|help_text:'rcds'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.rcds|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'supply_test'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.supply_test|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.earthing|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
|
|
||||||
<dd class="col-2">
|
|
||||||
{{ object.pat|yesnoi }}
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
{% else %}
|
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
<dt class="col-10">{{ object|help_text:'source_rcd'|safe }}</dt>
|
<dt class="col-10">{{ object|help_text:'source_rcd'|safe }}</dt>
|
||||||
<dd class="col-2">
|
<dd class="col-2">
|
||||||
@@ -238,8 +212,28 @@
|
|||||||
</dl>
|
</dl>
|
||||||
<hr>
|
<hr>
|
||||||
{% include 'partials/ec_power_info.html' %}
|
{% include 'partials/ec_power_info.html' %}
|
||||||
|
{% else %}
|
||||||
|
<dl class="row">
|
||||||
|
<dt class="col-10">{{ object|help_text:'rcds'|safe }}</dt>
|
||||||
|
<dd class="col-2">
|
||||||
|
{{ object.rcds|yesnoi }}
|
||||||
|
</dd>
|
||||||
|
<dt class="col-10">{{ object|help_text:'supply_test'|safe }}</dt>
|
||||||
|
<dd class="col-2">
|
||||||
|
{{ object.supply_test|yesnoi }}
|
||||||
|
</dd>
|
||||||
|
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
|
||||||
|
<dd class="col-2">
|
||||||
|
{{ object.earthing|yesnoi }}
|
||||||
|
</dd>
|
||||||
|
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
|
||||||
|
<dd class="col-2">
|
||||||
|
{{ object.pat|yesnoi }}
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 text-right">
|
<div class="col-12 text-right">
|
||||||
{% button 'edit' url='ec_edit' pk=object.pk %}
|
{% button 'edit' url='ec_edit' pk=object.pk %}
|
||||||
|
|||||||
@@ -7,19 +7,25 @@
|
|||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
|
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/>
|
||||||
|
<link rel="stylesheet" href="{% static 'css/ajax-bootstrap-select.css' %}"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block preload_js %}
|
{% block preload_js %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<script src="{% static 'js/selects.js' %}"></script>
|
<script src="{% static 'js/bootstrap-select.js' %}"></script>
|
||||||
|
<script src="{% static 'js/ajax-bootstrap-select.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
<script src="{% static 'js/jquery-ui.js' %}"></script><!--TODO optimise-->
|
||||||
|
<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/tooltip.js' %}"></script>
|
||||||
|
|
||||||
|
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||||
|
|
||||||
{% include 'partials/datetime-fix.html' %}
|
{% include 'partials/datetime-fix.html' %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -128,14 +134,14 @@
|
|||||||
<tbody id="vehiclest" data-pk="-1">
|
<tbody id="vehiclest" data-pk="-1">
|
||||||
<tr id="vehicles_new" style="display: none;">
|
<tr id="vehicles_new" style="display: none;">
|
||||||
<td><input type="text" class="form-control" name="vehicle_new" disabled="true"/></td>
|
<td><input type="text" class="form-control" name="vehicle_new" disabled="true"/></td>
|
||||||
<td><select data-container="body" class="form-control" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" name="driver_new" disabled="true"></select></td>
|
<td><select class="form-control" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" name="driver_new" disabled="true"></select></td>
|
||||||
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-action='delete' data-target='#vehicle'><span class="fas fa-times"></span></button></td>
|
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-action='delete' data-target='#vehicle'><span class="fas fa-times"></span></button></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% for i in object.vehicles.all %}
|
{% for i in object.vehicles.all %}
|
||||||
<tr id="vehicles_{{i.pk}}">
|
<tr id="vehicles_{{i.pk}}">
|
||||||
<td><input name="vehicle_{{i.pk}}" type="text" class="form-control" value="{{ i.vehicle }}"/></td>
|
<td><input name="vehicle_{{i.pk}}" type="text" class="form-control" value="{{ i.vehicle }}"/></td>
|
||||||
<td>
|
<td>
|
||||||
<select data-container="body" name="driver_{{i.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
|
<select name="driver_{{i.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
|
||||||
{% if i.driver != '' %}
|
{% if i.driver != '' %}
|
||||||
<option value="{{i.driver.pk}}" selected="selected">{{ i.driver.name }}</option>
|
<option value="{{i.driver.pk}}" selected="selected">{{ i.driver.name }}</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -196,7 +202,7 @@
|
|||||||
<tbody id="crewmemberst" data-pk="-1">
|
<tbody id="crewmemberst" data-pk="-1">
|
||||||
<tr id="crew_new" style="display: none;">
|
<tr id="crew_new" style="display: none;">
|
||||||
<td>
|
<td>
|
||||||
<select name="crewmember_new" class="form-control" data-container="body" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" disabled="true"></select>
|
<select name="crewmember_new" class="form-control" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" disabled="true"></select>
|
||||||
</td>
|
</td>
|
||||||
<td style="min-width: 15ch"><input name="start_new" type="datetime-local" class="form-control" value="{{ i.start }}" disabled=""/></td>
|
<td style="min-width: 15ch"><input name="start_new" type="datetime-local" class="form-control" value="{{ i.start }}" disabled=""/></td>
|
||||||
<td style="min-width: 15ch"><input name="role_new" type="text" class="form-control" value="{{ i.role }}" disabled="true"/></td>
|
<td style="min-width: 15ch"><input name="role_new" type="text" class="form-control" value="{{ i.role }}" disabled="true"/></td>
|
||||||
@@ -206,7 +212,7 @@
|
|||||||
{% for crew in object.crew.all %}
|
{% for crew in object.crew.all %}
|
||||||
<tr id="crew_{{crew.pk}}">
|
<tr id="crew_{{crew.pk}}">
|
||||||
<td>
|
<td>
|
||||||
<select data-container="body" name="crewmember_{{crew.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
|
<select name="crewmember_{{crew.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
|
||||||
{% if crew.crewmember != '' %}
|
{% if crew.crewmember != '' %}
|
||||||
<option value="{{crew.crewmember.pk}}" selected="selected">{{ crew.crewmember.name }}</option>
|
<option value="{{crew.crewmember.pk}}" selected="selected">{{ crew.crewmember.name }}</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -244,19 +250,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% elif event.riskassessment.event_size == 1 %}
|
||||||
<div class="row my-3" id="size-1">
|
<div class="row my-3" id="size-1">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
{% if event.riskassessment.event_size == 1 %}
|
|
||||||
<div class="card border-warning">
|
<div class="card border-warning">
|
||||||
<div class="card-header">Electrical Checks <small>for ‘Medium’ TEC Events </small></div>
|
<div class="card-header">Electrical Checks <small>for ‘Medium’ TEC Events </small></div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{% else %}
|
|
||||||
<div class="card border-danger">
|
|
||||||
<div class="card-header">Electrical Checks <small>for ‘Large’ TEC Events</small></div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="alert alert-danger"><strong>Here be dragons. Ensure you have appeased the Power Gods before continuing... (If you didn't check with a Supervisor, <em>you cannot continue your event!</em>)</strong></div>
|
|
||||||
{% endif %}
|
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.source_rcd %}
|
{% include 'partials/checklist_checkbox.html' with formitem=form.source_rcd %}
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.labelling %}
|
{% include 'partials/checklist_checkbox.html' with formitem=form.labelling %}
|
||||||
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
|
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
|
||||||
@@ -346,6 +345,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="row my-3" id="size-2">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card border-danger">
|
||||||
|
<div class="card-header">Electrical Checks <small>for ‘Large’ TEC Events</small></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>Outside the scope of this assessment. <strong>I really hope you checked with a supervisor...</strong></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col-sm-12 text-right">
|
<div class="col-sm-12 text-right">
|
||||||
|
|||||||
@@ -1,20 +1,63 @@
|
|||||||
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
|
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
|
||||||
|
{% load linkornone from filters %}
|
||||||
{% load markdown_tags %}
|
{% load namewithnotes from filters %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row my-3 py-3">
|
<div class="row my-3 py-3">
|
||||||
{% if not request.is_ajax %}
|
{% if not request.is_ajax %}
|
||||||
{% if perms.RIGS.view_event %}
|
{% if perms.RIGS.view_event %}
|
||||||
<div class="col-sm-12 text-right">
|
<div class="col-sm-12 text-right">
|
||||||
{% include 'partials/event_detail_buttons.html' %}
|
{% include 'event_detail_buttons.html' %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if object.is_rig and perms.RIGS.view_event %}
|
{% if object.is_rig and perms.RIGS.view_event %}
|
||||||
{# only need contact details for a rig #}
|
{# only need contact details for a rig #}
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% include 'partials/contact_details.html' %}
|
{% if event.person %}
|
||||||
|
<div class="card card-default mb-3">
|
||||||
|
<div class="card-header">Contact Details</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<dl class="row">
|
||||||
|
<dt class="col-sm-6">Person</dt>
|
||||||
|
<dd class="col-sm-6">
|
||||||
|
{% if object.person %}
|
||||||
|
<a href="{% url 'person_detail' object.person.pk %}" class="modal-href">
|
||||||
|
{{ object.person|namewithnotes:'person_detail' }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</dd>
|
||||||
|
<dt class="col-sm-6">Email</dt>
|
||||||
|
<dd class="col-sm-6">{{ object.person.email|linkornone:'mailto' }}</dd>
|
||||||
|
<dt class="col-sm-6">Phone Number</dt>
|
||||||
|
<dd class="col-sm-6">{{ object.person.phone|linkornone:'tel' }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if event.organisation %}
|
||||||
|
<div class="card card-default">
|
||||||
|
<div class="card-header">Organisation</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<dl class="row">
|
||||||
|
<dt class="col-sm-6">Organisation</dt>
|
||||||
|
<dd class="col-sm-6">
|
||||||
|
{% if object.organisation %}
|
||||||
|
<a href="{% url 'organisation_detail' object.organisation.pk %}" class="modal-href">
|
||||||
|
{{ object.organisation|namewithnotes:'organisation_detail' }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</dd>
|
||||||
|
<dt class="col-sm-6">Email</dt>
|
||||||
|
<dd class="col-sm-6">{{ object.organisation.email|linkornone:'mailto' }}</dd>
|
||||||
|
<dt class="col-sm-6">Phone Number</dt>
|
||||||
|
<dd class="col-sm-6">{{ object.organisation.phone|linkornone:'tel' }}</dd>
|
||||||
|
<dt class="col-sm-6">Has SU Account</dt>
|
||||||
|
<dd class="col-sm-6">{{ event.organisation.union_account|yesno|capfirst }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -34,7 +77,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not request.is_ajax and perms.RIGS.view_event %}
|
{% if not request.is_ajax and perms.RIGS.view_event %}
|
||||||
<div class="col-sm-12 text-right">
|
<div class="col-sm-12 text-right">
|
||||||
{% include 'partials/event_detail_buttons.html' %}
|
{% include 'event_detail_buttons.html' %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if event.is_rig %}
|
{% if event.is_rig %}
|
||||||
@@ -45,16 +88,16 @@
|
|||||||
{% if perms.RIGS.view_event %}
|
{% if perms.RIGS.view_event %}
|
||||||
<h4>Notes</h4>
|
<h4>Notes</h4>
|
||||||
<hr>
|
<hr>
|
||||||
<p class="dont-break-out">{{ event.notes|markdown }}</p>
|
<p class="dont-break-out">{{ event.notes|linebreaksbr }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<br>
|
<br>
|
||||||
{% include 'partials/item_table.html' %}
|
{% include 'item_table.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if not request.is_ajax and perms.RIGS.view_event %}
|
{% if not request.is_ajax and perms.RIGS.view_event %}
|
||||||
<div class="col-sm-12 text-right">
|
<div class="col-sm-12 text-right">
|
||||||
{% include 'partials/event_detail_buttons.html' %}
|
{% include 'event_detail_buttons.html' %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
{% if event.internal %}
|
{% if event.internal %}
|
||||||
<a class="btn item-add modal-href event-authorise-request
|
<a class="btn item-add modal-href event-authorise-request
|
||||||
{% if event.authorised %}
|
{% if event.authorised %}
|
||||||
btn-success active
|
btn-success
|
||||||
{% elif event.authorisation and event.authorisation.amount != event.total and event.authorisation.last_edited_at > event.auth_request_at %}
|
{% elif event.authorisation and event.authorisation.amount != event.total and event.authorisation.last_edited_at > event.auth_request_at %}
|
||||||
btn-warning
|
btn-warning
|
||||||
{% elif event.auth_request_to %}
|
{% elif event.auth_request_to %}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
"
|
"
|
||||||
href="{% url 'event_authorise_request' object.pk %}">
|
href="{% url 'event_authorise_request' object.pk %}">
|
||||||
<span class="fas fa-paper-plane"></span>
|
<i class="fas fa-paper-plane"></i>
|
||||||
<span class="d-none d-sm-inline">
|
<span class="d-none d-sm-inline">
|
||||||
{% if event.authorised %}
|
{% if event.authorised %}
|
||||||
Authorised
|
Authorised
|
||||||
@@ -47,7 +47,5 @@
|
|||||||
class="fas fa-pound-sign"></span>
|
class="fas fa-pound-sign"></span>
|
||||||
<span class="d-none d-sm-inline">Invoice</span></a>
|
<span class="d-none d-sm-inline">Invoice</span></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<a href="https://docs.google.com/forms/d/e/1FAIpQLSf-TBOuJZCTYc2L8DWdAaC3_Werq0ulsUs8-6G85I6pA9WVsg/viewform" class="btn btn-danger"><span class="fas fa-file-invoice-dollar"></span> <span class="d-none d-sm-inline">Subhire Insurance Form</span></a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
{% extends 'base_embed.html' %}
|
{% extends 'base_embed.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block extra-head %}
|
{% block js %}
|
||||||
<link href="{% static 'fontawesome_free/css/fontawesome.css' %}" rel="stylesheet" type="text/css">
|
<script src="{% static 'js/all.js' %}"></script>
|
||||||
<link href="{% static 'fontawesome_free/css/solid.css' %}" rel="stylesheet" type="text/css">
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|||||||
@@ -7,48 +7,44 @@
|
|||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'css/selects.css' %}"/>
|
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/>
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'css/simplemde.min.css' %}">
|
<link rel="stylesheet" href="{% static 'css/ajax-bootstrap-select.css' %}"/>
|
||||||
|
<link rel="stylesheet" href="{% static 'css/flatpickr.css' %}"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block preload_js %}
|
{% block preload_js %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<script src="{% static 'js/selects.js' %}"></script>
|
<script src="{% static 'js/bootstrap-select.js' %}"></script>
|
||||||
<script src="{% static 'js/simplemde.min.js' %}"></script>
|
<script src="{% static 'js/ajax-bootstrap-select.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
<script src="{% static 'js/jquery-ui.js' %}"></script><!--TODO optimise--->
|
||||||
<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/tooltip.js' %}"></script>
|
||||||
|
|
||||||
|
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||||
|
|
||||||
{% include 'partials/datetime-fix.html' %}
|
{% include 'partials/datetime-fix.html' %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const matches = window.matchMedia("(prefers-reduced-motion: reduce)").matches || window.matchMedia("(update: slow)").matches;
|
const matches = window.matchMedia("(prefers-reduced-motion: reduce)").matches || window.matchMedia("(update: slow)").matches;
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
dur = matches ? 0 : 500;
|
dur = matches ? 0 : 500;
|
||||||
{% if object.pk %}
|
{% if not object.pk and not form.errors %}
|
||||||
// Editing
|
$('.form-hws').slideUp(dur, function () {
|
||||||
{% if not object.is_rig %}
|
$('.form-is_rig').slideUp(dur);
|
||||||
$('.form-is_rig').hide();
|
});
|
||||||
{% endif %}
|
{% elif not object.pk and form.errors %}
|
||||||
//Creation
|
|
||||||
{% else %}
|
|
||||||
// If there were errors, apply the previous Rig/not-Rig selection
|
|
||||||
{% if form.errors %}
|
|
||||||
$('.form-hws').show();
|
|
||||||
if ($('#{{form.is_rig.auto_id}}').attr('checked') !== 'checked') {
|
if ($('#{{form.is_rig.auto_id}}').attr('checked') !== 'checked') {
|
||||||
$('.form-is_rig').hide();
|
$('.form-is_rig').hide();
|
||||||
}
|
}
|
||||||
{% else %}
|
|
||||||
//Initial hide
|
|
||||||
$('.form-hws').slideUp(dur);
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
//Button handling
|
{% if not object.pk %}
|
||||||
$('#is_rig-selector button').on('click', function () {
|
$('#is_rig-selector button').on('click', function () {
|
||||||
$('.form-non_rig').slideDown(dur); //Non rig stuff also needed for rig, so always slide down
|
$('.form-non_rig').slideDown(dur);
|
||||||
if ($(this).data('is_rig') === 1) {
|
if ($(this).data('is_rig') === 1) {
|
||||||
$('#{{form.is_rig.auto_id}}').prop('checked', true);
|
$('#{{form.is_rig.auto_id}}').prop('checked', true);
|
||||||
if ($('.form-non_rig').is(':hidden')) {
|
if ($('.form-non_rig').is(':hidden')) {
|
||||||
@@ -58,6 +54,7 @@
|
|||||||
}
|
}
|
||||||
$('.form-hws, .form-hws .form-is_rig').css('overflow', 'visible');
|
$('.form-hws, .form-hws .form-is_rig').css('overflow', 'visible');
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$('#{{form.is_rig.auto_id}}').prop('checked', false);
|
$('#{{form.is_rig.auto_id}}').prop('checked', false);
|
||||||
$('.form-is_rig').slideUp(dur);
|
$('.form-is_rig').slideUp(dur);
|
||||||
}
|
}
|
||||||
@@ -65,26 +62,23 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
});
|
});
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
setupMDE('#id_description');
|
|
||||||
setupMDE('#id_notes');
|
|
||||||
setupMDE('#item_description');
|
|
||||||
|
|
||||||
$('#itemModal').on('shown.bs.modal', function (e) {
|
|
||||||
$('#item_description').data('mde_editor').value(
|
|
||||||
$('#item_description').val()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
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>
|
||||||
|
<noscript>
|
||||||
|
<style>
|
||||||
|
.form-hws {
|
||||||
|
display: inherit !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</noscript>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'partials/item_modal.html' %}
|
{% include 'item_modal.html' %}
|
||||||
<form class="itemised_form" role="form" method="POST">
|
<form class="itemised_form" role="form" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -180,7 +174,7 @@
|
|||||||
<label for="{{ form.description.id_for_label }}"
|
<label for="{{ form.description.id_for_label }}"
|
||||||
class="col-sm-4 col-form-label">{{ form.description.label }}</label>
|
class="col-sm-4 col-form-label">{{ form.description.label }}</label>
|
||||||
|
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-8">
|
||||||
{% render_field form.description class+="form-control" %}
|
{% render_field form.description class+="form-control" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -338,7 +332,7 @@
|
|||||||
|
|
||||||
<div class="form-group" data-toggle="tooltip" title="The purchase order number (for external clients)">
|
<div class="form-group" data-toggle="tooltip" 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 col-fitem_tableorm-label">{{ form.purchase_order.label }}</label>
|
class="col-sm-4 col-form-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" %}
|
||||||
@@ -357,10 +351,10 @@
|
|||||||
<div class="col-sm-12">
|
<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">
|
<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>
|
<label for="{{ form.notes.id_for_label }}">{{ form.notes.label }}</label>
|
||||||
{% render_field form.notes class+="form-control md-enabled" %}
|
{% render_field form.notes class+="form-control" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% include 'partials/item_table.html' %}
|
{% include 'item_table.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<!DOCTYPE document SYSTEM "rml.dtd">
|
<!DOCTYPE document SYSTEM "rml.dtd">
|
||||||
|
|
||||||
<document filename="{{filename}}">
|
<document filename="{{filename}}">
|
||||||
<docinit>
|
<docinit>
|
||||||
<registerTTFont faceName="OpenSans" fileName="static/fonts/OpenSans-Regular.tff"/>
|
<registerTTFont faceName="OpenSans" fileName="static/fonts/OpenSans-Regular.tff"/>
|
||||||
@@ -74,14 +75,6 @@
|
|||||||
<lineStyle kind="linebelow" start="3,0" stop="3,0" colorName="black"/>
|
<lineStyle kind="linebelow" start="3,0" stop="3,0" colorName="black"/>
|
||||||
<lineStyle kind="linebelow" start="5,0" stop="5,0" colorName="black"/>
|
<lineStyle kind="linebelow" start="5,0" stop="5,0" colorName="black"/>
|
||||||
</blockTableStyle>
|
</blockTableStyle>
|
||||||
|
|
||||||
<listStyle name="ol"
|
|
||||||
bulletFormat="%s."
|
|
||||||
bulletFontSize="10" />
|
|
||||||
|
|
||||||
<listStyle name="ul"
|
|
||||||
start="bulletchar"
|
|
||||||
bulletFontSize="10"/>
|
|
||||||
</stylesheet>
|
</stylesheet>
|
||||||
|
|
||||||
<template > {# Note: page is 595x842 points (1 point=1/72in) #}
|
<template > {# Note: page is 595x842 points (1 point=1/72in) #}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
{% load markdown_tags %}
|
|
||||||
{% load filters %}
|
{% load filters %}
|
||||||
|
|
||||||
<setNextFrame name="main"/>
|
<setNextFrame name="main"/>
|
||||||
<nextFrame/>
|
<nextFrame/>
|
||||||
<blockTable style="headLayout" colWidths="330,165">
|
<blockTable style="headLayout" colWidths="330,165">
|
||||||
@@ -12,8 +10,10 @@
|
|||||||
<b>{{object.start_date|date:"D jS N Y"}}</b>
|
<b>{{object.start_date|date:"D jS N Y"}}</b>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<keepInFrame maxHeight="500" onOverflow="shrink">
|
<keepInFrame>
|
||||||
{{ object.description|default_if_none:""|markdown:"rml" }}
|
<para style="style.event_description">
|
||||||
|
{{ object.description|default_if_none:""|linebreaksxml }}
|
||||||
|
</para>
|
||||||
</keepInFrame>
|
</keepInFrame>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -184,7 +184,7 @@
|
|||||||
{% if item.description %}
|
{% if item.description %}
|
||||||
</para>
|
</para>
|
||||||
<para style="item_description">
|
<para style="item_description">
|
||||||
{{ item.description|markdown:"rml" }}
|
<em>{{ item.description|linebreaksxml }}</em>
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -198,13 +198,11 @@
|
|||||||
</blockTable>
|
</blockTable>
|
||||||
<keepTogether>
|
<keepTogether>
|
||||||
<blockTable style="totalTable" colWidths="300,115,80">
|
<blockTable style="totalTable" colWidths="300,115,80">
|
||||||
{% if object.vat > 0 %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% if quote %}VAT Registration Number: 170734807</td>
|
<td>{% if quote %}VAT Registration Number: 170734807{% endif %}</td>
|
||||||
<td>Total (ex. VAT){% endif %}</td>
|
<td>Total (ex. VAT)</td>
|
||||||
<td>£ {{ object.sum_total|floatformat:2 }}</td>
|
<td>£ {{ object.sum_total|floatformat:2 }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{% if quote %}
|
{% if quote %}
|
||||||
@@ -213,10 +211,8 @@
|
|||||||
</para>
|
</para>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% if object.vat > 0 %}
|
|
||||||
<td>VAT @ {{ object.vat_rate.as_percent|floatformat:2 }}%</td>
|
<td>VAT @ {{ object.vat_rate.as_percent|floatformat:2 }}%</td>
|
||||||
<td>£ {{ object.vat|floatformat:2 }}</td>
|
<td>£ {{ object.vat|floatformat:2 }}</td>
|
||||||
{% endif %}
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
@@ -285,7 +281,7 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td>
|
<td>
|
||||||
<para>
|
<para>
|
||||||
<b>Balance</b> {% if object.vat > 0 %}(ex. VAT){% endif %}
|
<b>Balance</b> (ex. VAT)
|
||||||
</para>
|
</para>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -320,7 +316,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>General Enquires and 24 Hour Emergency Contact: 0115 84 68720</td>
|
<td>General Enquires and 24 Hour Emergency Contact: 0115 84 68720</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% elif object.vat > 0 %}
|
{% else %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<para>VAT Registration Number: 170734807</para>
|
<para>VAT Registration Number: 170734807</para>
|
||||||
|
|||||||
10
RIGS/templates/event_print_signature.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<blockTable style="signatureTable" colWidths="50,120,60,120,35,110">
|
||||||
|
<tr>
|
||||||
|
<td>Signature</td>
|
||||||
|
<td></td>
|
||||||
|
<td>Print Name</td>
|
||||||
|
<td></td>
|
||||||
|
<td>Date</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</blockTable>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
{% with object=event auth=True %}
|
{% with object=event auth=True %}
|
||||||
{% include 'partials/item_table.html' %}
|
{% include 'item_table.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,14 +4,16 @@
|
|||||||
|
|
||||||
<p>Hi {{ to_name|default:"there" }},</p>
|
<p>Hi {{ to_name|default:"there" }},</p>
|
||||||
|
|
||||||
<p><b>{{ request.user.get_full_name }}</b> has requested that you authorise <b>{{ object.display_id }}
|
<p><b>{{ request.user.get_full_name }}</b> has requested that you authorise <b>N{{ object.pk|stringformat:"05d" }}
|
||||||
| {{ object.name }}</b>{% if not to_name %} on behalf of <b>{% if object.person %}{{ object.person.name }}{% else %}{{ object.organisation.name }}{% endif %}</b>{% endif %}.</p>
|
| {{ object.name }}</b>{% if not to_name %} on behalf of <b>{{ object.person.name }}</b>{% endif %}.</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Please find the link below to complete the event booking process.
|
Please find the link below to complete the event booking process.
|
||||||
|
{% if object.event.organisation and object.event.organisation.union_account %}{# internal #}
|
||||||
Remember that only Presidents or Treasurers are allowed to sign off payments. You may need to forward
|
Remember that only Presidents or Treasurers are allowed to sign off payments. You may need to forward
|
||||||
this
|
this
|
||||||
email on.
|
email on.
|
||||||
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@
|
|||||||
<table border="0" cellspacing="0" cellpadding="0">
|
<table border="0" cellspacing="0" cellpadding="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="button" align="center">
|
<td class="button" align="center">
|
||||||
<a href="{{ request.scheme }}://{{ request.get_host }}{% url target|default:'event_authorise' object.pk hmac %}">
|
<a href="{{ request.scheme }}://{{ request.get_host }}{% url 'event_authorise' object.pk hmac %}">
|
||||||
Complete Authorisation Form
|
Complete Authorisation Form
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Hi {{ to_name|default:"there" }},
|
Hi {{ to_name|default:"there" }},
|
||||||
|
|
||||||
{{ request.user.get_full_name }} has requested that you authorise N{{ object.pk|stringformat:"05d" }}| {{ object.name }}{% if not to_name %} on behalf of {% if object.person %}{{ object.person.name }}{% else %}{{ object.organisation.name }}{% endif %}{% endif %}.
|
{{ request.user.get_full_name }} has requested that you authorise N{{ object.pk|stringformat:"05d" }}| {{ object.name }}{% if not to_name %} on behalf of {{ object.person.name }}{% endif %}.
|
||||||
|
|
||||||
Please find the link below to complete the event booking process.
|
Please find the link below to complete the event booking process.
|
||||||
{% if object.event.organisation and object.event.organisation.union_account %}{# internal #}
|
{% if object.event.organisation and object.event.organisation.union_account %}{# internal #}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
{% extends 'eventauthorisation.html' %}
|
{% extends 'eventauthorisation.html' %}
|
||||||
{% load widget_tweaks %}
|
{% load widget_tweaks %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block authorisation %}
|
{% block authorisation %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
@@ -83,7 +86,7 @@
|
|||||||
|
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-primary btn-lg" type="submit" {% if preview %}disabled="" data-toggle="tooltip" title="This is only a preview!"{%endif%}>Authorise</button>
|
<button class="btn btn-primary btn-lg" type="submit">Authorise</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,48 +1,24 @@
|
|||||||
{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %}
|
{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %}
|
||||||
{% load widget_tweaks %}
|
{% load widget_tweaks %}
|
||||||
{% load static %}
|
|
||||||
{% load button from filters %}
|
|
||||||
|
|
||||||
{% block title %}Request Authorisation{% endblock %}
|
{% block title %}Request Authorisation{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
|
||||||
<script src="{% static 'js/tooltip.js' %}"></script>
|
|
||||||
<script src="{% static 'js/popover.js' %}"></script>
|
|
||||||
<script src="{% static 'js/clipboard.min.js' %}"></script>
|
|
||||||
<script>
|
|
||||||
var clipboard = new ClipboardJS('.btn');
|
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
|
||||||
$(e.trigger).popover('show');
|
|
||||||
window.setTimeout(function () {$(e.trigger).popover('hide')}, 3000);
|
|
||||||
e.clearSelection();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class="alert alert-warning pb-0">
|
<div class="alert alert-warning">
|
||||||
<h1>Send authorisation request email.</h1>
|
<h1>Send authorisation request email.</h1>
|
||||||
<p>Pressing send will email the address provided. <strong>Please triple check everything before continuing.</strong></p>
|
<p>Pressing send will email the address provided. Please triple check everything before continuing.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-info pb-0">
|
<div class="alert alert-info">
|
||||||
{% if object.person.email or object.organisation.email %}
|
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
{% if object.person.email %}
|
|
||||||
<dt>Person Email</dt>
|
<dt>Person Email</dt>
|
||||||
<dd><span id="person-email">{{ object.person.email }}</span>{% button 'copy' id='#person-email' %}</dd>
|
<dd>{{ object.person.email }}</dd>
|
||||||
{% endif %}
|
|
||||||
{% if object.organisation.email %}
|
|
||||||
<dt>Organisation Email</dt>
|
<dt>Organisation Email</dt>
|
||||||
<dd><span id="org-email">{{ object.organisation.email }}</span>{% button 'copy' id='#org-email' %}</dd>
|
<dd>{{ object.organisation.email }}</dd>
|
||||||
{% endif %}
|
|
||||||
</dl>
|
</dl>
|
||||||
{% else %}
|
|
||||||
<p>No email addresses saved to the client ಠ_ಠ</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ form.action|default:request.path }}" method="POST" id="auth-request-form">
|
<form action="{{ form.action|default:request.path }}" method="POST" id="auth-request-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@@ -54,6 +30,14 @@
|
|||||||
{% render_field form.email type="email" class+="form-control" %}
|
{% render_field form.email type="email" class+="form-control" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="text-right col-sm-3 offset-sm-9">
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="form-control btn btn-primary">
|
||||||
|
<i class="fas fa-paper-plane"></i>
|
||||||
|
Send
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -64,14 +48,3 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block footer %}
|
|
||||||
<div class="form-row">
|
|
||||||
<div class="btn-group" role="group">
|
|
||||||
<a type="button" target="_blank" href="{% url 'event_authorise_preview' object.pk %}" class="btn btn-info text-nowrap"><span class="fas fa-drafting-compass"></span> Preview</a>
|
|
||||||
<button type="submit" class="form-control btn btn-primary" form="auth-request-form">
|
|
||||||
<span class="fas fa-paper-plane"></span> Send
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|||||||
@@ -4,11 +4,10 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table mb-0 table-sm">
|
<table class="table mb-0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Event</th>
|
<th scope="col">Event</th>
|
||||||
<th scope="col">MIC</th>
|
|
||||||
<th scope="col">Dates</th>
|
<th scope="col">Dates</th>
|
||||||
<th scope="col">RA</th>
|
<th scope="col">RA</th>
|
||||||
<th scope="col">Checklists</th>
|
<th scope="col">Checklists</th>
|
||||||
@@ -17,8 +16,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for event in object_list %}
|
{% for event in object_list %}
|
||||||
<tr id="event_row">
|
<tr id="event_row">
|
||||||
<th scope="row" id="event_number"><a href="{% url 'event_detail' event.pk %}">{{ event }}</a><br><small>{{ event.get_status_display }}</small></th>
|
<th scope="row" id="event_number"><a href="{% url 'event_detail' event.pk %}">{{ event }}</a></th>
|
||||||
<td>{% if event.mic is not None %}<a href="{% url 'profile_detail' event.mic.pk %}">{% else %}<span class="text-danger">{% endif %}{{ event.mic }}{% if event.mic is not None %}</a>{% else %}</span>{%endif%}</td>
|
|
||||||
<!--Dates-->
|
<!--Dates-->
|
||||||
<td id="event_dates">
|
<td id="event_dates">
|
||||||
<span><strong>{{ event.start_date|date:"D d/m/Y" }}</strong></span>
|
<span><strong>{{ event.start_date|date:"D d/m/Y" }}</strong></span>
|
||||||
|
|||||||
@@ -15,13 +15,13 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table mb-0 table-sm">
|
<table class="table mb-0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Event</th>
|
<th scope="col">Event</th>
|
||||||
{# mmm hax #}
|
{# mmm hax #}
|
||||||
{% if object_list.0 != None %}
|
{% if object_list.0 != None %}
|
||||||
{% for field in object_list.0.fieldz %}
|
{% for field in object_list.0.fields %}
|
||||||
<th scope="col">{{ object_list.0|verbose_name:field|title }}</th>
|
<th scope="col">{{ object_list.0|verbose_name:field|title }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -32,8 +32,8 @@
|
|||||||
{% for object in object_list %}
|
{% for object in object_list %}
|
||||||
<tr class="{% if object.reviewed_by %}table-success{%endif%}">
|
<tr class="{% if object.reviewed_by %}table-success{%endif%}">
|
||||||
{# General #}
|
{# General #}
|
||||||
<th scope="row"><a href="{% url 'event_detail' object.event.pk %}">{{ object.event }}</a><br><small>{{ object.event.get_status_display }}</small></th>
|
<th scope="row"><a href="{% url 'event_detail' object.event.pk %}">{{ object.event }}</a></th>
|
||||||
{% for field in object_list.0.fieldz %}
|
{% for field in object_list.0.fields %}
|
||||||
<td>{{ object|get_field:field }}</td>
|
<td>{{ object|get_field:field }}</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{# Buttons #}
|
{# Buttons #}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
{% load button from filters %}
|
{% load button from filters %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row py-4">
|
<div class="col-sm-12">
|
||||||
<div class="col-sm-12 text-right px-0">
|
<div class="row justify-content-end py-3">
|
||||||
<div class="btn-group">
|
<div class="col-sm-4 text-right">
|
||||||
<a href="{% url 'event_detail' object.event.pk %}" class="btn btn-primary">Open Event Page <span class="fas fa-eye"></span></a>
|
<div class="btn-group btn-page">
|
||||||
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-danger" title="Delete Invoice">
|
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-danger" title="Delete Invoice">
|
||||||
<span class="fas fa-times"></span> <span
|
<span class="fas fa-times"></span> <span
|
||||||
class="d-none d-sm-inline">Delete</span>
|
class="d-none d-sm-inline">Delete</span>
|
||||||
@@ -17,11 +17,24 @@
|
|||||||
{% button 'print' url='invoice_print' pk=object.pk %}
|
{% button 'print' url='invoice_print' pk=object.pk %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</div>
|
||||||
<div class="row py-4">
|
|
||||||
{% with object.event as object %}
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
{% include 'partials/contact_details.html' %}
|
<div class="card card-default">
|
||||||
|
<div class="card-header">Invoice Details<span class="float-right">
|
||||||
|
{% if object.void %}(VOID){% elif object.is_closed %}(PAID){% else %}(OUTSTANDING){% endif %}
|
||||||
|
</span></div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% if object.event.organisation %}
|
||||||
|
{{ object.event.organisation.name }}<br/>
|
||||||
|
{{ object.event.organisation.address|linebreaksbr }}
|
||||||
|
{% else %}
|
||||||
|
{{ object.event.person.name }}<br/>
|
||||||
|
{{ object.event.person.address|linebreaksbr }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
{% include 'partials/event_details.html' %}
|
{% include 'partials/event_details.html' %}
|
||||||
@@ -31,8 +44,8 @@
|
|||||||
{% include 'partials/auth_details.html' %}
|
{% include 'partials/auth_details.html' %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row py-4">
|
<div class="row py-4">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="card card-default">
|
<div class="card card-default">
|
||||||
@@ -78,7 +91,7 @@
|
|||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
{% with object.event as object %}
|
{% with object.event as object %}
|
||||||
{% include 'partials/item_table.html' %}
|
{% include 'item_table.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,4 +99,5 @@
|
|||||||
<div class="col-12 text-right">
|
<div class="col-12 text-right">
|
||||||
{% include 'partials/last_edited.html' with target="invoice_history" %}
|
{% include 'partials/last_edited.html' with target="invoice_history" %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -16,10 +16,10 @@
|
|||||||
id="item_name"/>
|
id="item_name"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group form-row" data-toggle="tooltip" title="A detailed description of the kit. MD enabled.">
|
<div class="form-group form-row">
|
||||||
<label for="item_description" class="col-sm-2 col-form-label">Description</label>
|
<label for="item_description" class="col-sm-2 col-form-label">Description</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea type="text" placeholder="Description" class="form-control md-enabled"
|
<textarea type="text" placeholder="Description" class="form-control"
|
||||||
id="item_description" rows="8"></textarea>
|
id="item_description" rows="8"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
{% load markdown_tags %}
|
|
||||||
<tr id="item-{{item.pk}}" data-pk="{{item.pk}}" class="item_row">
|
<tr id="item-{{item.pk}}" data-pk="{{item.pk}}" class="item_row">
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<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|markdown}}</em>
|
<em class="description">{{item.description|linebreaksbr}}</em>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
{% if perms.RIGS.view_event %}
|
{% if perms.RIGS.view_event %}
|
||||||
<td>£<span class="cost">{{item.cost|floatformat:2}}</span></td>
|
<td>£ <span class="cost">{{item.cost|floatformat:2}}</span></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="quantity">{{item.quantity}}</td>
|
<td class="quantity">{{item.quantity}}</td>
|
||||||
{% if perms.RIGS.view_event %}
|
{% if perms.RIGS.view_event %}
|
||||||
<td>£<span class="sub-total" data-subtotal="{{item.total_cost}}">{{item.total_cost|floatformat:2}}</span></td>
|
<td>£ <span class="sub-total" data-subtotal="{{item.total_cost}}">{{item.total_cost|floatformat:2}}</span></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if edit %}
|
{% if edit %}
|
||||||
<td class="vert-align text-right">
|
<td class="vert-align text-right">
|
||||||
@@ -23,17 +23,16 @@
|
|||||||
</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 'partials/item_row.html' %}
|
{% include 'item_row.html' %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
{% if auth or perms.RIGS.view_event %}
|
{% if auth or perms.RIGS.view_event %}
|
||||||
<tfoot style="font-weight: bold">
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan="3" colspan="2"></td>
|
<td rowspan="3" colspan="2"></td>
|
||||||
<td>Total {% if object.vat > 0 or not object.pk %}(ex. VAT){% endif %}</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>
|
||||||
{% if object.vat > 0 or not object.pk %}
|
|
||||||
<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 @
|
||||||
@@ -48,7 +47,6 @@
|
|||||||
<td>Total</td>
|
<td>Total</td>
|
||||||
<td colspan="2">£ <span id="total">{{object.total|default:0|floatformat:2}}</span></td>
|
<td colspan="2">£ <span id="total">{{object.total|default:0|floatformat:2}}</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
|
||||||
</tfoot>
|
</tfoot>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
@@ -61,9 +59,9 @@
|
|||||||
<em class="description"></em>
|
<em class="description"></em>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>£<span class="cost"></span></td>
|
<td>£ <span class="cost"></span></td>
|
||||||
<td class="quantity"></td>
|
<td class="quantity"></td>
|
||||||
<td>£<span class="sub-total"></span></td>
|
<td>£ <span class="sub-total"></span></td>
|
||||||
{% if edit %}
|
{% if edit %}
|
||||||
<td class="vert-align text-right">
|
<td class="vert-align text-right">
|
||||||
<div class="btn-group" role="group" aria-label="Action buttons">
|
<div class="btn-group" role="group" aria-label="Action buttons">
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
<div class="card card-default
|
<div class="card card-default
|
||||||
{% if event.authorised %}
|
{% if object.authorised %}
|
||||||
border-success
|
card-success
|
||||||
{% elif event.authorisation and event.authorisation.amount != event.total and event.authorisation.last_edited_at > event.auth_request_at %}
|
{% elif event.authorisation and event.authorisation.amount != event.total and event.authorisation.last_edited_at > event.auth_request_at %}
|
||||||
border-warning
|
card-warning
|
||||||
{% elif event.auth_request_to %}
|
{% elif event.auth_request_to %}
|
||||||
border-info
|
card-info
|
||||||
{% endif %}
|
{% endif %}
|
||||||
">
|
">
|
||||||
<div class="card-header">Client Authorisation</div>
|
<div class="card-header">Client Authorisation</div>
|
||||||
<div class="card-body row">
|
<div class="card-body row">
|
||||||
<dl class="col-sm-6">
|
<dl class="col-md-6">
|
||||||
<dt>Authorisation Request</dt>
|
<dt>Authorisation Request</dt>
|
||||||
<dd>{{ object.auth_request_to|yesno:"Yes,No" }}</dd>
|
<dd>{{ object.auth_request_to|yesno:"Yes,No" }}</dd>
|
||||||
|
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
<dt>To</dt>
|
<dt>To</dt>
|
||||||
<dd>{{ object.auth_request_to }}</dd>
|
<dd>{{ object.auth_request_to }}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<dl class="col-sm-6">
|
<dd class="d-block d-sm-none"> </dd>
|
||||||
<hr class="d-block d-sm-none">
|
<dl class="col-md-6">
|
||||||
<dt>Authorised</dt>
|
<dt>Authorised</dt>
|
||||||
<dd>{{ object.authorised|yesno:"Yes,No" }}</dd>
|
<dd>{{ object.authorised|yesno:"Yes,No" }}</dd>
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,27 @@
|
|||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
{% if event.person %}
|
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header">Contact Details</div>
|
<div class="card-header">Contact Details</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
<dt class="col-sm-5">Person</dt>
|
<dt class="col-sm-5">Person</dt>
|
||||||
<dd class="col-sm-7">
|
<dd class="col-sm-7">
|
||||||
|
{% if event.person %}
|
||||||
{{ event.person.name }}
|
{{ event.person.name }}
|
||||||
|
{% endif %}
|
||||||
</dd>
|
</dd>
|
||||||
{% if event.person.email %}
|
|
||||||
<dt class="col-sm-5">Email</dt>
|
<dt class="col-sm-5">Email</dt>
|
||||||
<dd class="col-sm-7">
|
<dd class="col-sm-7">
|
||||||
<span class="overflow-ellipsis">{{ event.person.email }}</span>
|
<span class="overflow-ellipsis">{{ event.person.email }}</span>
|
||||||
</dd>
|
</dd>
|
||||||
{% endif %}
|
|
||||||
{% if event.person.phone %}
|
|
||||||
<dt class="col-sm-5">Phone Number</dt>
|
<dt class="col-sm-5">Phone Number</dt>
|
||||||
<dd class="col-sm-7">{{ event.person.phone }}</dd>
|
<dd class="col-sm-7">{{ event.person.phone }}</dd>
|
||||||
{% endif %}
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
{% if event.organisation %}
|
{% if event.organisation %}
|
||||||
<div class="card">
|
<div class="card mt-3">
|
||||||
<div class="card-header">Organisation Details</div>
|
<div class="card-header">Organisation Details</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
@@ -31,10 +29,9 @@
|
|||||||
<dd class="col-sm-7">
|
<dd class="col-sm-7">
|
||||||
{{ event.organisation.name }}
|
{{ event.organisation.name }}
|
||||||
</dd>
|
</dd>
|
||||||
{% if event.organisation.phone %}
|
|
||||||
<dt class="col-sm-5">Phone Number</dt>
|
<dt class="col-sm-5">Phone Number</dt>
|
||||||
<dd class="col-sm-7">{{ event.organisation.phone }}</dd>
|
<dd class="col-sm-7">{{ object.organisation.phone }}</dd>
|
||||||
{% endif %}
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -46,12 +43,15 @@
|
|||||||
<div class="card-header">Event Info</div>
|
<div class="card-header">Event Info</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
{% if event.venue %}
|
|
||||||
<dt class="col-sm-5">Event Venue</dt>
|
<dt class="col-sm-5">Event Venue</dt>
|
||||||
<dd class="col-sm-7">
|
<dd class="col-sm-7">
|
||||||
{{ event.venue }}
|
{% if object.venue %}
|
||||||
</dd>
|
<a href="{% url 'venue_detail' object.venue.pk %}" class="modal-href">
|
||||||
|
{{ object.venue }}
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</dd>
|
||||||
|
|
||||||
<dt class="col-sm-5">Status</dt>
|
<dt class="col-sm-5">Status</dt>
|
||||||
<dd class="col-sm-7">{{ event.get_status_display }}</dd>
|
<dd class="col-sm-7">{{ event.get_status_display }}</dd>
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
{% load linkornone from filters %}
|
|
||||||
{% load namewithnotes from filters %}
|
|
||||||
|
|
||||||
{% if object.person %}
|
|
||||||
<div class="card card-default mb-3">
|
|
||||||
<div class="card-header">Person Details</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-sm-6">Person</dt>
|
|
||||||
<dd class="col-sm-6">
|
|
||||||
{% if object.person %}
|
|
||||||
<a href="{% url 'person_detail' object.person.pk %}" class="modal-href">
|
|
||||||
{{ object.person|namewithnotes:'person_detail' }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-sm-6">Email</dt>
|
|
||||||
<dd class="col-sm-6">{{ object.person.email|linkornone:'mailto' }}</dd>
|
|
||||||
<dt class="col-sm-6">Phone Number</dt>
|
|
||||||
<dd class="col-sm-6">{{ object.person.phone|linkornone:'tel' }}</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if object.organisation %}
|
|
||||||
<div class="card card-default">
|
|
||||||
<div class="card-header">Organisation Details</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<dl class="row">
|
|
||||||
<dt class="col-sm-6">Organisation</dt>
|
|
||||||
<dd class="col-sm-6">
|
|
||||||
{% if object.organisation %}
|
|
||||||
<a href="{% url 'organisation_detail' object.organisation.pk %}" class="modal-href">
|
|
||||||
{{ object.organisation|namewithnotes:'organisation_detail' }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</dd>
|
|
||||||
<dt class="col-sm-6">Email</dt>
|
|
||||||
<dd class="col-sm-6">{{ object.organisation.email|linkornone:'mailto' }}</dd>
|
|
||||||
<dt class="col-sm-6">Phone Number</dt>
|
|
||||||
<dd class="col-sm-6">{{ object.organisation.phone|linkornone:'tel' }}</dd>
|
|
||||||
<dt class="col-sm-6">Has SU Account</dt>
|
|
||||||
<dd class="col-sm-6">{{ event.organisation.union_account|yesno|capfirst }}</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
{% load namewithnotes from filters %}
|
{% load namewithnotes from filters %}
|
||||||
{% load markdown_tags %}
|
|
||||||
<div class="card card-info">
|
<div class="card card-info">
|
||||||
<div class="card-header">Event Info</div>
|
<div class="card-header">Event Info</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -21,7 +20,15 @@
|
|||||||
|
|
||||||
{% if event.is_rig %}
|
{% if event.is_rig %}
|
||||||
<dt class="col-sm-6">Event MIC</dt>
|
<dt class="col-sm-6">Event MIC</dt>
|
||||||
<dd class="col-sm-6">{% include 'partials/linked_name.html' with profile=event.mic %}</dd>
|
<dd class="col-sm-6">
|
||||||
|
{% if event.mic and perms.RIGS.view_profile %}
|
||||||
|
<a href="{% url 'profile_detail' event.mic.pk %}" class="modal-href">
|
||||||
|
{{ event.mic.name }}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
{{ event.mic.name }}
|
||||||
|
{% endif %}
|
||||||
|
</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<dt class="col-sm-6">Status</dt>
|
<dt class="col-sm-6">Status</dt>
|
||||||
@@ -47,7 +54,7 @@
|
|||||||
<dd class="col-sm-12"> </dd>
|
<dd class="col-sm-12"> </dd>
|
||||||
|
|
||||||
<dt class="col-sm-6">Event Description</dt>
|
<dt class="col-sm-6">Event Description</dt>
|
||||||
<dd class="dont-break-out col-sm-12">{{ event.description|markdown }}</dd>
|
<dd class="dont-break-out col-sm-12">{{ event.description|linebreaksbr }}</dd>
|
||||||
|
|
||||||
<dd class="col-sm-12"> </dd>
|
<dd class="col-sm-12"> </dd>
|
||||||
|
|
||||||
@@ -64,7 +71,7 @@
|
|||||||
|
|
||||||
{% if event.dry_hire %}
|
{% if event.dry_hire %}
|
||||||
<dt class="col-sm-6">Checked In By</dt>
|
<dt class="col-sm-6">Checked In By</dt>
|
||||||
<dd class="col-sm-6">{% include 'partials/linked_name.html' with profile=event.checked_in_by %}</dd>
|
<dd class="col-sm-6">{{ object.checked_in_by.name }}</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if event.is_rig %}
|
{% if event.is_rig %}
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
<div>
|
<h5>
|
||||||
<span class="badge badge-{% if event.confirmed %}success{% elif event.cancelled %}dark{% else %}warning{% endif %}">Status: {{ event.get_status_display }}</span>
|
<span class="badge badge-{% if event.confirmed %}success{% elif event.cancelled %}dark{% else %}warning{% endif %}">Status: {{ event.get_status_display }}</span>
|
||||||
{% if event.is_rig %}
|
{% if event.is_rig %}
|
||||||
{% if event.sum_total > 0 %}
|
|
||||||
{% if event.purchase_order %}
|
{% if event.purchase_order %}
|
||||||
<span class="badge badge-success">PO: {{ event.purchase_order }}</span>
|
<span class="badge badge-success">PO: {{ event.purchase_order }}</span>
|
||||||
{% elif event.authorised %}
|
{% elif event.authorised %}
|
||||||
<span class="badge badge-success">Authorisation: Complete <span class="fas fa-check"></span></span>
|
<span class="badge badge-success">Authorisation: Complete <span class="fas fa-check"></span></span>
|
||||||
{% elif event.authorisation and event.authorisation.amount != event.total and event.authorisation.last_edited_at > event.auth_request_at %}
|
|
||||||
<span class="badge badge-warning"> Authorisation: Issue <span class="fas fa-exclamation-circle"></span></span>
|
|
||||||
{% elif event.auth_request_to %}
|
|
||||||
<span class="badge badge-info"> Authorisation: Sent <span class="fas fa-paper-plane"></span></span>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="badge badge-danger">Authorisation: <span class="fas fa-times"></span></span>
|
<span class="badge badge-danger">Authorisation: <span class="fas fa-times"></span></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
{% if not event.dry_hire %}
|
{% if not event.dry_hire %}
|
||||||
{% if event.riskassessment %}
|
{% if event.riskassessment %}
|
||||||
<span class="badge badge-success">RA: <span class="fas fa-check"></span>{%if event.riskassessment.reviewed_by%}<span class="fas fa-check"></span>{%endif%}</span>
|
<span class="badge badge-success">RA: <span class="fas fa-check"></span>{%if event.riskassessment.reviewed_by%}<span class="fas fa-check"></span>{%endif%}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="badge badge-danger">RA: <span class="fas fa-times"></span></span>
|
<span class="badge badge-danger">RA: <span class="fas fa-times"></span></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<span class="badge badge-secondary">RA: N/A</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not event.dry_hire %}
|
{% if not event.dry_hire %}
|
||||||
{% if event.hs_done %}
|
{% if event.hs_done %}
|
||||||
@@ -28,6 +24,8 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<span class="badge badge-danger">Checklist: <span class="fas fa-times"></span></span>
|
<span class="badge badge-danger">Checklist: <span class="fas fa-times"></span></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<span class="badge badge-secondary">Checklist: N/A</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.RIGS.view_invoice %}
|
{% if perms.RIGS.view_invoice %}
|
||||||
{% if event.invoice %}
|
{% if event.invoice %}
|
||||||
@@ -43,4 +41,4 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</h5>
|
||||||
|
|||||||
@@ -25,30 +25,30 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
table-warning
|
table-warning
|
||||||
{% endif %}" {% if event.cancelled %}style="opacity: 50% !important;"{% endif %} id="event_row">
|
{% endif %}" id="event_row">
|
||||||
<!---Number-->
|
<!---Number-->
|
||||||
<th scope="row" id="event_number">{{ event.display_id }}</th>
|
<th scope="row" id="event_number">{{ event.display_id }}</th>
|
||||||
<!--Dates & Times-->
|
<!--Dates & Times-->
|
||||||
<td id="event_dates">
|
<td id="event_dates">
|
||||||
<span class="text-nowrap">Start: <strong>{{ event.start_date|date:"D d/m/Y" }}
|
<span class="text-nowrap">Start: <strong>{{ event.start_date|date:"D d/m/Y" }}</strong>
|
||||||
{% if event.has_start_time %}
|
{% if event.has_start_time %}
|
||||||
{{ event.start_time|date:"H:i" }}
|
{{ event.start_time|date:"H:i" }}
|
||||||
{% endif %}</strong>
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% if event.end_date %}
|
{% if event.end_date %}
|
||||||
<br>
|
<br>
|
||||||
<span class="text-nowrap">End: {% if event.end_date != event.start_date %}<strong>{{ event.end_date|date:"D d/m/Y" }}{% endif %}
|
<span class="text-nowrap">End: {% if event.end_date != event.start_date %}<strong>{{ event.end_date|date:"D d/m/Y" }}</strong>{% endif %}
|
||||||
{% if event.has_end_time %}
|
{% if event.has_end_time %}
|
||||||
{{ event.end_time|date:"H:i" }}
|
{{ event.end_time|date:"H:i" }}
|
||||||
{% endif %}</strong>
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not event.cancelled %}
|
{% if not event.cancelled %}
|
||||||
{% if event.meet_at %}
|
{% if event.meet_at %}
|
||||||
<br><span class="text-nowrap">Meet: <strong>{{ event.meet_at|date:"D d/m/Y H:i" }}</strong></span>
|
<br><span>Crew meet: <strong>{{ event.meet_at|date:"H:i" }}</strong> {{ event.meet_at|date:"(d/m/Y)" }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if event.access_at %}
|
{% if event.access_at %}
|
||||||
<br><span class="text-nowrap">Access: <strong>{{ event.access_at|date:" D d/m/Y H:i" }}</strong></span>
|
<br><span>Access at: <strong>{{ event.access_at|date:"H:i" }}</strong> {{ event.access_at|date:"(d/m/Y)" }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
@@ -67,9 +67,9 @@
|
|||||||
</h4>
|
</h4>
|
||||||
{% if event.is_rig and not event.cancelled %}
|
{% if event.is_rig and not event.cancelled %}
|
||||||
<h5>
|
<h5>
|
||||||
<a href="{{ event.person.get_absolute_url }}">{{ event.person.name }}</a>
|
{{ event.person.name }}
|
||||||
{% if event.organisation %}
|
{% if event.organisation %}
|
||||||
for <a href="{{ event.organisation.get_absolute_url }}">{{ event.organisation.name }}</a>
|
for {{ event.organisation.name }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h5>
|
</h5>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif event.is_rig %}
|
{% elif event.is_rig %}
|
||||||
<span class="fas fa-user-slash"></span>
|
<span class="fas fa-exclamation"></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{% if profile and perms.RIGS.view_profile %}
|
|
||||||
<a href="{% url 'profile_detail' profile.pk %}" class="modal-href">
|
|
||||||
{{ profile.name }}
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
{{ profile.name }}
|
|
||||||
{% endif %}
|
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
{% load button from filters %}
|
{% load button from filters %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row align-items-center justify-content-between py-2 align-middle">
|
<div class="row align-items-center justify-content-between py-2">
|
||||||
<div class="col-sm-12 col-md align-middle">
|
<div class="col-sm-12 col-md">
|
||||||
Key: <span class="table-success mr-1 px-2 rounded">Ready</span><span class="table-warning mr-1 px-2 rounded">Action Required</span><span class="table-danger mr-1 px-2 rounded">Needs MIC</span><span class="table-secondary mr-1 px-2 rounded">Cancelled</span><span class="table-info px-2 rounded">Non-Rig</span>
|
Key: <span class="table-success mr-1 px-2">Ready</span><span class="table-warning mr-1 px-2">Action Required</span><span class="table-danger mr-1 px-2">Needs MIC</span><span class="table-secondary mr-1 px-2">Cancelled</span><span class="table-info px-2">Non-Rig</span>
|
||||||
</div>
|
</div>
|
||||||
{% if perms.RIGS.add_event %}
|
{% if perms.RIGS.add_event %}
|
||||||
<div class="col text-right">
|
<div class="col text-right">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
|
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
|
||||||
|
{% block title %}Risk Assessment for Event N{{ object.event.pk|stringformat:"05d" }} {{ object.event.name }}{% endblock %}
|
||||||
{% load help_text from filters %}
|
{% load help_text from filters %}
|
||||||
{% load yesnoi from filters %}
|
{% load yesnoi from filters %}
|
||||||
{% load linkornone from filters %}
|
{% load linkornone from filters %}
|
||||||
@@ -6,6 +7,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row py-3">
|
<div class="row py-3">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
<h3>Risk Assessment for Event N{{ object.event.pk|stringformat:"05d" }} {{ object.event.name }}</h3>
|
||||||
<div class="card card-default mb-3">
|
<div class="card card-default mb-3">
|
||||||
<div class="card-header">General</div>
|
<div class="card-header">General</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -45,15 +47,15 @@
|
|||||||
<dd class="col-sm-6">
|
<dd class="col-sm-6">
|
||||||
{{ object.big_power|yesnoi:'invert' }}
|
{{ object.big_power|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-sm-6">{{ object|help_text:'power_mic'|safe }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'power_mic' }}</dt>
|
||||||
<dd class="col-sm-6">
|
<dd class="col-sm-6">
|
||||||
{{ object.power_mic.name|default:'None' }}
|
{{ object.power_mic.name|default:'None' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-sm-6">{{ object|help_text:'outside' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'generators' }}</dt>
|
||||||
<dd class="col-sm-6">
|
<dd class="col-sm-6">
|
||||||
{{ object.outside|yesnoi:'invert' }}
|
{{ object.outside|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-sm-6">{{ object|help_text:'generators' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'outside' }}</dt>
|
||||||
<dd class="col-sm-6">
|
<dd class="col-sm-6">
|
||||||
{{ object.generators|yesnoi:'invert' }}
|
{{ object.generators|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
@@ -95,67 +97,61 @@
|
|||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-6 col-12">
|
|
||||||
<div class="card card-default mb-3">
|
<div class="card card-default mb-3">
|
||||||
<div class="card-header">Site Details</div>
|
<div class="card-header">Site Details</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
<dt class="col-10">{{ object|help_text:'known_venue' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'known_venue' }}</dt>
|
||||||
<dd class="col-2">
|
<dd class="col-sm-6">
|
||||||
{{ object.known_venue|yesnoi:'invert' }}
|
{{ object.known_venue|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-10">{{ object|help_text:'safe_loading'|safe }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'safe_loading'|safe }}</dt>
|
||||||
<dd class="col-2">
|
<dd class="col-sm-6">
|
||||||
{{ object.safe_loading|yesnoi:'invert' }}
|
{{ object.safe_loading|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-10">{{ object|help_text:'safe_storage' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'safe_storage' }}</dt>
|
||||||
<dd class="col-2">
|
<dd class="col-sm-6">
|
||||||
{{ object.safe_storage|yesnoi:'invert' }}
|
{{ object.safe_storage|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-10">{{ object|help_text:'area_outside_of_control' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'area_outside_of_control' }}</dt>
|
||||||
<dd class="col-2">
|
<dd class="col-sm-6">
|
||||||
{{ object.area_outside_of_control|yesnoi:'invert' }}
|
{{ object.area_outside_of_control|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-10">{{ object|help_text:'barrier_required' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'barrier_required' }}</dt>
|
||||||
<dd class="col-2">
|
<dd class="col-sm-6">
|
||||||
{{ object.barrier_required|yesnoi:'invert' }}
|
{{ object.barrier_required|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-10">{{ object|help_text:'nonstandard_emergency_procedure' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'nonstandard_emergency_procedure' }}</dt>
|
||||||
<dd class="col-2">
|
<dd class="col-sm-6">
|
||||||
{{ object.nonstandard_emergency_procedure|yesnoi:'invert' }}
|
{{ object.nonstandard_emergency_procedure|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="col-lg-6 col-12">
|
|
||||||
<div class="card card-default mb-3">
|
<div class="card card-default mb-3">
|
||||||
<div class="card-header">Structures</div>
|
<div class="card-header">Structures</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
<dt class="col-10">{{ object|help_text:'special_structures' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'special_structures' }}</dt>
|
||||||
<dd class="col-2">
|
<dd class="col-sm-6">
|
||||||
{{ object.special_structures|yesnoi:'invert' }}
|
{{ object.special_structures|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-10">{{ object|help_text:'suspended_structures' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'suspended_structures' }}</dt>
|
||||||
<dd class="col-2">
|
<dd class="col-sm-6">
|
||||||
{{ object.suspended_structures|yesnoi:'invert' }}
|
{{ object.suspended_structures|yesnoi:'invert' }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-12">{{ object|help_text:'persons_responsible_structures' }}</dt>
|
<dt class="col-sm-6">{{ object|help_text:'persons_responsible_structures' }}</dt>
|
||||||
<dd class="col-12">
|
<dd class="col-sm-6">
|
||||||
{{ object.persons_responsible_structures.name|default:'N/A'|linebreaks }}
|
{{ object.persons_responsible_structures.name|default:'N/A'|linebreaks }}
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="col-12">{{ object|help_text:'rigging_plan'|safe }}</dt>
|
<dt class="col-6">{{ object|help_text:'rigging_plan'|safe }}</dt>
|
||||||
<dd class="col-12">
|
<dd class="col-6">
|
||||||
{{ object.rigging_plan|linkornone|default:'N/A' }}
|
{{ object.rigging_plan|linkornone }}
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 text-right">
|
<div class="col-12 text-right">
|
||||||
<a href="{% url 'ra_edit' object.pk %}" class="btn btn-warning my-3"><span class="fas fa-edit"></span> <span
|
<a href="{% url 'ra_edit' object.pk %}" class="btn btn-warning my-3"><span class="fas fa-edit"></span> <span
|
||||||
class="d-none d-sm-inline">Edit</span></a>
|
class="d-none d-sm-inline">Edit</span></a>
|
||||||
|
|||||||
@@ -5,17 +5,26 @@
|
|||||||
{% load nice_errors from filters %}
|
{% load nice_errors from filters %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/>
|
||||||
|
<link rel="stylesheet" href="{% static 'css/ajax-bootstrap-select.css' %}"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block preload_js %}
|
{% block preload_js %}
|
||||||
<script src="{% static 'js/selects.js' %}" async></script>
|
{{ block.super }}
|
||||||
|
<script src="{% static 'js/bootstrap-select.js' %}"></script>
|
||||||
|
<script src="{% static 'js/ajax-bootstrap-select.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
{{ block.super }}
|
||||||
|
<script src="{% static 'js/jquery-ui.js' %}"></script><!--TODO optimise--->
|
||||||
|
<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/tooltip.js' %}"></script>
|
||||||
|
|
||||||
|
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function parseBool(str) {
|
function parseBool(str) {
|
||||||
return str.toLowerCase() == 'true';
|
return str.toLowerCase() == 'true';
|
||||||
|
|||||||
@@ -114,8 +114,10 @@ def orderby(request, field, attr):
|
|||||||
|
|
||||||
return dict_.urlencode()
|
return dict_.urlencode()
|
||||||
|
|
||||||
|
# Used for accessing outside of a form, i.e. in detail views of RiskAssessment and EventChecklist
|
||||||
|
|
||||||
@register.filter(needs_autoescape=True) # Used for accessing outside of a form, i.e. in detail views of RiskAssessment and EventChecklist
|
|
||||||
|
@register.filter(needs_autoescape=True)
|
||||||
def get_field(obj, field, autoescape=True):
|
def get_field(obj, field, autoescape=True):
|
||||||
value = getattr(obj, field)
|
value = getattr(obj, field)
|
||||||
if(isinstance(value, bool)):
|
if(isinstance(value, bool)):
|
||||||
@@ -171,7 +173,7 @@ def title_spaced(string):
|
|||||||
@register.filter(needs_autoescape=True)
|
@register.filter(needs_autoescape=True)
|
||||||
def namewithnotes(obj, url, autoescape=True):
|
def namewithnotes(obj, url, autoescape=True):
|
||||||
if hasattr(obj, 'notes') and obj.notes is not None and len(obj.notes) > 0:
|
if hasattr(obj, 'notes') and obj.notes is not None and len(obj.notes) > 0:
|
||||||
return mark_safe(obj.name + " <a href='{}'><span class='fas fa-sticky-note'></span></a>".format(reverse(url, kwargs={'pk': obj.pk})))
|
return mark_safe(obj.name + " <a href='{}'><span class='far fa-sticky-note'></span></a>".format(reverse(url, kwargs={'pk': obj.pk})))
|
||||||
else:
|
else:
|
||||||
return obj.name
|
return obj.name
|
||||||
|
|
||||||
@@ -210,25 +212,8 @@ def button(type, url=None, pk=None, clazz="", icon=None, text="", id=None, style
|
|||||||
clazz += " btn-primary "
|
clazz += " btn-primary "
|
||||||
icon = "fa-plus"
|
icon = "fa-plus"
|
||||||
text = "New"
|
text = "New"
|
||||||
elif type == 'copy':
|
|
||||||
return {'copy': True, 'id': id, 'style': style}
|
|
||||||
elif type == 'search':
|
elif type == 'search':
|
||||||
return {'submit': True, 'class': 'btn-info', 'icon': 'fa-search', 'text': 'Search', 'id': id, 'style': style}
|
return {'submit': True, 'class': 'btn-info', 'icon': 'fa-search', 'text': 'Search', 'id': id, 'style': style}
|
||||||
elif type == 'submit':
|
elif type == 'submit':
|
||||||
return {'submit': True, 'class': 'btn-primary', 'icon': 'fa-save', 'text': 'Save', 'id': id, 'style': style}
|
return {'submit': True, 'class': 'btn-primary', 'icon': 'fa-save', 'text': 'Save', 'id': id, 'style': style}
|
||||||
return {'target': url, 'pk': pk, 'class': clazz, 'icon': icon, 'text': text, 'id': id, 'style': style}
|
return {'target': url, 'pk': pk, 'class': clazz, 'icon': icon, 'text': text, 'id': id, 'style': style}
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag # TODO Can these be done with annotation/aggregation?
|
|
||||||
def invoices_waiting():
|
|
||||||
return len(models.Event.objects.waiting_invoices())
|
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
|
||||||
def invoices_outstanding():
|
|
||||||
return len(models.Invoice.objects.outstanding_invoices())
|
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
|
||||||
def total_invoices_todo():
|
|
||||||
return len(models.Event.objects.waiting_invoices()) + len(models.Invoice.objects.outstanding_invoices())
|
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
from bs4 import BeautifulSoup
|
|
||||||
from django import template
|
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
import markdown
|
|
||||||
|
|
||||||
__author__ = 'ghost'
|
|
||||||
|
|
||||||
register = template.Library()
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name="markdown")
|
|
||||||
def markdown_filter(text, input_format='html'):
|
|
||||||
# markdown library can't handle text=None
|
|
||||||
if text is None:
|
|
||||||
return text
|
|
||||||
html = markdown.markdown(text, extensions=['markdown.extensions.nl2br'])
|
|
||||||
# Convert format to RML
|
|
||||||
soup = BeautifulSoup(html, "html.parser")
|
|
||||||
# Prevent code injection
|
|
||||||
for script in soup('script'):
|
|
||||||
script.string = "Your script shall not pass!"
|
|
||||||
if input_format == 'html':
|
|
||||||
return mark_safe('<div class="markdown">' + str(soup) + '</div>')
|
|
||||||
elif input_format == 'rml':
|
|
||||||
|
|
||||||
# Image aren't supported so remove them
|
|
||||||
for img in soup('img'):
|
|
||||||
img.parent.extract()
|
|
||||||
|
|
||||||
# <code> should become <font>
|
|
||||||
for c in soup('code'):
|
|
||||||
c.name = 'font'
|
|
||||||
c['face'] = "Courier"
|
|
||||||
|
|
||||||
# blockquotes don't exist but we can still do something to show
|
|
||||||
for bq in soup('blockquote'):
|
|
||||||
bq.name = 'pre'
|
|
||||||
bq.string = bq.text
|
|
||||||
|
|
||||||
for alist in soup.find_all(['ul', 'ol']):
|
|
||||||
alist['style'] = alist.name
|
|
||||||
for li in alist.find_all('li', recursive=False):
|
|
||||||
text = li.find(text=True)
|
|
||||||
text.wrap(soup.new_tag('p'))
|
|
||||||
|
|
||||||
if alist.parent.name != 'li':
|
|
||||||
indent = soup.new_tag('indent')
|
|
||||||
indent['left'] = '0.6cm'
|
|
||||||
|
|
||||||
alist.wrap(indent)
|
|
||||||
|
|
||||||
# Paragraphs have a different tag
|
|
||||||
for p in soup('p'):
|
|
||||||
p.name = 'para'
|
|
||||||
|
|
||||||
return mark_safe(str(soup))
|
|
||||||
@@ -25,15 +25,6 @@ def ra(basic_event, admin_user):
|
|||||||
ra.delete()
|
ra.delete()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def medium_ra(ra):
|
|
||||||
ra.big_power = True
|
|
||||||
ra.save()
|
|
||||||
yield ra
|
|
||||||
ra.big_power = False
|
|
||||||
ra.save()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def venue(db):
|
def venue(db):
|
||||||
venue = models.Venue.objects.create(name="Venue 1")
|
venue = models.Venue.objects.create(name="Venue 1")
|
||||||
@@ -42,7 +33,7 @@ def venue(db):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture # TODO parameterise with Event sizes
|
@pytest.fixture # TODO parameterise with Event sizes
|
||||||
def checklist(basic_event, venue, admin_user, ra):
|
def checklist(basic_event, venue, admin_user):
|
||||||
checklist = models.EventChecklist.objects.create(event=basic_event, power_mic=admin_user, safe_parking=False,
|
checklist = models.EventChecklist.objects.create(event=basic_event, power_mic=admin_user, safe_parking=False,
|
||||||
safe_packing=False, exits=False, trip_hazard=False, warning_signs=False,
|
safe_packing=False, exits=False, trip_hazard=False, warning_signs=False,
|
||||||
ear_plugs=False, hs_location="Locked away safely",
|
ear_plugs=False, hs_location="Locked away safely",
|
||||||
@@ -53,7 +44,7 @@ def checklist(basic_event, venue, admin_user, ra):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def many_events(db, admin_user, scope="class"):
|
def many_events(db, scope="class"):
|
||||||
many_events = {
|
many_events = {
|
||||||
# produce 7 normal events - 5 current
|
# produce 7 normal events - 5 current
|
||||||
1: models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6),
|
1: models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6),
|
||||||
@@ -83,12 +74,12 @@ def many_events(db, admin_user, scope="class"):
|
|||||||
10: models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True,
|
10: models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True,
|
||||||
description="dryhire today"),
|
description="dryhire today"),
|
||||||
11: models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True,
|
11: models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True,
|
||||||
checked_in_by=admin_user,
|
checked_in_by=cls.profile,
|
||||||
description="dryhire today, checked in"),
|
description="dryhire today, checked in"),
|
||||||
12: models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True,
|
12: models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True,
|
||||||
status=models.Event.BOOKED, description="dryhire past"),
|
status=models.Event.BOOKED, description="dryhire past"),
|
||||||
13: models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True,
|
13: models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True,
|
||||||
checked_in_by=admin_user, description="dryhire past checked in"),
|
checked_in_by=cls.profile, description="dryhire past checked in"),
|
||||||
14: models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True,
|
14: models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True,
|
||||||
status=models.Event.CANCELLED, description="dryhire today cancelled"),
|
status=models.Event.CANCELLED, description="dryhire today cancelled"),
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class EventDetail(BasePage):
|
|||||||
|
|
||||||
# TODO Refactor into regions to match template fragmentation
|
# TODO Refactor into regions to match template fragmentation
|
||||||
_event_name_selector = (By.XPATH, '//h2')
|
_event_name_selector = (By.XPATH, '//h2')
|
||||||
_person_panel_selector = (By.XPATH, '//div[contains(text(), "Person Details")]/..')
|
_person_panel_selector = (By.XPATH, '//div[contains(text(), "Contact Details")]/..')
|
||||||
_name_selector = (By.XPATH, '//dt[text()="Person"]/following-sibling::dd[1]')
|
_name_selector = (By.XPATH, '//dt[text()="Person"]/following-sibling::dd[1]')
|
||||||
_email_selector = (By.XPATH, '//dt[text()="Email"]/following-sibling::dd[1]')
|
_email_selector = (By.XPATH, '//dt[text()="Email"]/following-sibling::dd[1]')
|
||||||
_phone_selector = (By.XPATH, '//dt[text()="Phone Number"]/following-sibling::dd[1]')
|
_phone_selector = (By.XPATH, '//dt[text()="Phone Number"]/following-sibling::dd[1]')
|
||||||
@@ -96,7 +96,7 @@ class CreateEvent(FormPage):
|
|||||||
_warning_selector = (By.XPATH, '/html/body/div[1]/div[1]')
|
_warning_selector = (By.XPATH, '/html/body/div[1]/div[1]')
|
||||||
|
|
||||||
form_items = {
|
form_items = {
|
||||||
'description': (regions.SimpleMDETextArea, (By.ID, 'id_description')),
|
'description': (regions.TextBox, (By.ID, 'id_description')),
|
||||||
|
|
||||||
'name': (regions.TextBox, (By.ID, 'id_name')),
|
'name': (regions.TextBox, (By.ID, 'id_name')),
|
||||||
'start_date': (regions.DatePicker, (By.ID, 'id_start_date')),
|
'start_date': (regions.DatePicker, (By.ID, 'id_start_date')),
|
||||||
@@ -110,7 +110,7 @@ class CreateEvent(FormPage):
|
|||||||
'collected_by': (regions.TextBox, (By.ID, 'id_collector')),
|
'collected_by': (regions.TextBox, (By.ID, 'id_collector')),
|
||||||
'po': (regions.TextBox, (By.ID, 'id_purchase_order')),
|
'po': (regions.TextBox, (By.ID, 'id_purchase_order')),
|
||||||
|
|
||||||
'notes': (regions.SimpleMDETextArea, (By.ID, 'id_notes'))
|
'notes': (regions.TextBox, (By.ID, 'id_notes'))
|
||||||
}
|
}
|
||||||
|
|
||||||
def select_event_type(self, type_name):
|
def select_event_type(self, type_name):
|
||||||
@@ -230,11 +230,9 @@ class CreateEventChecklist(FormPage):
|
|||||||
URL_TEMPLATE = 'event/{event_id}/checklist'
|
URL_TEMPLATE = 'event/{event_id}/checklist'
|
||||||
|
|
||||||
_submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]")
|
_submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]")
|
||||||
_power_mic_selector = (By.XPATH, "//div[select[@id='id_power_mic']]")
|
_power_mic_selector = (By.XPATH, "//div[@id='id_power_mic-group']//div[contains(@class, 'bootstrap-select')]")
|
||||||
_add_vehicle_locator = (By.XPATH, "//button[contains(., 'Vehicle')]")
|
_add_vehicle_locator = (By.XPATH, "//button[contains(., 'Vehicle')]")
|
||||||
_add_crew_locator = (By.XPATH, "//button[contains(., 'Crew')]")
|
_add_crew_locator = (By.XPATH, "//button[contains(., 'Crew')]")
|
||||||
_vehicle_row_locator = ('xpath', "//tr[@id[starts-with(., 'vehicle') and not(contains(.,'new'))]]")
|
|
||||||
_crew_row_locator = ('xpath', "//tr[@id[starts-with(., 'crew') and not(contains(.,'new'))]]")
|
|
||||||
|
|
||||||
form_items = {
|
form_items = {
|
||||||
'safe_parking': (regions.CheckBox, (By.ID, 'id_safe_parking')),
|
'safe_parking': (regions.CheckBox, (By.ID, 'id_safe_parking')),
|
||||||
@@ -273,61 +271,11 @@ class CreateEventChecklist(FormPage):
|
|||||||
def power_mic(self):
|
def power_mic(self):
|
||||||
return regions.BootstrapSelectElement(self, self.find_element(*self._power_mic_selector))
|
return regions.BootstrapSelectElement(self, self.find_element(*self._power_mic_selector))
|
||||||
|
|
||||||
@property
|
|
||||||
def vehicles(self):
|
|
||||||
return [self.VehicleRow(self, el) for el in self.find_elements(*self._vehicle_row_locator)]
|
|
||||||
|
|
||||||
class VehicleRow(Region):
|
|
||||||
_name_locator = ('xpath', ".//input")
|
|
||||||
_select_locator = ('xpath', ".//div[contains(@class,'bootstrap-select')]/..")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return regions.TextBox(self, self.root.find_element(*self._name_locator))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def vehicle(self):
|
|
||||||
return regions.BootstrapSelectElement(self, self.root.find_element(*self._select_locator))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def crew(self):
|
|
||||||
return [self.CrewRow(self, el) for el in self.find_elements(*self._crew_row_locator)]
|
|
||||||
|
|
||||||
class CrewRow(Region):
|
|
||||||
_select_locator = ('xpath', ".//div[contains(@class,'bootstrap-select')]/..")
|
|
||||||
_start_time_locator = ('xpath', ".//input[@name[starts-with(., 'start') and not(contains(.,'new'))]]")
|
|
||||||
_end_time_locator = ('xpath', ".//input[@name[starts-with(., 'end') and not(contains(.,'new'))]]")
|
|
||||||
_role_locator = ('xpath', ".//input[@name[starts-with(., 'role') and not(contains(.,'new'))]]")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def crewmember(self):
|
|
||||||
return regions.BootstrapSelectElement(self, self.root.find_element(*self._select_locator))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def start_time(self):
|
|
||||||
return regions.DateTimePicker(self, self.root.find_element(*self._start_time_locator))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def end_time(self):
|
|
||||||
return regions.DateTimePicker(self, self.root.find_element(*self._end_time_locator))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def role(self):
|
|
||||||
return regions.TextBox(self, self.root.find_element(*self._role_locator))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def success(self):
|
def success(self):
|
||||||
return '{event_id}' not in self.driver.current_url
|
return '{event_id}' not in self.driver.current_url
|
||||||
|
|
||||||
|
|
||||||
class EditEventChecklist(CreateEventChecklist):
|
|
||||||
URL_TEMPLATE = '/event/checklist/{pk}/edit'
|
|
||||||
|
|
||||||
@property
|
|
||||||
def success(self):
|
|
||||||
return 'edit' not in self.driver.current_url
|
|
||||||
|
|
||||||
|
|
||||||
class GenericList(BasePage):
|
class GenericList(BasePage):
|
||||||
_search_selector = (By.CSS_SELECTOR, 'div.input-group:nth-child(2) > input:nth-child(1)')
|
_search_selector = (By.CSS_SELECTOR, 'div.input-group:nth-child(2) > input:nth-child(1)')
|
||||||
_search_go_selector = (By.ID, 'id_search')
|
_search_go_selector = (By.ID, 'id_search')
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from pypom import Region
|
from pypom import Region
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
|
|
||||||
from PyRIGS.tests.regions import TextBox, Modal, SimpleMDETextArea
|
from PyRIGS.tests.regions import TextBox, Modal
|
||||||
|
|
||||||
|
|
||||||
class Header(Region):
|
class Header(Region):
|
||||||
@@ -42,7 +42,7 @@ class ItemModal(Modal):
|
|||||||
|
|
||||||
form_items = {
|
form_items = {
|
||||||
'name': (TextBox, (By.ID, 'item_name')),
|
'name': (TextBox, (By.ID, 'item_name')),
|
||||||
'description': (SimpleMDETextArea, (By.ID, 'item_description')),
|
'description': (TextBox, (By.ID, 'item_description')),
|
||||||
'quantity': (TextBox, (By.ID, 'item_quantity')),
|
'quantity': (TextBox, (By.ID, 'item_quantity')),
|
||||||
'price': (TextBox, (By.ID, 'item_cost'))
|
'price': (TextBox, (By.ID, 'item_cost'))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from RIGS import models
|
|||||||
from RIGS.tests import regions
|
from RIGS.tests import regions
|
||||||
from . import pages
|
from . import pages
|
||||||
import pytest
|
import pytest
|
||||||
import time as t
|
|
||||||
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db(transaction=True)
|
pytestmark = pytest.mark.django_db(transaction=True)
|
||||||
@@ -638,190 +637,270 @@ class TestCalendar(BaseRigboardTest):
|
|||||||
else:
|
else:
|
||||||
self.assertNotContains(response, "TE E" + str(test) + " ")
|
self.assertNotContains(response, "TE E" + str(test) + " ")
|
||||||
|
|
||||||
|
def test_calendar_buttons(self): # If FullCalendar fails to load for whatever reason, the buttons don't work
|
||||||
def test_calendar_buttons(logged_in_browser, live_server): # If FullCalendar fails to load for whatever reason, the buttons don't work
|
self.page = pages.CalendarPage(self.driver, self.live_server_url).open()
|
||||||
page = pages.CalendarPage(logged_in_browser.driver, live_server.url).open()
|
self.assertIn(timezone.now().strftime("%Y-%m"), self.driver.current_url)
|
||||||
assert timezone.now().strftime("%Y-%m") in logged_in_browser.url
|
|
||||||
|
|
||||||
target_date = datetime.date(2020, 1, 1)
|
target_date = datetime.date(2020, 1, 1)
|
||||||
page.target_date.set_value(target_date)
|
self.page.target_date.set_value(target_date)
|
||||||
page.go()
|
self.page.go()
|
||||||
assert page.target_date.value.strftime("%Y-%m") in logged_in_browser.url
|
self.assertIn(self.page.target_date.value.strftime("%Y-%m"), self.driver.current_url)
|
||||||
|
|
||||||
page.next()
|
self.page.next()
|
||||||
target_date += datetime.timedelta(days=32)
|
target_date += datetime.timedelta(days=32)
|
||||||
assert target_date.strftime("%m") in logged_in_browser.url
|
self.assertIn(target_date.strftime("%m"), self.driver.current_url)
|
||||||
|
|
||||||
|
|
||||||
def test_ra_edit(logged_in_browser, live_server, ra):
|
@screenshot_failure_cls
|
||||||
page = pages.EditRiskAssessment(logged_in_browser.driver, live_server.url, pk=ra.pk).open()
|
class TestHealthAndSafety(BaseRigboardTest):
|
||||||
page.nonstandard_equipment = nse = True
|
def setUp(self):
|
||||||
page.general_notes = gn = "There are some notes, but I've not written them here as that would be helpful"
|
super().setUp()
|
||||||
page.submit()
|
self.profile = models.Profile.objects.get_or_create(
|
||||||
assert not page.success
|
first_name='Test',
|
||||||
page.supervisor_consulted = True
|
last_name='TEC User',
|
||||||
page.submit()
|
username='eventtest',
|
||||||
assert page.success
|
email='teccie@functional.test',
|
||||||
# Check that data is right
|
is_superuser=True # lazily grant all permissions
|
||||||
ra = models.RiskAssessment.objects.get(pk=ra.pk)
|
)[0]
|
||||||
assert ra.general_notes == gn
|
self.venue = models.Venue.objects.create(name="Venue 1")
|
||||||
assert ra.nonstandard_equipment == nse
|
|
||||||
|
|
||||||
|
|
||||||
def small_ec(page, admin_user):
|
|
||||||
page.safe_parking = True
|
|
||||||
page.safe_packing = True
|
|
||||||
page.exits = True
|
|
||||||
page.trip_hazard = True
|
|
||||||
page.warning_signs = True
|
|
||||||
page.ear_plugs = True
|
|
||||||
page.hs_location = "The Moon"
|
|
||||||
page.extinguishers_location = "With the rest of the fire"
|
|
||||||
# If we do this first the search fails, for ... reasons
|
|
||||||
page.power_mic.search(admin_user.name)
|
|
||||||
page.power_mic.toggle()
|
|
||||||
assert not page.power_mic.is_open
|
|
||||||
page.earthing = True
|
|
||||||
page.rcds = True
|
|
||||||
page.supply_test = True
|
|
||||||
page.pat = True
|
|
||||||
|
|
||||||
|
|
||||||
def test_ec_create_small(logged_in_browser, live_server, admin_user, ra):
|
|
||||||
page = pages.CreateEventChecklist(logged_in_browser.driver, live_server.url, event_id=ra.event.pk).open()
|
|
||||||
small_ec(page, admin_user)
|
|
||||||
page.submit()
|
|
||||||
assert page.success
|
|
||||||
|
|
||||||
|
|
||||||
def test_ec_create_medium(logged_in_browser, live_server, admin_user, medium_ra):
|
|
||||||
page = pages.CreateEventChecklist(logged_in_browser.driver, live_server.url, event_id=medium_ra.event.pk).open()
|
|
||||||
|
|
||||||
page.safe_parking = True
|
|
||||||
page.safe_packing = True
|
|
||||||
page.exits = True
|
|
||||||
page.trip_hazard = True
|
|
||||||
page.warning_signs = True
|
|
||||||
page.ear_plugs = True
|
|
||||||
page.hs_location = "Death Valley"
|
|
||||||
page.extinguishers_location = "With the rest of the fire"
|
|
||||||
# If we do this first the search fails, for ... reasons
|
|
||||||
page.power_mic.search(admin_user.name)
|
|
||||||
page.power_mic.toggle()
|
|
||||||
assert not page.power_mic.is_open
|
|
||||||
|
|
||||||
# Gotta scroll to make the button clickable
|
|
||||||
logged_in_browser.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
|
|
||||||
|
|
||||||
page.earthing = True
|
|
||||||
page.pat = True
|
|
||||||
page.source_rcd = True
|
|
||||||
page.labelling = True
|
|
||||||
page.fd_voltage_l1 = 240
|
|
||||||
page.fd_voltage_l2 = 235
|
|
||||||
page.fd_voltage_l3 = 0
|
|
||||||
page.fd_phase_rotation = True
|
|
||||||
page.fd_earth_fault = "1.21"
|
|
||||||
page.fd_pssc = 1984
|
|
||||||
page.w1_description = "In the carpark, by the bins"
|
|
||||||
page.w1_polarity = True
|
|
||||||
page.w1_voltage = 240
|
|
||||||
page.w1_earth_fault = "0.42"
|
|
||||||
|
|
||||||
page.submit()
|
|
||||||
assert page.success
|
|
||||||
|
|
||||||
|
|
||||||
def test_ec_create_vehicle(logged_in_browser, live_server, admin_user, checklist):
|
|
||||||
page = pages.EditEventChecklist(logged_in_browser.driver, live_server.url, pk=checklist.pk).open()
|
|
||||||
small_ec(page, admin_user)
|
|
||||||
page.add_vehicle()
|
|
||||||
assert len(page.vehicles) == 1
|
|
||||||
vehicle_name = 'Brian'
|
|
||||||
page.vehicles[0].name.set_value(vehicle_name)
|
|
||||||
# Appears we're moving too fast for javascript...
|
|
||||||
t.sleep(1)
|
|
||||||
page.vehicles[0].vehicle.search(admin_user.first_name)
|
|
||||||
t.sleep(1)
|
|
||||||
page.submit()
|
|
||||||
assert page.success
|
|
||||||
# Check data is correct
|
|
||||||
checklist.refresh_from_db()
|
|
||||||
vehicle = models.EventChecklistVehicle.objects.get(checklist=checklist.pk)
|
|
||||||
assert vehicle_name == vehicle.vehicle
|
|
||||||
|
|
||||||
|
|
||||||
# TODO Test validation of end before start
|
|
||||||
def test_ec_create_crew(logged_in_browser, live_server, admin_user, checklist):
|
|
||||||
page = pages.EditEventChecklist(logged_in_browser.driver, live_server.url, pk=checklist.pk).open()
|
|
||||||
small_ec(page, admin_user)
|
|
||||||
page.add_crew()
|
|
||||||
assert len(page.crew) == 1
|
|
||||||
role = "MIC"
|
|
||||||
start_time = timezone.make_aware(datetime.datetime(2015, 1, 1, 9, 0))
|
|
||||||
end_time = timezone.make_aware(datetime.datetime(2015, 1, 1, 10, 30))
|
|
||||||
crew = page.crew[0]
|
|
||||||
t.sleep(2)
|
|
||||||
crew.crewmember.search(admin_user.first_name)
|
|
||||||
t.sleep(2)
|
|
||||||
crew.role.set_value(role)
|
|
||||||
crew.start_time.set_value(start_time)
|
|
||||||
crew.end_time.set_value(end_time)
|
|
||||||
page.submit()
|
|
||||||
assert page.success
|
|
||||||
# Check data is correct
|
|
||||||
crew_obj = models.EventChecklistCrew.objects.get(checklist=checklist.pk)
|
|
||||||
assert admin_user.pk == crew_obj.crewmember.pk
|
|
||||||
assert role == crew_obj.role
|
|
||||||
assert start_time == crew_obj.start
|
|
||||||
assert end_time == crew_obj.end
|
|
||||||
|
|
||||||
|
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',
|
||||||
|
person=self.client,
|
||||||
|
venue=self.venue)
|
||||||
|
self.testEvent2 = models.Event.objects.create(name="TE E2", status=models.Event.PROVISIONAL,
|
||||||
|
start_date=date.today() + timedelta(days=6),
|
||||||
|
description="start future no end",
|
||||||
|
purchase_order='TESTPO',
|
||||||
|
person=self.client,
|
||||||
|
venue=self.venue)
|
||||||
|
self.testEvent3 = models.Event.objects.create(name="TE E3", status=models.Event.PROVISIONAL,
|
||||||
|
start_date=date.today() + timedelta(days=6),
|
||||||
|
description="start future no end",
|
||||||
|
purchase_order='TESTPO',
|
||||||
|
person=self.client,
|
||||||
|
venue=self.venue)
|
||||||
|
self.testRA = models.RiskAssessment.objects.create(event=self.testEvent2, supervisor_consulted=False, nonstandard_equipment=False,
|
||||||
|
nonstandard_use=False,
|
||||||
|
contractors=False,
|
||||||
|
other_companies=False,
|
||||||
|
crew_fatigue=False,
|
||||||
|
big_power=False,
|
||||||
|
generators=False,
|
||||||
|
other_companies_power=False,
|
||||||
|
nonstandard_equipment_power=False,
|
||||||
|
multiple_electrical_environments=False,
|
||||||
|
noise_monitoring=False,
|
||||||
|
known_venue=True,
|
||||||
|
safe_loading=True,
|
||||||
|
safe_storage=True,
|
||||||
|
area_outside_of_control=False,
|
||||||
|
barrier_required=False,
|
||||||
|
nonstandard_emergency_procedure=False,
|
||||||
|
special_structures=False,
|
||||||
|
suspended_structures=False,
|
||||||
|
outside=False)
|
||||||
|
self.testRA2 = models.RiskAssessment.objects.create(event=self.testEvent3, supervisor_consulted=False, nonstandard_equipment=False,
|
||||||
|
nonstandard_use=False,
|
||||||
|
contractors=False,
|
||||||
|
other_companies=False,
|
||||||
|
crew_fatigue=False,
|
||||||
|
big_power=True,
|
||||||
|
generators=False,
|
||||||
|
other_companies_power=False,
|
||||||
|
nonstandard_equipment_power=False,
|
||||||
|
multiple_electrical_environments=False,
|
||||||
|
noise_monitoring=False,
|
||||||
|
known_venue=True,
|
||||||
|
safe_loading=True,
|
||||||
|
safe_storage=True,
|
||||||
|
area_outside_of_control=False,
|
||||||
|
barrier_required=False,
|
||||||
|
nonstandard_emergency_procedure=False,
|
||||||
|
special_structures=False,
|
||||||
|
suspended_structures=False,
|
||||||
|
outside=False)
|
||||||
|
self.page = pages.EventDetail(self.driver, self.live_server_url, event_id=self.testEvent.pk).open()
|
||||||
|
|
||||||
# TODO Can I loop through all the boolean fields and test them at once?
|
# TODO Can I loop through all the boolean fields and test them at once?
|
||||||
def test_ra_creation(logged_in_browser, live_server, admin_user, basic_event):
|
def test_ra_creation(self):
|
||||||
page = pages.CreateRiskAssessment(logged_in_browser.driver, live_server.url, event_id=basic_event.pk).open()
|
self.page = pages.CreateRiskAssessment(self.driver, self.live_server_url, event_id=self.testEvent.pk).open()
|
||||||
|
|
||||||
# Check there are no defaults
|
# Check there are no defaults
|
||||||
assert page.nonstandard_equipment is None
|
self.assertIsNone(self.page.nonstandard_equipment)
|
||||||
|
|
||||||
# No database side validation, only HTML5.
|
# No database side validation, only HTML5.
|
||||||
page.nonstandard_equipment = False
|
|
||||||
page.nonstandard_use = False
|
self.page.nonstandard_equipment = False
|
||||||
page.contractors = False
|
self.page.nonstandard_use = False
|
||||||
page.other_companies = False
|
self.page.contractors = False
|
||||||
page.crew_fatigue = False
|
self.page.other_companies = False
|
||||||
page.general_notes = "There are no notes."
|
self.page.crew_fatigue = False
|
||||||
page.big_power = False
|
self.page.general_notes = "There are no notes."
|
||||||
page.outside = False
|
self.page.big_power = False
|
||||||
page.power_mic.search(admin_user.first_name)
|
self.page.outside = False
|
||||||
page.generators = False
|
self.page.power_mic.search(self.profile.name)
|
||||||
page.other_companies_power = False
|
self.page.power_mic.set_option(self.profile.name, True)
|
||||||
page.nonstandard_equipment_power = False
|
# TODO This should not be necessary, normally closes automatically
|
||||||
page.multiple_electrical_environments = False
|
self.page.power_mic.toggle()
|
||||||
page.power_notes = "Remember to bring some power"
|
self.assertFalse(self.page.power_mic.is_open)
|
||||||
page.noise_monitoring = False
|
self.page.generators = False
|
||||||
page.sound_notes = "Loud, but not too loud"
|
self.page.other_companies_power = False
|
||||||
page.known_venue = False
|
self.page.nonstandard_equipment_power = False
|
||||||
page.safe_loading = False
|
self.page.multiple_electrical_environments = False
|
||||||
page.safe_storage = False
|
self.page.power_notes = "Remember to bring some power"
|
||||||
page.area_outside_of_control = False
|
self.page.noise_monitoring = False
|
||||||
page.barrier_required = False
|
self.page.sound_notes = "Loud, but not too loud"
|
||||||
page.nonstandard_emergency_procedure = False
|
self.page.known_venue = False
|
||||||
page.special_structures = False
|
self.page.safe_loading = False
|
||||||
|
self.page.safe_storage = False
|
||||||
|
self.page.area_outside_of_control = False
|
||||||
|
self.page.barrier_required = False
|
||||||
|
self.page.nonstandard_emergency_procedure = False
|
||||||
|
self.page.special_structures = False
|
||||||
# self.page.persons_responsible_structures = "Nobody and her cat, She"
|
# self.page.persons_responsible_structures = "Nobody and her cat, She"
|
||||||
|
|
||||||
page.suspended_structures = True
|
self.page.suspended_structures = True
|
||||||
# TODO Test for this proper
|
# TODO Test for this proper
|
||||||
page.rigging_plan = "https://nottinghamtec.sharepoint.com/test/"
|
self.page.rigging_plan = "https://nottinghamtec.sharepoint.com/test/"
|
||||||
page.submit()
|
self.page.submit()
|
||||||
assert not page.success
|
self.assertFalse(self.page.success)
|
||||||
|
|
||||||
page.suspended_structures = False
|
self.page.suspended_structures = False
|
||||||
page.submit()
|
self.page.submit()
|
||||||
assert page.success
|
self.assertTrue(self.page.success)
|
||||||
|
|
||||||
|
|
||||||
def test_ra_no_duplicates(logged_in_browser, live_server, ra):
|
|
||||||
# Test that we can't make another one
|
# Test that we can't make another one
|
||||||
page = pages.CreateRiskAssessment(logged_in_browser.driver, live_server.url, event_id=ra.event.pk).open()
|
self.page = pages.CreateRiskAssessment(self.driver, self.live_server_url, event_id=self.testEvent.pk).open()
|
||||||
assert 'edit' in logged_in_browser.url
|
self.assertIn('edit', self.driver.current_url)
|
||||||
|
|
||||||
|
def test_ra_edit(self):
|
||||||
|
self.page = pages.EditRiskAssessment(self.driver, self.live_server_url, pk=self.testRA.pk).open()
|
||||||
|
self.page.nonstandard_equipment = nse = True
|
||||||
|
self.page.general_notes = gn = "There are some notes, but I've not written them here as that would be helpful"
|
||||||
|
self.page.submit()
|
||||||
|
self.assertFalse(self.page.success)
|
||||||
|
self.page.supervisor_consulted = True
|
||||||
|
self.page.submit()
|
||||||
|
self.assertTrue(self.page.success)
|
||||||
|
# Check that data is right
|
||||||
|
ra = models.RiskAssessment.objects.get(pk=self.testRA.pk)
|
||||||
|
self.assertEqual(ra.general_notes, gn)
|
||||||
|
self.assertEqual(ra.nonstandard_equipment, nse)
|
||||||
|
|
||||||
|
def test_ec_create_small(self):
|
||||||
|
self.page = pages.CreateEventChecklist(self.driver, self.live_server_url, event_id=self.testEvent2.pk).open()
|
||||||
|
|
||||||
|
self.page.safe_parking = True
|
||||||
|
self.page.safe_packing = True
|
||||||
|
self.page.exits = True
|
||||||
|
self.page.trip_hazard = True
|
||||||
|
self.page.warning_signs = True
|
||||||
|
self.page.ear_plugs = True
|
||||||
|
self.page.hs_location = "The Moon"
|
||||||
|
self.page.extinguishers_location = "With the rest of the fire"
|
||||||
|
# If we do this first the search fails, for ... reasons
|
||||||
|
self.page.power_mic.search(self.profile.name)
|
||||||
|
self.page.power_mic.toggle()
|
||||||
|
self.assertFalse(self.page.power_mic.is_open)
|
||||||
|
|
||||||
|
# Gotta scroll to make the button clickable
|
||||||
|
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
|
||||||
|
|
||||||
|
self.page.earthing = True
|
||||||
|
self.page.rcds = True
|
||||||
|
self.page.supply_test = True
|
||||||
|
self.page.pat = True
|
||||||
|
|
||||||
|
self.page.submit()
|
||||||
|
self.assertTrue(self.page.success)
|
||||||
|
|
||||||
|
def test_ec_create_medium(self):
|
||||||
|
self.page = pages.CreateEventChecklist(self.driver, self.live_server_url, event_id=self.testEvent3.pk).open()
|
||||||
|
|
||||||
|
self.page.safe_parking = True
|
||||||
|
self.page.safe_packing = True
|
||||||
|
self.page.exits = True
|
||||||
|
self.page.trip_hazard = True
|
||||||
|
self.page.warning_signs = True
|
||||||
|
self.page.ear_plugs = True
|
||||||
|
self.page.hs_location = "Death Valley"
|
||||||
|
self.page.extinguishers_location = "With the rest of the fire"
|
||||||
|
# If we do this first the search fails, for ... reasons
|
||||||
|
self.page.power_mic.search(self.profile.name)
|
||||||
|
self.page.power_mic.toggle()
|
||||||
|
self.assertFalse(self.page.power_mic.is_open)
|
||||||
|
|
||||||
|
# Gotta scroll to make the button clickable
|
||||||
|
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
|
||||||
|
|
||||||
|
self.page.earthing = True
|
||||||
|
self.page.pat = True
|
||||||
|
self.page.source_rcd = True
|
||||||
|
self.page.labelling = True
|
||||||
|
self.page.fd_voltage_l1 = 240
|
||||||
|
self.page.fd_voltage_l2 = 235
|
||||||
|
self.page.fd_voltage_l3 = 0
|
||||||
|
self.page.fd_phase_rotation = True
|
||||||
|
self.page.fd_earth_fault = 666
|
||||||
|
self.page.fd_pssc = 1984
|
||||||
|
self.page.w1_description = "In the carpark, by the bins"
|
||||||
|
self.page.w1_polarity = True
|
||||||
|
self.page.w1_voltage = 240
|
||||||
|
self.page.w1_earth_fault = 333
|
||||||
|
|
||||||
|
self.page.submit()
|
||||||
|
self.assertTrue(self.page.success)
|
||||||
|
|
||||||
|
def test_ec_create_extras(self):
|
||||||
|
eid = self.testEvent2.pk
|
||||||
|
self.page = pages.CreateEventChecklist(self.driver, self.live_server_url, event_id=eid).open()
|
||||||
|
self.page.add_vehicle()
|
||||||
|
self.page.add_crew()
|
||||||
|
|
||||||
|
self.page.safe_parking = True
|
||||||
|
self.page.safe_packing = True
|
||||||
|
self.page.exits = True
|
||||||
|
self.page.trip_hazard = True
|
||||||
|
self.page.warning_signs = True
|
||||||
|
self.page.ear_plugs = True
|
||||||
|
self.page.hs_location = "The Moon"
|
||||||
|
self.page.extinguishers_location = "With the rest of the fire"
|
||||||
|
# If we do this first the search fails, for ... reasons
|
||||||
|
self.page.power_mic.search("Test") # FIXME
|
||||||
|
self.page.power_mic.toggle()
|
||||||
|
self.assertFalse(self.page.power_mic.is_open)
|
||||||
|
|
||||||
|
vehicle_name = 'Brian'
|
||||||
|
self.driver.find_element(By.XPATH, '//*[@name="vehicle_-1"]').send_keys(vehicle_name)
|
||||||
|
driver = base_regions.BootstrapSelectElement(self.page, self.driver.find_element(By.XPATH, '//tr[@id="vehicles_-1"]//div[contains(@class, "bootstrap-select")]'))
|
||||||
|
driver.search(self.profile.name)
|
||||||
|
|
||||||
|
crew = self.profile
|
||||||
|
role = "MIC"
|
||||||
|
crew_select = base_regions.BootstrapSelectElement(self.page, self.driver.find_element(By.XPATH, '//tr[@id="crew_-1"]//div[contains(@class, "bootstrap-select")]'))
|
||||||
|
start_time = base_regions.DateTimePicker(self.page, self.driver.find_element(By.XPATH, '//*[@name="start_-1"]'))
|
||||||
|
end_time = base_regions.DateTimePicker(self.page, self.driver.find_element(By.XPATH, '//*[@name="end_-1"]'))
|
||||||
|
|
||||||
|
start_time.set_value(timezone.make_aware(datetime.datetime(2015, 1, 1, 9, 0)))
|
||||||
|
# TODO Test validation of end before start
|
||||||
|
end_time.set_value(timezone.make_aware(datetime.datetime(2015, 1, 1, 10, 30)))
|
||||||
|
crew_select.search(crew.name)
|
||||||
|
self.driver.find_element(By.XPATH, '//*[@name="role_-1"]').send_keys(role)
|
||||||
|
|
||||||
|
self.page.earthing = True
|
||||||
|
self.page.rcds = True
|
||||||
|
self.page.supply_test = True
|
||||||
|
self.page.pat = True
|
||||||
|
|
||||||
|
self.page.submit()
|
||||||
|
self.assertTrue(self.page.success)
|
||||||
|
|
||||||
|
checklist = models.EventChecklist.objects.get(event=eid)
|
||||||
|
vehicle = models.EventChecklistVehicle.objects.get(checklist=checklist.pk)
|
||||||
|
self.assertEqual(vehicle_name, vehicle.vehicle)
|
||||||
|
crew_obj = models.EventChecklistCrew.objects.get(checklist=checklist.pk)
|
||||||
|
self.assertEqual(crew.pk, crew_obj.crewmember.pk)
|
||||||
|
self.assertEqual(role, crew_obj.role)
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ from datetime import date
|
|||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils.safestring import SafeText
|
|
||||||
from RIGS.templatetags.markdown_tags import markdown_filter
|
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from pytest_django.asserts import assertRedirects, assertNotContains, assertContains
|
from pytest_django.asserts import assertRedirects, assertNotContains, assertContains
|
||||||
@@ -172,7 +170,6 @@ class TestInvoiceDelete(TestCase):
|
|||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True,
|
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True,
|
||||||
is_active=True, is_staff=True)
|
is_active=True, is_staff=True)
|
||||||
cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
|
|
||||||
cls.events = {
|
cls.events = {
|
||||||
1: models.Event.objects.create(name="TE E1", start_date=date.today()),
|
1: models.Event.objects.create(name="TE E1", start_date=date.today()),
|
||||||
2: models.Event.objects.create(name="TE E2", start_date=date.today())
|
2: models.Event.objects.create(name="TE E2", start_date=date.today())
|
||||||
@@ -284,11 +281,11 @@ def test_xframe_headers(admin_client, basic_event):
|
|||||||
|
|
||||||
response = admin_client.get(event_url, follow=True)
|
response = admin_client.get(event_url, follow=True)
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
response.headers["X-Frame-Options"]
|
response._headers["X-Frame-Options"]
|
||||||
|
|
||||||
response = admin_client.get(login_url, follow=True)
|
response = admin_client.get(login_url, follow=True)
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
response.headers["X-Frame-Options"]
|
response._headers["X-Frame-Options"]
|
||||||
|
|
||||||
|
|
||||||
def test_oembed(client, basic_event):
|
def test_oembed(client, basic_event):
|
||||||
@@ -366,215 +363,6 @@ def test_checklist_review(admin_client, admin_user, checklist):
|
|||||||
def test_ra_redirect(admin_client, admin_user, ra):
|
def test_ra_redirect(admin_client, admin_user, ra):
|
||||||
request_url = reverse('event_ra', kwargs={'pk': ra.event.pk})
|
request_url = reverse('event_ra', kwargs={'pk': ra.event.pk})
|
||||||
expected_url = reverse('ra_edit', kwargs={'pk': ra.pk})
|
expected_url = reverse('ra_edit', kwargs={'pk': ra.pk})
|
||||||
|
|
||||||
response = admin_client.get(request_url, follow=True)
|
response = admin_client.get(request_url, follow=True)
|
||||||
assertRedirects(response, expected_url, status_code=302, target_status_code=200)
|
assertRedirects(response, expected_url, status_code=302, target_status_code=200)
|
||||||
|
|
||||||
|
|
||||||
class TestMarkdownTemplateTags(TestCase):
|
|
||||||
markdown = """
|
|
||||||
An h1 header
|
|
||||||
============
|
|
||||||
|
|
||||||
Paragraphs are separated by a blank line.
|
|
||||||
|
|
||||||
2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
|
|
||||||
look like:
|
|
||||||
|
|
||||||
* this one
|
|
||||||
* that one
|
|
||||||
* the other one
|
|
||||||
|
|
||||||
Note that --- not considering the asterisk --- the actual text
|
|
||||||
content starts at 4-columns in.
|
|
||||||
|
|
||||||
> Block quotes are
|
|
||||||
> written like so.
|
|
||||||
>
|
|
||||||
> They can span multiple paragraphs,
|
|
||||||
> if you like.
|
|
||||||
|
|
||||||
Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all
|
|
||||||
in chapters 12--14"). Three dots ... will be converted to an ellipsis.
|
|
||||||
Unicode is supported.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
An h2 header
|
|
||||||
------------
|
|
||||||
|
|
||||||
Here's a numbered list:
|
|
||||||
|
|
||||||
1. first item
|
|
||||||
2. second item
|
|
||||||
3. third item
|
|
||||||
|
|
||||||
Note again how the actual text starts at 4 columns in (4 characters
|
|
||||||
from the left side). Here's a code sample:
|
|
||||||
|
|
||||||
# Let me re-iterate ...
|
|
||||||
for i in 1 .. 10 { do-something(i) }
|
|
||||||
|
|
||||||
As you probably guessed, indented 4 spaces. By the way, instead of
|
|
||||||
indenting the block, you can use delimited blocks, if you like:
|
|
||||||
|
|
||||||
~~~
|
|
||||||
define foobar() {
|
|
||||||
print "Welcome to flavor country!";
|
|
||||||
}
|
|
||||||
~~~
|
|
||||||
|
|
||||||
(which makes copying & pasting easier). You can optionally mark the
|
|
||||||
delimited block for Pandoc to syntax highlight it:
|
|
||||||
|
|
||||||
~~~python
|
|
||||||
import time
|
|
||||||
# Quick, count to ten!
|
|
||||||
for i in range(10):
|
|
||||||
# (but not *too* quick)
|
|
||||||
time.sleep(0.5)
|
|
||||||
print i
|
|
||||||
~~~
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### An h3 header ###
|
|
||||||
|
|
||||||
Now a nested list:
|
|
||||||
|
|
||||||
1. First, get these ingredients:
|
|
||||||
|
|
||||||
* carrots
|
|
||||||
* celery
|
|
||||||
* lentils
|
|
||||||
|
|
||||||
2. Boil some water.
|
|
||||||
|
|
||||||
3. Dump everything in the pot and follow
|
|
||||||
this algorithm:
|
|
||||||
|
|
||||||
find wooden spoon
|
|
||||||
uncover pot
|
|
||||||
stir
|
|
||||||
cover pot
|
|
||||||
balance wooden spoon precariously on pot handle
|
|
||||||
wait 10 minutes
|
|
||||||
goto first step (or shut off burner when done)
|
|
||||||
|
|
||||||
Do not bump wooden spoon or it will fall.
|
|
||||||
|
|
||||||
Notice again how text always lines up on 4-space indents (including
|
|
||||||
that last line which continues item 3 above).
|
|
||||||
|
|
||||||
Here's a link to [a website](http://foo.bar). Here's a footnote [^1].
|
|
||||||
|
|
||||||
[^1]: Footnote text goes here.
|
|
||||||
|
|
||||||
Tables can look like this:
|
|
||||||
|
|
||||||
size material color
|
|
||||||
---- ------------ ------------
|
|
||||||
9 leather brown
|
|
||||||
10 hemp canvas natural
|
|
||||||
11 glass transparent
|
|
||||||
|
|
||||||
Table: Shoes, their sizes, and what they're made of
|
|
||||||
|
|
||||||
(The above is the caption for the table.) Pandoc also supports
|
|
||||||
multi-line tables:
|
|
||||||
|
|
||||||
-------- -----------------------
|
|
||||||
keyword text
|
|
||||||
-------- -----------------------
|
|
||||||
red Sunsets, apples, and
|
|
||||||
other red or reddish
|
|
||||||
things.
|
|
||||||
|
|
||||||
green Leaves, grass, frogs
|
|
||||||
and other things it's
|
|
||||||
not easy being.
|
|
||||||
-------- -----------------------
|
|
||||||
|
|
||||||
A horizontal rule follows.
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
Here's a definition list:
|
|
||||||
|
|
||||||
apples
|
|
||||||
: Good for making applesauce.
|
|
||||||
oranges
|
|
||||||
: Citrus!
|
|
||||||
tomatoes
|
|
||||||
: There's no "e" in tomatoe.
|
|
||||||
|
|
||||||
Again, text is indented 4 spaces. (Put a blank line between each
|
|
||||||
term/definition pair to spread things out more.)
|
|
||||||
|
|
||||||
Here's a "line block":
|
|
||||||
|
|
||||||
| Line one
|
|
||||||
| Line too
|
|
||||||
| Line tree
|
|
||||||
|
|
||||||
and images can be specified like so:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Inline math equations go in like so: $\\omega = d\\phi / dt$. Display
|
|
||||||
math should get its own line and be put in in double-dollarsigns:
|
|
||||||
|
|
||||||
$$I = \\int \rho R^{2} dV$$
|
|
||||||
|
|
||||||
And note that you can backslash-escape any punctuation characters
|
|
||||||
which you wish to be displayed literally, ex.: \\`foo\\`, \\*bar\\*, etc.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_html_safe(self):
|
|
||||||
html = markdown_filter(self.markdown)
|
|
||||||
self.assertIsInstance(html, SafeText)
|
|
||||||
|
|
||||||
def test_img_strip(self):
|
|
||||||
rml = markdown_filter(self.markdown, 'rml')
|
|
||||||
self.assertNotIn("<img", rml)
|
|
||||||
|
|
||||||
def test_code(self):
|
|
||||||
rml = markdown_filter(self.markdown, 'rml')
|
|
||||||
self.assertIn('<font face="Courier">monospace</font>', rml)
|
|
||||||
|
|
||||||
def test_blockquote(self):
|
|
||||||
rml = markdown_filter(self.markdown, 'rml')
|
|
||||||
self.assertIn("<pre>\nBlock quotes", rml)
|
|
||||||
|
|
||||||
def test_lists(self):
|
|
||||||
rml = markdown_filter(self.markdown, 'rml')
|
|
||||||
self.assertIn("<li><para>second item</para></li>", rml) # <ol>
|
|
||||||
self.assertIn("<li><para>that one</para></li>", rml) # <ul>
|
|
||||||
|
|
||||||
def test_in_print(self):
|
|
||||||
event = models.Event.objects.create(
|
|
||||||
name="MD Print Test",
|
|
||||||
description=self.markdown,
|
|
||||||
start_date='2016-01-01',
|
|
||||||
)
|
|
||||||
user = models.Profile.objects.create(
|
|
||||||
username='RML test',
|
|
||||||
is_superuser=True, # Don't care about permissions
|
|
||||||
is_active=True,
|
|
||||||
)
|
|
||||||
user.set_password('rmltester')
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
self.assertTrue(self.client.login(username=user.username, password='rmltester'))
|
|
||||||
response = self.client.get(reverse('event_print', kwargs={'pk': event.pk}))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
# By the time we have a PDF it should be larger than the original by some margin
|
|
||||||
# RML hard fails if something doesn't work
|
|
||||||
self.assertGreater(len(response.content), len(self.markdown))
|
|
||||||
|
|
||||||
def test_nonetype(self):
|
|
||||||
html = markdown_filter(None)
|
|
||||||
self.assertIsNone(html)
|
|
||||||
|
|
||||||
def test_linebreaks(self):
|
|
||||||
html = markdown_filter(self.markdown)
|
|
||||||
self.assertIn("Itemized lists<br/>\nlook like", html)
|
|
||||||
|
|||||||
@@ -132,8 +132,6 @@ urlpatterns = [
|
|||||||
name='event_authorise_preview'),
|
name='event_authorise_preview'),
|
||||||
re_path(r'^event/(?P<pk>\d+)/(?P<hmac>[-:\w]+)/$', rigboard.EventAuthorise.as_view(),
|
re_path(r'^event/(?P<pk>\d+)/(?P<hmac>[-:\w]+)/$', rigboard.EventAuthorise.as_view(),
|
||||||
name='event_authorise'),
|
name='event_authorise'),
|
||||||
re_path(r'^event/(?P<pk>\d+)/(?P<hmac>[-:\w]+)/preview/$', rigboard.EventAuthorise.as_view(preview=True),
|
|
||||||
name='event_authorise_form_preview'),
|
|
||||||
|
|
||||||
# ICS Calendar - API key authentication
|
# ICS Calendar - API key authentication
|
||||||
re_path(r'^ical/(?P<api_pk>\d+)/(?P<api_key>\w+)/rigs.ics$', api_key_required(ical.CalendarICS()),
|
re_path(r'^ical/(?P<api_pk>\d+)/(?P<api_key>\w+)/rigs.ics$', api_key_required(ical.CalendarICS()),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class PersonList(GenericListView):
|
|||||||
model = models.Person
|
model = models.Person
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(PersonList, self).get_context_data(**kwargs)
|
||||||
context['page_title'] = "People"
|
context['page_title'] = "People"
|
||||||
context['create'] = 'person_create'
|
context['create'] = 'person_create'
|
||||||
context['edit'] = 'person_update'
|
context['edit'] = 'person_update'
|
||||||
@@ -19,7 +19,7 @@ class PersonDetail(GenericDetailView):
|
|||||||
model = models.Person
|
model = models.Person
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(PersonDetail, self).get_context_data(**kwargs)
|
||||||
context['history_link'] = 'person_history'
|
context['history_link'] = 'person_history'
|
||||||
context['detail_link'] = 'person_detail'
|
context['detail_link'] = 'person_detail'
|
||||||
context['update_link'] = 'person_update'
|
context['update_link'] = 'person_update'
|
||||||
@@ -49,7 +49,7 @@ class OrganisationList(GenericListView):
|
|||||||
model = models.Organisation
|
model = models.Organisation
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(OrganisationList, self).get_context_data(**kwargs)
|
||||||
context['create'] = 'organisation_create'
|
context['create'] = 'organisation_create'
|
||||||
context['edit'] = 'organisation_update'
|
context['edit'] = 'organisation_update'
|
||||||
context['can_edit'] = self.request.user.has_perm('RIGS.change_organisation')
|
context['can_edit'] = self.request.user.has_perm('RIGS.change_organisation')
|
||||||
@@ -62,7 +62,7 @@ class OrganisationDetail(GenericDetailView):
|
|||||||
model = models.Organisation
|
model = models.Organisation
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(OrganisationDetail, self).get_context_data(**kwargs)
|
||||||
context['history_link'] = 'organisation_history'
|
context['history_link'] = 'organisation_history'
|
||||||
context['detail_link'] = 'organisation_detail'
|
context['detail_link'] = 'organisation_detail'
|
||||||
context['update_link'] = 'organisation_update'
|
context['update_link'] = 'organisation_update'
|
||||||
@@ -92,7 +92,7 @@ class VenueList(GenericListView):
|
|||||||
model = models.Venue
|
model = models.Venue
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(VenueList, self).get_context_data(**kwargs)
|
||||||
context['create'] = 'venue_create'
|
context['create'] = 'venue_create'
|
||||||
context['edit'] = 'venue_update'
|
context['edit'] = 'venue_update'
|
||||||
context['can_edit'] = self.request.user.has_perm('RIGS.change_venue')
|
context['can_edit'] = self.request.user.has_perm('RIGS.change_venue')
|
||||||
@@ -104,7 +104,7 @@ class VenueDetail(GenericDetailView):
|
|||||||
model = models.Venue
|
model = models.Venue
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super(VenueDetail, self).get_context_data(**kwargs)
|
||||||
context['history_link'] = 'venue_history'
|
context['history_link'] = 'venue_history'
|
||||||
context['detail_link'] = 'venue_detail'
|
context['detail_link'] = 'venue_detail'
|
||||||
context['update_link'] = 'venue_update'
|
context['update_link'] = 'venue_update'
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
default_app_config = 'assets.apps.AssetsAppConfig'
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import urllib.parse
|
|
||||||
|
|
||||||
|
|
||||||
class AssetIDConverter: # Forces lowercase to uppercase
|
|
||||||
regex = '[^/]+'
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
return str(value).upper()
|
|
||||||
|
|
||||||
def to_url(self, value):
|
|
||||||
return str(value).upper()
|
|
||||||
|
|
||||||
|
|
||||||
class ListConverter:
|
|
||||||
regex = '[^/]+'
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
return value.split(',')
|
|
||||||
|
|
||||||
def to_url(self, value):
|
|
||||||
string = ""
|
|
||||||
for i in value:
|
|
||||||
string += "," + str(i)
|
|
||||||
return string[1:]
|
|
||||||
@@ -32,8 +32,6 @@ class AssetSearchForm(forms.Form):
|
|||||||
q = forms.CharField(required=False)
|
q = forms.CharField(required=False)
|
||||||
category = forms.ModelMultipleChoiceField(models.AssetCategory.objects.all(), required=False)
|
category = forms.ModelMultipleChoiceField(models.AssetCategory.objects.all(), required=False)
|
||||||
status = forms.ModelMultipleChoiceField(models.AssetStatus.objects.all(), required=False)
|
status = forms.ModelMultipleChoiceField(models.AssetStatus.objects.all(), required=False)
|
||||||
is_cable = forms.BooleanField(required=False)
|
|
||||||
date_acquired = forms.DateField(required=False)
|
|
||||||
|
|
||||||
|
|
||||||
class SupplierForm(forms.ModelForm):
|
class SupplierForm(forms.ModelForm):
|
||||||
@@ -46,3 +44,11 @@ class CableTypeForm(forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = models.CableType
|
model = models.CableType
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
form_data = self.cleaned_data
|
||||||
|
queryset = models.CableType.objects.filter(Q(plug=form_data['plug']) & Q(socket=form_data['socket']) & Q(circuits=form_data['circuits']) & Q(cores=form_data['cores']))
|
||||||
|
# Being identical to itself shouldn't count...
|
||||||
|
if queryset.exists() and self.instance.pk != queryset[0].pk:
|
||||||
|
raise forms.ValidationError("A cable type that exactly matches this one already exists, please use that instead.", code="notunique")
|
||||||
|
return form_data
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ class Command(BaseCommand):
|
|||||||
assets = []
|
assets = []
|
||||||
|
|
||||||
def handle(self, *args, **kwargs):
|
def handle(self, *args, **kwargs):
|
||||||
print("Generating sample assets data")
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
if not (settings.DEBUG or settings.STAGING):
|
if not (settings.DEBUG or settings.STAGING):
|
||||||
@@ -35,7 +34,6 @@ class Command(BaseCommand):
|
|||||||
self.create_assets()
|
self.create_assets()
|
||||||
self.create_connectors()
|
self.create_connectors()
|
||||||
self.create_cables()
|
self.create_cables()
|
||||||
print("Done generating sample assets data")
|
|
||||||
|
|
||||||
def create_categories(self):
|
def create_categories(self):
|
||||||
choices = ['Case', 'Video', 'General', 'Sound', 'Lighting', 'Rigging']
|
choices = ['Case', 'Video', 'General', 'Sound', 'Lighting', 'Rigging']
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ def add_default(apps, schema_editor):
|
|||||||
Connector = apps.get_model('assets', 'Connector')
|
Connector = apps.get_model('assets', 'Connector')
|
||||||
for cable_type in CableType.objects.all():
|
for cable_type in CableType.objects.all():
|
||||||
if cable_type.plug is None:
|
if cable_type.plug is None:
|
||||||
cable_type.plug = Connector.objects.first()
|
cable_type.plug = Connector.first()
|
||||||
if cable_type.socket is None:
|
if cable_type.socket is None:
|
||||||
cable_type.socket = Connector.objects.first()
|
cable_type.socket = Connector.first()
|
||||||
cable_type.save()
|
cable_type.save()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 3.1.7 on 2021-03-02 12:04
|
# Generated by Django 3.1.5 on 2021-02-08 16:03
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
@@ -7,7 +7,7 @@ import django.db.models.deletion
|
|||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('assets', '0020_auto_20210302_1201'),
|
('assets', '0019_fix_cabletype'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 3.1.7 on 2021-03-02 12:01
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
def postgres_migration_prep(apps, schema_editor):
|
|
||||||
model = apps.get_model("assets", "Supplier")
|
|
||||||
fields = ["address", "email", "notes", "phone"]
|
|
||||||
for field in fields:
|
|
||||||
filter_param = {"{}__isnull".format(field): True}
|
|
||||||
update_param = {field: ""}
|
|
||||||
model.objects.filter(**filter_param).update(**update_param)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0019_fix_cabletype'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(postgres_migration_prep, migrations.RunPython.noop)
|
|
||||||
]
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 3.2.11 on 2022-01-12 19:11
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('assets', '0021_auto_20210302_1204'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='cabletype',
|
|
||||||
unique_together={('plug', 'socket', 'circuits', 'cores')},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -6,8 +6,7 @@ from django.urls import reverse
|
|||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
from reversion.models import Version
|
from reversion.models import Version
|
||||||
|
|
||||||
from RIGS.models import Profile
|
from RIGS.models import RevisionMixin, Profile
|
||||||
from versioning.versioning import RevisionMixin
|
|
||||||
|
|
||||||
|
|
||||||
class AssetCategory(models.Model):
|
class AssetCategory(models.Model):
|
||||||
@@ -50,7 +49,7 @@ class Supplier(models.Model, RevisionMixin):
|
|||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('supplier_detail', kwargs={'pk': self.pk})
|
return reverse('supplier_list')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@@ -76,17 +75,13 @@ class CableType(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['plug', 'socket', '-circuits']
|
ordering = ['plug', 'socket', '-circuits']
|
||||||
unique_together = ['plug', 'socket', 'circuits', 'cores']
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.plug and self.socket:
|
if self.plug and self.socket:
|
||||||
return f"{self.plug.description} → {self.socket.description}"
|
return "%s → %s" % (self.plug.description, self.socket.description)
|
||||||
else:
|
else:
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('cable_type_detail', kwargs={'pk': self.pk})
|
|
||||||
|
|
||||||
|
|
||||||
def get_available_asset_id(wanted_prefix=""):
|
def get_available_asset_id(wanted_prefix=""):
|
||||||
sql = """
|
sql = """
|
||||||
@@ -104,7 +99,6 @@ def get_available_asset_id(wanted_prefix=""):
|
|||||||
return 9000
|
return 9000
|
||||||
else:
|
else:
|
||||||
return row[0]
|
return row[0]
|
||||||
cursor.close()
|
|
||||||
|
|
||||||
|
|
||||||
@reversion.register
|
@reversion.register
|
||||||
@@ -149,7 +143,7 @@ class Asset(models.Model, RevisionMixin):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.asset_id} | {self.description}"
|
return "{} | {}".format(self.asset_id, self.description)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('asset_detail', kwargs={'pk': self.asset_id})
|
return reverse('asset_detail', kwargs={'pk': self.asset_id})
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 18 KiB |
@@ -64,16 +64,16 @@
|
|||||||
<div class="form-group form-row">
|
<div class="form-group form-row">
|
||||||
{% include 'partials/form_field.html' with field=form.length append=form.length.help_text col="col-6" %}
|
{% include 'partials/form_field.html' with field=form.length append=form.length.help_text col="col-6" %}
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<button class="btn btn-danger" onclick="setFieldValue('{{ form.length.id_for_label }}','5');" tabindex="-1" type="button">5{{ form.length.help_text }}</button>
|
<button class="btn btn-danger" onclick="setFieldValue('{{ form.length.id_for_label }}','5');" tabindex="-1">5{{ form.length.help_text }}</button>
|
||||||
<button class="btn btn-success" onclick="setFieldValue('{{ form.length.id_for_label }}','10');" tabindex="-1" type="button">10{{ form.length.help_text }}</button>
|
<button class="btn btn-success" onclick="setFieldValue('{{ form.length.id_for_label }}','10');" tabindex="-1">10{{ form.length.help_text }}</button>
|
||||||
<button class="btn btn-info" onclick="setFieldValue('{{ form.length.id_for_label }}','20');" tabindex="-1" type="button">20{{ form.length.help_text }}</button>
|
<button class="btn btn-info" onclick="setFieldValue('{{ form.length.id_for_label }}','20');" tabindex="-1">20{{ form.length.help_text }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group form-row">
|
<div class="form-group form-row">
|
||||||
{% include 'partials/form_field.html' with field=form.csa append=form.csa.help_text title='CSA' col="col-6" %}
|
{% include 'partials/form_field.html' with field=form.csa append=form.csa.help_text title='CSA' col="col-6" %}
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<button class="btn btn-secondary" onclick="setFieldValue('{{ form.csa.id_for_label }}', '1.5');" tabindex="-1" type="button">1.5{{ form.csa.help_text }}</button>
|
<button class="btn btn-secondary" onclick="setFieldValue('{{ form.csa.id_for_label }}', '1.5');" tabindex="-1">1.5{{ form.csa.help_text }}</button>
|
||||||
<button class="btn btn-secondary" onclick="setFieldValue('{{ form.csa.id_for_label }}', '2.5');" tabindex="-1" type="button">2.5{{ form.csa.help_text }}</button>
|
<button class="btn btn-secondary" onclick="setFieldValue('{{ form.csa.id_for_label }}', '2.5');" tabindex="-1">2.5{{ form.csa.help_text }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
{% load widget_tweaks %}
|
{% load widget_tweaks %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
|
<script src="{% static 'js/jquery-ui.js' %}"></script>
|
||||||
|
<script src="{% static "js/interaction.js" %}"></script>
|
||||||
|
<script src="{% static "js/modal.js" %}"></script>
|
||||||
<script>
|
<script>
|
||||||
$('document').ready(function(){
|
$('document').ready(function(){
|
||||||
$('#asset-search-form').submit(function () {
|
$('#asset-search-form').submit(function () {
|
||||||
@@ -12,7 +15,9 @@
|
|||||||
});
|
});
|
||||||
$('#searchButton').click(function (e) {
|
$('#searchButton').click(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var url = "{% url 'asset_audit' None %}".replace('None', $("#{{form.q.id_for_label}}").val());
|
var url = "{% url 'asset_audit' None %}";
|
||||||
|
var id = $("#{{form.q.id_for_label}}").val();
|
||||||
|
url = url.replace('None', id);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
success: function(){
|
success: function(){
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
{% load widget_tweaks %}
|
{% load widget_tweaks %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row justify-content-end">
|
|
||||||
{% include 'partials/asset_buttons.html' %}
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
{% include 'partials/asset_detail_form.html' %}
|
{% include 'partials/asset_detail_form.html' %}
|
||||||
|
|||||||
@@ -3,20 +3,13 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ block.super }}
|
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/>
|
||||||
<link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
|
<link rel="stylesheet" href="{% static 'css/ajax-bootstrap-select.css' %}"/>
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'css/simplemde.min.css' %}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block preload_js %}
|
|
||||||
{{ block.super }}
|
|
||||||
<script src="{% static 'js/selects.js' %}"></script>
|
|
||||||
<script src="{% static 'js/simplemde.min.js' %}"></script>
|
|
||||||
<script src="{% static 'js/interaction.js' %}"></script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ block.super }}
|
<script src="{% static 'js/bootstrap-select.js' %}"></script>
|
||||||
|
<script src="{% static 'js/ajax-bootstrap-select.js' %}"></script>
|
||||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||||
<script>
|
<script>
|
||||||
const matches = window.matchMedia("(prefers-reduced-motion: reduce)").matches || window.matchMedia("(update: slow)").matches;
|
const matches = window.matchMedia("(prefers-reduced-motion: reduce)").matches || window.matchMedia("(update: slow)").matches;
|
||||||
@@ -37,7 +30,7 @@
|
|||||||
})
|
})
|
||||||
.ajaxSelectPicker({
|
.ajaxSelectPicker({
|
||||||
ajax: {
|
ajax: {
|
||||||
url: "{% url 'asset_search_json' %}",
|
url: '{% url 'asset_search_json' %}',
|
||||||
type: "GET",
|
type: "GET",
|
||||||
data: function () {
|
data: function () {
|
||||||
let params = {
|
let params = {
|
||||||
@@ -75,11 +68,6 @@
|
|||||||
preserveSelected: false
|
preserveSelected: false
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
|
||||||
$(document).ready(function () {
|
|
||||||
setupMDE('#id_comments');
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
{% extends 'base_assets.html' %}
|
{% extends 'base_assets.html' %}
|
||||||
{% load paginator from filters %}
|
{% load paginator from filters %}
|
||||||
{% load button from filters %}
|
{% load button from filters %}
|
||||||
{% load ids_from_objects from asset_tags %}
|
|
||||||
{% load widget_tweaks %}
|
{% load widget_tweaks %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ block.super }}
|
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/>
|
||||||
<link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
|
<link rel="stylesheet" href="{% static 'css/ajax-bootstrap-select.css' %}"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block preload_js %}
|
{% block preload_js %}
|
||||||
{{ block.super }}
|
<script src="{% static 'js/bootstrap-select.js' %}"></script>
|
||||||
<script src="{% static 'js/selects.js' %}" async></script>
|
<script src="{% static 'js/ajax-bootstrap-select.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ block.super }}
|
|
||||||
<script>
|
<script>
|
||||||
//Get querystring value
|
//Get querystring value
|
||||||
function getParameterByName(name) {
|
function getParameterByName(name) {
|
||||||
@@ -61,54 +59,27 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col px-0">
|
<div class="col px-0">
|
||||||
<form id="asset-search-form" method="GET">
|
<form id="asset-search-form" method="GET" class="form-inline justify-content-end">
|
||||||
<div class="form-row">
|
|
||||||
<div class="col">
|
|
||||||
<div class="input-group px-1 mb-2 mb-sm-0 flex-nowrap">
|
<div class="input-group px-1 mb-2 mb-sm-0 flex-nowrap">
|
||||||
{% render_field form.q|add_class:'form-control' placeholder='Enter Asset ID/Desc/Serial' %}
|
{% render_field form.q|add_class:'form-control' placeholder='Enter Asset ID/Desc/Serial' %}
|
||||||
<label for="q" class="sr-only">Asset ID/Description/Serial Number:</label>
|
<label for="q" class="sr-only">Asset ID/Description/Serial Number:</label>
|
||||||
<span class="input-group-append">{% button 'search' id="id_search" %}</span>
|
<span class="input-group-append">{% button 'search' id="id_search" %}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-row mt-2">
|
|
||||||
<div class="col">
|
|
||||||
<div id="category-group" class="form-group px-1" style="margin-bottom: 0;">
|
<div id="category-group" class="form-group px-1" style="margin-bottom: 0;">
|
||||||
<label for="category" class="sr-only">Category</label>
|
<label for="category" class="sr-only">Category</label>
|
||||||
{% render_field form.category|attr:'multiple'|add_class:'form-control custom-select selectpicker col-sm' data-none-selected-text="Categories" data-header="Categories" data-actions-box="true" %}
|
{% render_field form.category|attr:'multiple'|add_class:'form-control custom-select selectpicker col-sm' data-none-selected-text="Categories" data-header="Categories" data-actions-box="true" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<div id="status-group" class="form-group px-1" style="margin-bottom: 0;">
|
<div id="status-group" class="form-group px-1" style="margin-bottom: 0;">
|
||||||
<label for="status" class="sr-only">Status</label>
|
<label for="status" class="sr-only">Status</label>
|
||||||
{% render_field form.status|attr:'multiple'|add_class:'form-control custom-select selectpicker col-sm' data-none-selected-text="Statuses" data-header="Statuses" data-actions-box="true" %}
|
{% render_field form.status|attr:'multiple'|add_class:'form-control custom-select selectpicker col-sm' data-none-selected-text="Statuses" data-header="Statuses" data-actions-box="true" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="col mt-2">
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
{% render_field form.is_cable|add_class:'form-check-input' %}
|
|
||||||
<label class="form-check-label" for="is_cable">Only Cables?</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<div class="form-group d-flex flex-nowrap">
|
|
||||||
<label for="date_acquired" class="text-nowrap mt-auto">Date Acquired</label>
|
|
||||||
{% render_field form.date_acquired|add_class:'form-control mx-2' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto mr-auto">
|
|
||||||
<button id="filter-submit" type="submit" class="btn btn-secondary" style="width: 6em">Filter</button>
|
<button id="filter-submit" type="submit" class="btn btn-secondary" style="width: 6em">Filter</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row my-2">
|
<div class="row my-2">
|
||||||
<div class="col text-right px-0">
|
<div class="col text-right px-0">
|
||||||
{% button 'new' 'asset_create' style="width: 6em" %}
|
{% button 'new' 'asset_create' style="width: 6em" %}
|
||||||
{% if object_list %}
|
|
||||||
<a class="btn btn-primary" href="{% url 'generate_labels' object_list|ids_from_objects %}"><span class="fas fa-barcode"></span> Generate Labels</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row my-2">
|
<div class="row my-2">
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<!DOCTYPE document SYSTEM "rml.dtd">
|
|
||||||
{% load multiply from filters %}
|
|
||||||
{% load index from asset_tags %}
|
|
||||||
<document filename="{{filename}}">
|
|
||||||
<template>
|
|
||||||
<pageTemplate id="main">
|
|
||||||
<pageGraphics>
|
|
||||||
</pageGraphics>
|
|
||||||
<frame id="first" x1="5" y1="-10" width="581" height="842"/>
|
|
||||||
</pageTemplate>
|
|
||||||
</template>
|
|
||||||
<stylesheet>
|
|
||||||
<blockTableStyle id="table">
|
|
||||||
<!-- show a grid: this also comes in handy for debugging your tables.-->
|
|
||||||
<lineStyle kind="GRID" colorName="black" thickness="1" start="0,0" stop="-1,-1" />
|
|
||||||
</blockTableStyle>
|
|
||||||
</stylesheet>
|
|
||||||
<story>
|
|
||||||
<blockTable style="table">
|
|
||||||
{% for i in images0 %}
|
|
||||||
<tr>
|
|
||||||
<td>{% with images0|index:forloop.counter0 as image %}{% if image %}<illustration width="120" height="35"><image file="data:image/png;base64,{{image}}" x="0" y="0"
|
|
||||||
width="120" height="35"/></illustration>{% endif %}{% endwith %}</td>
|
|
||||||
<td>{% with images1|index:forloop.counter0 as image %}{% if image %}<illustration width="120" height="35"><image file="data:image/png;base64,{{image}}" x="0" y="0"
|
|
||||||
width="120" height="35"/></illustration>{% endif %}{% endwith %}</td>
|
|
||||||
<td>{% with images2|index:forloop.counter0 as image %}{% if image %}<illustration width="120" height="35"><image file="data:image/png;base64,{{image}}" x="0" y="0"
|
|
||||||
width="120" height="35"/></illustration>{% endif %}{% endwith %}</td>
|
|
||||||
<td>{% with images3|index:forloop.counter0 as image %}{% if image %}<illustration width="120" height="35"><image file="data:image/png;base64,{{image}}" x="0" y="0"
|
|
||||||
width="120" height="35"/></illustration>{% endif %}{% endwith %}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</blockTable>
|
|
||||||
</story>
|
|
||||||
</document>
|
|
||||||
@@ -5,14 +5,13 @@
|
|||||||
{% button 'submit' %}
|
{% button 'submit' %}
|
||||||
{% elif duplicate %}
|
{% elif duplicate %}
|
||||||
<!--duplicate-->
|
<!--duplicate-->
|
||||||
<button type="submit" class="btn btn-success"><span class="fas fa-check"></span> Create Duplicate</button>
|
<button type="submit" class="btn btn-success"><i class="fas fa-check"></i> Create Duplicate</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<!--detail view-->
|
<!--detail view-->
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
{% button 'edit' url='asset_update' pk=object.asset_id %}
|
{% button 'edit' url='asset_update' pk=object.asset_id %}
|
||||||
{% button 'duplicate' url='asset_duplicate' pk=object.asset_id %}
|
{% button 'duplicate' url='asset_duplicate' pk=object.asset_id %}
|
||||||
<a type="button" class="btn btn-info" href="{% url 'asset_audit' object.asset_id %}"><span class="fas fa-certificate"></span> Audit</a>
|
<a type="button" class="btn btn-info" href="{% url 'asset_audit' object.asset_id %}"><i class="fas fa-certificate"></i> Audit</a>
|
||||||
<a type="button" class="btn btn-primary" href="{% url 'generate_label' object.asset_id %}"><span class="fas fa-barcode"></span> Generate Label</a>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if create or edit or duplicate %}
|
{% if create or edit or duplicate %}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
{% load widget_tweaks %}
|
{% load widget_tweaks %}
|
||||||
{% load markdown_tags %}
|
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
Asset Details
|
Asset Details
|
||||||
@@ -40,14 +38,14 @@
|
|||||||
<!---TODO: Lower default number of lines in comments box-->
|
<!---TODO: Lower default number of lines in comments box-->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="{{ form.comments.id_for_label }}">Comments</label>
|
<label for="{{ form.comments.id_for_label }}">Comments</label>
|
||||||
{% render_field form.comments|add_class:'form-control md-enabled' %}
|
{% render_field form.comments|add_class:'form-control' %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<dt>Asset ID</dt>
|
<dt>Asset ID</dt>
|
||||||
<dd>{{ object.asset_id }}</dd>
|
<dd>{{ object.asset_id }}</dd>
|
||||||
|
|
||||||
<dt>Description</dt>
|
<dt>Description</dt>
|
||||||
<dd>{{ object.description }}</dd>
|
<dd style="overflow-wrap: break-word;">{{ object.description }}</dd>
|
||||||
|
|
||||||
<dt>Category</dt>
|
<dt>Category</dt>
|
||||||
<dd>{{ object.category }}</dd>
|
<dd>{{ object.category }}</dd>
|
||||||
@@ -59,7 +57,7 @@
|
|||||||
<dd>{{ object.serial_number|default:'-' }}</dd>
|
<dd>{{ object.serial_number|default:'-' }}</dd>
|
||||||
|
|
||||||
<dt>Comments</dt>
|
<dt>Comments</dt>
|
||||||
<dd style="overflow-wrap: break-word;">{{ object.comments|default:'-'|markdown }}</dd>
|
<dd style="overflow-wrap: break-word;">{{ object.comments|default:'-'|linebreaksbr }}</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,9 +17,11 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Cable Type</dt>
|
<dt>Cable Type</dt>
|
||||||
<dd>{% if object.cable_type %}<a href="{{object.cable_type.get_absolute_url}}">{{ object.cable_type }}</a>{%else%}-{%endif%}</dd>
|
<dd>{{ object.cable_type|default_if_none:'-' }}</dd>
|
||||||
|
|
||||||
<dt>Length</dt>
|
<dt>Length</dt>
|
||||||
<dd>{{ object.length|default_if_none:'-' }}m</dd>
|
<dd>{{ object.length|default_if_none:'-' }}m</dd>
|
||||||
|
|
||||||
<dt>Cross Sectional Area</dt>
|
<dt>Cross Sectional Area</dt>
|
||||||
<dd>{{ object.csa|default_if_none:'-' }}mm²</dd>
|
<dd>{{ object.csa|default_if_none:'-' }}mm²</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|||||||
@@ -28,13 +28,13 @@
|
|||||||
|
|
||||||
<dt>Children</dt>
|
<dt>Children</dt>
|
||||||
{% if object.asset_parent.all %}
|
{% if object.asset_parent.all %}
|
||||||
<div style="max-height: 200px; overflow-y: auto; -webkit-overflow-scrolling: touch; ">
|
|
||||||
{% for child in object.asset_parent.all %}
|
{% for child in object.asset_parent.all %}
|
||||||
<dd>
|
<dd>
|
||||||
<a href="{% url 'asset_detail' child.asset_id %}">{{ child }}</a>
|
<a href="{% url 'asset_detail' child.asset_id %}">
|
||||||
|
{{ child.asset_id }} - {{ child.description }}
|
||||||
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<dd><span>-</span></dd>
|
<dd><span>-</span></dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||