Compare commits

...

55 Commits

Author SHA1 Message Date
d6c789d90a Whoops 2021-03-02 11:24:22 +00:00
4fe7b874a4 Implement django doctor suggestions 2021-03-02 11:21:44 +00:00
ac0425740e Revert "Very initial pass at reimplementing event table fully responsive"
This reverts commit f5d875d153.
2021-03-02 11:11:18 +00:00
5382d8ae00 Revert "Further work at grid-table, including tablet layout"
This reverts commit b3939d8426.
2021-03-02 11:11:18 +00:00
08a55644c5 Delete unused signature template 2021-02-25 14:07:40 +00:00
b3939d8426 Further work at grid-table, including tablet layout 2021-02-25 14:02:34 +00:00
f775cee7dd Should fix CI 2021-02-24 22:42:30 +00:00
f5d875d153 Very initial pass at reimplementing event table fully responsive 2021-02-24 22:41:15 +00:00
f88f418503 Fixed a couple of places where profiles should have been linked but weren't 2021-02-24 18:20:51 +00:00
f3d9646607 Enable preview of event authorisation 2021-02-23 16:01:05 +00:00
798689403d Another go at CI caching 2021-02-23 13:58:03 +00:00
d1069a1745 That was silly 2021-02-23 01:27:42 +00:00
238d53a547 Try reimplementing caching on CI 2021-02-23 01:25:32 +00:00
8756e7f880 Oops 2021-02-23 01:19:38 +00:00
25c9cf25f7 Disable asset cache on ci, try to get assets working on heroku
Whyyy do things just randomly fall over
2021-02-23 01:11:33 +00:00
ad66f434de bah staticfiles 2021-02-23 01:01:36 +00:00
1aae2bff28 Damn you pipeline 2021-02-23 00:40:27 +00:00
9302cccd58 Swithc to pathlib in settings 2021-02-23 00:34:27 +00:00
123c8bfd80 Serve logo in next gen format 2021-02-22 23:53:53 +00:00
02af3870aa Can't async jquery 2021-02-22 23:48:23 +00:00
6d3a861df5 Preload fontawesome 2021-02-22 23:46:20 +00:00
0e07c50c18 Prevent search engines indexing us
Might help cut down the spam
2021-02-22 23:41:58 +00:00
8505d5e93a Squash jquery and popper together 2021-02-22 23:41:42 +00:00
ff780f2042 >.> 2021-02-22 14:00:06 +00:00
4a6d69c002 Drop jquery-ui in favour of html5sortable
10x smaller dependency!
2021-02-22 13:42:39 +00:00
28a70667c2 Fixes 2021-02-22 12:54:28 +00:00
5edb61f243 Fix dark theme test when no user is available 2021-02-22 12:17:40 +00:00
4e4492bc01 Load dark css only when required 2021-02-22 11:26:37 +00:00
697024e91b Slimmer way of including FOntAwesome 2021-02-22 11:00:09 +00:00
b5e80382b9 Various fixes for prior 2021-02-22 10:43:11 +00:00
ffbcfe28a9 Concat select styles/js 2021-02-22 01:03:27 +00:00
10af465a06 Use moment to keep cached timeagos up to date
Blerugh.
2021-02-22 00:43:45 +00:00
f1af5925b1 Concat 1st and 3rd party base js 2021-02-22 00:32:46 +00:00
b3adadceff Minify base js 2021-02-21 18:13:15 +00:00
2044cbdac2 Use pip installed fontawesome and css/webfont loading rather than JS 2021-02-21 02:15:16 +00:00
a789184c1c Disable template coverage plugin
Is that what's crashing??? Plausibly: https://github.com/suda/pytest_django_coverage_test
2021-02-21 01:04:47 +00:00
cebff5adda When in doubt, sleep 2021-02-21 00:22:51 +00:00
cc538c659c poke 2021-02-17 14:54:33 +00:00
f3409d0680 Turn up the verbosity of CI tests 2021-02-15 18:03:59 +00:00
1a30a418b1 Whoops 2021-02-15 17:49:52 +00:00
3aeafde96e Port RA interaction test(s) to pytest 2021-02-15 17:40:50 +00:00
f4a163f63c Minor futzing with status display 2021-02-15 16:45:45 +00:00
e14e250896 Fix crew test 2021-02-15 16:38:36 +00:00
59a9fd5bb4 Oops 2021-02-15 00:45:31 +00:00
925498be02 Fix database locking shenanigans 2021-02-15 00:44:26 +00:00
6c9e360927 More regions for checklist interaction tests 2021-02-14 22:50:22 +00:00
c02e2e6bbf Partial refactor of event checklist tests 2021-02-14 19:00:30 +00:00
be5aa892f0 Middle align homepage list icons 2021-02-14 18:37:35 +00:00
23ac9fb62a etc 2021-02-14 11:15:39 +00:00
7f05468483 argh 2021-02-14 02:36:07 +00:00
5a36e33bf0 etc 2021-02-14 02:19:28 +00:00
b3ceed777e etc 2021-02-14 02:07:46 +00:00
a7119599ca more poking 2021-02-14 01:37:06 +00:00
2396e27943 Potentially fix tests, init splinter 2021-02-13 18:42:56 +00:00
8204fdae1f Minor template fix 2021-02-13 18:42:48 +00:00
63 changed files with 1126 additions and 679 deletions

View File

