mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-01-30 03:42:15 +00:00
Compare commits
66 Commits
optimisati
...
de5997b9da
| Author | SHA1 | Date | |
|---|---|---|---|
|
de5997b9da
|
|||
|
4a121964dc
|
|||
|
df5e4c8e0a
|
|||
|
3601c14ab7
|
|||
|
adde6496f5
|
|||
|
ad734d94b2
|
|||
|
7d3ada822d
|
|||
|
732af53fda
|
|||
|
4fb0529cc0
|
|||
|
aa23b1cd09
|
|||
|
0c4228da57
|
|||
|
246a52d19e
|
|||
|
8b10aaf700
|
|||
|
4d0d4f02aa
|
|||
|
af987c1ebb
|
|||
|
d406a911bb
|
|||
|
63c5a68933
|
|||
|
66f7f830db
|
|||
|
9590c2066d
|
|||
|
8b48b02ca7
|
|||
|
68e7ec2a0d
|
|||
|
11636809ca
|
|||
|
5779ebdf7e
|
|||
|
d7458f6366
|
|||
| febf9cf3ed | |||
| 3322a5ddf8 | |||
| be648c20d5 | |||
| b6ef7c1d89 | |||
| 85f40b358a | |||
| 2698798035 | |||
| dbaab5cf8c | |||
| 0a9f82e480 | |||
| 54f2bd36bd | |||
| e836195fef | |||
| 68a424d62b | |||
| 5e15b8bb59 | |||
| d26c1b535e | |||
| dff5ac2308 | |||
| a3729fa930 | |||
| 458a734331 | |||
| b1646d556c | |||
| f8624d3b7a | |||
| f6836fdab6 | |||
|
|
673bee4215 | ||
| b3949f2903 | |||
|
|
bab31107f7 | ||
|
|
2d8473b698 | ||
| d81ecd9015 | |||
| b42c583897 | |||
| 57e966826e | |||
|
6a5de4a9d6
|
|||
| 56bbf4c17c | |||
|
|
698f0be281 | ||
|
|
483f06e96f | ||
|
|
22193f3c39 | ||
|
|
59b63fe7aa | ||
| 5976ce9ea2 | |||
|
780d05e27c
|
|||
| 8cfa4bd79d | |||
|
36f83ee59b
|
|||
|
6d768832f4
|
|||
|
38da8642fa
|
|||
|
f75e1d5bfc
|
|||
|
3f959f8d56
|
|||
|
b63a01120b
|
|||
| 911336ceec |
5
.github/workflows/django.yml
vendored
5
.github/workflows/django.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
python-version: 3.9.1
|
||||
- uses: actions/cache@v2
|
||||
id: pcache
|
||||
with:
|
||||
@@ -27,8 +27,7 @@ jobs:
|
||||
${{ runner.os }}-pipenv-
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pipenv
|
||||
python -m pip install --upgrade pip pipenv
|
||||
pipenv install -d
|
||||
# if: steps.pcache.outputs.cache-hit != 'true'
|
||||
- name: Cache Static Files
|
||||
|
||||
8
Pipfile
8
Pipfile
@@ -19,7 +19,7 @@ cssselect = "~=1.1.0"
|
||||
cssutils = "~=1.0.2"
|
||||
dj-database-url = "~=0.5.0"
|
||||
dj-static = "~=0.0.6"
|
||||
Django = "~=3.1.5"
|
||||
Django = "~=3.1.12"
|
||||
django-debug-toolbar = "~=3.2"
|
||||
django-filter = "~=2.4.0"
|
||||
django-ical = "~=1.7.1"
|
||||
@@ -35,11 +35,11 @@ gunicorn = "~=20.0.4"
|
||||
icalendar = "~=4.0.7"
|
||||
idna = "~=2.10"
|
||||
importlib-metadata = "~=3.4.0"
|
||||
lxml = "~=4.6.2"
|
||||
lxml = "~=4.6.3"
|
||||
Markdown = "~=3.3.3"
|
||||
msgpack = "~=1.0.2"
|
||||
pep517 = "~=0.9.1"
|
||||
Pillow = "~=8.1.0"
|
||||
Pillow = "~=8.2.0"
|
||||
premailer = "~=3.7.0"
|
||||
progress = "~=1.5"
|
||||
psutil = "~=5.8.0"
|
||||
@@ -62,7 +62,7 @@ static3 = "~=0.7.0"
|
||||
svg2rlg = "~=0.3"
|
||||
tini = "~=3.0.1"
|
||||
tornado = "~=6.1"
|
||||
urllib3 = "~=1.26.2"
|
||||
urllib3 = "~=1.26.5"
|
||||
whitenoise = "~=5.2.0"
|
||||
yolk = "~=0.4.3"
|
||||
"z3c.rml" = "~=4.1.2"
|
||||
|
||||
537
Pipfile.lock
generated
537
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "4f5b2f535c10a1b2bcbb4f73aa01ccef700c679685311e740e47686b3942673c"
|
||||
"sha256": "ef5cfe5505d1e3cd15d0c0690a3a1b7475364b9a42555c0d90d0ee30b11bf322"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -121,11 +121,11 @@
|
||||
},
|
||||
"configparser": {
|
||||
"hashes": [
|
||||
"sha256:005c3b102c96f4be9b8f40dafbd4997db003d07d1caa19f37808be8031475f2a",
|
||||
"sha256:08e8a59ef1817ac4ed810bb8e17d049566dd6e024e7566f6285c756db2bb4ff8"
|
||||
"sha256:85d5de102cfe6d14a5172676f09d19c465ce63d6019cf0a4ef13385fc535e828",
|
||||
"sha256:af59f2cdd7efbdd5d111c1976ecd0b82db9066653362f0962d7bf1d3ab89a1fa"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.0.1"
|
||||
"version": "==5.0.2"
|
||||
},
|
||||
"contextlib2": {
|
||||
"hashes": [
|
||||
@@ -176,19 +176,19 @@
|
||||
},
|
||||
"django": {
|
||||
"hashes": [
|
||||
"sha256:32ce792ee9b6a0cbbec340123e229ac9f765dff8c2a4ae9247a14b2ba3a365a7",
|
||||
"sha256:baf099db36ad31f970775d0be5587cc58a6256a6771a44eb795b554d45f211b8"
|
||||
"sha256:a523d62b7ab2908f551dabc32b99017a86aa7784e32b761708e52be3dce6d35d",
|
||||
"sha256:dc41bf07357f1f4810c1c555b685cb51f780b41e37892d6cc92b89789f2847e1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.1.7"
|
||||
"version": "==3.1.12"
|
||||
},
|
||||
"django-debug-toolbar": {
|
||||
"hashes": [
|
||||
"sha256:84e2607d900dbd571df0a2acf380b47c088efb787dce9805aefeb407341961d2",
|
||||
"sha256:9e5a25d0c965f7e686f6a8ba23613ca9ca30184daa26487706d4829f5cfb697a"
|
||||
"sha256:a5ff2a54f24bf88286f9872836081078f4baa843dc3735ee88524e89f8821e33",
|
||||
"sha256:e759e63e3fe2d3110e0e519639c166816368701eab4a47fed75d7de7018467b9"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.2"
|
||||
"version": "==3.2.1"
|
||||
},
|
||||
"django-filter": {
|
||||
"hashes": [
|
||||
@@ -279,6 +279,7 @@
|
||||
"sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d",
|
||||
"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"
|
||||
},
|
||||
"icalendar": {
|
||||
@@ -307,54 +308,63 @@
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d",
|
||||
"sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37",
|
||||
"sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01",
|
||||
"sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2",
|
||||
"sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644",
|
||||
"sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75",
|
||||
"sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80",
|
||||
"sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2",
|
||||
"sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780",
|
||||
"sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98",
|
||||
"sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308",
|
||||
"sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf",
|
||||
"sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388",
|
||||
"sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d",
|
||||
"sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3",
|
||||
"sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8",
|
||||
"sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af",
|
||||
"sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2",
|
||||
"sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e",
|
||||
"sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939",
|
||||
"sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03",
|
||||
"sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d",
|
||||
"sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a",
|
||||
"sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5",
|
||||
"sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a",
|
||||
"sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711",
|
||||
"sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf",
|
||||
"sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089",
|
||||
"sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505",
|
||||
"sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b",
|
||||
"sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f",
|
||||
"sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc",
|
||||
"sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e",
|
||||
"sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931",
|
||||
"sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc",
|
||||
"sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe",
|
||||
"sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e"
|
||||
"sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d",
|
||||
"sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3",
|
||||
"sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2",
|
||||
"sha256:1b38116b6e628118dea5b2186ee6820ab138dbb1e24a13e478490c7db2f326ae",
|
||||
"sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f",
|
||||
"sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927",
|
||||
"sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3",
|
||||
"sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7",
|
||||
"sha256:3082c518be8e97324390614dacd041bb1358c882d77108ca1957ba47738d9d59",
|
||||
"sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f",
|
||||
"sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade",
|
||||
"sha256:36108c73739985979bf302006527cf8a20515ce444ba916281d1c43938b8bb96",
|
||||
"sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468",
|
||||
"sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b",
|
||||
"sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4",
|
||||
"sha256:4c61b3a0db43a1607d6264166b230438f85bfed02e8cff20c22e564d0faff354",
|
||||
"sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83",
|
||||
"sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04",
|
||||
"sha256:5c8c163396cc0df3fd151b927e74f6e4acd67160d6c33304e805b84293351d16",
|
||||
"sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791",
|
||||
"sha256:6f12e1427285008fd32a6025e38e977d44d6382cf28e7201ed10d6c1698d2a9a",
|
||||
"sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51",
|
||||
"sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1",
|
||||
"sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a",
|
||||
"sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f",
|
||||
"sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee",
|
||||
"sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec",
|
||||
"sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969",
|
||||
"sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28",
|
||||
"sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a",
|
||||
"sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa",
|
||||
"sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106",
|
||||
"sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d",
|
||||
"sha256:c47ff7e0a36d4efac9fd692cfa33fbd0636674c102e9e8d9b26e1b93a94e7617",
|
||||
"sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4",
|
||||
"sha256:cdaf11d2bd275bf391b5308f86731e5194a21af45fbaaaf1d9e8147b9160ea92",
|
||||
"sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0",
|
||||
"sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4",
|
||||
"sha256:d916d31fd85b2f78c76400d625076d9124de3e4bda8b016d25a050cc7d603f24",
|
||||
"sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2",
|
||||
"sha256:e1cbd3f19a61e27e011e02f9600837b921ac661f0c40560eefb366e4e4fb275e",
|
||||
"sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0",
|
||||
"sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654",
|
||||
"sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2",
|
||||
"sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23",
|
||||
"sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.6.2"
|
||||
"version": "==4.6.3"
|
||||
},
|
||||
"markdown": {
|
||||
"hashes": [
|
||||
"sha256:5d9f2b5ca24bc4c7a390d22323ca4bad200368612b5aaa7796babf971d2b2f18",
|
||||
"sha256:c109c15b7dc20a9ac454c9e6025927d44460b85bd039da028d85e2b6d0bcc328"
|
||||
"sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49",
|
||||
"sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.3.3"
|
||||
"version": "==3.3.4"
|
||||
},
|
||||
"msgpack": {
|
||||
"hashes": [
|
||||
@@ -400,75 +410,82 @@
|
||||
},
|
||||
"pikepdf": {
|
||||
"hashes": [
|
||||
"sha256:02815df9499d3a6dfac2e07e4d2fdbe25fcbefae208970e76bff90af4a402d49",
|
||||
"sha256:06b0c3004cc9e9068ebc62bb59c3c3a54e7af13867f4a326690d79c69a1cf288",
|
||||
"sha256:1a471c6ca288fbcd0e1b0e128ef12bb14c5e7db745786308ba292fc7cff30bb5",
|
||||
"sha256:489ed0fd1281beb0343a34fe8b9d94407c440ed0419ab2e6f5ea297a41824a31",
|
||||
"sha256:5106b27f7085ed449e057b9988f07c80a87292d2bf46c585a8635ac7a3ccf0d5",
|
||||
"sha256:51acffba6f3d21674eea7a0432ce1adaf0743641d57844a5e3dc92b4a7e81c85",
|
||||
"sha256:53d694d70dd072a47bd2dd71329dcef0f809dcd8084d1d11c31baf3b64cd345c",
|
||||
"sha256:6a640fef52dc785abd354d6800a52ecc02656c98dbfc2ecde559323b001bd43f",
|
||||
"sha256:7006ef95f847412605dea6e772019f637949eaeaf65363d5d6afd9aa96bf5623",
|
||||
"sha256:81e13b62877dbc089095e7efa03c27834bdf6b49d404d064cb227b0e179ce049",
|
||||
"sha256:8fc3e97b24dafbd4b1ac057c8f144c18467ce55d342a1a8c42688890ead58ef3",
|
||||
"sha256:9158dc4d3ef4e2301fb1879d5825530fdb32143ced770d60fa8e5badeee70a35",
|
||||
"sha256:961337a10b42bd656b59116ce1c574eafd515b45a513221df6ae1f11734bfb6c",
|
||||
"sha256:994ccac972357a7b9b147217e1beea2f7688697944b862dbb2a3e64aa9a5ff14",
|
||||
"sha256:a8e9abf7db0351357b55c3f935979e7dc14f3f259a25d15bcc86abce730955a7",
|
||||
"sha256:b836eda7f70b9dd75ccdeaf4e78b38393118a66821a69a10054b1430f945d1fd",
|
||||
"sha256:b859a225f6bd953472c50f4df612b4575df646e560189e3720310ad65b6805a0",
|
||||
"sha256:cae106bd461cfad73c554c09f6db1d5f2c6a28f5b8cb0602b63046840f488226",
|
||||
"sha256:cdc75b8fa5a650f4fc91214a315358fde7470e09b95a00981b73a7c4ac5ddb97",
|
||||
"sha256:e2c28da4f37ad9a3efbedbbfc6f1084941bde43903d30dfdbb338d5ba94c9f50",
|
||||
"sha256:e596bd8fdbf40bfb8dc8068cdbc7e5b72052188d1ad8ca84da9d6b77658a8b31",
|
||||
"sha256:ee957b9c60b6def20cbdf656d35859ce211eec02dafa3abb9d5ca937d32a3c3b",
|
||||
"sha256:f9428d4b1f70af4f4560be4dccbbc5ab5308c00c5b62ed2f1c44ce9e2591b3d2"
|
||||
"sha256:1bc381015edd3793bd2f458b1b7fa0412550b0769f7371b4329481892cc482c8",
|
||||
"sha256:2526569db97a67ea00308145fd0ed56c65fb268befba1a67f58f262be8f70262",
|
||||
"sha256:2a6689fc87f3886cf6d801a5cb606e4464b71f0408557010f503cf02f3ce95d2",
|
||||
"sha256:33643d8ff2339e7cef803227fdf9fe96461d38d79c9bf9fd26d910e27d3c49d1",
|
||||
"sha256:394b56c095d45e9a312ced15e5f24c3d2b5f690702ff066bca5da169e66da27c",
|
||||
"sha256:3bc9f6842865cfaa128802cdf15d7dcae60ba86b11af46ad65db5c3e9ecdbbb6",
|
||||
"sha256:4331bbab201136b83bac6977b750477e83dbb8356080cfaf99b0eca98ec12ce4",
|
||||
"sha256:502ce98b1bc3e96aee9b38f2f4dc1542452d32a98f351f198ee6e2b387257ee4",
|
||||
"sha256:624e2d988627c1362bf83ff341b07ddee87809c9966da4eca48d8fca11099b0a",
|
||||
"sha256:66dd2be87c0ec317f80253d50296cfcf22c81667c69894ec7d4c892b1a613fbf",
|
||||
"sha256:6d665d4f1f98f8b264dbe677dd59f0c80bf3ad5c739a349186ab3f076bf7e3f0",
|
||||
"sha256:7d156055a94fc0b01656afc49602311b40f0224013092ad27373826ea034aac2",
|
||||
"sha256:8afb75b9933b2cb7eb4cd34af4fff2b97e45ba9b7c3ce2cc42fbd72458ad3b0b",
|
||||
"sha256:8cd16afcc374cf5b122870603492e31a446285c3ef5f8389bf616cb85c504db2",
|
||||
"sha256:8e3c03319013ec00406c09671413c7b47ac1dd23e67a872c2386cd0538d7e371",
|
||||
"sha256:9484921319aa072f7471b823bd196879f97945e811f9d9bc0a15e52ae05a25b6",
|
||||
"sha256:9c479fc03a68ad0d34d0de59d9264d0c9f4fd7d573c3173117f287123b19c244",
|
||||
"sha256:a414898948ffb27ab797b41a19869e3b68a1476044a7e6cdd24e574077f1e9b3",
|
||||
"sha256:a6f0774e83a72175a166ac75dd3b75320efecf506a6e5b65c007f61bb9552376",
|
||||
"sha256:ae8b85cd77af424d0cc07a0aee539c5da11bd545bf72193841ad5f58bd26f979",
|
||||
"sha256:b18a90b4c6abaa1bf4b2b39266fa02c833be87c219b0e7cdfdb02c43e77eefe7",
|
||||
"sha256:bcfc8841d911b9c3a637ffa721ab83166876c6245ccb04fdeae1580cf2f7b3f9",
|
||||
"sha256:bfe796abe75e75659c5966332e08dda1a94b2f00ffddd5547802df454def6080",
|
||||
"sha256:d19b8f611d1c660d2048511921ed8ac26b2c6a694dbf96cd668c15397c857f63",
|
||||
"sha256:d7426734b3f1f22637e1b3cf0cfaacbc7c9daab8ef558e1a299eebc8266ce8da",
|
||||
"sha256:ed7b6f9fab3b4eef527301a0628ecc47586593d6c85fafc3d4b3e86206025e6e"
|
||||
],
|
||||
"version": "==2.5.2"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.13.0"
|
||||
},
|
||||
"pillow": {
|
||||
"hashes": [
|
||||
"sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6",
|
||||
"sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded",
|
||||
"sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865",
|
||||
"sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174",
|
||||
"sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032",
|
||||
"sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a",
|
||||
"sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e",
|
||||
"sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378",
|
||||
"sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17",
|
||||
"sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c",
|
||||
"sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913",
|
||||
"sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7",
|
||||
"sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0",
|
||||
"sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820",
|
||||
"sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba",
|
||||
"sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2",
|
||||
"sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b",
|
||||
"sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9",
|
||||
"sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234",
|
||||
"sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d",
|
||||
"sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5",
|
||||
"sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206",
|
||||
"sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9",
|
||||
"sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8",
|
||||
"sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59",
|
||||
"sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d",
|
||||
"sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7",
|
||||
"sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a",
|
||||
"sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0",
|
||||
"sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b",
|
||||
"sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d",
|
||||
"sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae"
|
||||
"sha256:01425106e4e8cee195a411f729cff2a7d61813b0b11737c12bd5991f5f14bcd5",
|
||||
"sha256:031a6c88c77d08aab84fecc05c3cde8414cd6f8406f4d2b16fed1e97634cc8a4",
|
||||
"sha256:083781abd261bdabf090ad07bb69f8f5599943ddb539d64497ed021b2a67e5a9",
|
||||
"sha256:0d19d70ee7c2ba97631bae1e7d4725cdb2ecf238178096e8c82ee481e189168a",
|
||||
"sha256:0e04d61f0064b545b989126197930807c86bcbd4534d39168f4aa5fda39bb8f9",
|
||||
"sha256:12e5e7471f9b637762453da74e390e56cc43e486a88289995c1f4c1dc0bfe727",
|
||||
"sha256:22fd0f42ad15dfdde6c581347eaa4adb9a6fc4b865f90b23378aa7914895e120",
|
||||
"sha256:238c197fc275b475e87c1453b05b467d2d02c2915fdfdd4af126145ff2e4610c",
|
||||
"sha256:3b570f84a6161cf8865c4e08adf629441f56e32f180f7aa4ccbd2e0a5a02cba2",
|
||||
"sha256:463822e2f0d81459e113372a168f2ff59723e78528f91f0bd25680ac185cf797",
|
||||
"sha256:4d98abdd6b1e3bf1a1cbb14c3895226816e666749ac040c4e2554231068c639b",
|
||||
"sha256:5afe6b237a0b81bd54b53f835a153770802f164c5570bab5e005aad693dab87f",
|
||||
"sha256:5b70110acb39f3aff6b74cf09bb4169b167e2660dabc304c1e25b6555fa781ef",
|
||||
"sha256:5cbf3e3b1014dddc45496e8cf38b9f099c95a326275885199f427825c6522232",
|
||||
"sha256:624b977355cde8b065f6d51b98497d6cd5fbdd4f36405f7a8790e3376125e2bb",
|
||||
"sha256:63728564c1410d99e6d1ae8e3b810fe012bc440952168af0a2877e8ff5ab96b9",
|
||||
"sha256:66cc56579fd91f517290ab02c51e3a80f581aba45fd924fcdee01fa06e635812",
|
||||
"sha256:6c32cc3145928c4305d142ebec682419a6c0a8ce9e33db900027ddca1ec39178",
|
||||
"sha256:8b56553c0345ad6dcb2e9b433ae47d67f95fc23fe28a0bde15a120f25257e291",
|
||||
"sha256:8bb1e155a74e1bfbacd84555ea62fa21c58e0b4e7e6b20e4447b8d07990ac78b",
|
||||
"sha256:95d5ef984eff897850f3a83883363da64aae1000e79cb3c321915468e8c6add5",
|
||||
"sha256:a013cbe25d20c2e0c4e85a9daf438f85121a4d0344ddc76e33fd7e3965d9af4b",
|
||||
"sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1",
|
||||
"sha256:a7d5e9fad90eff8f6f6106d3b98b553a88b6f976e51fce287192a5d2d5363713",
|
||||
"sha256:aac00e4bc94d1b7813fe882c28990c1bc2f9d0e1aa765a5f2b516e8a6a16a9e4",
|
||||
"sha256:b91c36492a4bbb1ee855b7d16fe51379e5f96b85692dc8210831fbb24c43e484",
|
||||
"sha256:c03c07ed32c5324939b19e36ae5f75c660c81461e312a41aea30acdd46f93a7c",
|
||||
"sha256:c5236606e8570542ed424849f7852a0ff0bce2c4c8d0ba05cc202a5a9c97dee9",
|
||||
"sha256:c6b39294464b03457f9064e98c124e09008b35a62e3189d3513e5148611c9388",
|
||||
"sha256:cb7a09e173903541fa888ba010c345893cd9fc1b5891aaf060f6ca77b6a3722d",
|
||||
"sha256:d68cb92c408261f806b15923834203f024110a2e2872ecb0bd2a110f89d3c602",
|
||||
"sha256:dc38f57d8f20f06dd7c3161c59ca2c86893632623f33a42d592f097b00f720a9",
|
||||
"sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e",
|
||||
"sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==8.1.0"
|
||||
"version": "==8.2.0"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.13.1"
|
||||
},
|
||||
"premailer": {
|
||||
@@ -598,49 +615,38 @@
|
||||
},
|
||||
"reportlab": {
|
||||
"hashes": [
|
||||
"sha256:009fa61710647cdc62eb373345248d8ebb93583a058990f7c4f9be46d90aa5b1",
|
||||
"sha256:04a08d284da86882ec3a41a7c719833362ef891b09ee8e2fbb47cee352aa684a",
|
||||
"sha256:07bff6742fba612da8d1b1f783c436338c6fdc6962828159827d5ca7d2b67935",
|
||||
"sha256:09fb11ab1500e679fc1b01199d2fed24435499856e75043a9ac0d31dd48fd881",
|
||||
"sha256:18a876449c9000c391dd3415ebc8454cd7bb9e488977b894886a2d7d018f16cd",
|
||||
"sha256:18eec161411026dde49767bee4e5e8eeb8014879554811a62581dc7433628d5b",
|
||||
"sha256:19353aead39fc115a4d6c598d6fb9fa26da7e69160a0443ebb49b02903e704e8",
|
||||
"sha256:1b85c20e89c22ae902ca973df2afdd2d64d27dc4ffd2b29ebad8c805a213756b",
|
||||
"sha256:1da3d7a35f918cee905facfa94bd00ae6091cadc06dca1b0b31b69ae02d41d1d",
|
||||
"sha256:33f3cfdc492575f8af3225701301a7e62fc478358729820c9e0091aff5831378",
|
||||
"sha256:3b0026c1129147befd4e5a8cf25da8dea1096fce371e7b2412e36d7254019c06",
|
||||
"sha256:3d7713dddaa8081ed709a1fa2456a43f6a74b0f07d605da8441fd53fef334f69",
|
||||
"sha256:3e2b4d69763103b9dc9b54c0952dc3cee05cedd06e28c0987fad7f84705b12c0",
|
||||
"sha256:4ca5233a19a5ceca23546290f43addec2345789c7d65bb32f8b2668aa148351f",
|
||||
"sha256:5214a289cf01ebbd65e49bae83709671dd9edb601891cf0ae8abf85f3c0b392f",
|
||||
"sha256:52f8237654acbc78ea2fa6fb4a6a06e5b023b6da93f7889adfe2deba09473fad",
|
||||
"sha256:5ed00894e0f8281c0b7c0494b4d3067c641fd90c8e5cf933089ec4cc9a48e491",
|
||||
"sha256:6191961533d49c9d860964d42bada4d7ac3bb28502d984feb8034093f2012fa8",
|
||||
"sha256:6f3ad2b1afe99c436563cd436d8693d4a12e2c4bd45f70c7705759ff7837fe53",
|
||||
"sha256:739b743b7ca1ba4b4d64c321de6fccb49b562d0507ea06c817d9cc4faed5cd22",
|
||||
"sha256:792efba0c0c6e4ee94f6dc95f305451733ee9230a1c7d51cb8e5301a549e0dfb",
|
||||
"sha256:79d63ca40231ca3860859b39a92daa5219035ba9553da89a5e1b218550744121",
|
||||
"sha256:83b28104edd58ad65748d2d0e60e0d97e3b91b3e90b4573ea6fe60de6811972c",
|
||||
"sha256:85650446538cd2f606ca234634142a7ccd74cb6db7cfec250f76a4242e0f2431",
|
||||
"sha256:9da445cb79e3f740756924c053edc952cde11a65ff5af8acfda3c0a1317136ef",
|
||||
"sha256:9fabd5fbd24f5971085ffe53150d663f158f7d3050b25c95736e29ebf676d454",
|
||||
"sha256:a0c377bc45e73c3f15f55d7de69fab270d174749d5b454ab0de502b15430ec2a",
|
||||
"sha256:a1d3f7022a920d4a5e165d264581f1862e1c1b877ceeabb96fe98cec98125ae5",
|
||||
"sha256:a315edef5c5610b0c75790142f49487e89ea34397fc247ae8aa890fe6d6dd057",
|
||||
"sha256:a755cca2dcf023130b03bb671670301a992157d5c3151d838c0b68ef89894536",
|
||||
"sha256:b1b20208ecdfffd7ca027955c4fe8972b28b30a4b3b80cf25099a08d3b20ed7c",
|
||||
"sha256:b26d6f416891cef93411d6d478a25db275766081a5fb66368248293ef459f3be",
|
||||
"sha256:b4ba4c30af7044ee987e61c88a5ffb76031ca0c53666bc85d823b7de55ddbc75",
|
||||
"sha256:b71faf3b6e4d7058e1af1b8afedaf39a962db4a219affc8177009d8244ec10d4",
|
||||
"sha256:cfa854bea525f8c913cb77e2bda724d94b965a0eb3bcfc4a645a9baa29bb86e2",
|
||||
"sha256:dd9687359e466086b9f6fe6d8069034017f8b6ca3080944fae5709767ca6814e",
|
||||
"sha256:de0c675fc2998a7eaa929c356ba49c84f53a892e9ab25e8ee7d8ebbbdcb2ac16",
|
||||
"sha256:e2b4e33fea2ce9d3a14ea39191b169e41eb2ac995274f54ac8fd27519974bce8",
|
||||
"sha256:f3d4a1a273dc141e03b72a553c11bc14dd7a27ec7654a071edcf83eb04f004bc",
|
||||
"sha256:ff547cf4c1de7e104cad1a378431ff81efcb03e90e40871ee686107da5b91442"
|
||||
"sha256:07f9d9c0360cb8fc780ca05264faa68b90583cd28dbdf2cda6bda34379b6e66c",
|
||||
"sha256:27a831da0d17153e33c985bd7a88307e206c5a28778cddb755d5372598d12637",
|
||||
"sha256:289539f7888239343ef7ebcd30c55e6204ef78d5f70e1547fdeb854a2da8bfa1",
|
||||
"sha256:3ec70873d99c14570e2a9c44b86c8c01526871e7af5ee4b2855246db15cb0c9f",
|
||||
"sha256:46745826657d35f86843487f4bc6f6f805f61260428f8ee13642bf6372f9df55",
|
||||
"sha256:4a784ecdf3008f533e5a032b96c395e8592ed5e679baaf5ef4dcc136b01c72e9",
|
||||
"sha256:4c42e85851f969e21fa4d6414587b7544e877ce685e2495d7d422589c70b6281",
|
||||
"sha256:58bec163f727c1c60515fc4704a961b3b4ccf2c76b4e6ec1a457ea7ed0c2d756",
|
||||
"sha256:6f007142f2b166f52cbb3e5d23319e3e496c429831e53b904e6db28c3370f279",
|
||||
"sha256:81898de0a0be2c8318468ae0ae1590f828805e9b7fd68e5a50667dce8b942171",
|
||||
"sha256:8707cc21a769150154bf4634dca6e9581ae24a05f0fb81a84fcc1143b1cbbfde",
|
||||
"sha256:99aeee49a61c85f1af1087e9e418f3d0c2352c4dd0f0abbfac17ae6c467185aa",
|
||||
"sha256:9ec95808b742ce70c1dab28b2c5bef9093816b92315b948419c2c6968658f9cc",
|
||||
"sha256:b2c7eedb4d19db63301c27ad1076086a099fd4c8ca0a6f62f6e9ed749fa5908f",
|
||||
"sha256:b2cf692ae7af995b499a31a3f58f2001d98e310e03f74812bcb97a08078239c0",
|
||||
"sha256:b9494986f35d82350b0ce0c29704a49a3945421b789dff92e93fbd3de554fa34",
|
||||
"sha256:ba2d10f368c9ea1e76c84b3bb6b9982eb5a8f243c434e821c505b75ca8d85852",
|
||||
"sha256:bc62187181582772688d65c557ad6a40a4c3bb8d1f74de463d35ea81983e9b75",
|
||||
"sha256:bdf751289efee4891f4f354ce9122da8de8258a40f328b3f11540c4888363337",
|
||||
"sha256:c12432575c793b8cd8552fddc219bbf2813541c64d02854ae345a108fb875b9d",
|
||||
"sha256:cdf8ff72cd6fa9303744c8409fb81ef7720da2e034c369762c2fdf496462179e",
|
||||
"sha256:d810bffd4bcd50fdcb2bab0d1fe9ea4e6187ed5237687e41c6ade6c884b00c1e",
|
||||
"sha256:d8fefd07072bfae2715283a821fb1acf8fc4946cf925509d5cc2af791c611809",
|
||||
"sha256:d92834993bf998853a04946729266a3276965e7b13f7423212f1c1abdfc4a1c7",
|
||||
"sha256:ee711804acdaf3ea7f0f2cd27f19478af993e730df8c8d923a678eb0e2572fba",
|
||||
"sha256:f0634740b099b69caed081acd89692996b5504c59f86f39781b6bebc82b267f5",
|
||||
"sha256:f92388e30bf6b5d2eceb3d7b05ee2df856635f74ce7d950a8f45d2b70c685a5b",
|
||||
"sha256:fd6712a8a6dca12181a3a12316f97810927861e77f2a98029efd2c5cfc8546dc",
|
||||
"sha256:fe5d98cdac07dd702bcd49f5723aacdd0af8c84d70fc82a5cc3781e52aedad52"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.5.59"
|
||||
"version": "==3.5.65"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
@@ -666,11 +672,11 @@
|
||||
},
|
||||
"sentry-sdk": {
|
||||
"hashes": [
|
||||
"sha256:4ae8d1ced6c67f1c8ea51d82a16721c166c489b76876c9f2c202b8a50334b237",
|
||||
"sha256:e75c8c58932bda8cd293ea8e4b242527129e1caaec91433d21b8b2f20fee030b"
|
||||
"sha256:71de00c9711926816f750bc0f57ef2abbcb1bfbdf5378c601df7ec978f44857a",
|
||||
"sha256:9221e985f425913204989d0e0e1cbb719e8b7fa10540f1bc509f660c06a34e66"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.20.3"
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"simplejson": {
|
||||
"hashes": [
|
||||
@@ -737,7 +743,6 @@
|
||||
"sha256:d3a5ea5b350423f47d07639f74475afedad48cf41c0ad7a82ca13a3928af34f6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.0'",
|
||||
"version": "==2.2"
|
||||
},
|
||||
"sqlparse": {
|
||||
@@ -775,6 +780,7 @@
|
||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.10.2"
|
||||
},
|
||||
"tornado": {
|
||||
@@ -826,11 +832,11 @@
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80",
|
||||
"sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
|
||||
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
|
||||
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.26.3"
|
||||
"version": "==1.26.5"
|
||||
},
|
||||
"webencodings": {
|
||||
"hashes": [
|
||||
@@ -863,11 +869,11 @@
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108",
|
||||
"sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"
|
||||
"sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76",
|
||||
"sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.4.0"
|
||||
"version": "==3.4.1"
|
||||
},
|
||||
"zope.component": {
|
||||
"hashes": [
|
||||
@@ -1061,19 +1067,13 @@
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"apipkg": {
|
||||
"hashes": [
|
||||
"sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6",
|
||||
"sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"
|
||||
],
|
||||
"version": "==1.5"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
|
||||
"sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
|
||||
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
|
||||
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
|
||||
],
|
||||
"version": "==20.3.0"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==21.2.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
@@ -1093,65 +1093,69 @@
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7",
|
||||
"sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5",
|
||||
"sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f",
|
||||
"sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde",
|
||||
"sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f",
|
||||
"sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f",
|
||||
"sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c",
|
||||
"sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66",
|
||||
"sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90",
|
||||
"sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337",
|
||||
"sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d",
|
||||
"sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4",
|
||||
"sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409",
|
||||
"sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37",
|
||||
"sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1",
|
||||
"sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247",
|
||||
"sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39",
|
||||
"sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c",
|
||||
"sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994",
|
||||
"sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c",
|
||||
"sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb",
|
||||
"sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc",
|
||||
"sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f",
|
||||
"sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca",
|
||||
"sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135",
|
||||
"sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3",
|
||||
"sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339",
|
||||
"sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9",
|
||||
"sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9",
|
||||
"sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af",
|
||||
"sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370",
|
||||
"sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19",
|
||||
"sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3",
|
||||
"sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44",
|
||||
"sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3",
|
||||
"sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a",
|
||||
"sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c",
|
||||
"sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b",
|
||||
"sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9",
|
||||
"sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8",
|
||||
"sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22",
|
||||
"sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f",
|
||||
"sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345",
|
||||
"sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880",
|
||||
"sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0",
|
||||
"sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b",
|
||||
"sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec",
|
||||
"sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3",
|
||||
"sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786"
|
||||
"sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c",
|
||||
"sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6",
|
||||
"sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45",
|
||||
"sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a",
|
||||
"sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03",
|
||||
"sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529",
|
||||
"sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a",
|
||||
"sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a",
|
||||
"sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2",
|
||||
"sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6",
|
||||
"sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759",
|
||||
"sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53",
|
||||
"sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a",
|
||||
"sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4",
|
||||
"sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff",
|
||||
"sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502",
|
||||
"sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793",
|
||||
"sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb",
|
||||
"sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905",
|
||||
"sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821",
|
||||
"sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b",
|
||||
"sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81",
|
||||
"sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0",
|
||||
"sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b",
|
||||
"sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3",
|
||||
"sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184",
|
||||
"sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701",
|
||||
"sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a",
|
||||
"sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82",
|
||||
"sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638",
|
||||
"sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5",
|
||||
"sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083",
|
||||
"sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6",
|
||||
"sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90",
|
||||
"sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465",
|
||||
"sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a",
|
||||
"sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3",
|
||||
"sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e",
|
||||
"sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066",
|
||||
"sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf",
|
||||
"sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b",
|
||||
"sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae",
|
||||
"sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669",
|
||||
"sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873",
|
||||
"sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b",
|
||||
"sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6",
|
||||
"sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb",
|
||||
"sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160",
|
||||
"sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c",
|
||||
"sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079",
|
||||
"sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d",
|
||||
"sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"
|
||||
],
|
||||
"version": "==5.4"
|
||||
"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.5"
|
||||
},
|
||||
"coveralls": {
|
||||
"hashes": [
|
||||
"sha256:5399c0565ab822a70a477f7031f6c88a9dd196b3de2877b3facb43b51bd13434",
|
||||
"sha256:f8384968c57dee4b7133ae701ecdad88e85e30597d496dcba0d7fbb470dca41f"
|
||||
"sha256:7bd173b3425733661ba3063c88f180127cc2b20e9740686f86d2622b31b41385",
|
||||
"sha256:cbb942ae5ef3d2b55388cb5b43e93a269544911535f1e750e1c656aef019ce60"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.0"
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"django-coverage-plugin": {
|
||||
"hashes": [
|
||||
@@ -1168,10 +1172,11 @@
|
||||
},
|
||||
"execnet": {
|
||||
"hashes": [
|
||||
"sha256:7a13113028b1e1cc4c6492b28098b3c6576c9dccc7973bfe47b342afadafb2ac",
|
||||
"sha256:b73c5565e517f24b62dea8a5ceac178c661c4309d3aa0c3e420856c072c411b4"
|
||||
"sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5",
|
||||
"sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"
|
||||
],
|
||||
"version": "==1.8.0"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==1.9.0"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
@@ -1193,6 +1198,7 @@
|
||||
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
|
||||
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==20.9"
|
||||
},
|
||||
"pluggy": {
|
||||
@@ -1200,6 +1206,7 @@
|
||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.13.1"
|
||||
},
|
||||
"psutil": {
|
||||
@@ -1241,15 +1248,16 @@
|
||||
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
|
||||
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.10.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
|
||||
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
|
||||
"sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068",
|
||||
"sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.6.0"
|
||||
"version": "==2.7.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
@@ -1296,6 +1304,7 @@
|
||||
"sha256:6aa9ac7e00ad1a539c41bec6d21011332de671e938c7637378ec9710204e37ca",
|
||||
"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"
|
||||
},
|
||||
"pytest-splinter": {
|
||||
@@ -1306,6 +1315,9 @@
|
||||
"version": "==3.3.1"
|
||||
},
|
||||
"pytest-xdist": {
|
||||
"extras": [
|
||||
"psutil"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:2447a1592ab41745955fb870ac7023026f20a5f0bfccf1b52a879bd193d46450",
|
||||
"sha256:718887296892f92683f6a51f25a3ae584993b06f7076ce1e1fd482e59a8220a2"
|
||||
@@ -1348,15 +1360,16 @@
|
||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.10.2"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80",
|
||||
"sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
|
||||
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
|
||||
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.26.3"
|
||||
"version": "==1.26.5"
|
||||
},
|
||||
"zope.component": {
|
||||
"hashes": [
|
||||
@@ -1366,22 +1379,6 @@
|
||||
"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",
|
||||
@@ -1493,52 +1490,6 @@
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ INSTALLED_APPS = (
|
||||
'users',
|
||||
'RIGS',
|
||||
'assets',
|
||||
'training',
|
||||
|
||||
'debug_toolbar',
|
||||
'registration',
|
||||
|
||||
@@ -12,6 +12,7 @@ urlpatterns = [
|
||||
path('', include('versioning.urls')),
|
||||
path('', include('RIGS.urls')),
|
||||
path('assets/', include('assets.urls')),
|
||||
path('training/', include('training.urls')),
|
||||
|
||||
path('', login_required(views.Index.as_view()), name='index'),
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ from django.views.decorators.clickjacking import xframe_options_exempt
|
||||
|
||||
from RIGS import models
|
||||
from assets import models as asset_models
|
||||
from training import models as training_models
|
||||
|
||||
|
||||
def is_ajax(request):
|
||||
@@ -38,7 +39,8 @@ class SecureAPIRequest(generic.View):
|
||||
'organisation': models.Organisation,
|
||||
'profile': models.Profile,
|
||||
'event': models.Event,
|
||||
'supplier': asset_models.Supplier
|
||||
'supplier': asset_models.Supplier,
|
||||
'training_item': training_models.TrainingItem,
|
||||
}
|
||||
|
||||
perms = {
|
||||
@@ -47,7 +49,8 @@ class SecureAPIRequest(generic.View):
|
||||
'organisation': 'RIGS.view_organisation',
|
||||
'profile': 'RIGS.view_profile',
|
||||
'event': None,
|
||||
'supplier': None
|
||||
'supplier': None,
|
||||
'training_item': None, # TODO
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
@@ -14,7 +14,7 @@ from reversion.admin import VersionAdmin
|
||||
from RIGS import models
|
||||
from users import forms as user_forms
|
||||
|
||||
# Register your models here.
|
||||
|
||||
admin.site.register(models.VatRate, VersionAdmin)
|
||||
admin.site.register(models.Event, VersionAdmin)
|
||||
admin.site.register(models.EventItem, VersionAdmin)
|
||||
|
||||
@@ -55,7 +55,13 @@ class InvoiceDetail(generic.DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(InvoiceDetail, self).get_context_data(**kwargs)
|
||||
context['page_title'] = "Invoice {} ({})".format(self.object.display_id, self.object.invoice_date.strftime("%d/%m/%Y"))
|
||||
context['page_title'] = "Invoice {} ({}) ".format(self.object.display_id, self.object.invoice_date.strftime("%d/%m/%Y"))
|
||||
if self.object.void:
|
||||
context['page_title'] += "<span class='badge badge-warning float-right'>VOID</span>"
|
||||
elif self.object.is_closed:
|
||||
context['page_title'] += "<span class='badge badge-success float-right'>PAID</span>"
|
||||
else:
|
||||
context['page_title'] += "<span class='badge badge-info float-right'>OUTSTANDING</span>"
|
||||
return context
|
||||
|
||||
|
||||
@@ -173,24 +179,7 @@ class InvoiceWaiting(generic.ListView):
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_objects()
|
||||
|
||||
def get_objects(self):
|
||||
# TODO find a way to select items
|
||||
events = self.model.objects.filter(
|
||||
(
|
||||
Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end
|
||||
Q(end_date__lte=datetime.date.today()) # Has end date, finishes before
|
||||
) & Q(invoice__isnull=True) & # Has not already been invoiced
|
||||
Q(is_rig=True) # Is a rig (not non-rig)
|
||||
|
||||
).order_by('start_date') \
|
||||
.select_related('person',
|
||||
'organisation',
|
||||
'venue', 'mic') \
|
||||
.prefetch_related('items')
|
||||
|
||||
return events
|
||||
return self.model.objects.waiting_invoices()
|
||||
|
||||
|
||||
class InvoiceEvent(generic.View):
|
||||
|
||||
@@ -3,7 +3,7 @@ from django.core.management.base import BaseCommand, CommandError
|
||||
from django.contrib.auth.models import Group
|
||||
from assets import models
|
||||
from RIGS import models as rigsmodels
|
||||
|
||||
from training import models as tmodels
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Deletes testing sample data'
|
||||
@@ -31,6 +31,9 @@ class Command(BaseCommand):
|
||||
self.delete_objects(rigsmodels.Payment)
|
||||
self.delete_objects(rigsmodels.RiskAssessment)
|
||||
self.delete_objects(rigsmodels.EventChecklist)
|
||||
self.delete_objects(tmodels.TrainingCategory)
|
||||
self.delete_objects(tmodels.TrainingItem)
|
||||
self.delete_objects(tmodels.TrainingLevel)
|
||||
|
||||
def delete_objects(self, model):
|
||||
for obj in model.objects.all():
|
||||
|
||||
@@ -12,3 +12,4 @@ class Command(BaseCommand):
|
||||
call_command('generateSampleUserData')
|
||||
call_command('generateSampleRIGSData')
|
||||
call_command('generateSampleAssetsData')
|
||||
call_command('generateSampleTrainingData')
|
||||
|
||||
@@ -21,6 +21,7 @@ class Command(BaseCommand):
|
||||
profiles = models.Profile.objects.all()
|
||||
|
||||
def handle(self, *args, **options):
|
||||
print("Generating rigboard data")
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
@@ -35,6 +36,7 @@ class Command(BaseCommand):
|
||||
self.setup_organisations()
|
||||
self.setup_venues()
|
||||
self.setup_events()
|
||||
print("Done generating rigboard data")
|
||||
|
||||
def setup_people(self):
|
||||
names = ["Regulus Black", "Sirius Black", "Lavender Brown", "Cho Chang", "Vincent Crabbe", "Vincent Crabbe",
|
||||
|
||||
67
RIGS/migrations/0040_auto_20210302_1148.py
Normal file
67
RIGS/migrations/0040_auto_20210302_1148.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# Generated by Django 3.1.7 on 2021-03-02 11:48
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def postgres_migration_prep(apps, schema_editor):
|
||||
model = apps.get_model("RIGS", "Event")
|
||||
for field in ["auth_request_to", "collector", "description", "notes", "purchase_order"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
model = apps.get_model("RIGS", "EventAuthorisation")
|
||||
for field in ["account_code", "uni_id"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
model = apps.get_model("RIGS", "EventChecklist")
|
||||
for field in ["extinguishers_location", "hs_location", "w1_description", "w2_description", "w3_description"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
model = apps.get_model("RIGS", "EventItem")
|
||||
for field in ["description"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
model = apps.get_model("RIGS", "Organisation")
|
||||
for field in ["address", "email", "notes", "phone"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
model = apps.get_model("RIGS", "Payment")
|
||||
for field in ["method"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
model = apps.get_model("RIGS", "Person")
|
||||
for field in ["address", "email", "notes", "phone"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
model = apps.get_model("RIGS", "Profile")
|
||||
for field in ["phone"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
model = apps.get_model("RIGS", "RiskAssessment")
|
||||
for field in ["general_notes", "persons_responsible_structures", "power_notes", "rigging_plan", "sound_notes"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
model = apps.get_model("RIGS", "Venue")
|
||||
for field in ["address", "email", "notes", "phone"]:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0039_auto_20210123_1910'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(postgres_migration_prep, migrations.RunPython.noop)
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.1.5 on 2021-02-06 10:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0039_auto_20210123_1910'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='dark_theme',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -1,4 +1,4 @@
|
||||
# Generated by Django 3.1.5 on 2021-02-08 16:03
|
||||
# Generated by Django 3.1.7 on 2021-03-02 12:04
|
||||
|
||||
import RIGS.models
|
||||
from django.db import migrations, models
|
||||
@@ -7,10 +7,27 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0040_profile_dark_theme'),
|
||||
('RIGS', '0040_auto_20210302_1148'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='event',
|
||||
name='meet_info',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='event',
|
||||
name='payment_method',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='event',
|
||||
name='payment_received',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='dark_theme',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='auth_request_to',
|
||||
@@ -26,26 +43,11 @@ class Migration(migrations.Migration):
|
||||
name='description',
|
||||
field=models.TextField(blank=True, default=''),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='meet_info',
|
||||
field=models.CharField(blank=True, default='', max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, default=''),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='payment_method',
|
||||
field=models.CharField(blank=True, default='', max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='payment_received',
|
||||
field=models.CharField(blank=True, default='', max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='purchase_order',
|
||||
@@ -144,7 +146,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='phone',
|
||||
field=models.CharField(default='', max_length=13, null=True),
|
||||
field=models.CharField(blank=True, default='', max_length=13),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='riskassessment',
|
||||
@@ -1,18 +0,0 @@
|
||||
# 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),
|
||||
),
|
||||
]
|
||||
@@ -278,6 +278,19 @@ class EventManager(models.Manager):
|
||||
).count()
|
||||
return event_count
|
||||
|
||||
def waiting_invoices(self):
|
||||
events = self.filter(
|
||||
(
|
||||
models.Q(start_date__lte=datetime.date.today(), end_date__isnull=True) | # Starts before with no end
|
||||
models.Q(end_date__lte=datetime.date.today()) # Or has end date, finishes before
|
||||
) & models.Q(invoice__isnull=True) & # Has not already been invoiced
|
||||
models.Q(is_rig=True) # Is a rig (not non-rig)
|
||||
).order_by('start_date') \
|
||||
.select_related('person', 'organisation', 'venue', 'mic') \
|
||||
.prefetch_related('items')
|
||||
|
||||
return events
|
||||
|
||||
|
||||
@reversion.register(follow=['items'])
|
||||
class Event(models.Model, RevisionMixin):
|
||||
@@ -312,7 +325,6 @@ class Event(models.Model, RevisionMixin):
|
||||
end_time = models.TimeField(blank=True, null=True)
|
||||
access_at = models.DateTimeField(blank=True, null=True)
|
||||
meet_at = models.DateTimeField(blank=True, null=True)
|
||||
meet_info = models.CharField(max_length=255, blank=True, default='')
|
||||
|
||||
# Crew management
|
||||
checked_in_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='event_checked_in', blank=True, null=True,
|
||||
@@ -321,8 +333,6 @@ class Event(models.Model, RevisionMixin):
|
||||
verbose_name="MIC", on_delete=models.CASCADE)
|
||||
|
||||
# Monies
|
||||
payment_method = models.CharField(max_length=255, blank=True, default='')
|
||||
payment_received = models.CharField(max_length=255, blank=True, default='')
|
||||
purchase_order = models.CharField(max_length=255, blank=True, default='', verbose_name='PO')
|
||||
collector = models.CharField(max_length=255, blank=True, default='', verbose_name='collected by')
|
||||
|
||||
|
||||
BIN
RIGS/static/imgs/assets.jpg
Normal file
BIN
RIGS/static/imgs/assets.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
RIGS/static/imgs/rigs.jpg
Normal file
BIN
RIGS/static/imgs/rigs.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 278 KiB |
BIN
RIGS/static/imgs/tappytaptap.gif
Normal file
BIN
RIGS/static/imgs/tappytaptap.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.3 MiB |
BIN
RIGS/static/imgs/training.jpg
Normal file
BIN
RIGS/static/imgs/training.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 852 KiB |
@@ -1,6 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load invoices_waiting from filters %}
|
||||
|
||||
{% block titleheader %}
|
||||
<a class="navbar-brand" href="/">RIGS</a>
|
||||
@@ -45,11 +46,11 @@
|
||||
{% if perms.RIGS.view_invoice %}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownInvoices" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Invoices
|
||||
Invoices <span class="badge badge-danger badge-pill">{% invoices_waiting %}</span>
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdownInvoices">
|
||||
{% if perms.RIGS.add_invoice %}
|
||||
<a class="dropdown-item" href="{% url 'invoice_waiting' %}"><span class="fas fa-briefcase text-danger"></span> Waiting</a>
|
||||
<a class="dropdown-item text-nowrap" href="{% url 'invoice_waiting' %}"><span class="fas fa-briefcase text-danger"></span> Waiting <span class="badge badge-danger badge-pill">{% invoices_waiting %}</span></a>
|
||||
{% endif %}
|
||||
<a class="dropdown-item" href="{% url 'invoice_list' %}"><span class="fas fa-pound-sign text-warning"></span> Outstanding</a>
|
||||
<a class="dropdown-item" href="{% url 'invoice_archive' %}"><span class="fas fa-book"></span> Archive</a>
|
||||
|
||||
@@ -32,7 +32,11 @@
|
||||
</dd>
|
||||
<dt class="col-6">{{ object|help_text:'power_mic' }}</dt>
|
||||
<dd class="col-6">
|
||||
{% if object.power_mic %}
|
||||
<a href="{% url 'profile_detail' object.power_mic.pk %}">{{ object.power_mic.name }}</a>
|
||||
{% else %}
|
||||
None
|
||||
{% endif %}
|
||||
</dd>
|
||||
</dl>
|
||||
<p>List vehicles and their drivers</p>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
|
||||
{% load linkornone from filters %}
|
||||
{% load namewithnotes from filters %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row my-3 py-3">
|
||||
@@ -14,50 +12,7 @@
|
||||
{% if object.is_rig and perms.RIGS.view_event %}
|
||||
{# only need contact details for a rig #}
|
||||
<div class="col-md-6">
|
||||
{% if event.person %}
|
||||
<div class="card card-default mb-3">
|
||||
<div class="card-header">Contact Details</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-6">Person</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% if object.person %}
|
||||
<a href="{% url 'person_detail' object.person.pk %}" class="modal-href">
|
||||
{{ object.person|namewithnotes:'person_detail' }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt class="col-sm-6">Email</dt>
|
||||
<dd class="col-sm-6">{{ object.person.email|linkornone:'mailto' }}</dd>
|
||||
<dt class="col-sm-6">Phone Number</dt>
|
||||
<dd class="col-sm-6">{{ object.person.phone|linkornone:'tel' }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if event.organisation %}
|
||||
<div class="card card-default">
|
||||
<div class="card-header">Organisation</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-6">Organisation</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% if object.organisation %}
|
||||
<a href="{% url 'organisation_detail' object.organisation.pk %}" class="modal-href">
|
||||
{{ object.organisation|namewithnotes:'organisation_detail' }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt class="col-sm-6">Email</dt>
|
||||
<dd class="col-sm-6">{{ object.organisation.email|linkornone:'mailto' }}</dd>
|
||||
<dt class="col-sm-6">Phone Number</dt>
|
||||
<dd class="col-sm-6">{{ object.organisation.phone|linkornone:'tel' }}</dd>
|
||||
<dt class="col-sm-6">Has SU Account</dt>
|
||||
<dd class="col-sm-6">{{ event.organisation.union_account|yesno|capfirst }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include 'partials/contact_details.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-md-6">
|
||||
@@ -91,7 +46,7 @@
|
||||
<p class="dont-break-out">{{ event.notes|linebreaksbr }}</p>
|
||||
{% endif %}
|
||||
<br>
|
||||
{% include 'item_table.html' %}
|
||||
{% include 'partials/item_table.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,18 +27,26 @@
|
||||
const matches = window.matchMedia("(prefers-reduced-motion: reduce)").matches || window.matchMedia("(update: slow)").matches;
|
||||
$(document).ready(function () {
|
||||
dur = matches ? 0 : 500;
|
||||
{% if not object.pk and not form.errors %}
|
||||
$('.form-hws').slideUp(dur, function () {
|
||||
$('.form-is_rig').slideUp(dur);
|
||||
});
|
||||
{% elif not object.pk and form.errors %}
|
||||
if ($('#{{form.is_rig.auto_id}}').attr('checked') !== 'checked') {
|
||||
{% if object.pk %}
|
||||
// Editing
|
||||
{% if not object.is_rig %}
|
||||
$('.form-is_rig').hide();
|
||||
}
|
||||
{% endif %}
|
||||
{% if not object.pk %}
|
||||
{% endif %}
|
||||
//Creation
|
||||
{% else %}
|
||||
// If there were errors, apply the previous Rig/not-Rig selection
|
||||
{% if form.errors %}
|
||||
$('.form-hws').show();
|
||||
if ($('#{{form.is_rig.auto_id}}').attr('checked') !== 'checked') {
|
||||
$('.form-is_rig').hide();
|
||||
}
|
||||
{% else %}
|
||||
//Initial hide
|
||||
$('.form-hws').slideUp(dur);
|
||||
{% endif %}
|
||||
//Button handling
|
||||
$('#is_rig-selector button').on('click', function () {
|
||||
$('.form-non_rig').slideDown(dur);
|
||||
$('.form-non_rig').slideDown(dur); //Non rig stuff also needed for rig, so always slide down
|
||||
if ($(this).data('is_rig') === 1) {
|
||||
$('#{{form.is_rig.auto_id}}').prop('checked', true);
|
||||
if ($('.form-non_rig').is(':hidden')) {
|
||||
@@ -48,7 +56,6 @@
|
||||
}
|
||||
$('.form-hws, .form-hws .form-is_rig').css('overflow', 'visible');
|
||||
} else {
|
||||
|
||||
$('#{{form.is_rig.auto_id}}').prop('checked', false);
|
||||
$('.form-is_rig').slideUp(dur);
|
||||
}
|
||||
@@ -62,17 +69,10 @@
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
})
|
||||
</script>
|
||||
<noscript>
|
||||
<style>
|
||||
.form-hws {
|
||||
display: inherit !important;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'item_modal.html' %}
|
||||
{% include 'partials/item_modal.html' %}
|
||||
<form class="itemised_form" role="form" method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
@@ -326,7 +326,7 @@
|
||||
|
||||
<div class="form-group" data-toggle="tooltip" title="The purchase order number (for external clients)">
|
||||
<label for="{{ form.purchase_order.id_for_label }}"
|
||||
class="col-sm-4 col-form-label">{{ form.purchase_order.label }}</label>
|
||||
class="col-sm-4 col-fitem_tableorm-label">{{ form.purchase_order.label }}</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
{% render_field form.purchase_order class+="form-control" %}
|
||||
@@ -348,7 +348,7 @@
|
||||
{% render_field form.notes class+="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
{% include 'item_table.html' %}
|
||||
{% include 'partials/item_table.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="col-sm-12">
|
||||
<div class="card">
|
||||
{% with object=event auth=True %}
|
||||
{% include 'item_table.html' %}
|
||||
{% include 'partials/item_table.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,102 +2,88 @@
|
||||
{% load button from filters %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-sm-12">
|
||||
<div class="row justify-content-end py-3">
|
||||
<div class="col-sm-4 text-right">
|
||||
<div class="btn-group btn-page">
|
||||
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-danger" title="Delete Invoice">
|
||||
<span class="fas fa-times"></span> <span
|
||||
class="d-none d-sm-inline">Delete</span>
|
||||
</a>
|
||||
<a href="{% url 'invoice_void' object.pk %}" class="btn btn-warning" title="Void Invoice">
|
||||
<span class="fas fa-ban"></span> <span
|
||||
class="d-none d-sm-inline">Void</span>
|
||||
</a>
|
||||
{% button 'print' url='invoice_print' pk=object.pk %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="card card-default">
|
||||
<div class="card-header">Invoice Details<span class="float-right">
|
||||
{% if object.void %}(VOID){% elif object.is_closed %}(PAID){% else %}(OUTSTANDING){% endif %}
|
||||
</span></div>
|
||||
<div class="card-body">
|
||||
{% if object.event.organisation %}
|
||||
{{ object.event.organisation.name }}<br/>
|
||||
{{ object.event.organisation.address|linebreaksbr }}
|
||||
{% else %}
|
||||
{{ object.event.person.name }}<br/>
|
||||
{{ object.event.person.address|linebreaksbr }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{% include 'partials/event_details.html' %}
|
||||
</div>
|
||||
{% if object.event.internal %}
|
||||
<div class="col-sm-6">
|
||||
{% include 'partials/auth_details.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="row py-4">
|
||||
<div class="col-sm-6">
|
||||
<div class="card card-default">
|
||||
<div class="card-body">
|
||||
<div class="text-right py-3">
|
||||
<a href="{% url 'payment_create' %}?invoice={{ object.pk }}"
|
||||
class="btn btn-success modal-href"
|
||||
data-target="#{{ form.person.id_for_label }}">
|
||||
<span class="fas fa-plus"></span> Add
|
||||
</a>
|
||||
</div>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Amount</th>
|
||||
<th scope="col">Method</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for payment in object.payment_set.all %}
|
||||
<tr>
|
||||
<th scope="row">{{ payment.date }}</th>
|
||||
<td>{{ payment.amount|floatformat:2 }}</td>
|
||||
<td>{{ payment.get_method_display }}</td>
|
||||
<td>
|
||||
<a href="{% url 'payment_delete' payment.pk %}" class="btn btn-small btn-danger"><span class="fas fa-times"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td class="text-right"><strong>Balance:</strong></td>
|
||||
<td>{{ object.balance|floatformat:2 }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<div class="card">
|
||||
{% with object.event as object %}
|
||||
{% include 'item_table.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 text-right">
|
||||
{% include 'partials/last_edited.html' with target="invoice_history" %}
|
||||
<div class="row py-4">
|
||||
<div class="col-sm-12 text-right px-0">
|
||||
<div class="btn-group">
|
||||
<a href="{% url 'event_detail' object.pk %}" class="btn btn-primary">Open Event Page <span class="fas fa-eye"></span></a>
|
||||
<a href="{% url 'invoice_delete' object.pk %}" class="btn btn-danger" title="Delete Invoice">
|
||||
<span class="fas fa-times"></span> <span
|
||||
class="d-none d-sm-inline">Delete</span>
|
||||
</a>
|
||||
<a href="{% url 'invoice_void' object.pk %}" class="btn btn-warning" title="Void Invoice">
|
||||
<span class="fas fa-ban"></span> <span
|
||||
class="d-none d-sm-inline">Void</span>
|
||||
</a>
|
||||
{% button 'print' url='invoice_print' pk=object.pk %}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="row py-4">
|
||||
{% with object.event as object %}
|
||||
<div class="col-sm-6">
|
||||
{% include 'partials/contact_details.html' %}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{% include 'partials/event_details.html' %}
|
||||
</div>
|
||||
{% if object.event.internal %}
|
||||
<div class="col-sm-6">
|
||||
{% include 'partials/auth_details.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="row py-4">
|
||||
<div class="col-sm-6">
|
||||
<div class="card card-default">
|
||||
<div class="card-body">
|
||||
<div class="text-right py-3">
|
||||
<a href="{% url 'payment_create' %}?invoice={{ object.pk }}"
|
||||
class="btn btn-success modal-href"
|
||||
data-target="#{{ form.person.id_for_label }}">
|
||||
<span class="fas fa-plus"></span> Add
|
||||
</a>
|
||||
</div>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Amount</th>
|
||||
<th scope="col">Method</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for payment in object.payment_set.all %}
|
||||
<tr>
|
||||
<th scope="row">{{ payment.date }}</th>
|
||||
<td>{{ payment.amount|floatformat:2 }}</td>
|
||||
<td>{{ payment.get_method_display }}</td>
|
||||
<td>
|
||||
<a href="{% url 'payment_delete' payment.pk %}" class="btn btn-small btn-danger"><span class="fas fa-times"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td class="text-right"><strong>Balance:</strong></td>
|
||||
<td>{{ object.balance|floatformat:2 }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<div class="card">
|
||||
{% with object.event as object %}
|
||||
{% include 'partials/item_table.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 text-right">
|
||||
{% include 'partials/last_edited.html' with target="invoice_history" %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
47
RIGS/templates/partials/contact_details.html
Normal file
47
RIGS/templates/partials/contact_details.html
Normal file
@@ -0,0 +1,47 @@
|
||||
{% load linkornone from filters %}
|
||||
{% load namewithnotes from filters %}
|
||||
|
||||
{% if object.person %}
|
||||
<div class="card card-default mb-3">
|
||||
<div class="card-header">Person Details</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-6">Person</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% if object.person %}
|
||||
<a href="{% url 'person_detail' object.person.pk %}" class="modal-href">
|
||||
{{ object.person|namewithnotes:'person_detail' }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt class="col-sm-6">Email</dt>
|
||||
<dd class="col-sm-6">{{ object.person.email|linkornone:'mailto' }}</dd>
|
||||
<dt class="col-sm-6">Phone Number</dt>
|
||||
<dd class="col-sm-6">{{ object.person.phone|linkornone:'tel' }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if object.organisation %}
|
||||
<div class="card card-default">
|
||||
<div class="card-header">Organisation Details</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-6">Organisation</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% if object.organisation %}
|
||||
<a href="{% url 'organisation_detail' object.organisation.pk %}" class="modal-href">
|
||||
{{ object.organisation|namewithnotes:'organisation_detail' }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt class="col-sm-6">Email</dt>
|
||||
<dd class="col-sm-6">{{ object.organisation.email|linkornone:'mailto' }}</dd>
|
||||
<dt class="col-sm-6">Phone Number</dt>
|
||||
<dd class="col-sm-6">{{ object.organisation.phone|linkornone:'tel' }}</dd>
|
||||
<dt class="col-sm-6">Has SU Account</dt>
|
||||
<dd class="col-sm-6">{{ event.organisation.union_account|yesno|capfirst }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -47,5 +47,7 @@
|
||||
class="fas fa-pound-sign"></span>
|
||||
<span class="d-none d-sm-inline">Invoice</span></a>
|
||||
{% endif %}
|
||||
|
||||
<a href="https://docs.google.com/forms/d/e/1FAIpQLSf-TBOuJZCTYc2L8DWdAaC3_Werq0ulsUs8-6G85I6pA9WVsg/viewform" class="btn btn-danger"><span class="fas fa-file-invoice-dollar"></span> Subhire Insurance Form</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -30,25 +30,25 @@
|
||||
<th scope="row" id="event_number">{{ event.display_id }}</th>
|
||||
<!--Dates & Times-->
|
||||
<td id="event_dates">
|
||||
<span class="text-nowrap">Start: <strong>{{ event.start_date|date:"D d/m/Y" }}</strong>
|
||||
<span class="text-nowrap">Start: <strong>{{ event.start_date|date:"D d/m/Y" }}
|
||||
{% if event.has_start_time %}
|
||||
{{ event.start_time|date:"H:i" }}
|
||||
{% endif %}
|
||||
{% endif %}</strong>
|
||||
</span>
|
||||
{% if event.end_date %}
|
||||
<br>
|
||||
<span class="text-nowrap">End: {% if event.end_date != event.start_date %}<strong>{{ event.end_date|date:"D d/m/Y" }}</strong>{% endif %}
|
||||
<span class="text-nowrap">End: {% if event.end_date != event.start_date %}<strong>{{ event.end_date|date:"D d/m/Y" }}{% endif %}
|
||||
{% if event.has_end_time %}
|
||||
{{ event.end_time|date:"H:i" }}
|
||||
{% endif %}
|
||||
{% endif %}</strong>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if not event.cancelled %}
|
||||
{% if event.meet_at %}
|
||||
<br><span>Crew meet: <strong>{{ event.meet_at|date:"H:i" }}</strong> {{ event.meet_at|date:"(d/m/Y)" }}</span>
|
||||
<br><span class="text-nowrap">Meet: <strong>{{ event.meet_at|date:"D d/m/Y H:i" }}</strong></span>
|
||||
{% endif %}
|
||||
{% if event.access_at %}
|
||||
<br><span>Access at: <strong>{{ event.access_at|date:"H:i" }}</strong> {{ event.access_at|date:"(d/m/Y)" }}</span>
|
||||
<br><span class="text-nowrap">Access: <strong>{{ event.access_at|date:" D d/m/Y H:i" }}</strong></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
@@ -90,7 +90,7 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
{% elif event.is_rig %}
|
||||
<span class="fas fa-exclamation"></span>
|
||||
<span class="fas fa-user-slash"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -51,11 +51,11 @@
|
||||
<dd class="col-sm-6">
|
||||
{{ object.power_mic.name|default:'None' }}
|
||||
</dd>
|
||||
<dt class="col-sm-6">{{ object|help_text:'generators' }}</dt>
|
||||
<dt class="col-sm-6">{{ object|help_text:'outside' }}</dt>
|
||||
<dd class="col-sm-6">
|
||||
{{ object.outside|yesnoi:'invert' }}
|
||||
</dd>
|
||||
<dt class="col-sm-6">{{ object|help_text:'outside' }}</dt>
|
||||
<dt class="col-sm-6">{{ object|help_text:'generators' }}</dt>
|
||||
<dd class="col-sm-6">
|
||||
{{ object.generators|yesnoi:'invert' }}
|
||||
</dd>
|
||||
|
||||
@@ -217,3 +217,8 @@ def button(type, url=None, pk=None, clazz="", icon=None, text="", id=None, style
|
||||
elif type == 'submit':
|
||||
return {'submit': True, 'class': 'btn-primary', 'icon': 'fa-save', 'text': 'Save', 'id': id, 'style': style}
|
||||
return {'target': url, 'pk': pk, 'class': clazz, 'icon': icon, 'text': text, 'id': id, 'style': style}
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def invoices_waiting():
|
||||
return len(models.Event.objects.waiting_invoices())
|
||||
|
||||
@@ -53,7 +53,7 @@ class EventDetail(BasePage):
|
||||
|
||||
# TODO Refactor into regions to match template fragmentation
|
||||
_event_name_selector = (By.XPATH, '//h2')
|
||||
_person_panel_selector = (By.XPATH, '//div[contains(text(), "Contact Details")]/..')
|
||||
_person_panel_selector = (By.XPATH, '//div[contains(text(), "Person Details")]/..')
|
||||
_name_selector = (By.XPATH, '//dt[text()="Person"]/following-sibling::dd[1]')
|
||||
_email_selector = (By.XPATH, '//dt[text()="Email"]/following-sibling::dd[1]')
|
||||
_phone_selector = (By.XPATH, '//dt[text()="Phone Number"]/following-sibling::dd[1]')
|
||||
|
||||
@@ -45,7 +45,7 @@ class CableTypeForm(forms.ModelForm):
|
||||
model = models.CableType
|
||||
fields = '__all__'
|
||||
|
||||
def clean(self):
|
||||
def clean(self): # TODO Does unique_together work better than this?
|
||||
form_data = self.cleaned_data
|
||||
queryset = models.CableType.objects.filter(Q(plug=form_data['plug']) & Q(socket=form_data['socket']) & Q(circuits=form_data['circuits']) & Q(cores=form_data['cores']))
|
||||
# Being identical to itself shouldn't count...
|
||||
|
||||
@@ -20,6 +20,7 @@ class Command(BaseCommand):
|
||||
assets = []
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
print("Generating sample assets data")
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
@@ -34,6 +35,7 @@ class Command(BaseCommand):
|
||||
self.create_assets()
|
||||
self.create_connectors()
|
||||
self.create_cables()
|
||||
print("Done generating sample assets data")
|
||||
|
||||
def create_categories(self):
|
||||
choices = ['Case', 'Video', 'General', 'Sound', 'Lighting', 'Rigging']
|
||||
|
||||
@@ -8,9 +8,9 @@ def add_default(apps, schema_editor):
|
||||
Connector = apps.get_model('assets', 'Connector')
|
||||
for cable_type in CableType.objects.all():
|
||||
if cable_type.plug is None:
|
||||
cable_type.plug = Connector.first()
|
||||
cable_type.plug = Connector.objects.first()
|
||||
if cable_type.socket is None:
|
||||
cable_type.socket = Connector.first()
|
||||
cable_type.socket = Connector.objects.first()
|
||||
cable_type.save()
|
||||
|
||||
|
||||
|
||||
23
assets/migrations/0020_auto_20210302_1201.py
Normal file
23
assets/migrations/0020_auto_20210302_1201.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.1.7 on 2021-03-02 12:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def postgres_migration_prep(apps, schema_editor):
|
||||
model = apps.get_model("assets", "Supplier")
|
||||
fields = ["address", "email", "notes", "phone"]
|
||||
for field in fields:
|
||||
filter_param = {"{}__isnull".format(field): True}
|
||||
update_param = {field: ""}
|
||||
model.objects.filter(**filter_param).update(**update_param)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0019_fix_cabletype'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(postgres_migration_prep, migrations.RunPython.noop)
|
||||
]
|
||||
@@ -1,4 +1,4 @@
|
||||
# Generated by Django 3.1.5 on 2021-02-08 16:03
|
||||
# Generated by Django 3.1.7 on 2021-03-02 12:04
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
@@ -7,7 +7,7 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0019_fix_cabletype'),
|
||||
('assets', '0020_auto_20210302_1201'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
@@ -64,16 +64,16 @@
|
||||
<div class="form-group form-row">
|
||||
{% include 'partials/form_field.html' with field=form.length append=form.length.help_text col="col-6" %}
|
||||
<div class="col-4">
|
||||
<button class="btn btn-danger" onclick="setFieldValue('{{ form.length.id_for_label }}','5');" tabindex="-1">5{{ form.length.help_text }}</button>
|
||||
<button class="btn btn-success" onclick="setFieldValue('{{ form.length.id_for_label }}','10');" tabindex="-1">10{{ form.length.help_text }}</button>
|
||||
<button class="btn btn-info" onclick="setFieldValue('{{ form.length.id_for_label }}','20');" tabindex="-1">20{{ form.length.help_text }}</button>
|
||||
<button class="btn btn-danger" onclick="setFieldValue('{{ form.length.id_for_label }}','5');" tabindex="-1" type="button">5{{ form.length.help_text }}</button>
|
||||
<button class="btn btn-success" onclick="setFieldValue('{{ form.length.id_for_label }}','10');" tabindex="-1" type="button">10{{ form.length.help_text }}</button>
|
||||
<button class="btn btn-info" onclick="setFieldValue('{{ form.length.id_for_label }}','20');" tabindex="-1" type="button">20{{ form.length.help_text }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group form-row">
|
||||
{% include 'partials/form_field.html' with field=form.csa append=form.csa.help_text title='CSA' col="col-6" %}
|
||||
<div class="col-4">
|
||||
<button class="btn btn-secondary" onclick="setFieldValue('{{ form.csa.id_for_label }}', '1.5');" tabindex="-1">1.5{{ form.csa.help_text }}</button>
|
||||
<button class="btn btn-secondary" onclick="setFieldValue('{{ form.csa.id_for_label }}', '2.5');" tabindex="-1">2.5{{ form.csa.help_text }}</button>
|
||||
<button class="btn btn-secondary" onclick="setFieldValue('{{ form.csa.id_for_label }}', '1.5');" tabindex="-1" type="button">1.5{{ form.csa.help_text }}</button>
|
||||
<button class="btn btn-secondary" onclick="setFieldValue('{{ form.csa.id_for_label }}', '2.5');" tabindex="-1" type="button">2.5{{ form.csa.help_text }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
});
|
||||
$('#searchButton').click(function (e) {
|
||||
e.preventDefault();
|
||||
var url = "{% url 'asset_audit' None %}";
|
||||
var id = $("#{{form.q.id_for_label}}").val();
|
||||
url = url.replace('None', id);
|
||||
var url = "{% url 'asset_audit' None %}".replace('None', $("#{{form.q.id_for_label}}").val();
|
||||
$.ajax({
|
||||
url: url,
|
||||
success: function(){
|
||||
|
||||
@@ -83,7 +83,7 @@ function browserSync(done) {
|
||||
notify: false,
|
||||
open: false,
|
||||
port: 8001,
|
||||
proxy: 'localhost:8000'
|
||||
proxy: '127.0.0.1:8000'
|
||||
});
|
||||
done();
|
||||
}
|
||||
|
||||
@@ -119,4 +119,18 @@
|
||||
background: #222;
|
||||
color: $gray-100;
|
||||
}
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:focus,
|
||||
textarea:-webkit-autofill,
|
||||
textarea:-webkit-autofill:hover,
|
||||
textarea:-webkit-autofill:focus,
|
||||
select:-webkit-autofill,
|
||||
select:-webkit-autofill:hover,
|
||||
select:-webkit-autofill:focus {
|
||||
border: 1px solid $info;
|
||||
-webkit-text-fill-color: white;
|
||||
-webkit-box-shadow: 0 0 0px 1000px rgba($info, .3) inset;
|
||||
transition: background-color 5000s ease-in-out 0s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +177,11 @@ svg {
|
||||
white-space: no-wrap;
|
||||
}
|
||||
|
||||
span.fas {
|
||||
padding-left: 0.1em !important;
|
||||
padding-right: 0.1em !important;
|
||||
}
|
||||
|
||||
html.embedded {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
<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="preload" href="{% static 'fonts/fa-solid-900.woff2' %}" as="font" type="font/woff2" crossorigin>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/screen.css' %}">
|
||||
{% block css %}
|
||||
@@ -32,14 +31,15 @@
|
||||
<a class="skip-link" href='#main'>Skip to content</a>
|
||||
{% include "analytics.html" %}
|
||||
{% 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 text-nowrap" role="navigation">
|
||||
<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.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">
|
||||
<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" id="logo">
|
||||
</a>
|
||||
<div class="container">
|
||||
<div class="container" style="padding-left: 0; padding-right: 0;">
|
||||
<div class="row w-100 flex-nowrap">
|
||||
{% block titleheader %}
|
||||
{% endblock %}
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<button class="navbar-toggler ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation" onclick="document.getElementById('logo').classList.toggle('d-none');">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
@@ -52,6 +52,7 @@
|
||||
{% endblock %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends 'base_rigs.html' %}
|
||||
{% load humanize %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}RIGS{% endblock %}
|
||||
|
||||
@@ -7,8 +8,9 @@
|
||||
<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>
|
||||
<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-4 mb-3">
|
||||
<div class="card">
|
||||
<img class="card-img-top" src="{% static 'imgs/rigs.jpg' %}" alt="" style="height: 150px; object-fit: cover;">
|
||||
<h4 class="card-header">Rigboard</h4>
|
||||
<div class="list-group list-group-flush">
|
||||
<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>
|
||||
@@ -17,6 +19,12 @@
|
||||
<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 %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<div class="card">
|
||||
{% now "m-d" as todays_date %}
|
||||
<img class="card-img-top" src="{% if todays_date == '04-01' %}{% static 'imgs/tappytaptap.gif' %}{%else%}{% static 'imgs/assets.jpg' %}{%endif%}" alt="" style="height: 150px; object-fit: cover;">
|
||||
<h4 class="card-header">Asset Database</h4>
|
||||
<div class="list-group list-group-flush">
|
||||
<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>
|
||||
@@ -28,11 +36,28 @@
|
||||
<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 %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 mb-3">
|
||||
<div class="card">
|
||||
<img class="card-img-top" src="{% static 'imgs/training.jpg' %}" alt="" style="height: 150px; object-fit: cover;">
|
||||
<h4 class="card-header">Training Database</h4>
|
||||
<div class="list-group list-group-flush">
|
||||
<a class="list-group-item list-group-item-action text-info" href="{% url 'trainee_detail' %}"><span class="fas fa-file-signature align-middle"></span><span class="align-middle"> My Training Record</span></a>
|
||||
<a class="list-group-item list-group-item-action" href="{% url 'trainee_list' %}"><span class="fas fa-clipboard-list align-middle"></span><span class="align-middle"> View Training Records</span></a>
|
||||
<a class="list-group-item list-group-item-action" href="{% url 'item_list' %}"><span class="fas fa-eye align-middle"></span><span class="align-middle"> View Training Items</span></a>
|
||||
<a class="list-group-item list-group-item-action" href="{% url 'session_log' %}"><span class="fas fa-plus align-middle"></span><span class="align-middle"> Log Training Session</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm mb-3">
|
||||
<div class="card">
|
||||
<h4 class="card-header">Quick Links</h4>
|
||||
<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"><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="https://forum.nottinghamtec.co.uk" target="_blank" rel="noopener noreferrer"><span class="fas fa-comment-alt text-primary align-middle"></span><span class="align-middle"> TEC Forum</span></a>
|
||||
<a class="list-group-item list-group-item-action" href="//nottinghamtec.sharepoint.com" target="_blank" rel="noopener noreferrer"><span class="fas fa-folder text-info align-middle"></span><span class="align-middle"> TEC Sharepoint</span></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.change_event %}
|
||||
<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 %}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{% if user.is_authenticated %}
|
||||
<form id="searchForm" class="form-inline flex-nowrap mx-md-3 px-2 border border-light rounded" role="form" method="GET" action="{% url 'event_archive' %}">
|
||||
<form id="searchForm" class="form-inline flex-nowrap mx-md-3 px-2 border border-light rounded w-75" role="form" method="GET" action="{% url 'event_archive' %}">
|
||||
<div class="input-group input-group-sm flex-nowrap">
|
||||
<div class="input-group-prepend">
|
||||
<input id="id_search_input" type="search" name="q" class="form-control form-control-sm" placeholder="Search..." value="{{ request.GET.q }}" />
|
||||
</div>
|
||||
<select id="search-options" class="custom-select form-control" style="border-top-right-radius: 0px; border-bottom-right-radius: 0px; width: 20ch;">
|
||||
<select id="search-options" class="custom-select form-control" style="border-top-right-radius: 0px; border-bottom-right-radius: 0px; width: 15ch;">
|
||||
<option selected data-action="{% url 'event_archive' %}" href="#">Events</option>
|
||||
<option data-action="{% url 'person_list' %}" href="#">People</option>
|
||||
<option data-action="{% url 'organisation_list' %}" href="#">Organisations</option>
|
||||
@@ -17,7 +17,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-info form-control form-control-sm btn-sm w-25" style="border-top-left-radius: 0px;border-bottom-left-radius: 0px;"><span class="fas fa-search"></span><span class="sr-only"> Search</span></button>
|
||||
<a href="{% url 'search_help' %}" class="nav-link modal-href ml-2"><span class="fas fa-question-circle"></span></a>
|
||||
<a href="{% url 'search_help' %}" class="nav-link modal-href ml-1"><span class="fas fa-question-circle"></span></a>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
|
||||
0
training/__init__.py
Normal file
0
training/__init__.py
Normal file
8
training/admin.py
Normal file
8
training/admin.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.contrib import admin
|
||||
from training import models
|
||||
from reversion.admin import VersionAdmin
|
||||
|
||||
#admin.site.register(models.Trainee, VersionAdmin)
|
||||
admin.site.register(models.TrainingCategory, VersionAdmin)
|
||||
admin.site.register(models.TrainingItem, VersionAdmin)
|
||||
admin.site.register(models.TrainingLevel, VersionAdmin)
|
||||
5
training/apps.py
Normal file
5
training/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class TrainingConfig(AppConfig):
|
||||
name = 'training'
|
||||
47
training/forms.py
Normal file
47
training/forms.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from django import forms
|
||||
|
||||
from datetime import date
|
||||
|
||||
from training import models
|
||||
from RIGS.models import Profile
|
||||
|
||||
class SessionLogForm(forms.Form):
|
||||
pass
|
||||
|
||||
|
||||
class QualificationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = models.TrainingItemQualification
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pk = kwargs.pop('pk', None)
|
||||
super(QualificationForm, self).__init__(*args, **kwargs)
|
||||
self.fields['trainee'].initial = Profile.objects.get(pk=pk)
|
||||
self.fields['date'].initial = date.today()
|
||||
|
||||
def clean_date(self):
|
||||
date = self.cleaned_data['date']
|
||||
if date > date.today():
|
||||
raise forms.ValidationError('Qualification date may not be in the future')
|
||||
return date
|
||||
|
||||
def clean_supervisor(self):
|
||||
supervisor = self.cleaned_data['supervisor']
|
||||
if supervisor.pk == self.cleaned_data['trainee'].pk:
|
||||
raise forms.ValidationError('One may not supervise oneself...')
|
||||
if not supervisor.is_supervisor:
|
||||
raise forms.ValidationError('Selected supervisor must actually *be* a supervisor...')
|
||||
return supervisor
|
||||
|
||||
class RequirementForm(forms.ModelForm):
|
||||
depth = forms.ChoiceField(choices=models.TrainingItemQualification.CHOICES)
|
||||
|
||||
class Meta:
|
||||
model = models.TrainingLevelRequirement
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pk = kwargs.pop('pk', None)
|
||||
super(RequirementForm, self).__init__(*args, **kwargs)
|
||||
self.fields['level'].initial = models.TrainingLevel.objects.get(pk=pk)
|
||||
0
training/management/commands/__init__.py
Normal file
0
training/management/commands/__init__.py
Normal file
93
training/management/commands/generateSampleTrainingData.py
Normal file
93
training/management/commands/generateSampleTrainingData.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import datetime
|
||||
import random
|
||||
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from training import models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Adds sample data to use for testing'
|
||||
can_import_settings = True
|
||||
|
||||
categories = []
|
||||
items = []
|
||||
levels = []
|
||||
|
||||
def handle(self, *args, **options):
|
||||
print("Generating training data")
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
raise CommandError('You cannot run this command in production')
|
||||
|
||||
random.seed('otherwise it is done by time, which could lead to inconsistant tests')
|
||||
|
||||
with transaction.atomic():
|
||||
self.setup_categories()
|
||||
self.setup_items()
|
||||
self.setup_levels()
|
||||
self.setup_supervisor()
|
||||
print("Done generating training data")
|
||||
|
||||
def setup_categories(self):
|
||||
names = [(1, "Basic"), (2, "Sound"), (3, "Lighting"), (4, "Rigging"), (5, "Power"), (6, "Haulage")]
|
||||
|
||||
for i, name in names:
|
||||
category = models.TrainingCategory.objects.create(reference_number=i, name=name)
|
||||
category.save()
|
||||
self.categories.append(category)
|
||||
|
||||
def setup_items(self):
|
||||
names = ["Motorised Power Towers", "Catering", "Forgetting Cables", "Gazebo Construction", "Balanced Audio", "Unbalanced Audio", "BBQ/Bin Interactions", "Pushing Boxes", "How Not To Die", "Setting up projectors", "Basketing truss", "First Aid", "Digging Trenches", "Avoiding Bin Lorries", "Getting cherry pickers stuck in mud", "Crashing the Van", "Getting pigs to fly", "Basketing picnics", "Python programming", "Building Cables", "Unbuilding Cables", "Cat Herding", "Pancake making", "Tidying up", "Reading Manuals", "Bikeshedding", "DJing", "Partying", "Teccie Gym", "Putting dust covers on", "Cleaning Lights", "Water Skiing", "Drinking", "Fundamentals of Audio", "Fundamentals of Photons", "Social Interaction", "Discourse Searching", "Discord Searching", "Coiling Cables", "Kit Amnesties", "Van Insurance", "Subhire Insurance", "Paperwork", "More Paperwork", "Second Aid", "Being Old", "Maxihoists", "Sleazyhoists", "Telehoists", "Prolyte", "Prolights", "Making Phonecalls", "Quoting For A Rig", "Basic MIC", "Advanced MIC", "Avoiding MIC", "Washing Cables", "Cable Ramp", "Van Loading", "Trailer Loading", "Storeroom Loading", "Welding", "Fire Extinguishers", "Boring Conference AV", "Flyaway", "Short Leads", "RF Systems", "QLab", "Use of Ladders", "Working at Height", "Organising Training", "Organising Organising Training Training", "Mental Health First Aid", "Writing RAMS", "Makros Runs", "PAT", "Kit Fixing", "Kit Breaking", "Replacing Lamps", "Flying Pig Systems", "Procrastination", "Drinking Beer", "Sending Emails", "Email Signatures", "Digital Sound Desks", "Digital Lighting Desks", "Painting PS10s", "Chain Lubrication", "Big Power", "BIGGER POWER", "Pixel Mapping", "RDM", "Ladder Inspections", "Losing Crimpaz", "Scrapping Trilite", "Bin Diving", "Wiki Editing"]
|
||||
|
||||
for i,name in enumerate(names):
|
||||
item = models.TrainingItem.objects.create(category=random.choice(self.categories), reference_number=random.randint(0, 100), name=name)
|
||||
self.items.append(item)
|
||||
|
||||
def setup_levels(self):
|
||||
items = self.items.copy()
|
||||
ta = models.TrainingLevel.objects.create(level=models.TrainingLevel.TA, description="Passion will hatred faithful evil suicide noble battle. Truth aversion gains grandeur noble. Dead play gains prejudice god ascetic grandeur zarathustra dead good. Faithful ultimate justice overcome love will mountains inexpedient.")
|
||||
self.levels.append(ta)
|
||||
tech_ccs = models.TrainingLevel.objects.create(level=models.TrainingLevel.TECHNICIAN, description="Technician Common Competencies. Spirit abstract endless insofar horror sexuality depths war decrepit against strong aversion revaluation free. Christianity reason joy sea law mountains transvaluation. Sea battle aversion dead ultimate morality self. Faithful morality.")
|
||||
tech_ccs.prerequisite_levels.add(ta)
|
||||
super_ccs = models.TrainingLevel.objects.create(level=models.TrainingLevel.SUPERVISOR, description="Depths disgust hope faith of against hatred will victorious. Law...")
|
||||
for i in range(0, 5):
|
||||
if len(items) == 0:
|
||||
break
|
||||
item = random.choice(items)
|
||||
items.remove(item)
|
||||
if i % 3 == 0:
|
||||
models.TrainingLevelRequirement.objects.create(level=tech_ccs, item=item, depth=random.choice(models.TrainingItemQualification.CHOICES)[0])
|
||||
else:
|
||||
models.TrainingLevelRequirement.objects.create(level=super_ccs, item=item, depth=random.choice(models.TrainingItemQualification.CHOICES)[0])
|
||||
for i,name in models.TrainingLevel.DEPARTMENTS:
|
||||
technician = models.TrainingLevel.objects.create(level=models.TrainingLevel.TECHNICIAN, department=i, description="Moral pinnacle derive ultimate war dead. Strong fearful joy contradict battle christian faithful enlightenment prejudice zarathustra moral.")
|
||||
technician.prerequisite_levels.add(tech_ccs)
|
||||
supervisor = models.TrainingLevel.objects.create(level=models.TrainingLevel.SUPERVISOR, department=i, description="Spirit holiest merciful mountains inexpedient reason value. Suicide ultimate hope.")
|
||||
supervisor.prerequisite_levels.add(super_ccs, technician)
|
||||
|
||||
for i in range(0, 30):
|
||||
if len(items) == 0:
|
||||
break
|
||||
item = random.choice(items)
|
||||
items.remove(item)
|
||||
if i % 3 == 0:
|
||||
models.TrainingLevelRequirement.objects.create(level=technician, item=item, depth=random.choice(models.TrainingItemQualification.CHOICES)[0])
|
||||
else:
|
||||
models.TrainingLevelRequirement.objects.create(level=supervisor, item=item, depth=random.choice(models.TrainingItemQualification.CHOICES)[0])
|
||||
self.levels.append(technician)
|
||||
self.levels.append(supervisor)
|
||||
|
||||
def setup_supervisor(self):
|
||||
supervisor = models.Profile.objects.create(username="supervisor", first_name="Super", last_name="Visor",
|
||||
initials="SV",
|
||||
email="supervisor@example.com", is_active=True,
|
||||
is_staff=True)
|
||||
supervisor.set_password('supervisor')
|
||||
supervisor.save()
|
||||
models.TrainingLevelQualification.objects.create(trainee=supervisor, level=self.levels[-1], confirmed_on=timezone.now())
|
||||
80
training/migrations/0001_initial.py
Normal file
80
training/migrations/0001_initial.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# Generated by Django 3.1.5 on 2021-07-05 22:01
|
||||
|
||||
import RIGS.models
|
||||
import django.contrib.auth.models
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0041_auto_20210302_1204'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TrainingCategory',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('reference_number', models.CharField(max_length=3)),
|
||||
('name', models.CharField(max_length=50)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TrainingItem',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('reference_number', models.CharField(max_length=3)),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('category', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='items', to='training.trainingcategory')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TrainingLevel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('department', models.CharField(max_length=50, null=True)),
|
||||
('level', models.IntegerField(choices=[(0, 'Technical Assistant'), (1, 'Technician'), (2, 'Supervisor')])),
|
||||
],
|
||||
bases=(models.Model, RIGS.models.RevisionMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Trainee',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('RIGS.profile',),
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TrainingLevelQualification',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('confirmed_on', models.DateTimeField()),
|
||||
('confirmed_by', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='confirmer', to='training.trainee')),
|
||||
('level', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='training.traininglevel')),
|
||||
('trainee', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='levels', to='training.trainee')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TrainingItemQualification',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('depth', models.IntegerField(choices=[(0, 'Training Started'), (1, 'Training Complete'), (2, 'Passed Out')])),
|
||||
('date', models.DateField()),
|
||||
('notes', models.TextField(blank=True)),
|
||||
('item', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='training.trainingitem')),
|
||||
('supervisor', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='qualifications_granted', to='training.trainee')),
|
||||
('trainee', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='qualifications_obtained', to='training.trainee')),
|
||||
],
|
||||
),
|
||||
]
|
||||
42
training/migrations/0002_auto_20210706_0053.py
Normal file
42
training/migrations/0002_auto_20210706_0053.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# Generated by Django 3.1.5 on 2021-07-05 23:53
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('training', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='trainingcategory',
|
||||
options={'verbose_name_plural': 'Training Categories'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='traininglevel',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=120),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='traininglevel',
|
||||
name='prerequisite_levels',
|
||||
field=models.ManyToManyField(blank=True, related_name='prerequisites', to='training.TrainingLevel'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='traininglevel',
|
||||
name='department',
|
||||
field=models.IntegerField(choices=[(0, 'Sound'), (1, 'Lighting'), (2, 'Power'), (3, 'Rigging'), (4, 'Haulage')], null=True),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TrainingLevelRequirement',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('depth', models.IntegerField(verbose_name=((0, 'Training Started'), (1, 'Training Complete'), (2, 'Passed Out')))),
|
||||
('item', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='training.trainingitem')),
|
||||
('level', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='requirements', to='training.traininglevel')),
|
||||
],
|
||||
),
|
||||
]
|
||||
24
training/migrations/0003_auto_20210716_0150.py
Normal file
24
training/migrations/0003_auto_20210716_0150.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 3.1.5 on 2021-07-16 00:50
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('training', '0002_auto_20210706_0053'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='traininglevelqualification',
|
||||
name='confirmed_by',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.RESTRICT, related_name='confirmer', to='training.trainee'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='traininglevelqualification',
|
||||
name='confirmed_on',
|
||||
field=models.DateTimeField(null=True),
|
||||
),
|
||||
]
|
||||
21
training/migrations/0004_auto_20210819_1808.py
Normal file
21
training/migrations/0004_auto_20210819_1808.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 3.1.7 on 2021-08-19 17:08
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('training', '0003_auto_20210716_0150'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='trainingitemqualification',
|
||||
unique_together={('trainee', 'item', 'depth')},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='traininglevelqualification',
|
||||
unique_together={('trainee', 'level')},
|
||||
),
|
||||
]
|
||||
17
training/migrations/0005_auto_20210819_1833.py
Normal file
17
training/migrations/0005_auto_20210819_1833.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 3.1.7 on 2021-08-19 17:33
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('training', '0004_auto_20210819_1808'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='traininglevelrequirement',
|
||||
unique_together={('level', 'item')},
|
||||
),
|
||||
]
|
||||
0
training/migrations/__init__.py
Normal file
0
training/migrations/__init__.py
Normal file
189
training/models.py
Normal file
189
training/models.py
Normal file
@@ -0,0 +1,189 @@
|
||||
from django.db import models
|
||||
|
||||
from RIGS.models import RevisionMixin, Profile
|
||||
from reversion import revisions as reversion
|
||||
from django.urls import reverse
|
||||
|
||||
# 'shim' overtop the profile model to neatly contain all training related fields etc
|
||||
class Trainee(Profile):
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
@property
|
||||
def is_supervisor(self):
|
||||
# FIXME Efficiency
|
||||
for level_qualification in self.levels.select_related('level').all():
|
||||
if level_qualification.confirmed_on is not None and level_qualification.level.level >= TrainingLevel.SUPERVISOR:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_records_of_depth(self, depth):
|
||||
return self.qualifications_obtained.filter(depth=depth).select_related('item', 'trainee', 'supervisor')
|
||||
|
||||
def is_user_qualified_in(self, item, required_depth):
|
||||
qual = self.qualifications_obtained.filter(item=item).first() # this is a somewhat ghetto version of get_or_none
|
||||
return qual is not None and qual.depth >= required_depth
|
||||
|
||||
# Items
|
||||
class TrainingCategory(models.Model):
|
||||
reference_number = models.CharField(max_length=3)
|
||||
name = models.CharField(max_length=50)
|
||||
|
||||
def __str__(self):
|
||||
return "{}. {}".format(self.reference_number, self.name)
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'Training Categories'
|
||||
|
||||
class TrainingItem(models.Model):
|
||||
reference_number = models.CharField(max_length=3)
|
||||
category = models.ForeignKey('TrainingCategory', related_name='items', on_delete=models.RESTRICT)
|
||||
name = models.CharField(max_length=50)
|
||||
|
||||
def __str__(self):
|
||||
return "{}.{} {}".format(self.category.reference_number, self.reference_number, self.name)
|
||||
|
||||
@staticmethod
|
||||
def user_has_qualification(item, user, depth):
|
||||
for q in user.qualifications_obtained.all().select_related('item'):
|
||||
if q.item == item and q.depth > depth:
|
||||
return True
|
||||
|
||||
|
||||
class TrainingItemQualification(models.Model):
|
||||
STARTED = 0
|
||||
COMPLETE = 1
|
||||
PASSED_OUT = 2
|
||||
CHOICES = (
|
||||
(STARTED, 'Training Started'),
|
||||
(COMPLETE, 'Training Complete'),
|
||||
(PASSED_OUT, 'Passed Out'),
|
||||
)
|
||||
item = models.ForeignKey('TrainingItem', on_delete=models.RESTRICT)
|
||||
trainee = models.ForeignKey('Trainee', related_name='qualifications_obtained', on_delete=models.RESTRICT)
|
||||
depth = models.IntegerField(choices=CHOICES)
|
||||
date = models.DateField()
|
||||
# TODO Remember that some training is external. Support for making an organisation the trainer?
|
||||
supervisor = models.ForeignKey('Trainee', related_name='qualifications_granted', on_delete=models.RESTRICT)
|
||||
notes = models.TextField(blank=True)
|
||||
# TODO Maximum depth - some things stop at Complete and you can't be passed out in them
|
||||
|
||||
def __str__(self):
|
||||
return "{} in {} on {}".format(self.depth, self.item, self.date)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save()
|
||||
for level in TrainingLevel.objects.all(): # Mm yes efficiency FIXME
|
||||
if level.user_has_requirements(self.trainee):
|
||||
level_qualification = TrainingLevelQualification.objects.get_or_create(trainee=self.trainee, level=level)
|
||||
|
||||
@classmethod
|
||||
def get_colour_from_depth(obj, depth):
|
||||
if depth == 0:
|
||||
return "warning"
|
||||
elif depth == 1:
|
||||
return "success"
|
||||
else:
|
||||
return "info"
|
||||
|
||||
class Meta:
|
||||
unique_together = ["trainee", "item", "depth"]
|
||||
|
||||
|
||||
# Levels
|
||||
@reversion.register(follow=["requirements"])
|
||||
class TrainingLevel(models.Model, RevisionMixin):
|
||||
description = models.CharField(max_length=120, blank=True)
|
||||
TA = 0
|
||||
TECHNICIAN = 1
|
||||
SUPERVISOR = 2
|
||||
CHOICES = (
|
||||
(TA, 'Technical Assistant'),
|
||||
(TECHNICIAN, 'Technician'),
|
||||
(SUPERVISOR, 'Supervisor'),
|
||||
)
|
||||
DEPARTMENTS = (
|
||||
(0, 'Sound'),
|
||||
(1, 'Lighting'),
|
||||
(2, 'Power'),
|
||||
(3, 'Rigging'),
|
||||
(4, 'Haulage'),
|
||||
)
|
||||
department = models.IntegerField(choices=DEPARTMENTS, null=True) # N.B. Technical Assistant does not have a department
|
||||
level = models.IntegerField(choices=CHOICES)
|
||||
prerequisite_levels = models.ManyToManyField('self', related_name='prerequisites', symmetrical=False, blank=True)
|
||||
|
||||
def get_requirements_of_depth(self, depth):
|
||||
return self.requirements.filter(depth=depth)
|
||||
|
||||
@property
|
||||
def started_requirements(self):
|
||||
return self.get_requirements_of_depth(TrainingItemQualification.STARTED)
|
||||
|
||||
@property
|
||||
def complete_requirements(self):
|
||||
return self.get_requirements_of_depth(TrainingItemQualification.COMPLETE)
|
||||
|
||||
@property
|
||||
def passed_out_requirements(self):
|
||||
return self.get_requirements_of_depth(TrainingItemQualification.PASSED_OUT)
|
||||
|
||||
def percentage_complete(self, user): # FIXME
|
||||
needed_qualifications = self.requirements.all().select_related()
|
||||
relavant_qualifications = 0.0
|
||||
# TODO Efficiency...
|
||||
for req in needed_qualifications:
|
||||
if user.is_user_qualified_in(req.item, req.depth):
|
||||
relavant_qualifications += 1.0
|
||||
|
||||
if len(needed_qualifications) > 0:
|
||||
return int(relavant_qualifications / float(len(needed_qualifications)) * 100)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def user_has_requirements(self, user):
|
||||
return all(TrainingItem.user_has_qualification(req.item, user, req.depth) for req in self.requirements.select_related().all())
|
||||
|
||||
def __str__(self):
|
||||
if self.department is None:
|
||||
if self.level == self.TA:
|
||||
return self.get_level_display()
|
||||
else:
|
||||
return "{} Common Competencies".format(self.get_level_display())
|
||||
else:
|
||||
return "{} {}".format(self.get_department_display(), self.get_level_display())
|
||||
|
||||
@property
|
||||
def activity_feed_string(self):
|
||||
return str(self)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('level_detail', kwargs={'pk': self.pk})
|
||||
|
||||
|
||||
@reversion.register
|
||||
class TrainingLevelRequirement(models.Model):
|
||||
level = models.ForeignKey('TrainingLevel', related_name='requirements', on_delete=models.RESTRICT)
|
||||
item = models.ForeignKey('TrainingItem', on_delete=models.RESTRICT)
|
||||
depth = models.IntegerField(choices=TrainingItemQualification.CHOICES)
|
||||
|
||||
reversion_hide = True
|
||||
|
||||
def __str__(self):
|
||||
return "{} in {}".format(TrainingItemQualification.CHOICES[self.depth][1], self.item)
|
||||
|
||||
class Meta:
|
||||
unique_together = ["level", "item"]
|
||||
|
||||
|
||||
class TrainingLevelQualification(models.Model):
|
||||
trainee = models.ForeignKey('Trainee', related_name='levels', on_delete=models.RESTRICT)
|
||||
level = models.ForeignKey('TrainingLevel', on_delete=models.RESTRICT)
|
||||
confirmed_on = models.DateTimeField(null=True)
|
||||
confirmed_by = models.ForeignKey('Trainee', related_name='confirmer', on_delete=models.RESTRICT, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return "{} qualified as a {}".format(self.trainee, self.level)
|
||||
|
||||
class Meta:
|
||||
unique_together = ["trainee", "level"]
|
||||
30
training/templates/base_training.html
Normal file
30
training/templates/base_training.html
Normal file
@@ -0,0 +1,30 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block titleheader %}
|
||||
<a class="navbar-brand" href="{% url 'trainee_list' %}">Training</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block titleelements %}
|
||||
{% if user.is_authenticated %}
|
||||
<li class="nav-item"><a class="nav-link" href="/">Home</a></li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownRigboard" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
My Record
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdownRigboard">
|
||||
<a class="dropdown-item" href="{% url 'trainee_detail' %}"><span class="fas fa-eye"></span>
|
||||
Overview</a>
|
||||
<a class="dropdown-item" href="{% url 'trainee_item_detail' request.user.pk %}"><span class="fas fa-list"></span>
|
||||
Item Detail</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item"><a class="nav-link" href="{% url 'training_activity_table' %}"><span class="fas fa-random"></span> Recent Changes</a></li>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block titleelements_right %}
|
||||
{% include 'partials/search.html' %}
|
||||
{% include 'partials/navbar_user.html' %}
|
||||
{% endblock %}
|
||||
39
training/templates/edit_training_level.html
Normal file
39
training/templates/edit_training_level.html
Normal file
@@ -0,0 +1,39 @@
|
||||
{% extends 'base_training.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load widget_tweaks %}
|
||||
|
||||
{% block css %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/selects.css' %}"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block preload_js %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'js/selects.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||
<script src="{% static 'js/tooltip.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if form.errors %}
|
||||
{% include 'form_errors.html' %}
|
||||
{% endif %}
|
||||
<form id="requirement-form" action="{{ form.action|default:request.path }}" method="post">{% csrf_token %}
|
||||
{% render_field form.level|attr:'hidden' value=form.level.initial %}
|
||||
<div class="form-group form-row">
|
||||
<label for="item_id" class="col-sm-2 col-form-label">Item</label>
|
||||
<select name="item" id="item_id" class="form-control selectpicker custom-select col-sm-10" data-live-search="true" data-sourceurl="{% url 'api_secure' model='training_item' %}" required>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group form-row">
|
||||
<label for="depth" class="col-sm-2 col-form-label">Depth</label>
|
||||
{% render_field form.depth|add_class:'form-control col-sm'|attr:'required' %}
|
||||
</div>
|
||||
<input type="submit" class="btn btn-primary">
|
||||
</form>
|
||||
{% endblock %}
|
||||
68
training/templates/edit_training_record.html
Normal file
68
training/templates/edit_training_record.html
Normal file
@@ -0,0 +1,68 @@
|
||||
{% extends request.is_ajax|yesno:'base_ajax.html,base_training.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load widget_tweaks %}
|
||||
{% load button from filters %}
|
||||
|
||||
{% block css %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/selects.css' %}"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block preload_js %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'js/selects.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||
<script src="{% static 'js/tooltip.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form role="form" action="{{ form.action|default:request.path }}" method="POST" id="add_record_form">
|
||||
{% include 'form_errors.html' %}
|
||||
{% csrf_token %}
|
||||
{% render_field form.trainee|attr:'hidden' value=form.trainee.initial %}
|
||||
<div class="form-group form-row">
|
||||
<label for="item_id" class="col-sm-2 col-form-label">Item</label>
|
||||
<select name="item" id="item_id" class="form-control selectpicker custom-select col-sm-4" data-live-search="true" data-sourceurl="{% url 'api_secure' model='training_item' %}" required>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group form-row">
|
||||
<label for="depth" class="col-sm-2 col-form-label">Depth</label>
|
||||
{% render_field form.depth|add_class:'form-control custom-select col-sm-4' %}
|
||||
</div>
|
||||
<div class="form-group form-row">
|
||||
<label for="supervisor" class="col-sm-2 col-form-label">Supervisor</label>
|
||||
<select name="supervisor" id="supervisor_id" class="form-control selectpicker custom-select col-sm-10" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" required>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group form-row">
|
||||
<label for="date" class="col-sm-2 col-form-label">Training Date</label>
|
||||
<div class="col-sm-8">
|
||||
{% render_field form.date|add_class:'form-control'|attr:'type="date"' value=form.date.initial %}
|
||||
</div>
|
||||
<button class="btn btn-info col-sm-2" onclick="var date = new Date(); $('#id_date').val([date.getFullYear(), ('0' + (date.getMonth()+1)).slice(-2), ('0' + date.getDate()).slice(-2)].join('-'))" tabindex="-1" type="button">Today</button>
|
||||
</div>
|
||||
<div class="form-group form-row">
|
||||
<label for="item_description" class="col-sm-2 col-form-label">Notes</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea type="text" placeholder="Notes" class="form-control"
|
||||
id="notes" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
{% if not request.is_ajax %}
|
||||
<div class="col-sm-12 text-right pr-0">
|
||||
{% button 'submit' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<div class="col-sm-12 text-right pr-0">
|
||||
<button type="submit" class="btn btn-primary" title="Save" form="add_record_form"><span class="fas fa-save align-middle"></span> <span class="d-none d-sm-inline align-middle">Save</span></button>
|
||||
</div>
|
||||
{% endblock %}
|
||||
18
training/templates/item_list.html
Normal file
18
training/templates/item_list.html
Normal file
@@ -0,0 +1,18 @@
|
||||
{% extends 'base_rigs.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
{% for category in categories %}
|
||||
<div class="col">
|
||||
<div class="card mb-3">
|
||||
<h4 class="card-header">{{ category.name }}</h4>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for item in category.items.all %}
|
||||
<li class="list-group-item">{{ item }}</li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
77
training/templates/level_detail.html
Normal file
77
training/templates/level_detail.html
Normal file
@@ -0,0 +1,77 @@
|
||||
{% extends 'base_training.html' %}
|
||||
|
||||
{% load user_has_qualification from tags %}
|
||||
{% load user_level_if_present from tags %}
|
||||
|
||||
{% block content %}
|
||||
{% if request.user.is_supervisor or perms.training.change_traininglevel %}
|
||||
<div class="col-sm-12 text-right pr-0">
|
||||
<a type="button" class="btn btn-success mb-3" href="{% url 'add_requirement' pk=object.pk %}">
|
||||
<span class="fas fa-plus"></span> Add New Requirement
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card mb-3">
|
||||
<h4 class="card-header">Description</h4>
|
||||
<div class="card-body">
|
||||
<p>{{ object.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<h4 class="card-header">Users with this level</h4>
|
||||
<div class="card-body">
|
||||
<div class="card-columns">
|
||||
{% for user in users_with %}
|
||||
{% user_level_if_present user object as level_qualification %}
|
||||
<div class="card w-50" {% if not level_qualification.confirmed_on %}style="border-style: dashed; opacity: 70%"{%endif%}>
|
||||
<img src="{{user.profile_picture}}" class="card-img-top" />
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{user}}</h5>
|
||||
{% if level_qualification.confirmed_on %}<p class="card-text"><small>Qualified on {{ level_qualification.confirmed_on }}</small></p>{%else%}Unconfirmed{%endif%}
|
||||
</div>
|
||||
<div class="card-footer text-right pr-1"><a href="{% url 'profile_detail' user.pk %}" class="btn btn-primary btn-sm"><span class="fas fa-user"></span> View Profile</a></div>
|
||||
</div>
|
||||
{% empty %}
|
||||
Nobody here but us chickens...
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4 class="card-header">Level Requirements</h4>
|
||||
<table class="table card-body">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="table-warning">Training Started</th>
|
||||
<th scope="col" class="table-success">Training Complete</th>
|
||||
<th scope="col" class="table-info">Passed Out</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><ul class="list-unstyled">{% for req in object.started_requirements %}<li>{{ req.item }} {% user_has_qualification request.user req.item 0 %} {% if request.user.is_supervisor or perms.training.change_traininglevel %}<a type="button" class="btn btn-danger btn-sm" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-times-circle"></span></a>{% endif %}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.complete_requirements %}<li>{{ req.item }} {% user_has_qualification request.user req.item 1 %} {% if request.user.is_supervisor or perms.training.change_traininglevel %}<a type="button" class="btn btn-danger btn-sm" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-times-circle"></span></a>{% endif %}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.passed_out_requirements %}<li>{{ req.item }} {% user_has_qualification request.user req.item 2 %} {% if request.user.is_supervisor or perms.training.change_traininglevel %}<a type="button" class="btn btn-danger btn-sm" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-times-circle"></span></a>{%endif%}</li>{% endfor %}</ul></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4 class="card-header">Prerequisite Levels:</h4>
|
||||
<div class="card-body">
|
||||
<ul>
|
||||
{% for level in object.prerequisite_levels.all %}
|
||||
{% user_level_if_present request.user level as level_qualification %}
|
||||
<li><a href="{{level.get_absolute_url}}">{{ level }}</a> <span class="fas {% if level_qualification %}text-success fa-check{% if level_qualification.confirmed_by is not None %}-double{% endif %}{% else %}fa-hourglass-start text-warning{%endif%}"></span></li>
|
||||
{% for nested_level in level.prerequisite_levels.all %}
|
||||
{% user_level_if_present request.user level as nested_level_qualification %}
|
||||
<ul>
|
||||
<li><a href="{{nested_level.get_absolute_url}}">{{ nested_level }}</a> <span class="fas {% if nested_level_qualification %}text-success fa-check{% if nested_level_qualification.confirmed_by is not None %}-double{% endif %}{% else %}fa-hourglass-start text-warning{%endif%}"></span></li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
{% empty %}
|
||||
None
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'partials/last_edited.html' with target="traininglevel_history" %}
|
||||
{% endblock %}
|
||||
54
training/templates/session_log_form.html
Normal file
54
training/templates/session_log_form.html
Normal file
@@ -0,0 +1,54 @@
|
||||
{% extends 'base_rigs.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load button from filters %}
|
||||
|
||||
{% block css %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/selects.css' %}"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block preload_js %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'js/selects.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||
<script src="{% static 'js/interaction.js' %}"></script>
|
||||
<script src="{% static 'js/tooltip.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<form class="form">
|
||||
<h3>People</h3>
|
||||
<div class="form-group">
|
||||
<label for="selectpicker">Select Supervisor</label>
|
||||
<select name="supervisor" id="supervisor_id" class="form-control selectpicker custom-select" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="selectpicker">Select Attendees</label>
|
||||
<select multiple name="attendees" id="attendees_id" class="form-control selectpicker custom-select" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
|
||||
</select>
|
||||
</div>
|
||||
<h3>Training Items</h3>
|
||||
<div class="row">
|
||||
{% for depth in depths %}
|
||||
<div class="col">
|
||||
<h4>{{ depth.1 }}</h4>
|
||||
<select multiple name="{{ depth.0 }}" id="{{ depth.0 }}_id" class="form-control selectpicker custom-select" data-live-search="true" data-sourceurl="{% url 'api_secure' model='training_item' %}">
|
||||
</select>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="col-sm-12 text-right my-3">
|
||||
{% button 'submit' %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
114
training/templates/trainee_detail.html
Normal file
114
training/templates/trainee_detail.html
Normal file
@@ -0,0 +1,114 @@
|
||||
{% extends 'base_training.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load user_has_qualification from tags %}
|
||||
{% load percentage_complete from tags %}
|
||||
{% load user_level_if_present from tags %}
|
||||
{% load colour_from_depth from tags %}
|
||||
|
||||
{% block css %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/selects.css' %}"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block preload_js %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'js/selects.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||
<script src="{% static 'js/tooltip.js' %}"></script>
|
||||
<script>
|
||||
$('document').ready(function(){
|
||||
$('#add_record').click(function (e) {
|
||||
e.preventDefault();
|
||||
var url = $(this).attr("href");
|
||||
$.ajax({
|
||||
url: url,
|
||||
success: function(){
|
||||
$link = $(this);
|
||||
// Anti modal inception
|
||||
if ($link.parents('#modal').length === 0) {
|
||||
modaltarget = $link.data('target');
|
||||
modalobject = "";
|
||||
$('#modal').load(url, function (e) {
|
||||
$('#modal').modal();
|
||||
$(".selectpicker").selectpicker().each(function(){initPicker($(this))});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12 text-right">
|
||||
<a type="button" class="btn btn-success" href="{% url 'edit_record' object.pk %}" id="add_record">
|
||||
<span class="fas fa-plus"></span> Add New Training Record
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<h2 class="col-12">Training Levels</h2>
|
||||
<div class="alert alert-info" role="alert">
|
||||
<ul>
|
||||
<li>Technical Assistant is conferred automatically when the item requirements are met.</li>
|
||||
<li>Technician status is also automatic, but notification of status should be made at the next general meeting, at which point 'approval' should be granted on the system.</li>
|
||||
<li>Supervisor status is <em>not automatic</em> and until signed off at a general meeting, does not count.</li>
|
||||
</ul>
|
||||
<sup>Correct as of 7th July 2021, check the Training Policy.</sup>
|
||||
</div>
|
||||
<div class="card-columns">
|
||||
{% for level in levels %}
|
||||
<div class="card my-3">
|
||||
<h3 class="card-header"><a href="{% url 'level_detail' level.pk %}">{{ level }}</a></h3>
|
||||
<div class="card-body">
|
||||
<p>{{ level.description|truncatewords:30 }}</p>
|
||||
<div class="progress mb-2">
|
||||
{% percentage_complete level object as completion %}
|
||||
<div class="progress-bar progress-bar-striped" role="progressbar" style="width: {{completion}}%" aria-valuenow="{{completion}}" aria-valuemin="0" aria-valuemax="100">{{completion}}% complete</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer text-right">
|
||||
{% user_level_if_present object level as level_qualification %}
|
||||
{% if level_qualification %}
|
||||
{% if level_qualification.confirmed_by is None %}
|
||||
{% if request.user.is_supervisor or request.user.is_superuser %}
|
||||
<a class="btn btn-info" href="{% url 'confirm_level' object.pk level.pk %}">Confirm</a>
|
||||
{% else %}
|
||||
<button class="btn btn-warning" disabled>Awaiting Confirmation</button>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<button class="btn btn-success active">Confirmed <small>by {{ level_qualification.confirmed_by }}</small></button>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<button class="btn btn-danger" disabled>Incomplete</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h2 class="col-10">Training Items</h2><a href="{% url 'trainee_item_detail' object.pk %}" class="btn btn-info col-2"><span class="fas fa-info-circle"></span> View Detailed Record</a><br/>
|
||||
<div class="alert alert-info" role="alert"><h3 class="col-12">Key: <span class="badge badge-warning">Training Started</span> <span class="badge badge-success">Training Complete</span> <span class="badge badge-info">Passed Out</span></h3></div>
|
||||
<div class="card-deck">
|
||||
{% for category in categories %}
|
||||
<div class="card mb-3">
|
||||
<h3 class="card-header">{{ category }}</h3>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for q in object.qualifications_obtained.all %}
|
||||
{% if q.item.category == category %}
|
||||
<li class="list-group-item list-group-item-{% colour_from_depth q.depth %}">{{q.item}} ({{q.date}})</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
42
training/templates/trainee_item_list.html
Normal file
42
training/templates/trainee_item_list.html
Normal file
@@ -0,0 +1,42 @@
|
||||
{% extends 'base_training.html' %}
|
||||
|
||||
{% load url_replace from filters %}
|
||||
{% load paginator from filters %}
|
||||
{% load linkornone from filters %}
|
||||
{% load button from filters %}
|
||||
{% load colour_from_depth from tags %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Training Item</th>
|
||||
<th>Depth</th>
|
||||
<th>Date</th>
|
||||
<th>Supervisor</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
<tr id="row_item">
|
||||
<th scope="row" class="align-middle" id="cell_name">{{ object.item }}</th>
|
||||
<td class="table-{% colour_from_depth object.depth %}">{{ object.get_depth_display }}</td>
|
||||
<td>{{ object.date }}</td>
|
||||
<td>{{ object.supervisor }}</td>
|
||||
<td>{{ object.notes }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr class="table-warning">
|
||||
<td colspan="6" class="text-center">Nothing found</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
41
training/templates/trainee_list.html
Normal file
41
training/templates/trainee_list.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{% extends 'base_training.html' %}
|
||||
|
||||
{% load url_replace from filters %}
|
||||
{% load orderby from filters %}
|
||||
{% load paginator from filters %}
|
||||
{% load linkornone from filters %}
|
||||
{% load button from filters %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name<a href="?{% orderby request 'orderBy' 'name' %}"><span class="caret"></span></a></th>
|
||||
<th>Supervisor?</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
<tr id="row_item">
|
||||
<th scope="row" class="align-middle" id="cell_name">{{ object.name }} {% if request.user.pk == object.pk %}<span class="fas fa-user text-success"></span>{%endif%}</th>
|
||||
<td {% if object.is_supervisor %}class="table-success"{%endif%}>{{ object.is_supervisor|yesno|title }}</td>
|
||||
<td>
|
||||
<a class="btn btn-info" href="{% url 'trainee_detail' pk=object.pk %}"><span class="fas fa-eye"></span> View Training Record</a>
|
||||
<a href="{% url 'trainee_item_detail' pk=object.pk %}" class="btn btn-info"><span class="fas fa-info-circle"></span> View Detailed Record</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr class="table-warning">
|
||||
<td colspan="6" class="text-center">Nothing found</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,16 @@
|
||||
{% extends 'base_training.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<p>Are you sure you wish to delete {{ page_title }}</p>
|
||||
|
||||
<div class="text-right">
|
||||
<form action="{{ action_link }}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{% url 'level_detail' object.level.pk %}"/>
|
||||
<input type="submit" value="Yes" class="btn btn-danger col-sm-1"/>
|
||||
<a href="{% url 'level_detail' object.level.pk %}" class="btn btn-success col-sm-1">No</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
0
training/templatetags/__init__.py
Normal file
0
training/templatetags/__init__.py
Normal file
28
training/templatetags/tags.py
Normal file
28
training/templatetags/tags.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from django import forms
|
||||
from django import template
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import SafeData, mark_safe
|
||||
from django.utils.text import normalize_newlines
|
||||
|
||||
from training import models
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.simple_tag
|
||||
def user_has_qualification(user, item, depth):
|
||||
if models.TrainingItem.user_has_qualification(item, user, depth) is not None:
|
||||
return mark_safe("<span class='fas fa-check text-success'></span>")
|
||||
else:
|
||||
return mark_safe("<span class='fas fa-hourglass-start text-warning'></span>")
|
||||
|
||||
@register.simple_tag
|
||||
def user_level_if_present(user, level):
|
||||
return models.TrainingLevelQualification.objects.filter(trainee=user, level=level).first()
|
||||
|
||||
@register.simple_tag
|
||||
def percentage_complete(level, user):
|
||||
return level.percentage_complete(user)
|
||||
|
||||
@register.simple_tag
|
||||
def colour_from_depth(depth):
|
||||
return models.TrainingItemQualification.get_colour_from_depth(depth)
|
||||
24
training/urls.py
Normal file
24
training/urls.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from django.urls import path
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from PyRIGS.decorators import permission_required_with_403
|
||||
|
||||
from training import views
|
||||
|
||||
urlpatterns = [
|
||||
path('items/', views.ItemList.as_view(), name='item_list'),
|
||||
|
||||
path('trainee/list/', views.TraineeList.as_view(), name='trainee_list'),
|
||||
path('trainee/', login_required(views.TraineeDetail.as_view()), name='trainee_detail'),
|
||||
path('trainee/<int:pk>/',
|
||||
permission_required_with_403('RIGS.view_profile')(views.TraineeDetail.as_view()),
|
||||
name='trainee_detail'),
|
||||
path('trainee/<int:pk>/edit/', views.AddQualification.as_view(),
|
||||
name='edit_record'),
|
||||
path('session/', views.SessionLog.as_view(), name='session_log'),
|
||||
path('level/<int:pk>/', views.LevelDetail.as_view(), name='level_detail'),
|
||||
path('level/<int:pk>/add_requirement/', views.AddLevelRequirement.as_view(), name='add_requirement'),
|
||||
path('level/remove_requirement/<int:pk>/', views.RemoveRequirement.as_view(), name='remove_requirement'),
|
||||
path('trainee/<int:pk>/level/<int:level_pk>/confirm', views.ConfirmLevel.as_view(), name='confirm_level'),
|
||||
path('trainee/<int:pk>/item_record', views.TraineeItemDetail.as_view(), name='trainee_item_detail'),
|
||||
]
|
||||
163
training/views.py
Normal file
163
training/views.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import reversion
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse_lazy
|
||||
from django.views import generic
|
||||
from PyRIGS.views import OEmbedView, is_ajax, ModalURLMixin
|
||||
from training import models, forms
|
||||
from django.utils import timezone
|
||||
from django.db import transaction
|
||||
|
||||
from users import views
|
||||
|
||||
class ItemList(generic.ListView):
|
||||
template_name = "item_list.html"
|
||||
model = models.TrainingItem
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ItemList, self).get_context_data(**kwargs)
|
||||
context["page_title"] = "Training Items"
|
||||
context["categories"] = models.TrainingCategory.objects.all()
|
||||
return context
|
||||
|
||||
|
||||
class TraineeDetail(views.ProfileDetail, ModalURLMixin):
|
||||
template_name = "trainee_detail.html"
|
||||
model = models.Trainee
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(TraineeDetail, self).get_context_data(**kwargs)
|
||||
context["page_title"] = "{}'s Training Record".format(self.object.first_name + " " + self.object.last_name)
|
||||
context["levels"] = models.TrainingLevel.objects.all()
|
||||
context["categories"] = models.TrainingCategory.objects.all().prefetch_related('items')
|
||||
choices = models.TrainingItemQualification.CHOICES
|
||||
context["depths"] = choices
|
||||
for i in [x for x,_ in choices]:
|
||||
context[str(i)] = self.object.get_records_of_depth(i)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return self.get_close_url('trainee_detail', 'trainee_detail')
|
||||
|
||||
|
||||
class TraineeItemDetail(generic.ListView):
|
||||
model = models.TrainingItemQualification
|
||||
template_name = 'trainee_item_list.html'
|
||||
|
||||
def get_queryset(self):
|
||||
return models.Trainee.objects.get(pk=self.kwargs['pk']).qualifications_obtained.all()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["page_title"] = "Detailed Training Record for {}".format(models.Trainee.objects.get(pk=self.kwargs['pk']))
|
||||
return context
|
||||
|
||||
|
||||
class TraineeList(generic.ListView):
|
||||
model = models.Trainee
|
||||
template_name = 'trainee_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["page_title"] = "Profile List"
|
||||
return context
|
||||
|
||||
|
||||
class SessionLog(generic.FormView):
|
||||
template_name = "session_log_form.html"
|
||||
form_class = forms.SessionLogForm
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(SessionLog, self).get_context_data(**kwargs)
|
||||
context["page_title"] = "Log New Training Session"
|
||||
context["depths"] = models.TrainingItemQualification.CHOICES
|
||||
return context
|
||||
|
||||
|
||||
class AddQualification(generic.CreateView):
|
||||
template_name = "edit_training_record.html"
|
||||
model = models.TrainingItemQualification
|
||||
form_class = forms.QualificationForm
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AddQualification, self).get_context_data(**kwargs)
|
||||
context["depths"] = models.TrainingItemQualification.CHOICES
|
||||
if is_ajax(self.request):
|
||||
context['override'] = "base_ajax.html"
|
||||
else:
|
||||
context['override'] = 'base_training.html'
|
||||
context['page_title'] = "Add Qualification for {}".format(models.Trainee.objects.get(pk=self.kwargs['pk']))
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('trainee_detail', kwargs={"pk": self.object.pk })
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(AddQualification, self).get_form_kwargs()
|
||||
kwargs['pk'] = self.kwargs['pk']
|
||||
return kwargs
|
||||
|
||||
|
||||
class AddLevelRequirement(generic.CreateView):
|
||||
template_name = "edit_training_level.html"
|
||||
model = models.TrainingLevelRequirement
|
||||
form_class = forms.RequirementForm
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AddLevelRequirement, self).get_context_data(**kwargs)
|
||||
context["page_title"] = "Add Requirements to Training Level {}".format(models.TrainingLevel.objects.get(pk=self.kwargs['pk']))
|
||||
return context
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(AddLevelRequirement, self).get_form_kwargs()
|
||||
kwargs['pk'] = self.kwargs['pk']
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('level_detail', kwargs={"pk": self.kwargs['pk']})
|
||||
|
||||
@transaction.atomic()
|
||||
@reversion.create_revision()
|
||||
def form_valid(self, form, *args, **kwargs):
|
||||
reversion.add_to_revision(form.cleaned_data['level'])
|
||||
reversion.set_comment("Level requirement added")
|
||||
return super().form_valid(form, *args, **kwargs)
|
||||
|
||||
|
||||
class LevelDetail(generic.DetailView):
|
||||
template_name = "level_detail.html"
|
||||
model = models.TrainingLevel
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["page_title"] = "Training Level {}".format(self.object)
|
||||
context["users_with"] = map(lambda qual: qual.trainee, models.TrainingLevelQualification.objects.filter(level=self.object))
|
||||
return context
|
||||
|
||||
|
||||
class RemoveRequirement(generic.DeleteView):
|
||||
model = models.TrainingLevelRequirement
|
||||
template_name = 'traininglevelrequirement_confirm_delete.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["page_title"] = "Delete Requirement '{}' from Training Level {}?".format(self.object, self.object.level)
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return self.request.POST.get('next')
|
||||
|
||||
@transaction.atomic()
|
||||
@reversion.create_revision()
|
||||
def delete(self, *args, **kwargs):
|
||||
reversion.add_to_revision(self.get_object().level)
|
||||
return super().delete(*args, **kwargs)
|
||||
|
||||
|
||||
class ConfirmLevel(generic.RedirectView):
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
level_qualification = models.TrainingLevelQualification.objects.get(trainee=kwargs['pk'], level=kwargs['level_pk'])
|
||||
level_qualification.confirmed_by = self.request.user
|
||||
level_qualification.confirmed_on = timezone.now()
|
||||
level_qualification.save()
|
||||
return reverse_lazy('trainee_detail', kwargs={'pk': kwargs['pk']})
|
||||
@@ -20,6 +20,7 @@ class Command(BaseCommand):
|
||||
hs_group = None
|
||||
|
||||
def handle(self, *args, **options):
|
||||
print("Generating sample user data")
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
@@ -32,6 +33,7 @@ class Command(BaseCommand):
|
||||
self.setup_groups()
|
||||
self.setup_useful_profiles()
|
||||
self.setup_generic_profiles()
|
||||
print("Done generating sample user data")
|
||||
|
||||
def setup_groups(self):
|
||||
self.keyholder_group = Group.objects.create(name='Keyholders')
|
||||
|
||||
@@ -56,43 +56,37 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="col-lg-4 col-12 mb-2">
|
||||
<div class="card">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-md-3">
|
||||
<img src="{{object.profile_picture}}" class="card-img img-fluid" />
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-5">First Name</dt>
|
||||
<dd class="col-7">{{object.first_name}}</dd>
|
||||
<img src="{{object.profile_picture}}" class="card-img img-fluid" />
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-5">First Name</dt>
|
||||
<dd class="col-7">{{object.first_name}}</dd>
|
||||
|
||||
<dt class="col-5">Last Name</dt>
|
||||
<dd class="col-7">{{object.last_name}}</dd>
|
||||
<dt class="col-5">Last Name</dt>
|
||||
<dd class="col-7">{{object.last_name}}</dd>
|
||||
|
||||
<dt class="col-5">Email</dt>
|
||||
<dd class="col-7">{{object.email}}</dd>
|
||||
<dt class="col-5">Email</dt>
|
||||
<dd class="col-7">{{object.email}}</dd>
|
||||
|
||||
<dt class="col-5">Last Login</dt>
|
||||
<dd class="col-7">{{object.last_login|date:"d/m/Y H:i"}}</dd>
|
||||
<dt class="col-5">Last Login</dt>
|
||||
<dd class="col-7">{{object.last_login|date:"d/m/Y H:i"}}</dd>
|
||||
|
||||
<dt class="col-5">Date Joined</dt>
|
||||
<dd class="col-7">{{object.date_joined|date:"d/m/Y H:i"}}</dd>
|
||||
<dt class="col-5">Date Joined</dt>
|
||||
<dd class="col-7">{{object.date_joined|date:"d/m/Y H:i"}}</dd>
|
||||
|
||||
<dt class="col-5">Initials</dt>
|
||||
<dd class="col-7">{{object.initials}}</dd>
|
||||
<dt class="col-5">Initials</dt>
|
||||
<dd class="col-7">{{object.initials}}</dd>
|
||||
|
||||
<dt class="col-5">Phone</dt>
|
||||
<dd class="col-7">{{object.phone|linkornone:'tel'}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<dt class="col-5">Phone</dt>
|
||||
<dd class="col-7">{{object.phone|linkornone:'tel'}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if not request.is_ajax and object.pk == user.pk %}
|
||||
<div class="col-12 my-2">
|
||||
<div class="col-lg-8 col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">Personal iCal Details</div>
|
||||
<div class="card-body">
|
||||
@@ -152,9 +146,19 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col mb-2">
|
||||
<div class="card">
|
||||
<div class="card-header">Training Record</div>
|
||||
<div class="card-body">
|
||||
<a href="{% url 'trainee_detail' object.pk %}" class="btn btn-primary"><span class="fas fa-eye"></span> View Training Record</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header">Events</div>
|
||||
{% with object.latest_events as events %}
|
||||
{% include 'partials/event_table.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
<h4>Events</h4>
|
||||
{% with object.latest_events as events %}
|
||||
{% include 'partials/event_table.html' %}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -13,7 +13,7 @@ urlpatterns = [
|
||||
name='activity_feed'),
|
||||
]
|
||||
|
||||
for app in [apps.get_app_config(label) for label in ("RIGS", "assets")]:
|
||||
for app in [apps.get_app_config(label) for label in ("RIGS", "assets", "training")]:
|
||||
appname = str(app.label)
|
||||
if appname == 'RIGS':
|
||||
appname = 'rigboard'
|
||||
|
||||
@@ -21,7 +21,6 @@ class FieldComparison(object):
|
||||
def display_value(self, value):
|
||||
if (isinstance(self.field, IntegerField) or isinstance(self.field, CharField)) and self.field.choices is not None and len(self.field.choices) > 0:
|
||||
choice = [x[1] for x in self.field.choices if x[0] == value]
|
||||
# TODO This defensive piece should not be necessary?
|
||||
if len(choice) > 0:
|
||||
return choice[0]
|
||||
if isinstance(self.field, BooleanField):
|
||||
@@ -122,7 +121,7 @@ class ModelComparison(object):
|
||||
old_item_versions = self.version.parent.revision.version_set.exclude(content_type=item_type)
|
||||
new_item_versions = self.version.revision.version_set.exclude(content_type=item_type).exclude(content_type=ContentType.objects.get_for_model(models.EventAuthorisation))
|
||||
|
||||
comparisonParams = {'excluded_keys': ['id', 'event', 'order', 'checklist']}
|
||||
comparisonParams = {'excluded_keys': ['id', 'event', 'order', 'checklist', 'level']}
|
||||
|
||||
# Build some dicts of what we have
|
||||
item_dict = {} # build a list of items, key is the item_pk
|
||||
|
||||
Reference in New Issue
Block a user