@@ -1,3 +1,5 @@
[run] [run]
plugins = django_coverage_plugin omit = */migrations/*
omit = */migrations/*, */tests/* */tests/*
*/site-packages/*
*/distutils/*

View File

@@ -10,38 +10,37 @@ 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: Cache Static Files
id: static-cache
uses: actions/cache@v2
with:
path: 'static/'
key: ${{ hashFiles('package-lock.json') }}-${{ hashFiles('pipeline/source_assets') }}
- uses: bahmutov/npm-install@v1
if: steps.static-cache.outputs.cache-hit != 'true'
- run: node node_modules/gulp/bin/gulp build
if: steps.static-cache.outputs.cache-hit != 'true'
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.9 python-version: 3.9
- name: Cache python deps - uses: actions/cache@v2
uses: actions/cache@v2 id: pcache
with: with:
path: ${{ env.pythonLocation }} path: ~/.local/share/virtualenvs
key: ${{ env.pythonLocation }}-${{ hashFiles('Pipfile.lock') }} key: ${{ runner.os }}-pipenv-${{ hashFiles('Pipfile.lock') }}
restore-keys: |
${{ runner.os }}-pipenv-
- name: Install Dependencies - name: Install Dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install pipenv pip install pipenv
pipenv install -d pipenv install -d
# if: steps.pcache.outputs.cache-hit != 'true'
- name: Cache Static Files
id: static-cache
uses: actions/cache@v2
with:
path: 'pipeline/built_assets'
key: ${{ hashFiles('package-lock.json') }}-${{ hashFiles('pipeline/source_assets') }}
- uses: bahmutov/npm-install@v1
if: steps.static-cache.outputs.cache-hit != 'true'
- run: node node_modules/gulp/bin/gulp build
if: steps.static-cache.outputs.cache-hit != 'true'
- name: Basic Checks - name: Basic Checks
run: | run: |
pipenv run pycodestyle . --exclude=migrations,node_modules pipenv run pycodestyle . --exclude=migrations,node_modules
@@ -49,7 +48,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 --cov -n 4 run: pipenv run pytest -n auto -vv --cov
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
if: failure() if: failure()
with: with:

157
Pipfile
View File

@@ -4,89 +4,98 @@ 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"
diff-match-patch = "==20200713" dj-database-url = "~=0.5.0"
dj-database-url = "==0.5.0" dj-static = "~=0.0.6"
dj-static = "==0.0.6" Django = "~=3.1.5"
Django = "==3.1.5" django-debug-toolbar = "~=3.2"
django-debug-toolbar = "==3.2" django-filter = "~=2.4.0"
django-filter = "==2.4.0" django-ical = "~=1.7.1"
django-ical = "==1.7.1" django-recaptcha = "~=2.0.6"
django-recaptcha = "==2.0.6" django-recurrence = "~=1.10.3"
django-recurrence = "==1.10.3" django-registration-redux = "~=2.9"
django-registration-redux = "==2.9" django-reversion = "~=3.0.9"
django-reversion = "==3.0.9" django-toolbelt = "~=0.0.1"
django-toolbelt = "==0.0.1" django-widget-tweaks = "~=1.4.8"
django-widget-tweaks = "==1.4.8" django-htmlmin = "~=0.11.0"
django-htmlmin = "==0.11.0" envparse = "~=0.2.0"
envparse = "==0.2.0" gunicorn = "~=20.0.4"
gunicorn = "==20.0.4" icalendar = "~=4.0.7"
icalendar = "==4.0.7" idna = "~=2.10"
idna = "==2.10" importlib-metadata = "~=3.4.0"
importlib-metadata = "==3.4.0" lxml = "~=4.6.2"
lxml = "==4.6.2" Markdown = "~=3.3.3"
Markdown = "==3.3.3" msgpack = "~=1.0.2"
msgpack = "==1.0.2" pep517 = "~=0.9.1"
pep517 = "==0.9.1" Pillow = "~=8.1.0"
Pillow = "==8.1.0" premailer = "~=3.7.0"
premailer = "==3.7.0" progress = "~=1.5"
progress = "==1.5" psutil = "~=5.8.0"
psutil = "==5.8.0" psycopg2 = "~=2.8.6"
psycopg2 = "==2.8.6" Pygments = "~=2.7.4"
Pygments = "==2.7.4" pyparsing = "~=2.4.7"
pyparsing = "==2.4.7" PyPDF2 = "~=1.26.0"
PyPDF2 = "==1.26.0" PyPOM = "~=2.2.0"
PyPOM = "==2.2.0" python-dateutil = "~=2.8.1"
python-dateutil = "==2.8.1" pytoml = "~=0.1.21"
pytoml = "==0.1.21" pytz = "~=2020.5"
pytz = "==2020.5" reportlab = "~=3.5.59"
reportlab = "==3.5.59" requests = "~=2.25.1"
requests = "==2.25.1" retrying = "~=1.3.3"
retrying = "==1.3.3" simplejson = "~=3.17.2"
selenium = "==3.141.0" six = "~=1.15.0"
simplejson = "==3.17.2" soupsieve = "~=2.1"
six = "==1.15.0" sqlparse = "~=0.4.1"
soupsieve = "==2.1" static3 = "~=0.7.0"
sqlparse = "==0.4.1" svg2rlg = "~=0.3"
static3 = "==0.7.0" tini = "~=3.0.1"
svg2rlg = "==0.3" tornado = "~=6.1"
tini = "==3.0.1" urllib3 = "~=1.26.2"
tornado = "==6.1" whitenoise = "~=5.2.0"
urllib3 = "==1.26.2" yolk = "~=0.4.3"
whitenoise = "==5.2.0" "z3c.rml" = "~=4.1.2"
yolk = "==0.4.3" zipp = "~=3.4.0"
"z3c.rml" = "==4.1.2" "zope.component" = "~=4.6.2"
zipp = "==3.4.0" "zope.deferredimport" = "~=4.3.1"
"zope.component" = "==4.6.2" "zope.deprecation" = "~=4.4.0"
"zope.deferredimport" = "==4.3.1" "zope.event" = "~=4.5.0"
"zope.deprecation" = "==4.4.0" "zope.hookable" = "~=5.0.1"
"zope.event" = "==4.5.0" "zope.interface" = "~=5.2.0"
"zope.hookable" = "==5.0.1" "zope.proxy" = "~=4.3.5"
"zope.interface" = "==5.2.0" "zope.schema" = "~=6.0.1"
"zope.proxy" = "==4.3.5"
"zope.schema" = "==6.0.1"
sentry-sdk = "*" sentry-sdk = "*"
diff-match-patch = "*"
[dev-packages] [dev-packages]
selenium = "~=3.141.0"
pycodestyle = "*" pycodestyle = "*"
coveralls = "*" coveralls = "*"
django-coverage-plugin = "*" django-coverage-plugin = "*"
pytest-cov = "*" pytest-cov = "*"
pytest-django = "*" pytest-django = "*"
pytest-xdist = "*"
pluggy = "*" pluggy = "*"
pytest-splinter = "*"
pytest = "*"
[requires] [requires]
python_version = "3.9" python_version = "3.9"
[dev-packages.pytest-xdist]
extras = [ "psutil",]
version = "*"
[dev-packages.PyPOM]
extras = [ "splinter",]
version = "*"

294
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "d8a6e0dad40718af879d0bb1a932892ab6aec5405bdc875bf9499d1ebcc1499b" "sha256": "4f5b2f535c10a1b2bcbb4f73aa01ccef700c679685311e740e47686b3942673c"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@@ -176,11 +176,11 @@
}, },
"django": { "django": {
"hashes": [ "hashes": [
"sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7", "sha256:32ce792ee9b6a0cbbec340123e229ac9f765dff8c2a4ae9247a14b2ba3a365a7",
"sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9" "sha256:baf099db36ad31f970775d0be5587cc58a6256a6771a44eb795b554d45f211b8"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.1.5" "version": "==3.1.7"
}, },
"django-debug-toolbar": { "django-debug-toolbar": {
"hashes": [ "hashes": [
@@ -279,7 +279,6 @@
"sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d", "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d",
"sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f" "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.1" "version": "==1.1"
}, },
"icalendar": { "icalendar": {
@@ -425,7 +424,6 @@
"sha256:ee957b9c60b6def20cbdf656d35859ce211eec02dafa3abb9d5ca937d32a3c3b", "sha256:ee957b9c60b6def20cbdf656d35859ce211eec02dafa3abb9d5ca937d32a3c3b",
"sha256:f9428d4b1f70af4f4560be4dccbbc5ab5308c00c5b62ed2f1c44ce9e2591b3d2" "sha256:f9428d4b1f70af4f4560be4dccbbc5ab5308c00c5b62ed2f1c44ce9e2591b3d2"
], ],
"markers": "python_version >= '3.6'",
"version": "==2.5.2" "version": "==2.5.2"
}, },
"pillow": { "pillow": {
@@ -471,7 +469,6 @@
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1" "version": "==0.13.1"
}, },
"premailer": { "premailer": {
@@ -665,16 +662,15 @@
"sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c", "sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c",
"sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d" "sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d"
], ],
"index": "pypi",
"version": "==3.141.0" "version": "==3.141.0"
}, },
"sentry-sdk": { "sentry-sdk": {
"hashes": [ "hashes": [
"sha256:0a711ec952441c2ec89b8f5d226c33bc697914f46e876b44a4edd3e7864cf4d0", "sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237",
"sha256:737a094e49a529dd0fdcaafa9e97cf7c3d5eb964bd229821d640bc77f3502b3f" "sha256:e75c8c58932bda8cd293ea8e4b242527129e1caaec91433d21b8b2f20fee030b"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.19.5" "version": "==0.20.3"
}, },
"simplejson": { "simplejson": {
"hashes": [ "hashes": [
@@ -737,11 +733,12 @@
}, },
"soupsieve": { "soupsieve": {
"hashes": [ "hashes": [
"sha256:4bb21a6ee4707bf43b61230e80740e71bfe56e55d1f1f50924b087bb2975c851", "sha256:407fa1e8eb3458d1b5614df51d9651a1180ea5fedf07feb46e45d7e25e6d6cdd",
"sha256:6dc52924dc0bc710a5d16794e6b3480b2c7c08b07729505feab2b2c16661ff6e" "sha256:d3a5ea5b350423f47d07639f74475afedad48cf41c0ad7a82ca13a3928af34f6"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.1" "markers": "python_version >= '3.0'",
"version": "==2.2"
}, },
"sqlparse": { "sqlparse": {
"hashes": [ "hashes": [
@@ -778,7 +775,6 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2" "version": "==0.10.2"
}, },
"tornado": { "tornado": {
@@ -830,11 +826,11 @@
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08", "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80",
"sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.26.2" "version": "==1.26.3"
}, },
"webencodings": { "webencodings": {
"hashes": [ "hashes": [
@@ -1070,7 +1066,6 @@
"sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6", "sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6",
"sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c" "sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.5" "version": "==1.5"
}, },
"attrs": { "attrs": {
@@ -1078,7 +1073,6 @@
"sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
"sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.3.0" "version": "==20.3.0"
}, },
"certifi": { "certifi": {
@@ -1149,7 +1143,6 @@
"sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3", "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3",
"sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786" "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==5.4" "version": "==5.4"
}, },
"coveralls": { "coveralls": {
@@ -1178,7 +1171,6 @@
"sha256:7a13113028b1e1cc4c6492b28098b3c6576c9dccc7973bfe47b342afadafb2ac", "sha256:7a13113028b1e1cc4c6492b28098b3c6576c9dccc7973bfe47b342afadafb2ac",
"sha256:b73c5565e517f24b62dea8a5ceac178c661c4309d3aa0c3e420856c072c411b4" "sha256:b73c5565e517f24b62dea8a5ceac178c661c4309d3aa0c3e420856c072c411b4"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.8.0" "version": "==1.8.0"
}, },
"idna": { "idna": {
@@ -1201,7 +1193,6 @@
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.9" "version": "==20.9"
}, },
"pluggy": { "pluggy": {
@@ -1209,15 +1200,47 @@
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1" "version": "==0.13.1"
}, },
"psutil": {
"hashes": [
"sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64",
"sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131",
"sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c",
"sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6",
"sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023",
"sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df",
"sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394",
"sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4",
"sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b",
"sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2",
"sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d",
"sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65",
"sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d",
"sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef",
"sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7",
"sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60",
"sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6",
"sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8",
"sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b",
"sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d",
"sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac",
"sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935",
"sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d",
"sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28",
"sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876",
"sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0",
"sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3",
"sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"
],
"index": "pypi",
"version": "==5.8.0"
},
"py": { "py": {
"hashes": [ "hashes": [
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.10.0" "version": "==1.10.0"
}, },
"pycodestyle": { "pycodestyle": {
@@ -1236,12 +1259,20 @@
"index": "pypi", "index": "pypi",
"version": "==2.4.7" "version": "==2.4.7"
}, },
"pypom": {
"hashes": [
"sha256:4bdd57fceb72d7e6a3645cf6c9322f490d9cfb5d777eac2c851a3b658b813939",
"sha256:6772ec99f0a21a5bdc8c092007a8c813ed18359e67ed70258bbb233df5e28829"
],
"index": "pypi",
"version": "==2.2.0"
},
"pytest": { "pytest": {
"hashes": [ "hashes": [
"sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9", "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9",
"sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839" "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"
], ],
"markers": "python_version >= '3.6'", "index": "pypi",
"version": "==6.2.2" "version": "==6.2.2"
}, },
"pytest-cov": { "pytest-cov": {
@@ -1265,9 +1296,15 @@
"sha256:6aa9ac7e00ad1a539c41bec6d21011332de671e938c7637378ec9710204e37ca", "sha256:6aa9ac7e00ad1a539c41bec6d21011332de671e938c7637378ec9710204e37ca",
"sha256:dc4147784048e70ef5d437951728825a131b81714b398d5d52f17c7c144d8815" "sha256:dc4147784048e70ef5d437951728825a131b81714b398d5d52f17c7c144d8815"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.3.0" "version": "==1.3.0"
}, },
"pytest-splinter": {
"hashes": [
"sha256:16d93db719bcad19342935c1707b5c3ec7e34d9ae10df683f6fc2e9e982ddb39"
],
"index": "pypi",
"version": "==3.3.1"
},
"pytest-xdist": { "pytest-xdist": {
"hashes": [ "hashes": [
"sha256:2447a1592ab41745955fb870ac7023026f20a5f0bfccf1b52a879bd193d46450", "sha256:2447a1592ab41745955fb870ac7023026f20a5f0bfccf1b52a879bd193d46450",
@@ -1284,6 +1321,13 @@
"index": "pypi", "index": "pypi",
"version": "==2.25.1" "version": "==2.25.1"
}, },
"selenium": {
"hashes": [
"sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c",
"sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d"
],
"version": "==3.141.0"
},
"six": { "six": {
"hashes": [ "hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
@@ -1292,21 +1336,209 @@
"index": "pypi", "index": "pypi",
"version": "==1.15.0" "version": "==1.15.0"
}, },
"splinter": {
"hashes": [
"sha256:459e39e7a9f7572db6f1cdb5fdc5ccfc6404f021dccb969ee6287be2386a40db",
"sha256:7e5e69c5b76ada909283465cdc3636e2632f7e557932ce96ab9c0432b0b32f7f"
],
"version": "==0.14.0"
},
"toml": { "toml": {
"hashes": [ "hashes": [
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2" "version": "==0.10.2"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08", "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80",
"sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.26.2" "version": "==1.26.3"
},
"zope.component": {
"hashes": [
"sha256:607628e4c84f7887a69a958542b5c304663e726b73aba0882e3a3f059bff14f3",
"sha256:91628918218b3e6f6323de2a7b845e09ddc5cae131c034896c051b084bba3c92"
],
"index": "pypi",
"version": "==4.6.2"
},
"zope.deferredimport": {
"hashes": [
"sha256:57b2345e7b5eef47efcd4f634ff16c93e4265de3dcf325afc7315ade48d909e1",
"sha256:9a0c211df44aa95f1c4e6d2626f90b400f56989180d3ef96032d708da3d23e0a"
],
"index": "pypi",
"version": "==4.3.1"
},
"zope.deprecation": {
"hashes": [
"sha256:0d453338f04bacf91bbfba545d8bcdf529aa829e67b705eac8c1a7fdce66e2df",
"sha256:f1480b74995958b24ce37b0ef04d3663d2683e5d6debc96726eff18acf4ea113"
],
"index": "pypi",
"version": "==4.4.0"
},
"zope.event": {
"hashes": [
"sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42",
"sha256:5e76517f5b9b119acf37ca8819781db6c16ea433f7e2062c4afc2b6fbedb1330"
],
"index": "pypi",
"version": "==4.5.0"
},
"zope.hookable": {
"hashes": [
"sha256:0194b9b9e7f614abba60c90b231908861036578297515d3d6508eb10190f266d",
"sha256:0c2977473918bdefc6fa8dfb311f154e7f13c6133957fe649704deca79b92093",
"sha256:17b8bdb3b77e03a152ca0d5ca185a7ae0156f5e5a2dbddf538676633a1f7380f",
"sha256:29d07681a78042cdd15b268ae9decffed9ace68a53eebeb61d65ae931d158841",
"sha256:36fb1b35d1150267cb0543a1ddd950c0bc2c75ed0e6e92e3aaa6ac2e29416cb7",
"sha256:3aed60c2bb5e812bbf9295c70f25b17ac37c233f30447a96c67913ba5073642f",
"sha256:3cac1565cc768911e72ca9ec4ddf5c5109e1fef0104f19f06649cf1874943b60",
"sha256:3d4bc0cc4a37c3cd3081063142eeb2125511db3c13f6dc932d899c512690378e",
"sha256:3f73096f27b8c28be53ffb6604f7b570fbbb82f273c6febe5f58119009b59898",
"sha256:522d1153d93f2d48aa0bd9fb778d8d4500be2e4dcf86c3150768f0e3adbbc4ef",
"sha256:523d2928fb7377bbdbc9af9c0b14ad73e6eaf226349f105733bdae27efd15b5a",
"sha256:5848309d4fc5c02150a45e8f8d2227e5bfda386a508bbd3160fed7c633c5a2fa",
"sha256:6781f86e6d54a110980a76e761eb54590630fd2af2a17d7edf02a079d2646c1d",
"sha256:6fd27921ebf3aaa945fa25d790f1f2046204f24dba4946f82f5f0a442577c3e9",
"sha256:70d581862863f6bf9e175e85c9d70c2d7155f53fb04dcdb2f73cf288ca559a53",
"sha256:81867c23b0dc66c8366f351d00923f2bc5902820a24c2534dfd7bf01a5879963",
"sha256:81db29edadcbb740cd2716c95a297893a546ed89db1bfe9110168732d7f0afdd",
"sha256:86bd12624068cea60860a0759af5e2c3adc89c12aef6f71cf12f577e28deefe3",
"sha256:9c184d8f9f7a76e1ced99855ccf390ffdd0ec3765e5cbf7b9cada600accc0a1e",
"sha256:acc789e8c29c13555e43fe4bf9fcd15a65512c9645e97bbaa5602e3201252b02",
"sha256:afaa740206b7660d4cc3b8f120426c85761f51379af7a5b05451f624ad12b0af",
"sha256:b5f5fa323f878bb16eae68ea1ba7f6c0419d4695d0248bed4b18f51d7ce5ab85",
"sha256:bd89e0e2c67bf4ac3aca2a19702b1a37269fb1923827f68324ac2e7afd6e3406",
"sha256:c212de743283ec0735db24ec6ad913758df3af1b7217550ff270038062afd6ae",
"sha256:ca553f524293a0bdea05e7f44c3e685e4b7b022cb37d87bc4a3efa0f86587a8d",
"sha256:cab67065a3db92f636128d3157cc5424a145f82d96fb47159c539132833a6d36",
"sha256:d3b3b3eedfdbf6b02898216e85aa6baf50207f4378a2a6803d6d47650cd37031",
"sha256:d9f4a5a72f40256b686d31c5c0b1fde503172307beb12c1568296e76118e402c",
"sha256:df5067d87aaa111ed5d050e1ee853ba284969497f91806efd42425f5348f1c06",
"sha256:e2587644812c6138f05b8a41594a8337c6790e3baf9a01915e52438c13fc6bef",
"sha256:e27fd877662db94f897f3fd532ef211ca4901eb1a70ba456f15c0866a985464a",
"sha256:e427ebbdd223c72e06ba94c004bb04e996c84dec8a0fa84e837556ae145c439e",
"sha256:e583ad4309c203ef75a09d43434cf9c2b4fa247997ecb0dcad769982c39411c7",
"sha256:e760b2bc8ece9200804f0c2b64d10147ecaf18455a2a90827fbec4c9d84f3ad5",
"sha256:ea9a9cc8bcc70e18023f30fa2f53d11ae069572a162791224e60cd65df55fb69",
"sha256:ecb3f17dce4803c1099bd21742cd126b59817a4e76a6544d31d2cca6e30dbffd",
"sha256:ed794e3b3de42486d30444fb60b5561e724ee8a2d1b17b0c2e0f81e3ddaf7a87",
"sha256:ee885d347279e38226d0a437b6a932f207f691c502ee565aba27a7022f1285df",
"sha256:fd5e7bc5f24f7e3d490698f7b854659a9851da2187414617cd5ed360af7efd63",
"sha256:fe45f6870f7588ac7b2763ff1ce98cce59369717afe70cc353ec5218bc854bcc"
],
"index": "pypi",
"version": "==5.0.1"
},
"zope.interface": {
"hashes": [
"sha256:05a97ba92c1c7c26f25c9f671aa1ef85ffead6cdad13770e5b689cf983adc7e1",
"sha256:07d61722dd7d85547b7c6b0f5486b4338001fab349f2ac5cabc0b7182eb3425d",
"sha256:0a990dcc97806e5980bbb54b2e46b9cde9e48932d8e6984daf71ef1745516123",
"sha256:150e8bcb7253a34a4535aeea3de36c0bb3b1a6a47a183a95d65a194b3e07f232",
"sha256:1743bcfe45af8846b775086471c28258f4c6e9ee8ef37484de4495f15a98b549",
"sha256:1b5f6c8fff4ed32aa2dd43e84061bc8346f32d3ba6ad6e58f088fe109608f102",
"sha256:21e49123f375703cf824214939d39df0af62c47d122d955b2a8d9153ea08cfd5",
"sha256:21f579134a47083ffb5ddd1307f0405c91aa8b61ad4be6fd5af0171474fe0c45",
"sha256:27c267dc38a0f0079e96a2945ee65786d38ef111e413c702fbaaacbab6361d00",
"sha256:299bde0ab9e5c4a92f01a152b7fbabb460f31343f1416f9b7b983167ab1e33bc",
"sha256:2ab88d8f228f803fcb8cb7d222c579d13dab2d3622c51e8cf321280da01102a7",
"sha256:2ced4c35061eea623bc84c7711eedce8ecc3c2c51cd9c6afa6290df3bae9e104",
"sha256:2dcab01c660983ba5e5a612e0c935141ccbee67d2e2e14b833e01c2354bd8034",
"sha256:32546af61a9a9b141ca38d971aa6eb9800450fa6620ce6323cc30eec447861f3",
"sha256:32b40a4c46d199827d79c86bb8cb88b1bbb764f127876f2cb6f3a47f63dbada3",
"sha256:3cc94c69f6bd48ed86e8e24f358cb75095c8129827df1298518ab860115269a4",
"sha256:42b278ac0989d6f5cf58d7e0828ea6b5951464e3cf2ff229dd09a96cb6ba0c86",
"sha256:495b63fd0302f282ee6c1e6ea0f1c12cb3d1a49c8292d27287f01845ff252a96",
"sha256:4af87cdc0d4b14e600e6d3d09793dce3b7171348a094ba818e2a68ae7ee67546",
"sha256:4b94df9f2fdde7b9314321bab8448e6ad5a23b80542dcab53e329527d4099dcb",
"sha256:4c48ddb63e2b20fba4c6a2bf81b4d49e99b6d4587fb67a6cd33a2c1f003af3e3",
"sha256:4df9afd17bd5477e9f8c8b6bb8507e18dd0f8b4efe73bb99729ff203279e9e3b",
"sha256:518950fe6a5d56f94ba125107895f938a4f34f704c658986eae8255edb41163b",
"sha256:538298e4e113ccb8b41658d5a4b605bebe75e46a30ceca22a5a289cf02c80bec",
"sha256:55465121e72e208a7b69b53de791402affe6165083b2ea71b892728bd19ba9ae",
"sha256:588384d70a0f19b47409cfdb10e0c27c20e4293b74fc891df3d8eb47782b8b3e",
"sha256:6278c080d4afffc9016e14325f8734456831124e8c12caa754fd544435c08386",
"sha256:64ea6c221aeee4796860405e1aedec63424cda4202a7ad27a5066876db5b0fd2",
"sha256:681dbb33e2b40262b33fd383bae63c36d33fd79fa1a8e4092945430744ffd34a",
"sha256:6936aa9da390402d646a32a6a38d5409c2d2afb2950f045a7d02ab25a4e7d08d",
"sha256:778d0ec38bbd288b150a3ae363c8ffd88d2207a756842495e9bffd8a8afbc89a",
"sha256:8251f06a77985a2729a8bdbefbae79ee78567dddc3acbd499b87e705ca59fe24",
"sha256:83b4aa5344cce005a9cff5d0321b2e318e871cc1dfc793b66c32dd4f59e9770d",
"sha256:844fad925ac5c2ad4faaceb3b2520ad016b5280105c6e16e79838cf951903a7b",
"sha256:8ceb3667dd13b8133f2e4d637b5b00f240f066448e2aa89a41f4c2d78a26ce50",
"sha256:92dc0fb79675882d0b6138be4bf0cec7ea7c7eede60aaca78303d8e8dbdaa523",
"sha256:9789bd945e9f5bd026ed3f5b453d640befb8b1fc33a779c1fe8d3eb21fe3fb4a",
"sha256:a2b6d6eb693bc2fc6c484f2e5d93bd0b0da803fa77bf974f160533e555e4d095",
"sha256:aab9f1e34d810feb00bf841993552b8fcc6ae71d473c505381627143d0018a6a",
"sha256:abb61afd84f23099ac6099d804cdba9bd3b902aaaded3ffff47e490b0a495520",
"sha256:adf9ee115ae8ff8b6da4b854b4152f253b390ba64407a22d75456fe07dcbda65",
"sha256:aedc6c672b351afe6dfe17ff83ee5e7eb6ed44718f879a9328a68bdb20b57e11",
"sha256:b7a00ecb1434f8183395fac5366a21ee73d14900082ca37cf74993cf46baa56c",
"sha256:ba32f4a91c1cb7314c429b03afbf87b1fff4fb1c8db32260e7310104bd77f0c7",
"sha256:cbd0f2cbd8689861209cd89141371d3a22a11613304d1f0736492590aa0ab332",
"sha256:e4bc372b953bf6cec65a8d48482ba574f6e051621d157cf224227dbb55486b1e",
"sha256:eccac3d9aadc68e994b6d228cb0c8919fc47a5350d85a1b4d3d81d1e98baf40c",
"sha256:efd550b3da28195746bb43bd1d815058181a7ca6d9d6aa89dd37f5eefe2cacb7",
"sha256:efef581c8ba4d990770875e1a2218e856849d32ada2680e53aebc5d154a17e20",
"sha256:f057897711a630a0b7a6a03f1acf379b6ba25d37dc5dc217a97191984ba7f2fc",
"sha256:f37d45fab14ffef9d33a0dc3bc59ce0c5313e2253323312d47739192da94f5fd",
"sha256:f44906f70205d456d503105023041f1e63aece7623b31c390a0103db4de17537"
],
"index": "pypi",
"version": "==5.2.0"
},
"zope.proxy": {
"hashes": [
"sha256:00573dfa755d0703ab84bb23cb6ecf97bb683c34b340d4df76651f97b0bab068",
"sha256:092049280f2848d2ba1b57b71fe04881762a220a97b65288bcb0968bb199ec30",
"sha256:0cbd27b4d3718b5ec74fc65ffa53c78d34c65c6fd9411b8352d2a4f855220cf1",
"sha256:17fc7e16d0c81f833a138818a30f366696653d521febc8e892858041c4d88785",
"sha256:19577dfeb70e8a67249ba92c8ad20589a1a2d86a8d693647fa8385408a4c17b0",
"sha256:207aa914576b1181597a1516e1b90599dc690c095343ae281b0772e44945e6a4",
"sha256:219a7db5ed53e523eb4a4769f13105118b6d5b04ed169a283c9775af221e231f",
"sha256:2b50ea79849e46b5f4f2b0247a3687505d32d161eeb16a75f6f7e6cd81936e43",
"sha256:5903d38362b6c716e66bbe470f190579c530a5baf03dbc8500e5c2357aa569a5",
"sha256:5c24903675e271bd688c6e9e7df5775ac6b168feb87dbe0e4bcc90805f21b28f",
"sha256:5ef6bc5ed98139e084f4e91100f2b098a0cd3493d4e76f9d6b3f7b95d7ad0f06",
"sha256:61b55ae3c23a126a788b33ffb18f37d6668e79a05e756588d9e4d4be7246ab1c",
"sha256:63ddb992931a5e616c87d3d89f5a58db086e617548005c7f9059fac68c03a5cc",
"sha256:6943da9c09870490dcfd50c4909c0cc19f434fa6948f61282dc9cb07bcf08160",
"sha256:6ad40f85c1207803d581d5d75e9ea25327cd524925699a83dfc03bf8e4ba72b7",
"sha256:6b44433a79bdd7af0e3337bd7bbcf53dd1f9b0fa66bf21bcb756060ce32a96c1",
"sha256:6bbaa245015d933a4172395baad7874373f162955d73612f0b66b6c2c33b6366",
"sha256:7007227f4ea85b40a2f5e5a244479f6a6dfcf906db9b55e812a814a8f0e2c28d",
"sha256:74884a0aec1f1609190ec8b34b5d58fb3b5353cf22b96161e13e0e835f13518f",
"sha256:7d25fe5571ddb16369054f54cdd883f23de9941476d97f2b92eb6d7d83afe22d",
"sha256:7e162bdc5e3baad26b2262240be7d2bab36991d85a6a556e48b9dfb402370261",
"sha256:814d62678dc3a30f4aa081982d830b7c342cf230ffc9d030b020cb154eeebf9e",
"sha256:8878a34c5313ee52e20aa50b03138af8d472bae465710fb954d133a9bfd3c38d",
"sha256:a66a0d94e5b081d5d695e66d6667e91e74d79e273eee95c1747717ba9cb70792",
"sha256:a69f5cbf4addcfdf03dda564a671040127a6b7c34cf9fe4973582e68441b63fa",
"sha256:b00f9f0c334d07709d3f73a7cb8ae63c6ca1a90c790a63b5e7effa666ef96021",
"sha256:b6ed71e4a7b4690447b626f499d978aa13197a0e592950e5d7020308f6054698",
"sha256:bdf5041e5851526e885af579d2f455348dba68d74f14a32781933569a327fddf",
"sha256:be034360dd34e62608419f86e799c97d389c10a0e677a25f236a971b2f40dac9",
"sha256:cc8f590a5eed30b314ae6b0232d925519ade433f663de79cc3783e4b10d662ba",
"sha256:cd7a318a15fe6cc4584bf3c4426f092ed08c0fd012cf2a9173114234fe193e11",
"sha256:cf19b5f63a59c20306e034e691402b02055c8f4e38bf6792c23cad489162a642",
"sha256:cfc781ce442ec407c841e9aa51d0e1024f72b6ec34caa8fdb6ef9576d549acf2",
"sha256:dea9f6f8633571e18bc20cad83603072e697103a567f4b0738d52dd0211b4527",
"sha256:e4a86a1d5eb2cce83c5972b3930c7c1eac81ab3508464345e2b8e54f119d5505",
"sha256:e7106374d4a74ed9ff00c46cc00f0a9f06a0775f8868e423f85d4464d2333679",
"sha256:e98a8a585b5668aa9e34d10f7785abf9545fe72663b4bfc16c99a115185ae6a5",
"sha256:f64840e68483316eb58d82c376ad3585ca995e69e33b230436de0cdddf7363f9",
"sha256:f8f4b0a9e6683e43889852130595c8854d8ae237f2324a053cdd884de936aa9b",
"sha256:fc45a53219ed30a7f670a6d8c98527af0020e6fd4ee4c0a8fb59f147f06d816c"
],
"index": "pypi",
"version": "==4.3.5"
} }
} }
} }

View File

@@ -9,23 +9,18 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
""" """
import datetime import datetime
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) from pathlib import Path
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
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # Build paths inside the project like this: BASE_DIR / 'subdir'.
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)
@@ -97,7 +92,7 @@ WSGI_APPLICATION = 'PyRIGS.wsgi.application'
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 'NAME': str(BASE_DIR / 'db.sqlite3'),
} }
} }
@@ -235,19 +230,16 @@ 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 = os.path.join(BASE_DIR, 'static/') STATIC_ROOT = str(BASE_DIR / 'static/')
STATIC_DIRS = [
os.path.join(BASE_DIR, 'static/'),
]
STATICFILES_DIRS = [ STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'pipeline/built_assets'), str(BASE_DIR / 'pipeline/built_assets'),
] ]
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [ 'DIRS': [
os.path.join(BASE_DIR, 'templates') BASE_DIR / 'templates'
], ],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {

View File

@@ -71,6 +71,7 @@ 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()

View File

@@ -27,13 +27,14 @@ 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 ]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.7 on 2021-03-02 11:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0041_auto_20210208_1603'),
]
operations = [
migrations.AlterField(
model_name='profile',
name='phone',
field=models.CharField(blank=True, default='', max_length=13),
),
]

View File

@@ -21,7 +21,7 @@ from reversion.models import Version
class Profile(AbstractUser): class Profile(AbstractUser):
initials = models.CharField(max_length=5, unique=True, null=True, blank=False) initials = models.CharField(max_length=5, unique=True, null=True, blank=False)
phone = models.CharField(max_length=13, null=True, default='') phone = models.CharField(max_length=13, blank=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...
@@ -707,7 +707,7 @@ class RiskAssessment(models.Model, RevisionMixin):
] ]
@cached_property @cached_property
def fields(self): 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] 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
@@ -792,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._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)

View File

@@ -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.storage import staticfiles_storage from django.contrib.staticfiles import finders
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
@@ -274,6 +274,7 @@ 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()
@@ -301,6 +302,7 @@ class EventAuthorise(generic.UpdateView):
context['page_title'] = "{}: {}".format(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):
@@ -387,7 +389,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 = staticfiles_storage.path('css/email.css') css = finders.find('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')
@@ -402,8 +404,7 @@ 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):
from django.contrib.staticfiles.storage import staticfiles_storage css = finders.find('css/email.css')
css = staticfiles_storage.path('css/email.css')
response = super(EventAuthoriseRequestEmailPreview, self).render_to_response(context, **response_kwargs) 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()
@@ -417,4 +418,5 @@ class EventAuthoriseRequestEmailPreview(generic.DetailView):
'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

View File

@@ -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.storage import staticfiles_storage from django.contrib.staticfiles import finders
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 = staticfiles_storage.path('css/email.css') css = finders.find('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 = staticfiles_storage.path('css/email.css') css = finders.find('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')

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 28 KiB

BIN
RIGS/static/imgs/logo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -74,6 +74,7 @@
{% 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>

View File

@@ -5,11 +5,13 @@
{% load static %} {% load static %}
{% block css %} {% block css %}
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/> {{ block.super }}
<link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
{% endblock %} {% endblock %}
{% block preload_js %} {% block preload_js %}
<script src="{% static 'js/bootstrap-select.js' %}"></script> {{ block.super }}
<script src="{% static 'js/selects.js' %}" async></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -7,24 +7,18 @@
{% block css %} {% block css %}
{{ block.super }} {{ 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 }} {{ block.super }}
<script src="{% static 'js/bootstrap-select.js' %}"></script> <script src="{% static 'js/selects.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/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/autocompleter.js' %}"></script> <script src="{% static 'js/autocompleter.js' %}"></script>
<script src="{% static 'js/tooltip.js' %}"></script>
{% include 'partials/datetime-fix.html' %} {% include 'partials/datetime-fix.html' %}
@@ -134,14 +128,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 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 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><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 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 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">
{% 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 %}
@@ -202,7 +196,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-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-container="body" 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>
@@ -212,7 +206,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 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 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">
{% 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 %}

View File

@@ -7,7 +7,7 @@
{% 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 'event_detail_buttons.html' %} {% include 'partials/event_detail_buttons.html' %}
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -77,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 'event_detail_buttons.html' %} {% include 'partials/event_detail_buttons.html' %}
</div> </div>
{% endif %} {% endif %}
{% if event.is_rig %} {% if event.is_rig %}
@@ -97,7 +97,7 @@
</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 'event_detail_buttons.html' %} {% include 'partials/event_detail_buttons.html' %}
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@@ -1,8 +1,9 @@
{% extends 'base_embed.html' %} {% extends 'base_embed.html' %}
{% load static %} {% load static %}
{% block js %} {% block extra-head %}
<script src="{% static 'js/all.js' %}"></script> <link href="{% static 'fontawesome_free/css/fontawesome.css' %}" rel="stylesheet" type="text/css">
<link href="{% static 'fontawesome_free/css/solid.css' %}" rel="stylesheet" type="text/css">
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@@ -7,25 +7,19 @@
{% block css %} {% block css %}
{{ block.super }} {{ block.super }}
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/> <link rel="stylesheet" type="text/css" href="{% static 'css/selects.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/bootstrap-select.js' %}"></script> <script src="{% static 'js/selects.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/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/autocompleter.js' %}"></script> <script src="{% static 'js/autocompleter.js' %}"></script>
<script src="{% static 'js/interaction.js' %}"></script>
<script src="{% static 'js/tooltip.js' %}"></script>
{% include 'partials/datetime-fix.html' %} {% include 'partials/datetime-fix.html' %}

View File

@@ -1,6 +1,5 @@
<?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"/>

View File

@@ -1,10 +0,0 @@
<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>

View File

@@ -1,10 +1,10 @@
{% extends 'base_client_email.html' %} {% extends 'base_client_email.html' %}
{% block content %} {% block content %}
<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>N{{ object.pk|stringformat:"05d" }} <p><b>{{ request.user.get_full_name }}</b> has requested that you authorise <b>{{ object.display_id }}
| {{ object.name }}</b>{% if not to_name %} on behalf of <b>{{ object.person.name }}</b>{% endif %}.</p> | {{ object.name }}</b>{% if not to_name %} on behalf of <b>{{ object.person.name }}</b>{% endif %}.</p>
<p> <p>
@@ -23,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 'event_authorise' object.pk hmac %}"> <a href="{{ request.scheme }}://{{ request.get_host }}{% url target|default:'event_authorise' object.pk hmac %}">
Complete Authorisation Form Complete Authorisation Form
</a> </a>
</td> </td>

View File

@@ -1,9 +1,6 @@
{% 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">
@@ -86,7 +83,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">Authorise</button> <button class="btn btn-primary btn-lg" type="submit" {% if preview %}disabled="" data-toggle="tooltip" title="This is only a preview!"{%endif%}>Authorise</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -30,14 +30,6 @@
{% 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>
@@ -48,3 +40,14 @@
}); });
</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 %}

View File

@@ -21,7 +21,7 @@
<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.fields %} {% for field in object_list.0.fieldz %}
<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 %}
@@ -33,7 +33,7 @@
<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></th> <th scope="row"><a href="{% url 'event_detail' object.event.pk %}">{{ object.event }}</a></th>
{% for field in object_list.0.fields %} {% for field in object_list.0.fieldz %}
<td>{{ object|get_field:field }}</td> <td>{{ object|get_field:field }}</td>
{% endfor %} {% endfor %}
{# Buttons #} {# Buttons #}

View File

@@ -1,15 +1,15 @@
<div class="card card-default <div class="card card-default
{% if object.authorised %} {% if event.authorised %}
card-success border-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 %}
card-warning border-warning
{% elif event.auth_request_to %} {% elif event.auth_request_to %}
card-info border-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-md-6"> <dl class="col-sm-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>
<dd class="d-block d-sm-none">&nbsp;</dd> <dl class="col-sm-6">
<dl class="col-md-6"> <hr class="d-block d-sm-none">
<dt>Authorised</dt> <dt>Authorised</dt>
<dd>{{ object.authorised|yesno:"Yes,No" }}</dd> <dd>{{ object.authorised|yesno:"Yes,No" }}</dd>

View File

@@ -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 btn-success active
{% 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 %}">
<i class="fas fa-paper-plane"></i> <span class="fas fa-paper-plane"></span>
<span class="d-none d-sm-inline"> <span class="d-none d-sm-inline">
{% if event.authorised %} {% if event.authorised %}
Authorised Authorised

View File

@@ -20,15 +20,7 @@
{% 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"> <dd class="col-sm-6">{% include 'partials/linked_name.html' with profile=event.mic %}</dd>
{% if event.mic and perms.RIGS.view_profile %}
<a href="{% url 'profile_detail' event.mic.pk %}" class="modal-href">
{{ event.mic.name }}
</a>
{% else %}
{{ event.mic.name }}
{% endif %}
</dd>
{% endif %} {% endif %}
<dt class="col-sm-6">Status</dt> <dt class="col-sm-6">Status</dt>
@@ -71,7 +63,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">{{ object.checked_in_by.name }}</dd> <dd class="col-sm-6">{% include 'partials/linked_name.html' with profile=event.checked_in_by %}</dd>
{% endif %} {% endif %}
{% if event.is_rig %} {% if event.is_rig %}

View File

@@ -1,12 +1,14 @@
<h5> <div>
<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.purchase_order %} {% if event.sum_total > 0 %}
<span class="badge badge-success">PO: {{ event.purchase_order }}</span> {% if event.purchase_order %}
{% elif event.authorised %} <span class="badge badge-success">PO: {{ event.purchase_order }}</span>
<span class="badge badge-success">Authorisation: Complete <span class="fas fa-check"></span></span> {% elif event.authorised %}
{% else %} <span class="badge badge-success">Authorisation: Complete <span class="fas fa-check"></span></span>
<span class="badge badge-danger">Authorisation: <span class="fas fa-times"></span></span> {% else %}
<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 %}
@@ -14,8 +16,6 @@
{% 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 %}
@@ -24,8 +24,6 @@
{% 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 %}
@@ -41,4 +39,4 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endif %} {% endif %}
</h5> </div>

View File

@@ -25,7 +25,7 @@
{% endif %} {% endif %}
{% else %} {% else %}
table-warning table-warning
{% endif %}" id="event_row"> {% endif %}" {% if event.cancelled %}style="opacity: 50% !important;"{% 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-->

View File

@@ -0,0 +1,7 @@
{% if profile and perms.RIGS.view_profile %}
<a href="{% url 'profile_detail' profile.pk %}" class="modal-href">
{{ profile.name }}
</a>
{% else %}
{{ profile.name }}
{% endif %}

View File

@@ -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"> <div class="row align-items-center justify-content-between py-2 align-middle">
<div class="col-sm-12 col-md"> <div class="col-sm-12 col-md align-middle">
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> 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>
</div> </div>
{% if perms.RIGS.add_event %} {% if perms.RIGS.add_event %}
<div class="col text-right"> <div class="col text-right">

View File

@@ -47,7 +47,7 @@
<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' }}</dt> <dt class="col-sm-6">{{ object|help_text:'power_mic'|safe }}</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>

View File

@@ -6,24 +6,18 @@
{% block css %} {% block css %}
{{ block.super }} {{ 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 }} {{ 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 }} {{ 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/autocompleter.js' %}"></script> <script src="{% static 'js/autocompleter.js' %}"></script>
<script src="{% static 'js/tooltip.js' %}"></script>
<script> <script>
function parseBool(str) { function parseBool(str) {

View File

@@ -173,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='far fa-sticky-note'></span></a>".format(reverse(url, kwargs={'pk': obj.pk}))) return mark_safe(obj.name + " <a href='{}'><span class='fas fa-sticky-note'></span></a>".format(reverse(url, kwargs={'pk': obj.pk})))
else: else:
return obj.name return obj.name

View File

@@ -25,6 +25,15 @@ 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")
@@ -33,7 +42,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): def checklist(basic_event, venue, admin_user, ra):
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",

View File

@@ -230,9 +230,11 @@ 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[@id='id_power_mic-group']//div[contains(@class, 'bootstrap-select')]") _power_mic_selector = (By.XPATH, "//div[select[@id='id_power_mic']]")
_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')),
@@ -271,11 +273,61 @@ 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')

View File

@@ -16,6 +16,7 @@ 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)
@@ -637,270 +638,190 @@ 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
self.page = pages.CalendarPage(self.driver, self.live_server_url).open()
self.assertIn(timezone.now().strftime("%Y-%m"), self.driver.current_url)
target_date = datetime.date(2020, 1, 1) def test_calendar_buttons(logged_in_browser, live_server): # If FullCalendar fails to load for whatever reason, the buttons don't work
self.page.target_date.set_value(target_date) page = pages.CalendarPage(logged_in_browser.driver, live_server.url).open()
self.page.go() assert timezone.now().strftime("%Y-%m") in logged_in_browser.url
self.assertIn(self.page.target_date.value.strftime("%Y-%m"), self.driver.current_url)
self.page.next() target_date = datetime.date(2020, 1, 1)
target_date += datetime.timedelta(days=32) page.target_date.set_value(target_date)
self.assertIn(target_date.strftime("%m"), self.driver.current_url) page.go()
assert page.target_date.value.strftime("%Y-%m") in logged_in_browser.url
page.next()
target_date += datetime.timedelta(days=32)
assert target_date.strftime("%m") in logged_in_browser.url
@screenshot_failure_cls def test_ra_edit(logged_in_browser, live_server, ra):
class TestHealthAndSafety(BaseRigboardTest): page = pages.EditRiskAssessment(logged_in_browser.driver, live_server.url, pk=ra.pk).open()
def setUp(self): page.nonstandard_equipment = nse = True
super().setUp() page.general_notes = gn = "There are some notes, but I've not written them here as that would be helpful"
self.profile = models.Profile.objects.get_or_create( page.submit()
first_name='Test', assert not page.success
last_name='TEC User', page.supervisor_consulted = True
username='eventtest', page.submit()
email='teccie@functional.test', assert page.success
is_superuser=True # lazily grant all permissions # Check that data is right
)[0] ra = models.RiskAssessment.objects.get(pk=ra.pk)
self.venue = models.Venue.objects.create(name="Venue 1") assert ra.general_notes == gn
assert ra.nonstandard_equipment == nse
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? def small_ec(page, admin_user):
def test_ra_creation(self): page.safe_parking = True
self.page = pages.CreateRiskAssessment(self.driver, self.live_server_url, event_id=self.testEvent.pk).open() 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
# Check there are no defaults
self.assertIsNone(self.page.nonstandard_equipment)
# No database side validation, only HTML5. 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
self.page.nonstandard_equipment = False
self.page.nonstandard_use = False
self.page.contractors = False
self.page.other_companies = False
self.page.crew_fatigue = False
self.page.general_notes = "There are no notes."
self.page.big_power = False
self.page.outside = False
self.page.power_mic.search(self.profile.name)
self.page.power_mic.set_option(self.profile.name, True)
# TODO This should not be necessary, normally closes automatically
self.page.power_mic.toggle()
self.assertFalse(self.page.power_mic.is_open)
self.page.generators = False
self.page.other_companies_power = False
self.page.nonstandard_equipment_power = False
self.page.multiple_electrical_environments = False
self.page.power_notes = "Remember to bring some power"
self.page.noise_monitoring = False
self.page.sound_notes = "Loud, but not too loud"
self.page.known_venue = 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.suspended_structures = True def test_ec_create_medium(logged_in_browser, live_server, admin_user, medium_ra):
# TODO Test for this proper page = pages.CreateEventChecklist(logged_in_browser.driver, live_server.url, event_id=medium_ra.event.pk).open()
self.page.rigging_plan = "https://nottinghamtec.sharepoint.com/test/"
self.page.submit()
self.assertFalse(self.page.success)
self.page.suspended_structures = False page.safe_parking = True
self.page.submit() page.safe_packing = True
self.assertTrue(self.page.success) 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
# Test that we can't make another one # Gotta scroll to make the button clickable
self.page = pages.CreateRiskAssessment(self.driver, self.live_server_url, event_id=self.testEvent.pk).open() logged_in_browser.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
self.assertIn('edit', self.driver.current_url)
def test_ra_edit(self): page.earthing = True
self.page = pages.EditRiskAssessment(self.driver, self.live_server_url, pk=self.testRA.pk).open() page.pat = True
self.page.nonstandard_equipment = nse = True page.source_rcd = True
self.page.general_notes = gn = "There are some notes, but I've not written them here as that would be helpful" page.labelling = True
self.page.submit() page.fd_voltage_l1 = 240
self.assertFalse(self.page.success) page.fd_voltage_l2 = 235
self.page.supervisor_consulted = True page.fd_voltage_l3 = 0
self.page.submit() page.fd_phase_rotation = True
self.assertTrue(self.page.success) page.fd_earth_fault = 666
# Check that data is right page.fd_pssc = 1984
ra = models.RiskAssessment.objects.get(pk=self.testRA.pk) page.w1_description = "In the carpark, by the bins"
self.assertEqual(ra.general_notes, gn) page.w1_polarity = True
self.assertEqual(ra.nonstandard_equipment, nse) page.w1_voltage = 240
page.w1_earth_fault = 333
def test_ec_create_small(self): page.submit()
self.page = pages.CreateEventChecklist(self.driver, self.live_server_url, event_id=self.testEvent2.pk).open() assert page.success
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 def test_ec_create_vehicle(logged_in_browser, live_server, admin_user, checklist):
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") 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
self.page.earthing = True
self.page.rcds = True
self.page.supply_test = True
self.page.pat = True
self.page.submit() # TODO Test validation of end before start
self.assertTrue(self.page.success) 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
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 # TODO Can I loop through all the boolean fields and test them at once?
self.page.safe_packing = True def test_ra_creation(logged_in_browser, live_server, admin_user, basic_event):
self.page.exits = True page = pages.CreateRiskAssessment(logged_in_browser.driver, live_server.url, event_id=basic_event.pk).open()
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 # Check there are no defaults
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") assert page.nonstandard_equipment is None
self.page.earthing = True # No database side validation, only HTML5.
self.page.pat = True page.nonstandard_equipment = False
self.page.source_rcd = True page.nonstandard_use = False
self.page.labelling = True page.contractors = False
self.page.fd_voltage_l1 = 240 page.other_companies = False
self.page.fd_voltage_l2 = 235 page.crew_fatigue = False
self.page.fd_voltage_l3 = 0 page.general_notes = "There are no notes."
self.page.fd_phase_rotation = True page.big_power = False
self.page.fd_earth_fault = 666 page.outside = False
self.page.fd_pssc = 1984 page.power_mic.search(admin_user.first_name)
self.page.w1_description = "In the carpark, by the bins" page.generators = False
self.page.w1_polarity = True page.other_companies_power = False
self.page.w1_voltage = 240 page.nonstandard_equipment_power = False
self.page.w1_earth_fault = 333 page.multiple_electrical_environments = False
page.power_notes = "Remember to bring some power"
page.noise_monitoring = False
page.sound_notes = "Loud, but not too loud"
page.known_venue = False
page.safe_loading = False
page.safe_storage = False
page.area_outside_of_control = False
page.barrier_required = False
page.nonstandard_emergency_procedure = False
page.special_structures = False
# self.page.persons_responsible_structures = "Nobody and her cat, She"
self.page.submit() page.suspended_structures = True
self.assertTrue(self.page.success) # TODO Test for this proper
page.rigging_plan = "https://nottinghamtec.sharepoint.com/test/"
page.submit()
assert not page.success
def test_ec_create_extras(self): page.suspended_structures = False
eid = self.testEvent2.pk page.submit()
self.page = pages.CreateEventChecklist(self.driver, self.live_server_url, event_id=eid).open() assert page.success
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' def test_ra_no_duplicates(logged_in_browser, live_server, ra):
self.driver.find_element(By.XPATH, '//*[@name="vehicle_-1"]').send_keys(vehicle_name) # Test that we can't make another one
driver = base_regions.BootstrapSelectElement(self.page, self.driver.find_element(By.XPATH, '//tr[@id="vehicles_-1"]//div[contains(@class, "bootstrap-select")]')) page = pages.CreateRiskAssessment(logged_in_browser.driver, live_server.url, event_id=ra.event.pk).open()
driver.search(self.profile.name) assert 'edit' in logged_in_browser.url
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)

View File

@@ -132,6 +132,8 @@ 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()),

View File

@@ -99,6 +99,7 @@ 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

View File

@@ -4,9 +4,6 @@
{% 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 () {

View File

@@ -3,13 +3,17 @@
{% load static %} {% load static %}
{% block css %} {% block css %}
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/> {{ block.super }}
<link rel="stylesheet" href="{% static 'css/ajax-bootstrap-select.css' %}"/> <link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
{% endblock %}
{% block preload_js %}
{{ block.super }}
<script src="{% static 'js/selects.js' %}"></script>
{% endblock %} {% endblock %}
{% block js %} {% block js %}
<script src="{% static 'js/bootstrap-select.js' %}"></script> {{ block.super }}
<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;

View File

@@ -5,16 +5,17 @@
{% load static %} {% load static %}
{% block css %} {% block css %}
<link rel="stylesheet" href="{% static 'css/bootstrap-select.css' %}"/> {{ block.super }}
<link rel="stylesheet" href="{% static 'css/ajax-bootstrap-select.css' %}"/> <link rel="stylesheet" href="{% static 'css/selects.css' %}"/>
{% endblock %} {% endblock %}
{% block preload_js %} {% block preload_js %}
<script src="{% static 'js/bootstrap-select.js' %}"></script> {{ block.super }}
<script src="{% static 'js/ajax-bootstrap-select.js' %}"></script> <script src="{% static 'js/selects.js' %}" async></script>
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ block.super }}
<script> <script>
//Get querystring value //Get querystring value
function getParameterByName(name) { function getParameterByName(name) {

View File

@@ -5,7 +5,7 @@
{% button 'submit' %} {% button 'submit' %}
{% elif duplicate %} {% elif duplicate %}
<!--duplicate--> <!--duplicate-->
<button type="submit" class="btn btn-success"><i class="fas fa-check"></i> Create Duplicate</button> <button type="submit" class="btn btn-success"><span class="fas fa-check"></span> Create Duplicate</button>
{% else %} {% else %}
<!--detail view--> <!--detail view-->
<div class="btn-group"> <div class="btn-group">

View File

@@ -30,6 +30,6 @@ def test_cable(db, category, status):
@pytest.fixture @pytest.fixture
def test_asset(db, category, status): def test_asset(db, category, status):
asset = models.Asset.objects.create(asset_id="91991", description="Spaceflower", status=status, category=category, date_acquired=datetime.date(1991, 12, 26)) asset, created = models.Asset.objects.get_or_create(asset_id="91991", description="Spaceflower", status=status, category=category, date_acquired=datetime.date(1991, 12, 26))
yield asset yield asset
asset.delete() asset.delete()

View File

@@ -17,6 +17,7 @@ class AssetList(BasePage):
_status_select_locator = (By.CSS_SELECTOR, 'div#status-group>div.bootstrap-select') _status_select_locator = (By.CSS_SELECTOR, 'div#status-group>div.bootstrap-select')
_category_select_locator = (By.CSS_SELECTOR, 'div#category-group>div.bootstrap-select') _category_select_locator = (By.CSS_SELECTOR, 'div#category-group>div.bootstrap-select')
_go_button_locator = (By.ID, 'id_search') _go_button_locator = (By.ID, 'id_search')
_filter_button_locator = (By.ID, 'filter-submit')
class AssetListRow(Region): class AssetListRow(Region):
_asset_id_locator = (By.CLASS_NAME, "assetID") _asset_id_locator = (By.CLASS_NAME, "assetID")
@@ -56,6 +57,9 @@ class AssetList(BasePage):
def search(self): def search(self):
self.find_element(*self._go_button_locator).click() self.find_element(*self._go_button_locator).click()
def filter(self):
self.find_element(*self._filter_button_locator).click()
@property @property
def status_selector(self): def status_selector(self):
return regions.BootstrapSelectElement(self, self.find_element(*self._status_select_locator)) return regions.BootstrapSelectElement(self, self.find_element(*self._status_select_locator))

View File

@@ -78,7 +78,7 @@ class TestAssetList(AutoLoginTest):
self.page.status_selector.select_all() self.page.status_selector.select_all()
self.page.status_selector.toggle() self.page.status_selector.toggle()
self.assertFalse(self.page.status_selector.is_open) self.assertFalse(self.page.status_selector.is_open)
self.page.search() self.page.filter()
self.assertTrue(len(self.page.assets) == 4) self.assertTrue(len(self.page.assets) == 4)
self.page.category_selector.toggle() self.page.category_selector.toggle()
@@ -86,7 +86,7 @@ class TestAssetList(AutoLoginTest):
self.page.category_selector.set_option("Sound", True) self.page.category_selector.set_option("Sound", True)
self.page.category_selector.close() self.page.category_selector.close()
self.assertFalse(self.page.category_selector.is_open) self.assertFalse(self.page.category_selector.is_open)
self.page.search() self.page.filter()
self.assertTrue(len(self.page.assets) == 2) self.assertTrue(len(self.page.assets) == 2)
asset_ids = list(map(lambda x: x.id, self.page.assets)) asset_ids = list(map(lambda x: x.id, self.page.assets))
self.assertEqual("1", asset_ids[0]) self.assertEqual("1", asset_ids[0])
@@ -128,19 +128,18 @@ class TestAssetForm(AutoLoginTest):
self.page.serial_number = sn = "0124567890-SAUSAGE" self.page.serial_number = sn = "0124567890-SAUSAGE"
self.page.comments = comments = "This is actually a sledgehammer, not a cable..." self.page.comments = comments = "This is actually a sledgehammer, not a cable..."
self.page.purchase_price = "12.99"
self.page.salvage_value = "99.12"
self.page.date_acquired = acquired = datetime.date(2020, 5, 2)
self.page.purchased_from_selector.toggle() self.page.purchased_from_selector.toggle()
self.assertTrue(self.page.purchased_from_selector.is_open) self.assertTrue(self.page.purchased_from_selector.is_open)
self.page.purchased_from_selector.search(self.supplier.name[:-8]) self.page.purchased_from_selector.search(self.supplier.name[:-8])
self.page.purchased_from_selector.set_option(self.supplier.name, True) self.page.purchased_from_selector.set_option(self.supplier.name, True)
self.page.purchase_price = "12.99"
self.page.salvage_value = "99.12"
self.page.date_acquired = acquired = datetime.date(2020, 5, 2)
self.page.parent_selector.toggle() self.page.parent_selector.toggle()
self.assertTrue(self.page.parent_selector.is_open) self.assertTrue(self.page.parent_selector.is_open)
option = str(self.parent) option = str(self.parent)
self.page.parent_selector.search(option) self.page.parent_selector.search(option)
# Needed here but not earlier for whatever reason
self.driver.implicitly_wait(1) self.driver.implicitly_wait(1)
self.page.parent_selector.set_option(option, True) self.page.parent_selector.set_option(option, True)
self.assertTrue(self.page.parent_selector.options[0].selected) self.assertTrue(self.page.parent_selector.options[0].selected)
@@ -273,6 +272,16 @@ class TestSupplierCreateAndEdit(AutoLoginTest):
self.assertTrue(self.page.success) self.assertTrue(self.page.success)
def test_audit_search(logged_in_browser, live_server, test_asset):
page = pages.AssetAuditList(logged_in_browser.driver, live_server.url).open()
# Check that a failed search works
page.set_query("NOTFOUND")
page.search()
assert not logged_in_browser.find_by_id('modal').visible
logged_in_browser.driver.implicitly_wait(4)
assert logged_in_browser.is_text_present("Asset with that ID does not exist!")
@screenshot_failure_cls @screenshot_failure_cls
class TestAssetAudit(AutoLoginTest): class TestAssetAudit(AutoLoginTest):
def setUp(self): def setUp(self):
@@ -336,10 +345,3 @@ class TestAssetAudit(AutoLoginTest):
# Make sure audit log was NOT filled out # Make sure audit log was NOT filled out
audited = models.Asset.objects.get(asset_id=asset_row.id) audited = models.Asset.objects.get(asset_id=asset_row.id)
assert audited.last_audited_by is None assert audited.last_audited_by is None
def test_audit_search(self):
# Check that a failed search works
self.page.set_query("NOTFOUND")
self.page.search()
self.assertFalse(self.driver.find_element_by_id('modal').is_displayed())
self.assertIn("Asset with that ID does not exist!", self.page.error.text)

View File

@@ -2,9 +2,12 @@ from django.conf import settings
import django import django
import pytest import pytest
from django.core.management import call_command from django.core.management import call_command
from RIGS.models import VatRate from RIGS.models import VatRate, Profile
import random import random
from django.db import connection from django.db import connection
from PyRIGS.tests import pages
import os
from selenium import webdriver
def pytest_configure(): def pytest_configure():
@@ -13,9 +16,40 @@ def pytest_configure():
) )
settings.WHITENOISE_USE_FINDERS = True settings.WHITENOISE_USE_FINDERS = True
settings.WHITENOISE_AUTOREFRESH = True settings.WHITENOISE_AUTOREFRESH = True
# TODO Why do we need this, with the above options enabled?
settings.STATICFILES_DIRS += [
os.path.join(settings.BASE_DIR, 'static/'),
]
django.setup() django.setup()
@pytest.fixture # Overrides the one from pytest-django
def admin_user(admin_user):
admin_user.username = "EventTest"
admin_user.first_name = "Event"
admin_user.last_name = "Test"
admin_user.initials = "ETU"
admin_user.save()
return admin_user
@pytest.fixture
def logged_in_browser(live_server, admin_user, browser, db):
login_page = pages.LoginPage(browser.driver, live_server.url).open()
login_page.login(admin_user.username, "password")
yield browser
@pytest.fixture(scope='session')
def splinter_driver_kwargs():
options = webdriver.ChromeOptions()
options.add_argument("--window-size=1920,1080")
options.add_argument("--headless")
if settings.CI:
options.add_argument("--no-sandbox")
return {"options": options}
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def splinter_webdriver(): def splinter_webdriver():
return 'chrome' return 'chrome'

View File

@@ -12,17 +12,27 @@ const browsersync = require('browser-sync').create();
const { exec } = require("child_process"); const { exec } = require("child_process");
const spawn = require('child_process').spawn; const spawn = require('child_process').spawn;
const cssnano = require('cssnano'); const cssnano = require('cssnano');
const con = require('gulp-concat');
const gulpif = require('gulp-if');
sass.compiler = require('node-sass'); sass.compiler = require('node-sass');
function fonts(done) {
return gulp.src('node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.*')
.pipe(gulp.dest('pipeline/built_assets/fonts'))
.pipe(browsersync.stream());
}
function styles(done) { function styles(done) {
const bs_select = ["bootstrap-select.css", "ajax-bootstrap-select.css"]
return gulp.src(['pipeline/source_assets/scss/**/*.scss', return gulp.src(['pipeline/source_assets/scss/**/*.scss',
'node_modules/fullcalendar/main.css', 'node_modules/fullcalendar/main.css',
'node_modules/bootstrap-select/dist/css/bootstrap-select.css', 'node_modules/bootstrap-select/dist/css/bootstrap-select.css',
'node_modules/ajax-bootstrap-select/dist/css/ajax-bootstrap-select.css', 'node_modules/ajax-bootstrap-select/dist/css/ajax-bootstrap-select.css',
'node_modules/flatpickr/dist/flatpickr.css']) 'node_modules/flatpickr/dist/flatpickr.css',])
.pipe(sourcemaps.init()) .pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError)) .pipe(sass().on('error', sass.logError))
.pipe(gulpif(function(file) { return bs_select.includes(file.relative);}, con('selects.css')))
.pipe(postcss([ autoprefixer(), cssnano() ])) .pipe(postcss([ autoprefixer(), cssnano() ]))
.pipe(sourcemaps.write()) .pipe(sourcemaps.write())
.pipe(gulp.dest('pipeline/built_assets/css')) .pipe(gulp.dest('pipeline/built_assets/css'))
@@ -30,12 +40,14 @@ function styles(done) {
} }
function scripts() { function scripts() {
return gulp.src(['pipeline/source_assets/js/**/*.js', const dest = 'pipeline/built_assets/js';
'node_modules/jquery/dist/jquery.js', const base_scripts = ["src.js", "util.js", "alert.js", "collapse.js", "dropdown.js", "modal.js", "konami.js"];
const bs_select = ["bootstrap-select.js", "ajax-bootstrap-select.js"]
const interaction = ["html5sortable.min.js", "interaction.js"]
const jpop = ["jquery.min.js", "popper.min.js"]
return gulp.src(['node_modules/jquery/dist/jquery.min.js',
/* JQuery Plugins */ /* JQuery Plugins */
'node_modules/jquery-ui-dist/jquery-ui.js', 'node_modules/popper.js/dist/umd/popper.min.js',
'node_modules/popper.js/dist/umd/popper.js',
/* Bootstrap Plugins */ /* Bootstrap Plugins */
'node_modules/bootstrap/js/dist/util.js', 'node_modules/bootstrap/js/dist/util.js',
'node_modules/bootstrap/js/dist/tooltip.js', 'node_modules/bootstrap/js/dist/tooltip.js',
@@ -45,17 +57,22 @@ function scripts() {
'node_modules/bootstrap/js/dist/modal.js', 'node_modules/bootstrap/js/dist/modal.js',
'node_modules/bootstrap/js/dist/alert.js', 'node_modules/bootstrap/js/dist/alert.js',
'node_modules/html5sortable/dist/html5sortable.min.js',
'node_modules/clipboard/dist/clipboard.min.js', 'node_modules/clipboard/dist/clipboard.min.js',
'node_modules/flatpickr/dist/flatpickr.min.js', 'node_modules/flatpickr/dist/flatpickr.min.js',
'node_modules/@fortawesome/fontawesome-free/js/all.js',
'node_modules/moment/moment.js', 'node_modules/moment/moment.js',
'node_modules/fullcalendar/main.js', 'node_modules/fullcalendar/main.js',
'node_modules/bootstrap-select/dist/js/bootstrap-select.js', 'node_modules/bootstrap-select/dist/js/bootstrap-select.js',
'node_modules/ajax-bootstrap-select/dist/js/ajax-bootstrap-select.js', 'node_modules/ajax-bootstrap-select/dist/js/ajax-bootstrap-select.js',
'node_modules/konami/konami.js']) 'node_modules/konami/konami.js',
'pipeline/source_assets/js/**/*.js',])
.pipe(gulpif(function(file) { return base_scripts.includes(file.relative);}, con('base.js')))
.pipe(gulpif(function(file) { return bs_select.includes(file.relative);}, con('selects.js')))
.pipe(gulpif(function(file) { return interaction.includes(file.relative);}, con('interaction.js')))
.pipe(gulpif(function(file) { return jpop.includes(file.relative);}, con('jpop.js')))
.pipe(flatten()) .pipe(flatten())
.pipe(terser()) .pipe(terser())
.pipe(gulp.dest('pipeline/built_assets/js')) .pipe(gulp.dest(dest))
.pipe(browsersync.stream()); .pipe(browsersync.stream());
} }
@@ -82,5 +99,5 @@ function watchFiles() {
gulp.watch("**/templates/*.html", browserSyncReload); gulp.watch("**/templates/*.html", browserSyncReload);
} }
exports.build = gulp.parallel(styles, scripts); exports.build = gulp.parallel(styles, scripts, fonts);
exports.watch = gulp.parallel(watchFiles, browserSync); exports.watch = gulp.parallel(watchFiles, browserSync);

236
package-lock.json generated
View File

@@ -5,12 +5,11 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "PyRIGS",
"version": "1.0.0", "version": "1.0.0",
"license": "Custom", "license": "Custom",
"dependencies": { "dependencies": {
"@forevolve/bootstrap-dark": "^1.0.0-alpha.1075", "@forevolve/bootstrap-dark": "^1.0.0-alpha.1075",
"@fortawesome/fontawesome-free": "^5.13.1", "@fortawesome/fontawesome-free": "^5.15.2",
"ajax-bootstrap-select": "^1.4.5", "ajax-bootstrap-select": "^1.4.5",
"autocompleter": "^6.0.3", "autocompleter": "^6.0.3",
"autoprefixer": "^9.8.0", "autoprefixer": "^9.8.0",
@@ -21,13 +20,15 @@
"flatpickr": "^4.6.6", "flatpickr": "^4.6.6",
"fullcalendar": "^5.3.2", "fullcalendar": "^5.3.2",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
"gulp-flatten": "^0.4.0", "gulp-flatten": "^0.4.0",
"gulp-if": "^3.0.0",
"gulp-postcss": "^8.0.0", "gulp-postcss": "^8.0.0",
"gulp-sass": "^4.1.0", "gulp-sass": "^4.1.0",
"gulp-sourcemaps": "^2.6.5", "gulp-sourcemaps": "^2.6.5",
"gulp-uglify": "^3.0.2", "gulp-uglify": "^3.0.2",
"html5sortable": "^0.10.0",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"jquery-ui-dist": "^1.12.1",
"konami": "^1.6.2", "konami": "^1.6.2",
"moment": "^2.27.0", "moment": "^2.27.0",
"node-sass": "^5.0.0", "node-sass": "^5.0.0",
@@ -1006,7 +1007,6 @@
"dependencies": { "dependencies": {
"anymatch": "~3.1.1", "anymatch": "~3.1.1",
"braces": "~3.0.2", "braces": "~3.0.2",
"fsevents": "~2.3.1",
"glob-parent": "~5.1.0", "glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0", "is-binary-path": "~2.1.0",
"is-glob": "~4.0.1", "is-glob": "~4.0.1",
@@ -1349,6 +1349,14 @@
"typedarray": "^0.0.6" "typedarray": "^0.0.6"
} }
}, },
"node_modules/concat-with-sourcemaps": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz",
"integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==",
"dependencies": {
"source-map": "^0.6.1"
}
},
"node_modules/connect": { "node_modules/connect": {
"version": "3.6.6", "version": "3.6.6",
"resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
@@ -1874,6 +1882,30 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/duplexify": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
"integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
"dependencies": {
"end-of-stream": "^1.4.1",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1",
"stream-shift": "^1.0.0"
}
},
"node_modules/duplexify/node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/each-props": { "node_modules/each-props": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz",
@@ -2699,6 +2731,11 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/fork-stream": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz",
"integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA="
},
"node_modules/form-data": { "node_modules/form-data": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
@@ -3066,7 +3103,6 @@
"anymatch": "^2.0.0", "anymatch": "^2.0.0",
"async-each": "^1.0.1", "async-each": "^1.0.1",
"braces": "^2.3.2", "braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0", "glob-parent": "^3.1.0",
"inherits": "^2.0.3", "inherits": "^2.0.3",
"is-binary-path": "^1.0.0", "is-binary-path": "^1.0.0",
@@ -3462,6 +3498,19 @@
"object.assign": "^4.1.0" "object.assign": "^4.1.0"
} }
}, },
"node_modules/gulp-concat": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz",
"integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=",
"dependencies": {
"concat-with-sourcemaps": "^1.0.0",
"through2": "^2.0.0",
"vinyl": "^2.0.0"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/gulp-flatten": { "node_modules/gulp-flatten": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/gulp-flatten/-/gulp-flatten-0.4.0.tgz", "resolved": "https://registry.npmjs.org/gulp-flatten/-/gulp-flatten-0.4.0.tgz",
@@ -3536,6 +3585,33 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/gulp-if": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz",
"integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==",
"dependencies": {
"gulp-match": "^1.1.0",
"ternary-stream": "^3.0.0",
"through2": "^3.0.1"
}
},
"node_modules/gulp-if/node_modules/through2": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
"dependencies": {
"inherits": "^2.0.4",
"readable-stream": "2 || 3"
}
},
"node_modules/gulp-match": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz",
"integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==",
"dependencies": {
"minimatch": "^3.0.3"
}
},
"node_modules/gulp-postcss": { "node_modules/gulp-postcss": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz", "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz",
@@ -3996,6 +4072,11 @@
"resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ=="
}, },
"node_modules/html5sortable": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/html5sortable/-/html5sortable-0.10.0.tgz",
"integrity": "sha512-/F2sUHnSlqXY8Pg1AxLjR5i/ijngpkl2u1x6a6JfwSsoVRZ5b/ZO9MDZopSSzjo7bTZinQbXACTrZI6mpGugMw=="
},
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "1.7.3", "version": "1.7.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
@@ -4564,11 +4645,6 @@
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
}, },
"node_modules/jquery-ui-dist": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.12.1.tgz",
"integrity": "sha1-XAgV08xvkP9fqvWyaKbiO0ypBPo="
},
"node_modules/js-base64": { "node_modules/js-base64": {
"version": "2.6.4", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
@@ -4621,9 +4697,6 @@
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz",
"integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=",
"dev": true, "dev": true,
"dependencies": {
"graceful-fs": "^4.1.6"
},
"optionalDependencies": { "optionalDependencies": {
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
@@ -5281,6 +5354,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"node_modules/micromatch": { "node_modules/micromatch": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
@@ -8334,6 +8412,26 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/ternary-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz",
"integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==",
"dependencies": {
"duplexify": "^4.1.1",
"fork-stream": "^0.0.4",
"merge-stream": "^2.0.0",
"through2": "^3.0.1"
}
},
"node_modules/ternary-stream/node_modules/through2": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
"dependencies": {
"inherits": "^2.0.4",
"readable-stream": "2 || 3"
}
},
"node_modules/tfunk": { "node_modules/tfunk": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/tfunk/-/tfunk-4.0.0.tgz", "resolved": "https://registry.npmjs.org/tfunk/-/tfunk-4.0.0.tgz",
@@ -10328,6 +10426,14 @@
"typedarray": "^0.0.6" "typedarray": "^0.0.6"
} }
}, },
"concat-with-sourcemaps": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz",
"integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==",
"requires": {
"source-map": "^0.6.1"
}
},
"connect": { "connect": {
"version": "3.6.6", "version": "3.6.6",
"resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
@@ -10745,6 +10851,29 @@
"is-obj": "^2.0.0" "is-obj": "^2.0.0"
} }
}, },
"duplexify": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz",
"integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==",
"requires": {
"end-of-stream": "^1.4.1",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1",
"stream-shift": "^1.0.0"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"each-props": { "each-props": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz",
@@ -11425,6 +11554,11 @@
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
}, },
"fork-stream": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz",
"integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA="
},
"form-data": { "form-data": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
@@ -12047,6 +12181,16 @@
} }
} }
}, },
"gulp-concat": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz",
"integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=",
"requires": {
"concat-with-sourcemaps": "^1.0.0",
"through2": "^2.0.0",
"vinyl": "^2.0.0"
}
},
"gulp-flatten": { "gulp-flatten": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/gulp-flatten/-/gulp-flatten-0.4.0.tgz", "resolved": "https://registry.npmjs.org/gulp-flatten/-/gulp-flatten-0.4.0.tgz",
@@ -12102,6 +12246,35 @@
} }
} }
}, },
"gulp-if": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz",
"integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==",
"requires": {
"gulp-match": "^1.1.0",
"ternary-stream": "^3.0.0",
"through2": "^3.0.1"
},
"dependencies": {
"through2": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
"requires": {
"inherits": "^2.0.4",
"readable-stream": "2 || 3"
}
}
}
},
"gulp-match": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz",
"integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==",
"requires": {
"minimatch": "^3.0.3"
}
},
"gulp-postcss": { "gulp-postcss": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz", "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz",
@@ -12479,6 +12652,11 @@
"resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ=="
}, },
"html5sortable": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/html5sortable/-/html5sortable-0.10.0.tgz",
"integrity": "sha512-/F2sUHnSlqXY8Pg1AxLjR5i/ijngpkl2u1x6a6JfwSsoVRZ5b/ZO9MDZopSSzjo7bTZinQbXACTrZI6mpGugMw=="
},
"http-errors": { "http-errors": {
"version": "1.7.3", "version": "1.7.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
@@ -12898,11 +13076,6 @@
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
}, },
"jquery-ui-dist": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/jquery-ui-dist/-/jquery-ui-dist-1.12.1.tgz",
"integrity": "sha1-XAgV08xvkP9fqvWyaKbiO0ypBPo="
},
"js-base64": { "js-base64": {
"version": "2.6.4", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
@@ -13481,6 +13654,11 @@
"trim-newlines": "^1.0.0" "trim-newlines": "^1.0.0"
} }
}, },
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"micromatch": { "micromatch": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
@@ -15960,6 +16138,28 @@
} }
} }
}, },
"ternary-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz",
"integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==",
"requires": {
"duplexify": "^4.1.1",
"fork-stream": "^0.0.4",
"merge-stream": "^2.0.0",
"through2": "^3.0.1"
},
"dependencies": {
"through2": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
"requires": {
"inherits": "^2.0.4",
"readable-stream": "2 || 3"
}
}
}
},
"tfunk": { "tfunk": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/tfunk/-/tfunk-4.0.0.tgz", "resolved": "https://registry.npmjs.org/tfunk/-/tfunk-4.0.0.tgz",

View File

@@ -6,7 +6,7 @@
"license": "Custom", "license": "Custom",
"dependencies": { "dependencies": {
"@forevolve/bootstrap-dark": "^1.0.0-alpha.1075", "@forevolve/bootstrap-dark": "^1.0.0-alpha.1075",
"@fortawesome/fontawesome-free": "^5.13.1", "@fortawesome/fontawesome-free": "^5.15.2",
"ajax-bootstrap-select": "^1.4.5", "ajax-bootstrap-select": "^1.4.5",
"autocompleter": "^6.0.3", "autocompleter": "^6.0.3",
"autoprefixer": "^9.8.0", "autoprefixer": "^9.8.0",
@@ -17,13 +17,15 @@
"flatpickr": "^4.6.6", "flatpickr": "^4.6.6",
"fullcalendar": "^5.3.2", "fullcalendar": "^5.3.2",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
"gulp-flatten": "^0.4.0", "gulp-flatten": "^0.4.0",
"gulp-if": "^3.0.0",
"gulp-postcss": "^8.0.0", "gulp-postcss": "^8.0.0",
"gulp-sass": "^4.1.0", "gulp-sass": "^4.1.0",
"gulp-sourcemaps": "^2.6.5", "gulp-sourcemaps": "^2.6.5",
"gulp-uglify": "^3.0.2", "gulp-uglify": "^3.0.2",
"html5sortable": "^0.10.0",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"jquery-ui-dist": "^1.12.1",
"konami": "^1.6.2", "konami": "^1.6.2",
"moment": "^2.27.0", "moment": "^2.27.0",
"node-sass": "^5.0.0", "node-sass": "^5.0.0",

View File

@@ -117,23 +117,9 @@ $('body').on('submit', '.itemised_form', function (e) {
$('#id_items_json').val(JSON.stringify(objectitems)); $('#id_items_json').val(JSON.stringify(objectitems));
}); });
// Return a helper with preserved width of cells sortable("#item-table tbody")[0].addEventListener('sortupdate', function (e) {
var fixHelper = function (e, ui) { var items = e.detail.destination.items;
ui.children().each(function () { for(var i in items) {
$(this).width($(this).width()); objectitems[items[i].dataset.pk].fields.order = i;
});
return ui;
};
$("#item-table tbody").sortable({
helper: fixHelper,
update: function (e, ui) {
info = $(this).sortable("toArray");
itemorder = new Array();
$.each(info, function (key, value) {
pk = $('#' + value).data('pk');
objectitems[pk].fields.order = key;
});
} }
}); });

View File

@@ -0,0 +1,39 @@
Date.prototype.getISOString = function () {
var yyyy = this.getFullYear().toString();
var mm = (this.getMonth() + 1).toString(); // getMonth() is zero-based
var dd = this.getDate().toString();
return yyyy + '-' + (mm[1] ? mm : "0" + mm[0]) + '-' + (dd[1] ? dd : "0" + dd[0]); // padding
};
jQuery(document).ready(function () {
jQuery(document).on('click', '.modal-href', function (e) {
$link = jQuery(this);
// Anti modal inception
if ($link.parents('#modal').length == 0) {
e.preventDefault();
modaltarget = $link.data('target');
modalobject = "";
jQuery('#modal').load($link.attr('href'), function (e) {
jQuery('#modal').modal();
});
}
});
var easter_egg = new Konami();
easter_egg.code = function () {
var s = document.createElement('script');
s.type = 'text/javascript';
document.body.appendChild(s);
s.src = '{% static "js/asteroids.min.js"%}';
ga('send', 'event', 'easter_egg', 'activated');
}
easter_egg.load();
});
//CTRL-Enter form submission
document.body.addEventListener('keydown', function(e) {
if(e.keyCode == 13 && (e.metaKey || e.ctrlKey)) {
var target = e.target;
if(target.form) {
target.form.submit();
}
}
});
$('.navbar-collapse').addClass('collapse');

View File

@@ -6,8 +6,6 @@
@import "node_modules/bootstrap/scss/root"; @import "node_modules/bootstrap/scss/root";
@import "node_modules/bootstrap/scss/type"; @import "node_modules/bootstrap/scss/type";
@import "node_modules/bootstrap/scss/images"; @import "node_modules/bootstrap/scss/images";
//@import "node_modules/bootstrap/scss/code";
//@import "node_modules/bootstrap/scss/grid";
@import "node_modules/bootstrap/scss/tables"; @import "node_modules/bootstrap/scss/tables";
@import "node_modules/bootstrap/scss/forms"; @import "node_modules/bootstrap/scss/forms";
@import "node_modules/bootstrap/scss/buttons"; @import "node_modules/bootstrap/scss/buttons";
@@ -19,23 +17,21 @@
@import "node_modules/bootstrap/scss/nav"; @import "node_modules/bootstrap/scss/nav";
@import "node_modules/bootstrap/scss/navbar"; @import "node_modules/bootstrap/scss/navbar";
@import "node_modules/bootstrap/scss/card"; @import "node_modules/bootstrap/scss/card";
//@import "node_modules/bootstrap/scss/breadcrumb";
@import "node_modules/bootstrap/scss/pagination"; @import "node_modules/bootstrap/scss/pagination";
@import "node_modules/bootstrap/scss/badge"; @import "node_modules/bootstrap/scss/badge";
//@import "node_modules/bootstrap/scss/jumbotron";
@import "node_modules/bootstrap/scss/alert"; @import "node_modules/bootstrap/scss/alert";
//@import "node_modules/bootstrap/scss/progress";
@import "node_modules/bootstrap/scss/media"; @import "node_modules/bootstrap/scss/media";
@import "node_modules/bootstrap/scss/list-group"; @import "node_modules/bootstrap/scss/list-group";
@import "node_modules/bootstrap/scss/close"; @import "node_modules/bootstrap/scss/close";
//@import "node_modules/bootstrap/scss/toasts";
@import "node_modules/bootstrap/scss/modal"; @import "node_modules/bootstrap/scss/modal";
@import "node_modules/bootstrap/scss/tooltip"; @import "node_modules/bootstrap/scss/tooltip";
@import "node_modules/bootstrap/scss/popover"; @import "node_modules/bootstrap/scss/popover";
//@import "node_modules/bootstrap/scss/carousel";
@import "node_modules/bootstrap/scss/spinners"; @import "node_modules/bootstrap/scss/spinners";
@import "node_modules/bootstrap/scss/utilities"; @import "node_modules/bootstrap/scss/utilities";
//@import "node_modules/bootstrap/scss/print"; //FontAwesome
$fa-font-path: '/static/fonts';
@import "node_modules/@fortawesome/fontawesome-free/scss/fontawesome";
@import "node_modules/@fortawesome/fontawesome-free/scss/solid";
@media screen and @media screen and
(prefers-reduced-motion: reduce), (prefers-reduced-motion: reduce),

View File

@@ -15,15 +15,13 @@
<link rel="icon" type="image/png" href="{% static 'imgs/pyrigs-avatar.png' %}"> <link rel="icon" type="image/png" href="{% static 'imgs/pyrigs-avatar.png' %}">
<link rel="apple-touch-icon" href="{% static 'imgs/pyrigs-avatar.png' %}"> <link rel="apple-touch-icon" href="{% static 'imgs/pyrigs-avatar.png' %}">
<link href="{% static 'fonts/OpenSans-Regular.tff' %}"> <link rel="preload" href="{% static 'fonts/fa-solid-900.woff2' %}" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" type="text/css" href="{% static 'css/dark_screen.css' %}" {% if not request.user.dark_theme %}media="(prefers-color-scheme: dark)"{% endif %}>
<link rel="stylesheet" type="text/css" href="{% static 'css/screen.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'css/screen.css' %}">
{% block css %} {% block css %}
{% endblock %} {% endblock %}
<script src="{% static 'js/jquery.js' %}"></script> <script src="{% static 'js/jpop.js' %}"></script>
<script src="{% static 'js/popper.js' %}"></script>
{% block preload_js %} {% block preload_js %}
{% endblock %} {% endblock %}
@@ -36,7 +34,7 @@
{% block navbar %} {% block navbar %}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark flex-nowrap" role="navigation"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark flex-nowrap" role="navigation">
<a class="navbar-brand" href="{% if request.user.is_authenticated %}https://members.nottinghamtec.co.uk{%else%}https://nottinghamtec.co.uk{%endif%}"> <a class="navbar-brand" href="{% if request.user.is_authenticated %}https://members.nottinghamtec.co.uk{%else%}https://nottinghamtec.co.uk{%endif%}">
<img src="{% static 'imgs/logo.png' %}" width="40" height="40" alt="TEC's Logo: Serif 'TEC' vertically next to a blue box with the words 'PA and Lighting', surrounded by graduated rings"> <img src="{% static 'imgs/logo.webp' %}" width="40" height="40" alt="TEC's Logo: Serif 'TEC' vertically next to a blue box with the words 'PA and Lighting', surrounded by graduated rings">
</a> </a>
<div class="container"> <div class="container">
{% block titleheader %} {% block titleheader %}
@@ -80,67 +78,8 @@
</div> </div>
<div class="modal fade" id="modal" role="dialog" tabindex=-1></div> <div class="modal fade" id="modal" role="dialog" tabindex=-1></div>
<script> <script src="{% static 'js/base.js' %}"></script>
if({{ request.user.dark_theme|lower }} || window.matchMedia('(prefers-color-scheme: dark)').matches) { {% include 'partials/dark_theme.html' %}
document.body.setAttribute('data-theme', 'dark');
}
</script>
<script>
Date.prototype.getISOString = function () {
var yyyy = this.getFullYear().toString();
var mm = (this.getMonth() + 1).toString(); // getMonth() is zero-based
var dd = this.getDate().toString();
return yyyy + '-' + (mm[1] ? mm : "0" + mm[0]) + '-' + (dd[1] ? dd : "0" + dd[0]); // padding
};
</script>
<script src="{% static 'js/util.js' %}"></script>
<script src="{% static 'js/alert.js' %}"></script>
<script src="{% static 'js/collapse.js' %}"></script>
<script>
$('.navbar-collapse').addClass('collapse')
</script>
<script src="{% static 'js/dropdown.js' %}"></script>
<script src="{% static 'js/modal.js' %}"></script>
<script src="{% static 'js/konami.js' %}"></script>
<script src="{% static 'js/all.js' %}"></script> <!---FontAwesome--->
<script>
jQuery(document).ready(function () {
jQuery(document).on('click', '.modal-href', function (e) {
$link = jQuery(this);
// Anti modal inception
if ($link.parents('#modal').length == 0) {
e.preventDefault();
modaltarget = $link.data('target');
modalobject = "";
jQuery('#modal').load($link.attr('href'), function (e) {
jQuery('#modal').modal();
});
}
});
var easter_egg = new Konami();
easter_egg.code = function () {
var s = document.createElement('script');
s.type = 'text/javascript';
document.body.appendChild(s);
s.src = '{% static "js/asteroids.min.js"%}';
ga('send', 'event', 'easter_egg', 'activated');
}
easter_egg.load();
});
</script>
<script>
//CTRL-Enter form submission
document.body.addEventListener('keydown', function(e) {
if(e.keyCode == 13 && (e.metaKey || e.ctrlKey)) {
var target = e.target;
if(target.form) {
target.form.submit();
}
}
});
</script>
{% block js %} {% block js %}
{% endblock %} {% endblock %}
</body> </body>

View File

@@ -10,12 +10,8 @@
<base target="_blank" /><!-- Open all links in a new tab, not in the iframe --> <base target="_blank" /><!-- Open all links in a new tab, not in the iframe -->
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
<link rel="stylesheet" type="text/css" href="{% static 'css/dark_screen.css' %}" {% if not request.user.dark_theme %}media="(prefers-color-scheme: dark)"{% endif %}>
<link href="{% static 'fonts/OpenSans-Regular.tff' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/screen.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'css/screen.css' %}">
</head> </head>
<body> <body>
@@ -36,11 +32,7 @@
{% endblock %} {% endblock %}
</div> </div>
</div> </div>
<script> {% include 'partials/dark_theme.html' %}
if({{ request.user.dark_theme|lower }} || window.matchMedia('(prefers-color-scheme: dark)')) {
document.body.setAttribute('data-theme', 'dark');
}
</script>
{% block js %} {% block js %}
{% endblock %} {% endblock %}
</body> </body>

View File

@@ -6,34 +6,34 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<h1 class="col-sm-12 pb-3">R<small class="text-muted">ig</small> I<small class="text-muted">nformation</small> G<small class="text-muted">athering</small> S<small class="text-muted">ystem</small></h1> <h1 class="col-sm-12 pb-3">R<small class="text-muted">ig</small> I<small class="text-muted">nformation</small> G<small class="text-muted">athering</small> S<small class="text-muted">ystem</small></h1>
<h4 class="col-sm-12 pb-3">Welcome back {{ user.get_full_name }}, there {%if rig_count == 1 %}is one rig coming up{%else%}are {{ rig_count|apnumber }} rigs coming up.{%endif%}</h4> <h2 class="col-sm-12 pb-3">Welcome back {{ user.get_full_name }}, there {%if rig_count == 1 %}is one rig coming up{%else%}are {{ rig_count|apnumber }} rigs coming up.{%endif%}</h2>
<div class="col-sm mb-3"> <div class="col-sm mb-3">
<div class="card"> <div class="card">
<h4 class="card-header">Rigboard</h4> <h4 class="card-header">Rigboard</h4>
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
<a class="list-group-item list-group-item-action" href="{% url 'rigboard' %}"><i class="fas fa-list"></i> Rigboard</a> <a class="list-group-item list-group-item-action" href="{% url 'rigboard' %}"><span class="fas fa-list align-middle"></span><span class="align-middle"> Rigboard</span></a>
<a class="list-group-item list-group-item-action" href="{% url 'web_calendar' %}"><i class="fas fa-calendar"></i> Calendar</a> <a class="list-group-item list-group-item-action" href="{% url 'web_calendar' %}"><span class="fas fa-calendar align-middle"></span><span class="align-middle"> Calendar</span></a>
{% if perms.RIGS.add_event %} {% if perms.RIGS.add_event %}
<a class="list-group-item list-group-item-action" href="{% url 'event_create' %}"><i class="fas fa-plus"></i> New Event</a> <a class="list-group-item list-group-item-action" href="{% url 'event_create' %}"><span class="fas fa-plus align-middle"></span><span class="align-middle"> New Event</span></a>
{% endif %} {% endif %}
</div> </div>
<h4 class="card-header">Asset Database</h4> <h4 class="card-header">Asset Database</h4>
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
<a class="list-group-item list-group-item-action" href="{% url 'asset_index' %}"><i class="fas fa-tag"></i> Asset List </a> <a class="list-group-item list-group-item-action" href="{% url 'asset_index' %}"><span class="fas fa-tag align-middle"></span><span class="align-middle"> Asset List</span></a>
{% if perms.assets.add_asset %} {% if perms.assets.add_asset %}
<a class="list-group-item list-group-item-action" href="{% url 'asset_create' %}"><i class="fas fa-plus"></i> New Asset</a> <a class="list-group-item list-group-item-action" href="{% url 'asset_create' %}"><span class="fas fa-plus align-middle"></span><span class="align-middle"> New Asset</span></a>
{% endif %} {% endif %}
<a class="list-group-item list-group-item-action" href="{% url 'supplier_list' %}"><i class="fas fa-parachute-box"></i> Supplier List </a> <a class="list-group-item list-group-item-action" href="{% url 'supplier_list' %}"><span class="fas fa-parachute-box align-middle"></span><span class="align-middle"> Supplier List</span></a>
{% if perms.assets.add_asset %} {% if perms.assets.add_supplier %}
<a class="list-group-item list-group-item-action" href="{% url 'supplier_create' %}"><i class="fas fa-plus"></i> New Supplier</a> <a class="list-group-item list-group-item-action" href="{% url 'supplier_create' %}"><span class="fas fa-plus align-middle"></span><span class="align-middle"> New Supplier</span></a>
{% endif %} {% endif %}
</div> </div>
<h4 class="card-header">Quick Links</h4> <h4 class="card-header">Quick Links</h4>
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
<a class="list-group-item list-group-item-action" href="https://forum.nottinghamtec.co.uk" target="_blank" rel="noopener noreferrer"><i class="fas fa-comment-alt"></i> TEC Forum</a> <a class="list-group-item list-group-item-action" href="https://forum.nottinghamtec.co.uk" target="_blank" rel="noopener noreferrer"><span class="fas fa-comment-alt text-info align-middle"></span><span class="align-middle"> TEC Forum</span></a>
<a class="list-group-item list-group-item-action" href="//wiki.nottinghamtec.co.uk" target="_blank" rel="noopener noreferrer"><i class="fas fa-pen-square"></i> TEC Wiki</a> <a class="list-group-item list-group-item-action" href="//wiki.nottinghamtec.co.uk" target="_blank" rel="noopener noreferrer"><span class="fas fa-pen-square align-middle"></span><span class="align-middle"> TEC Wiki</span></a>
{% if perms.RIGS.view_event %} {% if perms.RIGS.view_event %}
<a class="list-group-item list-group-item-action" href="//members.nottinghamtec.co.uk/price" target="_blank"><i class="fas fa-pound-sign"></i> Price List</a> <a class="list-group-item list-group-item-action" href="//members.nottinghamtec.co.uk/price" target="_blank" rel="noopener noreferrer"><span class="fas fa-pound-sign text-warning align-middle"></span><span class="align-middle"> Price List</span></a>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@@ -1,7 +1,7 @@
{% if submit %} {% if submit %}
<button type="submit" class="btn {{ class }}" title="{{ text }}" {% if id %}id="{{id}}"{%endif%} {% if style %}style="{{style}}"{%endif%}><span class="fas {{ icon }}"></span> <span class="d-none d-sm-inline">{{ text }}</span></button> <button type="submit" class="btn {{ class }}" title="{{ text }}" {% if id %}id="{{id}}"{%endif%} {% if style %}style="{{style}}"{%endif%}><span class="fas {{ icon }} align-middle"></span> <span class="d-none d-sm-inline align-middle">{{ text }}</span></button>
{% elif pk %} {% elif pk %}
<a href="{% url target pk %}" class="btn {{ class }}" {% if id %}id="{{id}}"{%endif%} {% if style %}style="{{style}}"{%endif%} {% if text == 'Print' %}target="_blank"{%endif%}><span class="fas {{ icon }}"></span> <span class="d-none d-sm-inline">{{ text }}</span></a> <a href="{% url target pk %}" class="btn {{ class }}" {% if id %}id="{{id}}"{%endif%} {% if style %}style="{{style}}"{%endif%} {% if text == 'Print' %}target="_blank"{%endif%}><span class="fas {{ icon }} align-middle"></span> <span class="d-none d-sm-inline align-middle">{{ text }}</span></a>
{% else %} {% else %}
<a href="{% url target %}" class="btn {{ class }}" {% if id %}id="{{id}}"{%endif%} {% if style %}style="{{style}}"{%endif%}><span class="fas {{ icon }}"></span> <span class="d-none d-sm-inline">{{ text }}</span></a> <a href="{% url target %}" class="btn {{ class }}" {% if id %}id="{{id}}"{%endif%} {% if style %}style="{{style}}"{%endif%}><span class="fas {{ icon }} align-middle"></span> <span class="d-none d-sm-inline align-middle">{{ text }}</span></a>
{% endif %} {% endif %}

View File

@@ -0,0 +1,7 @@
{% load static %}
<script>
if({{ request.user.dark_theme|lower|default:'false' }} || window.matchMedia('(prefers-color-scheme: dark)').matches) {
$('<link>').prependTo('head').attr({type : 'text/css', rel : 'stylesheet'}).attr('href', '{% static "css/dark_screen.css" %}');
document.body.setAttribute('data-theme', 'dark');
}
</script>

2
templates/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow: /

View File

@@ -31,7 +31,7 @@
<div class="media-body"> <div class="media-body">
<h5> <h5>
{{ version.revision.user.name|default:'System' }} {{ version.revision.user.name|default:'System' }}
<span class="float-right"><small><span class="fas fa-clock"></span> {{version.revision.date_created|naturaltime}}</small></span> <span class="float-right"><small><span class="fas fa-clock"></span> <span class="time">{{version.revision.date_created|date:"c"}}</span> ({{version.revision.date_created}})</small></span>
</h5> </h5>
{% endif %} {% endif %}
<p> <p>
@@ -48,3 +48,15 @@
</div> </div>
{% endcache %} {% endcache %}
{% endblock %} {% endblock %}
{% block js %}
<script>
$(document).ready(function() {
const times = document.getElementsByClassName("time");
var i;
for(i = 0; i < times.length; i++) {
times[i].innerHTML = moment(times[i].innerHTML).fromNow();
}
});
</script>
{% endblock %}

View File

@@ -16,7 +16,7 @@
<th scope="row">{{ version.revision.date_created }}</th> <th scope="row">{{ version.revision.date_created }}</th>
<td><a href="{{ version.changes.new.get_absolute_url }}">{{ version.changes.new.display_id|default:version.changes.new.pk }} | {{version.changes.new|to_class_name}}</a></td> <td><a href="{{ version.changes.new.get_absolute_url }}">{{ version.changes.new.display_id|default:version.changes.new.pk }} | {{version.changes.new|to_class_name}}</a></td>
<td>{{ version.pk }}|{{ version.revision.pk }}</td> <td>{{ version.pk }}|{{ version.revision.pk }}</td>
<td>{{ version.revision.user.name|default:"System" }}</td> <td>{% include 'partials/linked_name.html' with profile=version.revision.user %}</td>
<td> <td>
{% if version.changes.old == None %} {% if version.changes.old == None %}
Created {{version.changes.new|to_class_name}} Created {{version.changes.new|to_class_name}}

View File

@@ -78,7 +78,7 @@ class ActivityFeed(generic.ListView): # Appears on homepage
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
# Call the base implementation first to get a context # Call the base implementation first to get a context
context = super(ActivityFeed, self).get_context_data(**kwargs) context = super(ActivityFeed, self).get_context_data(**kwargs)
context['page_title'] = "Activity Feed"
maxTimeDelta = datetime.timedelta(hours=1) maxTimeDelta = datetime.timedelta(hours=1)
items = [] items = []