Compare commits

...

12 Commits

Author SHA1 Message Date
dependabot[bot]
888300490c Build(deps): Bump socket.io-parser from 4.2.2 to 4.2.3 (#542)
Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 4.2.2 to 4.2.3.
- [Release notes](https://github.com/socketio/socket.io-parser/releases)
- [Changelog](https://github.com/socketio/socket.io-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io-parser/compare/4.2.2...4.2.3)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-24 10:50:41 +01:00
9201f9d896 Update level_list.html 2023-05-24 10:32:22 +01:00
9fae129e26 Update level_list.html 2023-05-24 10:22:19 +01:00
dependabot[bot]
8d45e260dd Build(deps): Bump requests from 2.25.1 to 2.31.0 (#541)
Bumps [requests](https://github.com/psf/requests) from 2.25.1 to 2.31.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.25.1...v2.31.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-23 21:36:41 +01:00
7d8dddb952 Update 0047_auto_20230517_0944.py
Testing in production for the L.
2023-05-19 12:56:39 +00:00
1104f10c91 Update 0047_auto_20230517_0944.py 2023-05-19 12:20:25 +00:00
3d5efba0af sigh 2023-05-19 11:49:14 +00:00
9a44aaf557 Update 0047_auto_20230517_0944.py
sigh
2023-05-19 11:37:16 +00:00
550eff83ee Update 0046_create_powertests.py
Fine, I'll do it that way.
2023-05-19 11:25:18 +00:00
bf3da5ae25 Update 0046_create_powertests.py
Allow it to migrate all foreignkey fields
2023-05-19 11:13:17 +00:00
87aa87bc0f Update 0046_create_powertests.py
Don't try and find a nonexistent notes field on EventChecklist.
2023-05-19 10:47:50 +00:00
01a0b8f831 Add event checkin functionality, seperate power tests into a different form (#536)
* Split power related parts of event checklist into a seperate form

* Revamp H&S overview, remove individual lists.

They were not a good thing.

* Remove old 'vehicle/crew' stuff

* Very initial version of checkin form

* Further work on checkin, add role field etc

* Fix tests after form split

* Add ability to edit checkins, more validation

* Basic checkin/out logic complete

* Add homepage checkin for events happening now

* Minor improvement to homepage UI

* Checkin button turns into checkout button where applicable

* UI work

* Clicking check out does not redirect the user

* Register check in model with the admin site

* Add power record status chip, checklist status chip displays number of checklists

* Minor fixes

* Implement codedoctor suggestions

* pep8

* Add data migration for crew/vehicles

* Checkin only requires login (no perms) and block users from editing other checkins at Django level
2023-05-19 10:28:51 +00:00
34 changed files with 1342 additions and 894 deletions

View File

@@ -47,7 +47,7 @@ python-dateutil = "~=2.8.1"
pytoml = "~=0.1.21"
pytz = "~=2020.5"
reportlab = "*"
requests = "~=2.25.1"
requests = "~=2.31.0"
retrying = "~=1.3.3"
simplejson = "~=3.17.2"
six = "~=1.15.0"

166
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "fd57c06cb6fe93ee6b4af9d978b7185d76bc32c962a847a209539534dcd2fb2a"
"sha256": "662400ec329dfe8267873531bf7c44d7a958f4f7b3493d24aee13d5fbddb1951"
},
"pipfile-spec": 6,
"requires": {
@@ -37,6 +37,7 @@
"sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b",
"sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"
],
"markers": "python_version >= '3.5'",
"version": "==1.10"
},
"attrs": {
@@ -44,6 +45,7 @@
"sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04",
"sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"
],
"markers": "python_version >= '3.7'",
"version": "==23.1.0"
},
"backports.tempfile": {
@@ -172,6 +174,7 @@
"sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
"sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
],
"markers": "python_version >= '3.6'",
"version": "==2023.5.7"
},
"chardet": {
@@ -182,6 +185,87 @@
"index": "pypi",
"version": "==4.0.0"
},
"charset-normalizer": {
"hashes": [
"sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6",
"sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1",
"sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e",
"sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373",
"sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62",
"sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230",
"sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be",
"sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c",
"sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0",
"sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448",
"sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f",
"sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649",
"sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d",
"sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0",
"sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706",
"sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a",
"sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59",
"sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23",
"sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5",
"sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb",
"sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e",
"sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e",
"sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c",
"sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28",
"sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d",
"sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41",
"sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974",
"sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce",
"sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f",
"sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1",
"sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d",
"sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8",
"sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017",
"sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31",
"sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7",
"sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8",
"sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e",
"sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14",
"sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd",
"sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d",
"sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795",
"sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b",
"sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b",
"sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b",
"sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203",
"sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f",
"sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19",
"sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1",
"sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a",
"sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac",
"sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9",
"sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0",
"sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137",
"sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f",
"sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6",
"sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5",
"sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909",
"sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f",
"sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0",
"sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324",
"sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755",
"sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb",
"sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854",
"sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c",
"sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60",
"sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84",
"sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0",
"sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b",
"sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1",
"sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531",
"sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1",
"sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11",
"sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326",
"sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df",
"sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"
],
"markers": "python_version >= '3.7'",
"version": "==3.1.0"
},
"configparser": {
"hashes": [
"sha256:85d5de102cfe6d14a5172676f09d19c465ce63d6019cf0a4ef13385fc535e828",
@@ -211,6 +295,7 @@
"sha256:1ccd984dab89fc68955043aca4e1b03e0cf29cad9880f6e28e3ba7a74b14aa5a",
"sha256:fd23a65bfd444595913f02fc71f6b286c29261e354c41d722ca7a261a49b5969"
],
"markers": "python_version >= '3.7'",
"version": "==0.7.0"
},
"cssutils": {
@@ -358,6 +443,7 @@
"sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e",
"sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"
],
"markers": "python_version < '3.11'",
"version": "==1.1.1"
},
"freetype-py": {
@@ -370,6 +456,7 @@
"sha256:ccdb1616794a8ad48beaa9e29d3494e6643d24d8e925cc39263de21c062ea5a7",
"sha256:f9b64ce3272a5c358dcee824800a32d70997fb872a0965a557adca20fce7a5d0"
],
"markers": "python_version >= '3.7'",
"version": "==2.3.0"
},
"gunicorn": {
@@ -385,6 +472,7 @@
"sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
"sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
],
"markers": "python_version >= '3.7'",
"version": "==0.14.0"
},
"html5lib": {
@@ -392,6 +480,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": {
@@ -416,7 +505,6 @@
"sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"
],
"index": "pypi",
"markers": "python_version < '3.10'",
"version": "==6.6.0"
},
"lxml": {
@@ -499,6 +587,7 @@
"sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f",
"sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.9.2"
},
"markdown": {
@@ -583,6 +672,7 @@
"sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672",
"sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"
],
"markers": "python_version >= '3.7'",
"version": "==1.2.0"
},
"packaging": {
@@ -590,6 +680,7 @@
"sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61",
"sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"
],
"markers": "python_version >= '3.7'",
"version": "==23.1"
},
"pep517": {
@@ -711,6 +802,7 @@
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
],
"markers": "python_version >= '3.6'",
"version": "==1.0.0"
},
"premailer": {
@@ -797,6 +889,7 @@
"sha256:e7cde633986435d87a86b6118b7b6109c384266fd719ef959883e2729f6eafae",
"sha256:ec305fc7f2f0299df78aadec0eaf6eb9accb90eda242b5d3492544d3f2b28027"
],
"markers": "python_version >= '3.7'",
"version": "==1.23.0"
},
"pygments": {
@@ -881,11 +974,11 @@
},
"requests": {
"hashes": [
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
"sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
"sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"
],
"index": "pypi",
"version": "==2.25.1"
"version": "==2.31.0"
},
"retrying": {
"hashes": [
@@ -900,6 +993,7 @@
"sha256:7cd1eac30fe69d98f75d67a54892f9c10534a047b9a959ef17bb3926a196e50a",
"sha256:a88bce206c45d2180f944b8754c6e2e9245f80506c90fdfb94c7fbdd27805c25"
],
"markers": "python_version >= '3.7'",
"version": "==0.2.0"
},
"selenium": {
@@ -918,6 +1012,14 @@
"index": "pypi",
"version": "==1.22.2"
},
"setuptools": {
"hashes": [
"sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f",
"sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"
],
"markers": "python_version >= '3.7'",
"version": "==67.8.0"
},
"simplejson": {
"hashes": [
"sha256:04e31fa6ac8e326480703fb6ded1488bfa6f1d3f760d32e29dbf66d0838982ce",
@@ -998,6 +1100,7 @@
"sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101",
"sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"
],
"markers": "python_version >= '3.7'",
"version": "==1.3.0"
},
"sortedcontainers": {
@@ -1013,7 +1116,6 @@
"sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"
],
"index": "pypi",
"markers": "python_version >= '3.0'",
"version": "==2.4.1"
},
"sqlparse": {
@@ -1042,6 +1144,7 @@
"hashes": [
"sha256:3ae765d3a9409ee60c0fb4d24c2deb6a80617aa927054f5bcd7fc98f0695e587"
],
"markers": "python_version >= '3.7'",
"version": "==1.5.1"
},
"tini": {
@@ -1057,6 +1160,7 @@
"sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847",
"sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"
],
"markers": "python_version >= '3.7'",
"version": "==1.2.1"
},
"toml": {
@@ -1064,6 +1168,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": {
@@ -1088,6 +1193,7 @@
"sha256:ce68f1c5400a47b137c5a4de72c7c901bd4e7a24fbdebfe9b41de8c6c04eaacf",
"sha256:f1dd0780a89bfc880c7c7994519cb53f62aacb2c25ff487001c0052bd721cdf0"
],
"markers": "python_version >= '3.7'",
"version": "==0.22.0"
},
"trio-websocket": {
@@ -1095,6 +1201,7 @@
"sha256:0908435e4eecc49d830ae1c4d6c47b978a75f00594a2be2104d58b61a04cdb53",
"sha256:af13e9393f9051111300287947ec595d601758ce3d165328e7d36325135a8d62"
],
"markers": "python_version >= '3.7'",
"version": "==0.10.2"
},
"urllib3": {
@@ -1125,6 +1232,7 @@
"sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065",
"sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"
],
"markers": "python_version >= '3.7'",
"version": "==1.2.0"
},
"yolk": {
@@ -1176,11 +1284,11 @@
},
"zope.event": {
"hashes": [
"sha256:4ab47faac13163ca3c5d6d8a5595212e14770322e95c338d955e3688ba19082a",
"sha256:8cca6074a2bbca140d32704694e74098d5621f2c75ea6e3a9458240a3b5382a9"
"sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42",
"sha256:5e76517f5b9b119acf37ca8819781db6c16ea433f7e2062c4afc2b6fbedb1330"
],
"index": "pypi",
"version": "==4.5.1"
"version": "==4.5.0"
},
"zope.hookable": {
"hashes": [
@@ -1347,6 +1455,7 @@
"sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b",
"sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"
],
"markers": "python_version >= '3.5'",
"version": "==1.10"
},
"attrs": {
@@ -1354,6 +1463,7 @@
"sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04",
"sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"
],
"markers": "python_version >= '3.7'",
"version": "==23.1.0"
},
"certifi": {
@@ -1361,6 +1471,7 @@
"sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
"sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
],
"markers": "python_version >= '3.6'",
"version": "==2023.5.7"
},
"charset-normalizer": {
@@ -1441,6 +1552,7 @@
"sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df",
"sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"
],
"markers": "python_version >= '3.7'",
"version": "==3.1.0"
},
"coverage": {
@@ -1496,6 +1608,7 @@
"sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84",
"sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"
],
"markers": "python_version >= '3.7'",
"version": "==6.5.0"
},
"coveralls": {
@@ -1525,6 +1638,7 @@
"sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e",
"sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"
],
"markers": "python_version < '3.11'",
"version": "==1.1.1"
},
"execnet": {
@@ -1532,6 +1646,7 @@
"sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5",
"sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.9.0"
},
"h11": {
@@ -1539,6 +1654,7 @@
"sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
"sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
],
"markers": "python_version >= '3.7'",
"version": "==0.14.0"
},
"idna": {
@@ -1554,6 +1670,7 @@
"sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3",
"sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"
],
"markers": "python_version >= '3.7'",
"version": "==2.0.0"
},
"outcome": {
@@ -1561,6 +1678,7 @@
"sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672",
"sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"
],
"markers": "python_version >= '3.7'",
"version": "==1.2.0"
},
"packaging": {
@@ -1568,6 +1686,7 @@
"sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61",
"sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"
],
"markers": "python_version >= '3.7'",
"version": "==23.1"
},
"pluggy": {
@@ -1575,6 +1694,7 @@
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
],
"markers": "python_version >= '3.6'",
"version": "==1.0.0"
},
"psutil": {
@@ -1676,6 +1796,9 @@
"version": "==3.3.2"
},
"pytest-xdist": {
"extras": [
"psutil"
],
"hashes": [
"sha256:1849bd98d8b242b948e472db7478e090bf3361912a8fed87992ed94085f54727",
"sha256:37290d161638a20b672401deef1cba812d110ac27e35d213f091d15b8beb40c9"
@@ -1685,11 +1808,11 @@
},
"requests": {
"hashes": [
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
"sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
"sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"
],
"index": "pypi",
"version": "==2.25.1"
"version": "==2.31.0"
},
"selenium": {
"hashes": [
@@ -1699,11 +1822,20 @@
"index": "pypi",
"version": "==4.9.1"
},
"setuptools": {
"hashes": [
"sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f",
"sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"
],
"markers": "python_version >= '3.7'",
"version": "==67.8.0"
},
"sniffio": {
"hashes": [
"sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101",
"sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"
],
"markers": "python_version >= '3.7'",
"version": "==1.3.0"
},
"sortedcontainers": {
@@ -1725,6 +1857,7 @@
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
],
"markers": "python_version < '3.11'",
"version": "==2.0.1"
},
"trio": {
@@ -1732,6 +1865,7 @@
"sha256:ce68f1c5400a47b137c5a4de72c7c901bd4e7a24fbdebfe9b41de8c6c04eaacf",
"sha256:f1dd0780a89bfc880c7c7994519cb53f62aacb2c25ff487001c0052bd721cdf0"
],
"markers": "python_version >= '3.7'",
"version": "==0.22.0"
},
"trio-websocket": {
@@ -1739,6 +1873,7 @@
"sha256:0908435e4eecc49d830ae1c4d6c47b978a75f00594a2be2104d58b61a04cdb53",
"sha256:af13e9393f9051111300287947ec595d601758ce3d165328e7d36325135a8d62"
],
"markers": "python_version >= '3.7'",
"version": "==0.10.2"
},
"urllib3": {
@@ -1754,6 +1889,7 @@
"sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065",
"sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"
],
"markers": "python_version >= '3.7'",
"version": "==1.2.0"
},
"zope.component": {
@@ -1766,11 +1902,11 @@
},
"zope.event": {
"hashes": [
"sha256:4ab47faac13163ca3c5d6d8a5595212e14770322e95c338d955e3688ba19082a",
"sha256:8cca6074a2bbca140d32704694e74098d5621f2c75ea6e3a9458240a3b5382a9"
"sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42",
"sha256:5e76517f5b9b119acf37ca8819781db6c16ea433f7e2062c4afc2b6fbedb1330"
],
"index": "pypi",
"version": "==4.5.1"
"version": "==4.5.0"
},
"zope.hookable": {
"hashes": [

View File

@@ -48,6 +48,7 @@ class Index(generic.TemplateView): # Displays the current rig count along with
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['rig_count'] = models.Event.objects.rig_count()
context['now'] = models.Event.objects.events_in_bounds(timezone.now(), timezone.now())
return context

View File

@@ -20,6 +20,7 @@ admin.site.register(models.VatRate, VersionAdmin)
admin.site.register(models.Event, VersionAdmin)
admin.site.register(models.EventItem, VersionAdmin)
admin.site.register(models.Invoice, VersionAdmin)
admin.site.register(models.EventCheckIn)
@transaction.atomic() # Copied from django-extensions. GenericForeignKey support removed as unnecessary.
@@ -206,3 +207,8 @@ class RiskAssessmentAdmin(VersionAdmin):
@admin.register(models.EventChecklist)
class EventChecklistAdmin(VersionAdmin):
list_display = ('id', 'event', 'reviewed_at', 'reviewed_by')
@admin.register(models.PowerTestRecord)
class EventChecklistAdmin(VersionAdmin):
list_display = ('id', 'event', 'reviewed_at', 'reviewed_by')

View File

@@ -44,7 +44,7 @@ class EventForm(forms.ModelForm):
return simplejson.dumps(items)
def __init__(self, *args, **kwargs):
super(EventForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.fields['items_json'].initial = self._get_items_json
self.fields['start_date'].widget.format = '%Y-%m-%d'
@@ -200,86 +200,41 @@ class EventChecklistForm(forms.ModelForm):
related_models = {
'venue': models.Venue,
'power_mic': models.Profile,
}
# Two possible formats
def parsedatetime(self, date_string):
try:
return timezone.make_aware(datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%S'))
except ValueError:
return timezone.make_aware(datetime.strptime(date_string, '%Y-%m-%dT%H:%M'))
# There's probably a thousand better ways to do this, but this one is mine
def clean(self):
vehicles = {key: val for key, val in self.data.items()
if key.startswith('vehicle')}
for key in vehicles:
pk = int(key.split('_')[1])
driver_key = 'driver_' + str(pk)
if (self.data[driver_key] == ''):
raise forms.ValidationError('Add a driver to vehicle ' + str(pk), code='vehicle_mismatch')
else:
try:
item = models.EventChecklistVehicle.objects.get(pk=pk)
except models.EventChecklistVehicle.DoesNotExist:
item = models.EventChecklistVehicle()
item.vehicle = vehicles['vehicle_' + str(pk)]
item.driver = models.Profile.objects.get(pk=self.data[driver_key])
item.full_clean('checklist')
# item does not have a database pk yet as it isn't saved
self.items['v' + str(pk)] = item
crewmembers = {key: val for key, val in self.data.items()
if key.startswith('crewmember')}
other_fields = ['start', 'role', 'end']
for key in crewmembers:
pk = int(key.split('_')[1])
for field in other_fields:
value = self.data[f'{field}_{pk}']
if value == '':
raise forms.ValidationError(f'Add a {field} to crewmember {pk}', code=f'{field}_mismatch')
try:
item = models.EventChecklistCrew.objects.get(pk=pk)
except models.EventChecklistCrew.DoesNotExist:
item = models.EventChecklistCrew()
item.crewmember = models.Profile.objects.get(pk=self.data['crewmember_' + str(pk)])
item.start = self.parsedatetime(self.data['start_' + str(pk)])
item.role = self.data['role_' + str(pk)]
item.end = self.parsedatetime(self.data['end_' + str(pk)])
item.full_clean('checklist')
# item does not have a database pk yet as it isn't saved
self.items['c' + str(pk)] = item
return super(EventChecklistForm, self).clean()
def save(self, commit=True):
checklist = super(EventChecklistForm, self).save(commit=False)
if (commit):
# Remove all existing, to be recreated from the form
checklist.vehicles.all().delete()
checklist.crew.all().delete()
checklist.save()
for key in self.items:
item = self.items[key]
reversion.add_to_revision(item)
# finish and save new database items
item.checklist = checklist
item.full_clean()
item.save()
self.items.clear()
return checklist
class Meta:
model = models.EventChecklist
fields = '__all__'
exclude = ['reviewed_at', 'reviewed_by']
class PowerTestRecordForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
if field.__class__ == forms.NullBooleanField:
# Only display yes/no to user, the 'none' is only ever set in the background
field.widget = forms.CheckboxInput()
class Meta:
model = models.PowerTestRecord
fields = '__all__'
exclude = ['reviewed_at', 'reviewed_by']
class EventCheckInForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['time'].initial = timezone.now()
self.fields['role'].initial = "Crew"
class Meta:
model = models.EventCheckIn
fields = '__all__'
exclude = ['end_time']
class EditCheckInForm(forms.ModelForm):
class Meta:
model = models.EventCheckIn
fields = '__all__'

View File

@@ -278,7 +278,7 @@ class Command(BaseCommand):
suspended_structures=bool(random.getrandbits(1)),
outside=bool(random.getrandbits(1)))
if i == 0 or random.randint(0, 1) > 0: # Event 1 and 1 in 10 have a Checklist
models.EventChecklist.objects.create(event=new_event, power_mic=random.choice(self.profiles),
models.EventChecklist.objects.create(event=new_event,
safe_parking=bool(random.getrandbits(1)),
safe_packing=bool(random.getrandbits(1)),
exits=bool(random.getrandbits(1)),
@@ -287,6 +287,4 @@ class Command(BaseCommand):
ear_plugs=bool(random.getrandbits(1)),
hs_location="Locked away safely",
extinguishers_location="Somewhere, I forgot",
earthing=bool(random.getrandbits(1)),
pat=bool(random.getrandbits(1)),
date=timezone.now(), venue=random.choice(self.venues))

View File

@@ -0,0 +1,71 @@
# Generated by Django 3.2.16 on 2023-05-08 15:58
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import versioning.versioning
def migrate_old_data(apps, schema_editor):
EventChecklist = apps.get_model('RIGS', 'EventChecklist')
PowerTestRecord = apps.get_model('RIGS', 'PowerTestRecord')
for ec in EventChecklist.objects.all():
# New highscore for the most pythonic BS I've ever written.
PowerTestRecord.objects.create(event=ec.event, venue=ec.venue, reviewed_by=ec.reviewed_by, **{i.name:getattr(ec, i.attname) for i in PowerTestRecord._meta.get_fields() if not (i.is_relation or i.auto_created or i.name == "notes")})
def revert(apps, schema_editor):
apps.get_model('RIGS', 'PowerTestRecord').objects.all().delete()
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0045_alter_profile_is_approved'),
]
operations = [
migrations.CreateModel(
name='PowerTestRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='power_tests', to='RIGS.event')),
('notes', models.TextField(blank=True, default='')),
('venue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='RIGS.venue')),
('reviewed_at', models.DateTimeField(null=True)),
('rcds', models.BooleanField(blank=True, help_text='RCDs installed where needed and tested?', null=True)),
('supply_test', models.BooleanField(blank=True, help_text='Electrical supplies tested?<br><small>(using socket tester)</small>', null=True)),
('earthing', models.BooleanField(blank=True, help_text='Equipment appropriately earthed?<br><small>(truss, stage, generators etc)</small>', null=True)),
('pat', models.BooleanField(blank=True, help_text='All equipment in PAT period?', null=True)),
('source_rcd', models.BooleanField(blank=True, help_text='Source RCD protected?<br><small>(if cable is more than 3m long) </small>', null=True)),
('labelling', models.BooleanField(blank=True, help_text='Appropriate and clear labelling on distribution and cabling?', null=True)),
('fd_voltage_l1', models.IntegerField(blank=True, help_text='L1 - N', null=True, verbose_name='First Distro Voltage L1-N')),
('fd_voltage_l2', models.IntegerField(blank=True, help_text='L2 - N', null=True, verbose_name='First Distro Voltage L2-N')),
('fd_voltage_l3', models.IntegerField(blank=True, help_text='L3 - N', null=True, verbose_name='First Distro Voltage L3-N')),
('fd_phase_rotation', models.BooleanField(blank=True, help_text='Phase Rotation<br><small>(if required)</small>', null=True, verbose_name='Phase Rotation')),
('fd_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
('fd_pssc', models.IntegerField(blank=True, help_text='Prospective Short Circuit Current', null=True, verbose_name='PSCC')),
('w1_description', models.CharField(blank=True, default='', help_text='Description', max_length=255)),
('w1_polarity', models.BooleanField(blank=True, help_text='Polarity Checked?', null=True)),
('w1_voltage', models.IntegerField(blank=True, help_text='Voltage', null=True)),
('w1_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
('w2_description', models.CharField(blank=True, default='', help_text='Description', max_length=255)),
('w2_polarity', models.BooleanField(blank=True, help_text='Polarity Checked?', null=True)),
('w2_voltage', models.IntegerField(blank=True, help_text='Voltage', null=True)),
('w2_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
('w3_description', models.CharField(blank=True, default='', help_text='Description', max_length=255)),
('w3_polarity', models.BooleanField(blank=True, help_text='Polarity Checked?', null=True)),
('w3_voltage', models.IntegerField(blank=True, help_text='Voltage', null=True)),
('w3_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
('all_rcds_tested', models.BooleanField(blank=True, help_text='All circuit RCDs tested?<br><small>(using test button)</small>', null=True)),
('public_sockets_tested', models.BooleanField(blank=True, help_text='Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>', null=True)),
('reviewed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Reviewer')),
],
options={
'abstract': False,
'ordering': ['event'],
'permissions': [('review_power', 'Can review Power Test Records')],
},
bases=(models.Model, versioning.versioning.RevisionMixin),
),
migrations.RunPython(migrate_old_data, reverse_code=revert),
]

View File

@@ -0,0 +1,44 @@
# Generated by Django 3.2.19 on 2023-05-17 08:44
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
from django.core.exceptions import ObjectDoesNotExist
def migrate_old_data(apps, schema_editor):
EventChecklist = apps.get_model('RIGS', 'EventChecklist')
EventCheckIn = apps.get_model('RIGS', 'EventCheckIn')
for ec in EventChecklist.objects.all():
for crew in ec.crew.all():
try:
EventCheckIn.objects.create(event=ec.event, person=crew.crewmember, role=crew.role, time=crew.start, end_time=crew.end, vehicle=ec.vehicles.get(driver=crew.crewmember).vehicle)
except ObjectDoesNotExist:
EventCheckIn.objects.create(event=ec.event, person=crew.crewmember, role=crew.role, time=crew.start, end_time=crew.end)
def revert(apps, schema_editor):
apps.get_model('RIGS', 'EventCheckIn').objects.all().delete()
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0046_create_powertests'),
]
operations = [
migrations.CreateModel(
name='EventCheckIn',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.DateTimeField()),
('role', models.CharField(blank=True, max_length=50)),
('vehicle', models.CharField(blank=True, max_length=100)),
('end_time', models.DateTimeField(blank=True, null=True)),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='crew', to='RIGS.event')),
('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='checkins', to=settings.AUTH_USER_MODEL)),
],
),
migrations.RunPython(migrate_old_data, reverse_code=revert),
]

View File

@@ -0,0 +1,156 @@
# Generated by Django 3.2.19 on 2023-05-18 11:56
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0047_auto_20230517_0944'),
]
operations = [
migrations.RemoveField(
model_name='eventchecklistvehicle',
name='checklist',
),
migrations.RemoveField(
model_name='eventchecklistvehicle',
name='driver',
),
migrations.RemoveField(
model_name='eventchecklist',
name='all_rcds_tested',
),
migrations.RemoveField(
model_name='eventchecklist',
name='earthing',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_earth_fault',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_phase_rotation',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_pssc',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_voltage_l1',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_voltage_l2',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_voltage_l3',
),
migrations.RemoveField(
model_name='eventchecklist',
name='labelling',
),
migrations.RemoveField(
model_name='eventchecklist',
name='pat',
),
migrations.RemoveField(
model_name='eventchecklist',
name='power_mic',
),
migrations.RemoveField(
model_name='eventchecklist',
name='public_sockets_tested',
),
migrations.RemoveField(
model_name='eventchecklist',
name='rcds',
),
migrations.RemoveField(
model_name='eventchecklist',
name='source_rcd',
),
migrations.RemoveField(
model_name='eventchecklist',
name='supply_test',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w1_description',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w1_earth_fault',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w1_polarity',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w1_voltage',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w2_description',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w2_earth_fault',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w2_polarity',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w2_voltage',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w3_description',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w3_earth_fault',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w3_polarity',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w3_voltage',
),
migrations.AddField(
model_name='powertestrecord',
name='power_mic',
field=models.ForeignKey(blank=True, help_text='Who is the Power MIC?', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='checklists', to=settings.AUTH_USER_MODEL, verbose_name='Power MIC'),
),
migrations.AlterField(
model_name='eventchecklist',
name='reviewed_at',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AlterField(
model_name='powertestrecord',
name='reviewed_at',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AlterField(
model_name='riskassessment',
name='reviewed_at',
field=models.DateTimeField(blank=True, null=True),
),
migrations.DeleteModel(
name='EventChecklistCrew',
),
migrations.DeleteModel(
name='EventChecklistVehicle',
),
]

View File

@@ -81,6 +81,10 @@ class Profile(AbstractUser):
def __str__(self):
return self.name
def current_event(self):
q = EventCheckIn.objects.filter(person=self, end_time=None)
return q.latest('time') if q.exists() else None
class ContactableManager(models.Manager):
def search(self, query=None):
@@ -405,7 +409,15 @@ class Event(models.Model, RevisionMixin):
@property
def hs_done(self):
return self.riskassessment is not None and len(self.checklists.all()) > 0
return self.riskassessment is not None and self.has_checklist and self.has_power
@property
def has_checklist(self):
return self.checklists.exists()
@property
def has_power(self):
return self.power_tests.exists()
@property
def has_start_time(self):
@@ -478,6 +490,15 @@ class Event(models.Model, RevisionMixin):
else:
return bool(self.purchase_order)
@property
def can_check_in(self):
earliest = self.earliest_time
if isinstance(self.earliest_time, datetime.date):
earliest = datetime.datetime.combine(self.start_date, datetime.time(00, 00))
tz = pytz.timezone(settings.TIME_ZONE)
earliest = tz.localize(earliest)
return not self.dry_hire and earliest <= timezone.now()
objects = EventManager()
def get_absolute_url(self):
@@ -689,8 +710,21 @@ def validate_url(value):
raise ValidationError('URL must point to a location on the TEC Sharepoint')
class ReviewableModel(models.Model):
reviewed_at = models.DateTimeField(null=True, blank=True)
reviewed_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
verbose_name="Reviewer", on_delete=models.CASCADE)
class Meta:
abstract = True
@cached_property
def fieldz(self):
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
@reversion.register
class RiskAssessment(models.Model, RevisionMixin):
class RiskAssessment(ReviewableModel, RevisionMixin):
SMALL = (0, 'Small')
MEDIUM = (1, 'Medium')
LARGE = (2, 'Large')
@@ -738,10 +772,6 @@ class RiskAssessment(models.Model, RevisionMixin):
# Blimey that was a lot of options
reviewed_at = models.DateTimeField(null=True)
reviewed_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
verbose_name="Reviewer", on_delete=models.CASCADE)
supervisor_consulted = models.BooleanField(null=True)
expected_values = {
@@ -778,10 +808,6 @@ class RiskAssessment(models.Model, RevisionMixin):
('review_riskassessment', 'Can review Risk Assessments')
]
@cached_property
def fieldz(self):
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
@property
def event_size(self):
# Confirm event size. Check all except generators, since generators entails outside
@@ -795,6 +821,12 @@ class RiskAssessment(models.Model, RevisionMixin):
def get_event_size_display(self):
return self.SIZES[self.event_size][1] + " Event"
def __str__(self):
return f"{self.pk} | {self.event}"
def get_absolute_url(self):
return reverse('ra_detail', kwargs={'pk': self.pk})
@property
def activity_feed_string(self):
return str(self.event)
@@ -803,20 +835,12 @@ class RiskAssessment(models.Model, RevisionMixin):
def name(self):
return str(self)
def get_absolute_url(self):
return reverse('ra_detail', kwargs={'pk': self.pk})
def __str__(self):
return f"{self.pk} | {self.event}"
@reversion.register(follow=['vehicles', 'crew'])
class EventChecklist(models.Model, RevisionMixin):
@reversion.register
class EventChecklist(ReviewableModel, RevisionMixin):
event = models.ForeignKey('Event', related_name='checklists', on_delete=models.CASCADE)
# General
power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='checklists',
verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC?")
venue = models.ForeignKey('Venue', on_delete=models.CASCADE)
date = models.DateField()
@@ -830,6 +854,32 @@ class EventChecklist(models.Model, RevisionMixin):
hs_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of Safety Bag/Box")
extinguishers_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of fire extinguishers")
inverted_fields = []
class Meta:
ordering = ['event']
permissions = [
('review_eventchecklist', 'Can review Event Checklists')
]
def __str__(self):
return f"{self.pk} - {self.event}"
@property
def activity_feed_string(self):
return str(self.event)
def get_absolute_url(self):
return reverse('ec_detail', kwargs={'pk': self.pk})
@reversion.register
class PowerTestRecord(ReviewableModel, RevisionMixin):
event = models.ForeignKey('Event', related_name='power_tests', on_delete=models.CASCADE)
power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='checklists',
verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC?")
venue = models.ForeignKey('Venue', on_delete=models.CASCADE)
notes = models.TextField(blank=True, default='')
# Small Electrical Checks
rcds = models.BooleanField(blank=True, null=True, help_text="RCDs installed where needed and tested?")
supply_test = models.BooleanField(blank=True, null=True, help_text="Electrical supplies tested?<br><small>(using socket tester)</small>")
@@ -865,58 +915,40 @@ class EventChecklist(models.Model, RevisionMixin):
all_rcds_tested = models.BooleanField(blank=True, null=True, help_text="All circuit RCDs tested?<br><small>(using test button)</small>")
public_sockets_tested = models.BooleanField(blank=True, null=True, help_text="Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>")
reviewed_at = models.DateTimeField(null=True)
reviewed_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
verbose_name="Reviewer", on_delete=models.CASCADE)
inverted_fields = []
class Meta:
ordering = ['event']
permissions = [
('review_eventchecklist', 'Can review Event Checklists')
('review_power', 'Can review Power Test Records')
]
@cached_property
def fieldz(self):
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
def __str__(self):
return f"{self.pk} - {self.event}"
@property
def activity_feed_string(self):
return str(self.event)
def get_absolute_url(self):
return reverse('ec_detail', kwargs={'pk': self.pk})
class EventCheckIn(models.Model):
event = models.ForeignKey('Event', related_name='crew', on_delete=models.CASCADE)
person = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='checkins', on_delete=models.CASCADE)
time = models.DateTimeField()
role = models.CharField(max_length=50, blank=True)
vehicle = models.CharField(max_length=100, blank=True)
end_time = models.DateTimeField(null=True, blank=True)
def __str__(self):
return f"{self.pk} - {self.event}"
@reversion.register
class EventChecklistVehicle(models.Model, RevisionMixin):
checklist = models.ForeignKey('EventChecklist', related_name='vehicles', blank=True, on_delete=models.CASCADE)
vehicle = models.CharField(max_length=255)
driver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='vehicles', on_delete=models.CASCADE)
reversion_hide = True
def __str__(self):
return f"{self.vehicle} driven by {self.driver}"
@reversion.register
class EventChecklistCrew(models.Model, RevisionMixin):
checklist = models.ForeignKey('EventChecklist', related_name='crew', blank=True, on_delete=models.CASCADE)
crewmember = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='crewed', on_delete=models.CASCADE)
role = models.CharField(max_length=255)
start = models.DateTimeField()
end = models.DateTimeField()
reversion_hide = True
return f"{self.person} on {self.event}"
def clean(self):
if self.start > self.end:
raise ValidationError('Unless you\'ve invented time travel, crew can\'t finish before they have started.')
sass = " Please invent time travel and retry."
if self.time > timezone.now():
raise ValidationError("May not check in in the future." + sass)
if self.end_time and self.end_time < self.time:
raise ValidationError("May not check out before you've checked in." + sass)
def __str__(self):
return f"{self.crewmember} ({self.role})"
def get_absolute_url(self):
return reverse('event_detail', kwargs={'pk': self.event_id})
def active(self):
return end_time is not None

View File

@@ -34,16 +34,7 @@
</div>
</li>
{% if perms.RIGS.view_riskassessment %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownHS" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
H&S
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownHS">
<a class="dropdown-item" href="{% url 'hs_list' %}"><span class="fas fa-eye"></span> Overview</a>
<a class="dropdown-item" href="{% url 'ra_list' %}"><span class="fas fa-file-medical"></span> Risk Assessments</a>
<a class="dropdown-item" href="{% url 'ec_list' %}"><span class="fas fa-tasks"></span> Event Checklists</a>
</div>
</li>
<li class="nav-item"><a class="nav-link" href="{% url 'hs_list' %}">H&S</a></li>
{% endif %}
{% if perms.RIGS.view_invoice %}
<li class="nav-item dropdown">

View File

@@ -1,6 +1,8 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% load markdown_tags %}
{% load button from filters %}
{% load static %}
{% block content %}
<div class="row my-3 py-3">
@@ -52,6 +54,43 @@
</div>
</div>
</div>
{% if event.can_check_in %}
<div class="col-sm-12">
<div class="card mt-3">
<div class="card-header">Crew Record</div>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Vehicle</th>
<th scope="col">Start Time</th>
<th scope="col">Role</th>
<th scope="col">End Time</th>
<th scope="col">{% if request.user.pk is event.mic.pk %}<a href="{% url 'event_checkin_override' event.pk %}" class="btn btn-sm btn-success"><span class="fas fa-plus"></span> Add</a>{% endif %}</th>
</tr>
</thead>
<tbody id="crewmembers">
{% for crew in object.crew.all %}
<tr>
<td>{{crew.person}}</td>
<td>{{crew.vehicle|default:"None"}}</td>
<td>{{crew.time}}</td>
<td>{{crew.role}}</td>
<td>{% if crew.end_time %}{{crew.end_time}}{% else %}<span class="text-success fas fa-clock" data-toggle="tooltip" title="This person is currently checked into this event"></span>{% endif %}</td>
<td>{% if crew.end_time %}{% if crew.person.pk == request.user.pk or event.mic.pk == request.user.pk %}{% button 'edit' 'edit_checkin' crew.pk clazz='btn-sm modal-href' %}{% endif %}{%endif%}</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="text-center bg-warning">Apparently this event happened by magic...</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
{% if not request.is_ajax and perms.RIGS.view_event %}
<div class="col-sm-12 text-right">
{% include 'partials/event_detail_buttons.html' %}

View File

@@ -30,21 +30,7 @@
</a>
{% endif %}
</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>
<ul>
{% for i in object.vehicles.all %}
<li>{{i}}</li>
{% endfor %}
</ul>
</div>
</div>
</div>
@@ -82,165 +68,6 @@
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-header">Crew Record</div>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Crewmember</th>
<th scope="col">Start Time</th>
<th scope="col">Role</th>
<th scope="col">End Time</th>
</tr>
</thead>
<tbody id="crewmemberst">
{% for crew in object.crew.all %}
<tr>
<td>{{crew.crewmember}}</td>
<td>{{crew.start}}</td>
<td>{{crew.role}}</td>
<td>{{crew.end}}</td>
</tr>
{% empty %}
<tr>
<td colspan="4" class="text-center bg-warning">Apparently this event happened by magic...</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="card mb-3">
<div class="card-header">Power {% include 'partials/event_size.html' with object=object.event.riskassessment %}</div>
<div class="card-body">
{% if object.event.riskassessment.event_size == 0 %}
<dl class="row">
<dt class="col-10">{{ object|help_text:'rcds'|safe }}</dt>
<dd class="col-2">
{{ object.rcds|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'supply_test'|safe }}</dt>
<dd class="col-2">
{{ object.supply_test|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
<dd class="col-2">
{{ object.earthing|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
<dd class="col-2">
{{ object.pat|yesnoi }}
</dd>
</dl>
{% else %}
<dl class="row">
<dt class="col-10">{{ object|help_text:'source_rcd'|safe }}</dt>
<dd class="col-2">
{{ object.source_rcd|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'labelling'|safe }}</dt>
<dd class="col-2">
{{ object.labelling|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
<dd class="col-2">
{{ object.earthing|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
<dd class="col-2">
{{ object.pat|yesnoi }}
</dd>
</dl>
<hr>
<p>Tests at first distro</p>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" colspan="3" class="text-center">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
<th>{{ object|help_text:'fd_voltage_l1' }}</th>
<th>{{ object|help_text:'fd_voltage_l2' }}</th>
<th>{{ object|help_text:'fd_voltage_l3' }}</th>
</tr>
<tr>
<td>{{ object.fd_voltage_l1 }}</td>
<td>{{ object.fd_voltage_l2 }}</td>
<td>{{ object.fd_voltage_l3 }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_phase_rotation'|safe }}</th>
<td colspan="3">{{ object.fd_phase_rotation|yesnoi }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_earth_fault'|safe}}</th>
<td colspan="3">{{ object.fd_earth_fault }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_pssc'}}</th>
<td colspan="3">{{ object.fd_pssc }}</td>
</tr>
</tbody>
</table>
<hr>
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" class="text-center">Point 1</th>
<th scope="col" class="text-center">Point 2</th>
<th scope="col" class="text-center">Point 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{ object|help_text:'w1_description'|safe}}</th>
<td>{{ object.w1_description }}</td>
<td>{{ object.w2_description|default:'' }}</td>
<td>{{ object.w3_description|default:'' }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_polarity'|safe}}</th>
<td>{{ object.w1_polarity|yesnoi }}</td>
<td>{{ object.w2_polarity|default:''|yesnoi }}</td>
<td>{{ object.w3_polarity|default:''|yesnoi }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_voltage'|safe}}</th>
<td>{{ object.w1_voltage }}</td>
<td>{{ object.w2_voltage|default:'' }}</td>
<td>{{ object.w3_voltage|default:'' }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_earth_fault'|safe}}</th>
<td>{{ object.w1_earth_fault }}</td>
<td>{{ object.w2_earth_fault|default:'' }}</td>
<td>{{ object.w3_earth_fault|default:'' }}</td>
</tr>
</tbody>
</table>
<hr>
<dl class="row">
<dt class="col-10">{{ object|help_text:'all_rcds_tested'|safe }}</dt>
<dd class="col-2">
{{ object.all_rcds_tested|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'public_sockets_tested'|safe }}</dt>
<dd class="col-2">
{{ object.public_sockets_tested|yesnoi }}
</dd>
</dl>
<hr>
{% include 'partials/ec_power_info.html' %}
{% endif %}
</div>
</div>
<div class="col-12 text-right">
{% button 'edit' url='ec_edit' pk=object.pk %}
{% button 'view' url='event_detail' pk=object.pk text="Event" %}

View File

@@ -13,57 +13,19 @@
{% block preload_js %}
{{ block.super }}
<script src="{% static 'js/selects.js' %}"></script>
<script src="{% static 'js/interaction.js' %}"></script>
{% endblock %}
{% block js %}
{{ block.super }}
<script src="{% static 'js/autocompleter.js' %}"></script>
<script src="{% static 'js/tooltip.js' %}"></script>
<script>
$(document).ready(function () {
$('button[data-action=add]').on('click', function (event) {
event.preventDefault();
let target = $($(this).attr('data-target'));
let newID = Number(target.attr('data-pk'));
let newRow = $($(this).attr('data-clone'))
.clone().attr('style', "")
.attr('id', function(i, val){
return val.split("_")[0] + '_' + newID;
})
.appendTo(target);
newRow.find('select,input').attr('name', function(i, val){
return val.split("_")[0] + '_' + newID;
})//Disabled is to prevent the hidden row being sent to the form
.removeAttr('disabled');
newRow.find('button[data-action=delete]').attr('data-id', newID);
newRow.find('select').addClass('selectpicker');
newRow.find('.selectpicker').selectpicker('refresh');
$(".selectpicker").each(function(){initPicker($(this))});
initDatetime();
$(target).attr('data-pk', newID - 1);
});
$(document).on('click', 'button[data-action=delete]', function(event) {
event.preventDefault();
$(this).closest('tr').remove();
});
//Somewhat rudimentary way of ensuring people fill in completely (if it hits the database validation the whole table row disappears when the page reloads...)
//the not is to avoid adding it to some of bootstrap-selects extra crap
$('#vehiclest,#crewmemberst').on('change', 'select,input', function () {
$(this).closest('tr').find("select,input").not(':input[type=search]').attr('required', 'true');
});
});
</script>
{% endblock %}
{% block content %}
<div class="col-12">
{% include 'form_errors.html' %}
{% if edit %}
<form role="form" method="POST" action="{% url 'ec_edit' pk=object.pk %}">
{% else %}
<form role="form" method="POST" action="{% url 'event_ec' pk=event.pk %}">
{% endif %}
<form role="form" method="POST" action="{% if edit %}{% url 'ec_edit' pk=object.pk %}{% else %}{% url 'event_ec' pk=event.pk %}{% endif %}">
<input type="hidden" name="{{ form.event.name }}" id="{{ form.event.id_for_label }}"
value="{{event.pk}}"/>
{% csrf_token %}
@@ -94,7 +56,7 @@
<div class="form-group form-row" id="{{ form.venue.id_for_label }}-group">
<label for="{{ form.venue.id_for_label }}"
class="col-4 col-form-label">{{ form.venue.label }}</label>
<select id="{{ form.venue.id_for_label }}" name="{{ form.venue.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='venue' %}">
<select id="{{ form.venue.id_for_label }}" name="{{ form.venue.name }}" class="selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='venue' %}">
{% if venue %}
<option value="{{venue.pk}}" selected="selected">{{ venue.name }}</option>
{% elif event.venue %}
@@ -102,52 +64,6 @@
{% endif %}
</select>
</div>
<div class="form-group form-row" id="{{ form.power_mic.id_for_label }}-group">
<label for="{{ form.power_mic.id_for_label }}"
class="col-4 col-form-label">{{ form.power_mic.help_text }}</label>
<select id="{{ form.power_mic.id_for_label }}" name="{{ form.power_mic.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" required="true">
{% if power_mic %}
<option value="{{power_mic.pk}}" selected="selected">{{ power_mic.name }}</option>
{% elif event.riskassessment.power_mic %}
<option value="{{event.riskassessment.power_mic.pk}}" selected="selected">{{ event.riskassessment.power_mic.name }}</option>
{% endif %}
</select>
</div>
<p class="pt-3 font-weight-bold">List vehicles and their drivers</p>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Vehicle</th>
<th scope="col">Driver</th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="vehiclest" data-pk="-1">
<tr id="vehicles_new" style="display: none;">
<td><input type="text" class="form-control" name="vehicle_new" disabled="true"/></td>
<td><select data-container="body" class="form-control" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" name="driver_new" disabled="true"></select></td>
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-action='delete' data-target='#vehicle'><span class="fas fa-times"></span></button></td>
</tr>
{% for i in object.vehicles.all %}
<tr id="vehicles_{{i.pk}}">
<td><input name="vehicle_{{i.pk}}" type="text" class="form-control" value="{{ i.vehicle }}"/></td>
<td>
<select data-container="body" name="driver_{{i.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if i.driver != '' %}
<option value="{{i.driver.pk}}" selected="selected">{{ i.driver.name }}</option>
{% endif %}
</select>
</td>
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-id='{{i.pk}}' data-action='delete' data-target='#vehicle'><span class="fas fa-times"></span></button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="text-right">
<button type="button" class="btn btn-secondary" id="vehicle-add" data-action='add' data-target='#vehiclest' data-clone='#vehicles_new'><span class="fas fa-plus"></span> Add Vehicle</button>
</div>
</div>
</div>
</div>
@@ -175,176 +91,6 @@
</div>
</div>
</div>
<div class="row my-3">
<div class="col-12">
<div class="card">
<div class="card-header">Crew Record</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Person</th>
<th scope="col">Start Time</th>
<th scope="col">Role</th>
<th scope="col">End Time</th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="crewmemberst" data-pk="-1">
<tr id="crew_new" style="display: none;">
<td>
<select name="crewmember_new" class="form-control" data-container="body" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" disabled="true"></select>
</td>
<td style="min-width: 15ch"><input name="start_new" type="datetime-local" class="form-control" value="{{ i.start }}" disabled=""/></td>
<td style="min-width: 15ch"><input name="role_new" type="text" class="form-control" value="{{ i.role }}" disabled="true"/></td>
<td style="min-width: 15ch"><input name="end_new" type="datetime-local" class="form-control" value="{{ i.end }}" disabled="true" /></td>
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-id='{{crew.pk}}' data-action='delete' data-target='#crewmember'><span class="fas fa-times"></span></button></td>
</tr>
{% for crew in object.crew.all %}
<tr id="crew_{{crew.pk}}">
<td>
<select data-container="body" name="crewmember_{{crew.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if crew.crewmember != '' %}
<option value="{{crew.crewmember.pk}}" selected="selected">{{ crew.crewmember.name }}</option>
{% endif %}
</select>
</td>
<td><input name="start_{{crew.pk}}" type="datetime-local" class="form-control" value="{{ crew.start|date:'Y-m-d' }}T{{ crew.start|date:'H:i:s' }}"/></td>
<td><input name="role_{{crew.pk}}" type="text" class="form-control" value="{{ crew.role }}"/></td>
<td><input name="end_{{crew.pk}}" type="datetime-local" class="form-control" value="{{ crew.end|date:'Y-m-d' }}T{{ crew.end|date:'H:i:s' }}"/></td>
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-id='{{crew.pk}}' data-action='delete' data-target='#crewmember'><span class="fas fa-times"></span></button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="card-footer">
<div class="text-right">
<button type="button" class="btn btn-secondary" data-action='add' data-target='#crewmemberst' data-clone='#crew_new'><span class="fas fa-plus"></span> Add Crewmember</button>
</div>
</div>
</div>
</div>
</div>
{% if event.riskassessment.event_size == 0 %}
<div class="row my-3" id="size-0">
<div class="col-12">
<div class="card border-success">
<div class="card-header">Electrical Checks <small>for Small TEC Events <6kVA (approx. 26A)</small></div>
<div class="card-body">
{% include 'partials/checklist_checkbox.html' with formitem=form.rcds %}
{% include 'partials/checklist_checkbox.html' with formitem=form.supply_test %}
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
</div>
</div>
</div>
</div>
{% else %}
<div class="row my-3" id="size-1">
<div class="col-12">
{% if event.riskassessment.event_size == 1 %}
<div class="card border-warning">
<div class="card-header">Electrical Checks <small>for Medium TEC Events </small></div>
<div class="card-body">
{% else %}
<div class="card border-danger">
<div class="card-header">Electrical Checks <small>for Large TEC Events</small></div>
<div class="card-body">
<div class="alert alert-danger"><strong>Here be dragons. Ensure you have appeased the Power Gods before continuing... (If you didn't check with a Supervisor, <em>you cannot continue your event!</em>)</strong></div>
{% endif %}
{% include 'partials/checklist_checkbox.html' with formitem=form.source_rcd %}
{% include 'partials/checklist_checkbox.html' with formitem=form.labelling %}
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
<hr>
<p>Tests at first distro</p>
<div class="table-responsive">
<table class="table table-bordered table-sm">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" colspan="3" class="text-center">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
<th class="text-center">{{ form.fd_voltage_l1.help_text }}</th>
<th class="text-center">{{ form.fd_voltage_l2.help_text }}</th>
<th class="text-center">{{ form.fd_voltage_l3.help_text }}</th>
</tr>
<tr>
<td>{% render_field form.fd_voltage_l1 class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.fd_voltage_l2 class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.fd_voltage_l3 class+="form-control" style="min-width: 5rem;" %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_phase_rotation.help_text|safe}}</th>
<td colspan="3">{% include 'partials/checklist_checkbox.html' with formitem=form.fd_phase_rotation %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_earth_fault.help_text|safe}}</th>
<td colspan="3">{% render_field form.fd_earth_fault class+="form-control" %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_pssc.help_text|safe}}</th>
<td colspan="3">{% render_field form.fd_pssc class+="form-control" %}</td>
</tr>
</tbody>
</table>
</div>
<hr>
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" class="text-center">Point 1</th>
<th scope="col" class="text-center">Point 2</th>
<th scope="col" class="text-center">Point 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{form.w1_description.help_text|safe}}</th>
<td>{% render_field form.w1_description class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.w2_description class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.w3_description class+="form-control" style="min-width: 5rem;" %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_polarity.help_text|safe}}</th>
<td>{% render_field form.w1_polarity %}</td>
<td>{% render_field form.w2_polarity %}</td>
<td>{% render_field form.w3_polarity %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_voltage.help_text|safe}}</th>
<td>{% render_field form.w1_voltage class+="form-control" %}</td>
<td>{% render_field form.w2_voltage class+="form-control" %}</td>
<td>{% render_field form.w3_voltage class+="form-control" %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_earth_fault.help_text|safe}}</th>
<td>{% render_field form.w1_earth_fault class+="form-control" %}</td>
<td>{% render_field form.w2_earth_fault class+="form-control" %}</td>
<td>{% render_field form.w3_earth_fault class+="form-control" %}</td>
</tr>
</tbody>
</table>
</div>
<hr/>
{% include 'partials/checklist_checkbox.html' with formitem=form.all_rcds_tested %}
{% include 'partials/checklist_checkbox.html' with formitem=form.public_sockets_tested %}
{% include 'partials/ec_power_info.html' %}
</div>
</div>
</div>
</div>
{% endif %}
<div class="row mt-3">
<div class="col-sm-12 text-right">
{% button 'submit' %}

View File

@@ -0,0 +1,105 @@
{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %}
{% load widget_tweaks %}
{% load static %}
{% load button from filters %}
{% block css %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static 'css/selects.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'css/easymde.min.css' %}">
{% endblock %}
{% block preload_js %}
{{ block.super }}
<script src="{% static 'js/selects.js' %}"></script>
<script src="{% static 'js/easymde.min.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="col-12">
{% include 'form_errors.html' %}
<form id="checkin" role="form" method="POST" action="{{ form.action|default:request.path }}">
<input type="hidden" name="{{ form.event.name }}" id="{{ form.event.id_for_label }}"
value="{{event.pk}}"/>
{% if not request.is_ajax and self.request.user.pk is form.event.mic.pk %}
<div class="form-group">
<label for="{{ form.person.id_for_label }}"
class="col-sm-4 col-form-label">{{ form.person.label }}</label>
<div class="col-sm-8">
<select id="{{ form.person.id_for_label }}" name="{{ form.person.name }}" class="px-0 selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if person %}
<option value="{{form.person.value}}" selected="selected" >{{ person.name }}</option>
{% endif %}
</select>
</div>
</div>
{% else %}
<input type="hidden" name="{{ form.person.name }}" id="{{ form.person.id_for_label }}"
value="{{request.user.pk}}"/>
{% endif %}
{% csrf_token %}
<div class="form-group">
<label for="{{ form.time.id_for_label }}"
class="col-sm-4 col-form-label">Start Time</label>
<div class="col-sm-8">
{% render_field form.time class+="form-control" %}
</div>
</div>
<div class="form-group">
<label for="{{ form.role.id_for_label }}" class="col col-form-label">Role</label>
<div class="row pl-3">
<div class="col-md-6 col-sm-12">
<button type="button" class="btn btn-primary" onclick="document.getElementById('id_role').value='MIC'">MIC</button>
<button type="button" class="btn btn-danger" onclick="document.getElementById('id_role').value='Power MIC'">Power MIC</button>
<button type="button" class="btn btn-info" onclick="document.getElementById('id_role').value='Crew'">Crew</button>
</div>
<div class="col-md-6 mt-2">
{% render_field form.role class+="form-control" placeholder="Other (enter text)" %}
</div>
</div>
</div>
<div class="form-group">
<label for="{{ form.vehicle.id_for_label }}" class="col col-form-label">Vehicle (if applicable)</label>
<div class="row pl-3">
<div class="col-md-6 col-sm-12">
<button type="button" class="btn btn-primary" onclick="document.getElementById('id_vehicle').value='Virgil'"><span class="fas fa-truck-moving"></span> Virgil</button>
<button type="button" class="btn btn-secondary" onclick="document.getElementById('id_vehicle').value='Virgil + Erms'"><span class="fas fa-trailer"></span><span class="fas fa-truck-moving"></span> Virgil + Erms</button>
</div>
<div class="col-md-6 mt-2">
{% render_field form.vehicle class+="form-control" placeholder="Other (enter text)" %}
</div>
</div>
</div>
{% if edit or manual %}
<div class="form-group">
<label for="{{ form.end_time.id_for_label }}"
class="col-sm-4 col-form-label">End Time</label>
<div class="col-sm-8">
{% render_field form.end_time class+="form-control" %}
</div>
</div>
{% endif %}
{% if not request.is_ajax %}
<div class="row mt-3">
<div class="col-sm-12 text-right">
{% button 'submit' %}
</div>
</div>
{% endif %}
</form>
</div>
{% endblock %}
{% block footer %}
<div class="col-sm-12 text-right pr-0">
<button type="submit" class="btn btn-primary" title="Save" form="checkin"
><span class="fas fa-save align-middle"></span> <span class="d-none d-sm-inline align-middle">Save</span></button>
</div>
{% endblock %}

View File

@@ -12,6 +12,7 @@
<th scope="col">Dates</th>
<th scope="col">RA</th>
<th scope="col">Checklists</th>
<th scope="col">Power Records</th>
</tr>
</thead>
<tbody>
@@ -35,6 +36,14 @@
<a href="{% url 'event_ec' event.pk %}" class="btn btn-info"><span class="fas fa-paperclip"></span> <span
class="d-none d-sm-inline">Create</span></a>
</td>
<td>
{% for record in event.power_tests.all %}
{% include 'partials/hs_status.html' with event=event object=record view='pt_detail' edit='pt_edit' create='event_pt' review='pt_review' perm=perms.RIGS.review_power %}
<br/>
{% endfor %}
<a href="{% url 'event_pt' event.pk %}" class="btn btn-info"><span class="fas fa-paperclip"></span> <span
class="hidden-xs">Create</span></a>
</td>
</tr>
{% empty %}
<tr class="bg-warning text-dark">

View File

@@ -1,59 +0,0 @@
{% extends 'base_rigs.html' %}
{% load paginator from filters %}
{% load help_text from filters %}
{% load verbose_name from filters %}
{% load get_field from filters %}
{% block title %}{{ title }} List{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<h2>{{title}} List</h2>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="table-responsive">
<table class="table mb-0 table-sm">
<thead>
<tr>
<th scope="col">Event</th>
{# mmm hax #}
{% if object_list.0 != None %}
{% for field in object_list.0.fieldz %}
<th scope="col">{{ object_list.0|verbose_name:field|title }}</th>
{% endfor %}
{% endif %}
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for object in object_list %}
<tr class="{% if object.reviewed_by %}table-success{%endif%}">
{# General #}
<th scope="row"><a href="{% url 'event_detail' object.event.pk %}">{{ object.event }}</a><br><small>{{ object.event.get_status_display }}</small></th>
{% for field in object_list.0.fieldz %}
<td>{{ object|get_field:field }}</td>
{% endfor %}
{# Buttons #}
<td>
{% include 'partials/hs_status.html' %}
</td>
</tr>
{% empty %}
<tr class="bg-warning">
<td colspan="6">Nothing found</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% if is_paginated %}
<div class="row justify-content-center">
{% paginator %}
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,175 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% load help_text from filters %}
{% load profile_by_index from filters %}
{% load yesnoi from filters %}
{% load button from filters %}
{% block content %}
<div class="row">
<div class="col-12 text-right my-3">
{% button 'edit' url='pt_edit' pk=object.pk %}
{% button 'view' url='event_detail' pk=object.event.pk text="Event" %}
{% include 'partials/review_status.html' with perm=perms.RIGS.review_power review='pt_review' %}
</div>
</div>
<div class="card mb-3">
<div class="card-header">{% include 'partials/event_size.html' with object=object.event.riskassessment %}</div>
<div class="card-body">
<dl class="row">
<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>
<dt class="col-6">Venue</dt>
<dd class="col-6">
{% if object.venue %}
<a href="{% url 'venue_detail' object.venue.pk %}" class="modal-href">
{{ object.venue }}
</a>
{% endif %}
</dd>
<dt class="col-6">Notes</dt>
<dd class="col-6">
{{ object.notes }}
</dd>
</dl>
{% if object.event.riskassessment.event_size == 0 %}
<dl class="row">
<dt class="col-10">{{ object|help_text:'rcds'|safe }}</dt>
<dd class="col-2">
{{ object.rcds|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'supply_test'|safe }}</dt>
<dd class="col-2">
{{ object.supply_test|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
<dd class="col-2">
{{ object.earthing|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
<dd class="col-2">
{{ object.pat|yesnoi }}
</dd>
</dl>
{% else %}
<dl class="row">
<dt class="col-10">{{ object|help_text:'source_rcd'|safe }}</dt>
<dd class="col-2">
{{ object.source_rcd|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'labelling'|safe }}</dt>
<dd class="col-2">
{{ object.labelling|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
<dd class="col-2">
{{ object.earthing|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
<dd class="col-2">
{{ object.pat|yesnoi }}
</dd>
</dl>
<hr>
<p>Tests at first distro</p>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" colspan="3" class="text-center">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
<th>{{ object|help_text:'fd_voltage_l1' }}</th>
<th>{{ object|help_text:'fd_voltage_l2' }}</th>
<th>{{ object|help_text:'fd_voltage_l3' }}</th>
</tr>
<tr>
<td>{{ object.fd_voltage_l1 }}</td>
<td>{{ object.fd_voltage_l2 }}</td>
<td>{{ object.fd_voltage_l3 }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_phase_rotation'|safe }}</th>
<td colspan="3">{{ object.fd_phase_rotation|yesnoi }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_earth_fault'|safe}}</th>
<td colspan="3">{{ object.fd_earth_fault }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_pssc'}}</th>
<td colspan="3">{{ object.fd_pssc }}</td>
</tr>
</tbody>
</table>
<hr>
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" class="text-center">Point 1</th>
<th scope="col" class="text-center">Point 2</th>
<th scope="col" class="text-center">Point 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{ object|help_text:'w1_description'|safe}}</th>
<td>{{ object.w1_description }}</td>
<td>{{ object.w2_description|default:'' }}</td>
<td>{{ object.w3_description|default:'' }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_polarity'|safe}}</th>
<td>{{ object.w1_polarity|yesnoi }}</td>
<td>{{ object.w2_polarity|default:''|yesnoi }}</td>
<td>{{ object.w3_polarity|default:''|yesnoi }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_voltage'|safe}}</th>
<td>{{ object.w1_voltage }}</td>
<td>{{ object.w2_voltage|default:'' }}</td>
<td>{{ object.w3_voltage|default:'' }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_earth_fault'|safe}}</th>
<td>{{ object.w1_earth_fault }}</td>
<td>{{ object.w2_earth_fault|default:'' }}</td>
<td>{{ object.w3_earth_fault|default:'' }}</td>
</tr>
</tbody>
</table>
<hr>
<dl class="row">
<dt class="col-10">{{ object|help_text:'all_rcds_tested'|safe }}</dt>
<dd class="col-2">
{{ object.all_rcds_tested|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'public_sockets_tested'|safe }}</dt>
<dd class="col-2">
{{ object.public_sockets_tested|yesnoi }}
</dd>
</dl>
<hr>
{% include 'partials/ec_power_info.html' %}
{% endif %}
</div>
</div>
<div class="col-12 text-right">
{% button 'edit' url='ec_edit' pk=object.pk %}
{% button 'view' url='event_detail' pk=object.pk text="Event" %}
{% include 'partials/review_status.html' with perm=perms.RIGS.review_eventchecklist review='ec_review' %}
</div>
<div class="col-12 text-right">
{% include 'partials/last_edited.html' with target="eventchecklist_history" %}
</div>
{% endblock %}

View File

@@ -0,0 +1,203 @@
{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %}
{% load widget_tweaks %}
{% load static %}
{% load help_text from filters %}
{% load profile_by_index from filters %}
{% load button from filters %}
{% block css %}
{{ block.super }}
<link rel="stylesheet" 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 %}
<div class="col-12">
{% include 'form_errors.html' %}
{% if edit %}
<form role="form" method="POST" action="{% url 'pt_edit' pk=object.pk %}">
{% else %}
<form role="form" method="POST" action="{% url 'event_pt' pk=event.pk %}">
{% endif %}
<input type="hidden" name="{{ form.event.name }}" id="{{ form.event.id_for_label }}"
value="{{event.pk}}"/>
{% csrf_token %}
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">Event Information</div>
<div class="card-body">
<dl class="row">
<dt class="col-4">Event Date</dt>
<dd class="col-8">{{ event.start_date}}{%if event.end_date %}-{{ event.end_date}}{%endif%}</dd>
<dt class="col-4">Event Name</dt>
<dd class="col-8">{{ event.name }}</dd>
<dt class="col-4">Client</dt>
<dd class="col-8">{{ event.person }}</dd>
<dt class="col-4">Event Size</dt>
<dd class="col-8">{% include 'partials/event_size.html' with object=event.riskassessment %}</dd>
</dl>
<hr>
<div class="form-group form-row" id="{{ form.power_mic.id_for_label }}-group">
<label for="{{ form.power_mic.id_for_label }}"
class="col-4 col-form-label">{{ form.power_mic.help_text }}</label>
<select id="{{ form.power_mic.id_for_label }}" name="{{ form.power_mic.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" required="true">
{% if power_mic %}
<option value="{{power_mic.pk}}" selected="selected">{{ power_mic.name }}</option>
{% elif event.riskassessment.power_mic %}
<option value="{{event.riskassessment.power_mic.pk}}" selected="selected">{{ event.riskassessment.power_mic.name }}</option>
{% endif %}
</select>
</div>
<div class="form-group form-row" id="{{ form.venue.id_for_label }}-group">
<label for="{{ form.venue.id_for_label }}"
class="col-4 col-form-label">{{ form.venue.label }}</label>
<select id="{{ form.venue.id_for_label }}" name="{{ form.venue.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='venue' %}">
{% if venue %}
<option value="{{venue.pk}}" selected="selected">{{ venue.name }}</option>
{% elif event.venue %}
<option value="{{event.venue.pk}}" selected="selected">{{ event.venue.name }}</option>
{% endif %}
</select>
</div>
<label for="{{ form.notes.id_for_label }}">Notes</label>
{% render_field form.notes class+="form-control" %}
</div>
</div>
</div>
</div>
{% if event.riskassessment.event_size == 0 %}
<div class="row my-3" id="size-0">
<div class="col-12">
<div class="card border-success">
<div class="card-header">Electrical Checks <small>for Small TEC Events <6kVA (approx. 26A)</small></div>
<div class="card-body">
{% include 'partials/checklist_checkbox.html' with formitem=form.rcds %}
{% include 'partials/checklist_checkbox.html' with formitem=form.supply_test %}
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
</div>
</div>
</div>
</div>
{% else %}
<div class="row my-3" id="size-1">
<div class="col-12">
{% if event.riskassessment.event_size == 1 %}
<div class="card border-warning">
<div class="card-header">Electrical Checks <small>for Medium TEC Events </small></div>
<div class="card-body">
{% else %}
<div class="card border-danger">
<div class="card-header">Electrical Checks <small>for Large TEC Events</small></div>
<div class="card-body">
<div class="alert alert-danger"><strong>Here be dragons. Ensure you have appeased the Power Gods before continuing... (If you didn't check with a Supervisor, <em>you cannot continue your event!</em>)</strong></div>
{% endif %}
{% include 'partials/checklist_checkbox.html' with formitem=form.source_rcd %}
{% include 'partials/checklist_checkbox.html' with formitem=form.labelling %}
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
<hr>
<p>Tests at first distro</p>
<div class="table-responsive">
<table class="table table-bordered table-sm">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" colspan="3" class="text-center">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
<th class="text-center">{{ form.fd_voltage_l1.help_text }}</th>
<th class="text-center">{{ form.fd_voltage_l2.help_text }}</th>
<th class="text-center">{{ form.fd_voltage_l3.help_text }}</th>
</tr>
<tr>
<td>{% render_field form.fd_voltage_l1 class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.fd_voltage_l2 class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.fd_voltage_l3 class+="form-control" style="min-width: 5rem;" %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_phase_rotation.help_text|safe}}</th>
<td colspan="3">{% include 'partials/checklist_checkbox.html' with formitem=form.fd_phase_rotation %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_earth_fault.help_text|safe}}</th>
<td colspan="3">{% render_field form.fd_earth_fault class+="form-control" %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_pssc.help_text|safe}}</th>
<td colspan="3">{% render_field form.fd_pssc class+="form-control" %}</td>
</tr>
</tbody>
</table>
</div>
<hr>
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" class="text-center">Point 1</th>
<th scope="col" class="text-center">Point 2</th>
<th scope="col" class="text-center">Point 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{form.w1_description.help_text|safe}}</th>
<td>{% render_field form.w1_description class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.w2_description class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.w3_description class+="form-control" style="min-width: 5rem;" %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_polarity.help_text|safe}}</th>
<td>{% render_field form.w1_polarity %}</td>
<td>{% render_field form.w2_polarity %}</td>
<td>{% render_field form.w3_polarity %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_voltage.help_text|safe}}</th>
<td>{% render_field form.w1_voltage class+="form-control" %}</td>
<td>{% render_field form.w2_voltage class+="form-control" %}</td>
<td>{% render_field form.w3_voltage class+="form-control" %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_earth_fault.help_text|safe}}</th>
<td>{% render_field form.w1_earth_fault class+="form-control" %}</td>
<td>{% render_field form.w2_earth_fault class+="form-control" %}</td>
<td>{% render_field form.w3_earth_fault class+="form-control" %}</td>
</tr>
</tbody>
</table>
</div>
<hr/>
{% include 'partials/checklist_checkbox.html' with formitem=form.all_rcds_tested %}
{% include 'partials/checklist_checkbox.html' with formitem=form.public_sockets_tested %}
{% include 'partials/ec_power_info.html' %}
</div>
</div>
</div>
</div>
{% endif %}
<div class="row mt-3">
<div class="col-sm-12 text-right">
{% button 'submit' %}
</div>
</div>
</form>
</div>
{% endblock %}

View File

@@ -47,6 +47,6 @@
</tr>
</tbody>
</table>
<p><strong>Voltage Drop on Circuit:</strong> 5% (approx. 12v)</p>
<p><strong>Voltage Drop on Circuit:</strong> &#8804;5% (approx. 12v)</p>
</div>
</div>

View File

@@ -49,5 +49,13 @@
{% endif %}
<a href="https://docs.google.com/forms/d/e/1FAIpQLSf-TBOuJZCTYc2L8DWdAaC3_Werq0ulsUs8-6G85I6pA9WVsg/viewform" class="btn btn-danger"><span class="fas fa-file-invoice-dollar"></span> <span class="d-none d-sm-inline">Subhire Insurance Form</span></a>
{% if event.can_check_in %}
{% if request.user.current_event %}
<a href="{% url 'event_checkout' %}" class="btn btn-warning">Check Out</a>
{% else %}
<a href="{% url 'event_checkin' event.pk %}" class="btn btn-success modal-href"><span class="fas fa-user-clock"></span> <span class="d-none d-sm-inline">Check In</span></a>
{% endif %}
{% endif %}
{% endif %}
</div>

View File

@@ -15,19 +15,21 @@
{% endif %}
{% endif %}
{% if not event.dry_hire %}
{% if event.riskassessment %}
{% if event.has_checklist %}
<a href="{{ event.riskassessment.get_absolute_url }}"><span class="badge badge-success">RA: <span class="fas fa-check{% if event.riskassessment.reviewed_by %}-double{%endif%}"></span></a>
{% else %}
<span class="badge badge-danger">RA: <span class="fas fa-times"></span></span>
{% endif %}
{% endif %}
{% if not event.dry_hire %}
{% if event.hs_done %}
{# TODO Display status of all checklists #}
<span class="badge badge-success">Checklist: <span class="fas fa-check"></span></span>
{% if event.has_checklist %}
<span class="badge badge-success">Checklist: <span class="fas fa-check"></span> {% if event.checklists.count > 1 %}({{event.checklists.count}}){% endif %}</span>
{% else %}
<span class="badge badge-danger">Checklist: <span class="fas fa-times"></span></span>
{% endif %}
{% if event.has_power %}
<span class="badge badge-success">Power Record: <span class="fas fa-check"></span> {% if event.power_tests.count > 1 %}({{event.power_tests.count}}){% endif %}</span>
{% else %}
<span class="badge badge-danger">Power Record: <span class="fas fa-times"></span></span>
{% endif %}
{% endif %}
{% if perms.RIGS.view_invoice %}
{% if event.invoice %}

View File

@@ -10,10 +10,18 @@
<hr>
<h5>Event Checklists:</h5>
{% for checklist in event.checklists.all %}
{% include 'partials/hs_status.html' with event=event object=checklist view='ec_detail' edit='ec_edit' create='event_ec' review='ec_review' perm=perms.RIGS.review_eventchecklist %}
<br/>
{% endfor %}
<a href="{% url 'event_ec' event.pk %}" class="btn btn-info mt-2"><span class="fas fa-paperclip"></span> <span
class="hidden-xs">Create</span></a>
{% include 'partials/hs_status.html' with event=event object=checklist view='ec_detail' edit='ec_edit' create='event_ec' review='ec_review' perm=perms.RIGS.review_eventchecklist %}
<br/>
{% endfor %}
<a href="{% url 'event_ec' event.pk %}" class="btn btn-info mt-2"><span class="fas fa-paperclip"></span> <span
class="hidden-xs">Create</span></a>
<hr>
<h5>Power Test Records:</h5>
{% for record in event.power_tests.all %}
{% include 'partials/hs_status.html' with event=event object=record view='pt_detail' edit='pt_edit' create='event_pt' review='pt_review' perm=perms.RIGS.review_power %}
<br/>
{% endfor %}
<a href="{% url 'event_pt' event.pk %}" class="btn btn-info mt-2"><span class="fas fa-paperclip"></span> <span
class="hidden-xs">Create</span></a>
</div>
</div>

View File

@@ -43,15 +43,22 @@ def venue(db):
@pytest.fixture # TODO parameterise with Event sizes
def checklist(basic_event, venue, admin_user, ra):
checklist = models.EventChecklist.objects.create(event=basic_event, power_mic=admin_user, safe_parking=False,
checklist = models.EventChecklist.objects.create(event=basic_event, safe_parking=False,
safe_packing=False, exits=False, trip_hazard=False, warning_signs=False,
ear_plugs=False, hs_location="Locked away safely",
extinguishers_location="Somewhere, I forgot", earthing=False, pat=False,
extinguishers_location="Somewhere, I forgot",
date=timezone.now(), venue=venue)
yield checklist
checklist.delete()
@pytest.fixture
def power_test(basic_event, venue, admin_user, ra):
power_test = models.PowerTestRecord.objects.create(event=basic_event, venue=venue)
yield power_test
power_test.delete()
@pytest.fixture
def many_events(db, admin_user, scope="class"):
many_events = {

View File

@@ -230,11 +230,6 @@ class CreateEventChecklist(FormPage):
URL_TEMPLATE = 'event/{event_id}/checklist'
_submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]")
_power_mic_selector = (By.XPATH, "//div[select[@id='id_power_mic']]")
_add_vehicle_locator = (By.XPATH, "//button[contains(., 'Vehicle')]")
_add_crew_locator = (By.XPATH, "//button[contains(., 'Crew')]")
_vehicle_row_locator = ('xpath', "//tr[@id[starts-with(., 'vehicle') and not(contains(.,'new'))]]")
_crew_row_locator = ('xpath', "//tr[@id[starts-with(., 'crew') and not(contains(.,'new'))]]")
form_items = {
'safe_parking': (regions.CheckBox, (By.ID, 'id_safe_parking')),
@@ -245,6 +240,20 @@ class CreateEventChecklist(FormPage):
'ear_plugs': (regions.CheckBox, (By.ID, 'id_ear_plugs')),
'hs_location': (regions.TextBox, (By.ID, 'id_hs_location')),
'extinguishers_location': (regions.TextBox, (By.ID, 'id_extinguishers_location')),
}
@property
def success(self):
return '{event_id}' not in self.driver.current_url
class CreatePowerTestRecord(FormPage):
URL_TEMPLATE = 'event/{event_id}/power'
_submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]")
_power_mic_selector = (By.XPATH, "//div[select[@id='id_power_mic']]")
form_items = {
'rcds': (regions.CheckBox, (By.ID, 'id_rcds')),
'supply_test': (regions.CheckBox, (By.ID, 'id_supply_test')),
'earthing': (regions.CheckBox, (By.ID, 'id_earthing')),
@@ -263,58 +272,10 @@ class CreateEventChecklist(FormPage):
'w1_earth_fault': (regions.TextBox, (By.ID, 'id_w1_earth_fault')),
}
def add_vehicle(self):
self.find_element(*self._add_vehicle_locator).click()
def add_crew(self):
self.find_element(*self._add_crew_locator).click()
@property
def power_mic(self):
return regions.BootstrapSelectElement(self, self.find_element(*self._power_mic_selector))
@property
def vehicles(self):
return [self.VehicleRow(self, el) for el in self.find_elements(*self._vehicle_row_locator)]
class VehicleRow(Region):
_name_locator = ('xpath', ".//input")
_select_locator = ('xpath', ".//div[contains(@class,'bootstrap-select')]/..")
@property
def name(self):
return regions.TextBox(self, self.root.find_element(*self._name_locator))
@property
def vehicle(self):
return regions.BootstrapSelectElement(self, self.root.find_element(*self._select_locator))
@property
def crew(self):
return [self.CrewRow(self, el) for el in self.find_elements(*self._crew_row_locator)]
class CrewRow(Region):
_select_locator = ('xpath', ".//div[contains(@class,'bootstrap-select')]/..")
_start_time_locator = ('xpath', ".//input[@name[starts-with(., 'start') and not(contains(.,'new'))]]")
_end_time_locator = ('xpath', ".//input[@name[starts-with(., 'end') and not(contains(.,'new'))]]")
_role_locator = ('xpath', ".//input[@name[starts-with(., 'role') and not(contains(.,'new'))]]")
@property
def crewmember(self):
return regions.BootstrapSelectElement(self, self.root.find_element(*self._select_locator))
@property
def start_time(self):
return regions.DateTimePicker(self, self.root.find_element(*self._start_time_locator))
@property
def end_time(self):
return regions.DateTimePicker(self, self.root.find_element(*self._end_time_locator))
@property
def role(self):
return regions.TextBox(self, self.root.find_element(*self._role_locator))
@property
def success(self):
return '{event_id}' not in self.driver.current_url

View File

@@ -676,14 +676,6 @@ def small_ec(page, admin_user):
page.ear_plugs = True
page.hs_location = "The Moon"
page.extinguishers_location = "With the rest of the fire"
# If we do this first the search fails, for ... reasons
page.power_mic.search(admin_user.name)
page.power_mic.toggle()
assert not page.power_mic.is_open
page.earthing = True
page.rcds = True
page.supply_test = True
page.pat = True
def test_ec_create_small(logged_in_browser, live_server, admin_user, ra):
@@ -704,14 +696,15 @@ def test_ec_create_medium(logged_in_browser, live_server, admin_user, medium_ra)
page.ear_plugs = True
page.hs_location = "Death Valley"
page.extinguishers_location = "With the rest of the fire"
# If we do this first the search fails, for ... reasons
page.power_mic.search(admin_user.name)
page.power_mic.toggle()
assert not page.power_mic.is_open
# Gotta scroll to make the button clickable
logged_in_browser.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
page.submit()
assert page.success
def test_power_checklist(logged_in_browser, live_server, admin_user, power_test, medium_ra):
page = pages.CreatePowerTestRecord(logged_in_browser.driver, live_server.url, event_id=medium_ra.event.pk).open()
page.earthing = True
page.pat = True
page.source_rcd = True
@@ -726,56 +719,15 @@ def test_ec_create_medium(logged_in_browser, live_server, admin_user, medium_ra)
page.w1_polarity = True
page.w1_voltage = 240
page.w1_earth_fault = "0.42"
# If we do this first the search fails, for ... reasons
page.power_mic.search(admin_user.name)
page.power_mic.toggle()
assert not page.power_mic.is_open
page.submit()
assert page.success
def test_ec_create_vehicle(logged_in_browser, live_server, admin_user, checklist):
page = pages.EditEventChecklist(logged_in_browser.driver, live_server.url, pk=checklist.pk).open()
small_ec(page, admin_user)
page.add_vehicle()
assert len(page.vehicles) == 1
vehicle_name = 'Brian'
page.vehicles[0].name.set_value(vehicle_name)
# Appears we're moving too fast for javascript...
t.sleep(1)
page.vehicles[0].vehicle.search(admin_user.first_name)
t.sleep(1)
page.submit()
assert page.success
# Check data is correct
checklist.refresh_from_db()
vehicle = models.EventChecklistVehicle.objects.get(checklist=checklist.pk)
assert vehicle_name == vehicle.vehicle
# TODO Test validation of end before start
def test_ec_create_crew(logged_in_browser, live_server, admin_user, checklist):
page = pages.EditEventChecklist(logged_in_browser.driver, live_server.url, pk=checklist.pk).open()
small_ec(page, admin_user)
page.add_crew()
assert len(page.crew) == 1
role = "MIC"
start_time = timezone.make_aware(datetime.datetime(2015, 1, 1, 9, 0))
end_time = timezone.make_aware(datetime.datetime(2015, 1, 1, 10, 30))
crew = page.crew[0]
t.sleep(2)
crew.crewmember.search(admin_user.first_name)
t.sleep(2)
crew.role.set_value(role)
crew.start_time.set_value(start_time)
crew.end_time.set_value(end_time)
page.submit()
assert page.success
# Check data is correct
crew_obj = models.EventChecklistCrew.objects.get(checklist=checklist.pk)
assert admin_user.pk == crew_obj.crewmember.pk
assert role == crew_obj.role
assert start_time == crew_obj.start
assert end_time == crew_obj.end
# TODO Can I loop through all the boolean fields and test them at once?
def test_ra_creation(logged_in_browser, live_server, admin_user, basic_event):
page = pages.CreateRiskAssessment(logged_in_browser.driver, live_server.url, event_id=basic_event.pk).open()

View File

@@ -79,10 +79,8 @@ urlpatterns = [
name='ra_detail'),
path('event/ra/<int:pk>/edit/', permission_required_with_403('RIGS.change_riskassessment')(views.EventRiskAssessmentEdit.as_view()),
name='ra_edit'),
path('event/ra/list', permission_required_with_403('RIGS.view_riskassessment')(views.EventRiskAssessmentList.as_view()),
name='ra_list'),
path('event/ra/<int:pk>/review/', permission_required_with_403('RIGS.review_riskassessment')(views.EventRiskAssessmentReview.as_view()),
name='ra_review'),
path('event/ra/<int:pk>/review/', permission_required_with_403('RIGS.review_riskassessment')(views.MarkReviewed.as_view()),
name='ra_review', kwargs={'model': 'RiskAssessment'}),
path('event/ra/<int:pk>/print/', permission_required_with_403('RIGS.view_riskassessment')(views.RAPrint.as_view()), name='ra_print'),
path('event/<int:pk>/checklist/', permission_required_with_403('RIGS.add_eventchecklist')(views.EventChecklistCreate.as_view()),
@@ -91,10 +89,26 @@ urlpatterns = [
name='ec_detail'),
path('event/checklist/<int:pk>/edit/', permission_required_with_403('RIGS.change_eventchecklist')(views.EventChecklistEdit.as_view()),
name='ec_edit'),
path('event/checklist/list', permission_required_with_403('RIGS.view_eventchecklist')(views.EventChecklistList.as_view()),
name='ec_list'),
path('event/checklist/<int:pk>/review/', permission_required_with_403('RIGS.review_eventchecklist')(views.EventChecklistReview.as_view()),
name='ec_review'),
path('event/checklist/<int:pk>/review/', permission_required_with_403('RIGS.review_eventchecklist')(views.MarkReviewed.as_view()),
name='ec_review', kwargs={'model': 'EventChecklist'}),
path('event/<int:pk>/power/', permission_required_with_403('RIGS.add_powertestrecord')(views.PowerTestCreate.as_view()),
name='event_pt'),
path('event/power/<int:pk>/', login_required(views.PowerTestDetail.as_view()),
name='pt_detail'),
path('event/power/<int:pk>/edit/', permission_required_with_403('RIGS.change_powertestrecord')(views.PowerTestEdit.as_view()),
name='pt_edit'),
path('event/power/<int:pk>/review/', permission_required_with_403('RIGS.review_power')(views.MarkReviewed.as_view()),
name='pt_review', kwargs={'model': 'PowerTestRecord'}),
path('event/<int:pk>/checkin/', login_required(views.EventCheckIn.as_view()),
name='event_checkin'),
path('event/checkout/', login_required(views.EventCheckOut.as_view()),
name='event_checkout'),
path('event/<int:pk>/checkin/edit/', login_required(views.EventCheckInEdit.as_view()),
name='edit_checkin'),
path('event/<int:pk>/checkin/add/', login_required(views.EventCheckInOverride.as_view()),
name='event_checkin_override'),
# Finance
path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceIndex.as_view()),

View File

@@ -1,16 +1,46 @@
from django.apps import apps
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
from django.urls import reverse
from django.utils import timezone
from django.views import generic
from reversion import revisions as reversion
from RIGS import models, forms
from RIGS.views.rigboard import get_related
from PyRIGS.views import PrintView
from PyRIGS.views import PrintView, ModalURLMixin
from django.shortcuts import redirect
class EventRiskAssessmentCreate(generic.CreateView):
class HSCreateView(generic.CreateView):
def get_form(self, **kwargs):
form = super().get_form(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
form.instance.event = event
return form
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
context['event'] = event
context['page_title'] = f'Create {self.model.__name__} for Event {event.display_id}'
return context
class MarkReviewed(generic.View):
def get(self, *args, **kwargs):
obj = apps.get_model('RIGS', kwargs.get('model')).objects.get(pk=kwargs.get('pk'))
with reversion.create_revision():
reversion.set_user(self.request.user)
obj.reviewed_by = self.request.user
obj.reviewed_at = timezone.now()
obj.save()
return HttpResponseRedirect(reverse('hs_list'))
class EventRiskAssessmentCreate(HSCreateView):
model = models.RiskAssessment
template_name = 'hs/risk_assessment_form.html'
form_class = forms.EventRiskAssessmentForm
@@ -23,28 +53,12 @@ class EventRiskAssessmentCreate(generic.CreateView):
ra = models.RiskAssessment.objects.filter(event=event).first()
if ra is not None:
return HttpResponseRedirect(reverse_lazy('ra_edit', kwargs={'pk': ra.pk}))
return HttpResponseRedirect(reverse('ra_edit', kwargs={'pk': ra.pk}))
return super(EventRiskAssessmentCreate, self).get(self)
def get_form(self, **kwargs):
form = super(EventRiskAssessmentCreate, self).get_form(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
form.instance.event = event
return form
def get_context_data(self, **kwargs):
context = super(EventRiskAssessmentCreate, self).get_context_data(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
context['event'] = event
context['page_title'] = f'Create Risk Assessment for Event {event.display_id}'
get_related(context['form'], context)
return context
def get_success_url(self):
return reverse_lazy('ra_detail', kwargs={'pk': self.object.pk})
return reverse('ra_detail', kwargs={'pk': self.object.pk})
class EventRiskAssessmentEdit(generic.UpdateView):
@@ -57,7 +71,7 @@ class EventRiskAssessmentEdit(generic.UpdateView):
ra.reviewed_by = None
ra.reviewed_at = None
ra.save()
return reverse_lazy('ra_detail', kwargs={'pk': self.object.pk})
return reverse('ra_detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super(EventRiskAssessmentEdit, self).get_context_data(**kwargs)
@@ -80,36 +94,6 @@ class EventRiskAssessmentDetail(generic.DetailView):
return context
class EventRiskAssessmentList(generic.ListView):
paginate_by = 20
model = models.RiskAssessment
template_name = 'hs/hs_object_list.html'
def get_queryset(self):
return self.model.objects.exclude(event__status=models.Event.CANCELLED).order_by('reviewed_at').select_related('event')
def get_context_data(self, **kwargs):
context = super(EventRiskAssessmentList, self).get_context_data(**kwargs)
context['title'] = 'Risk Assessment'
context['view'] = 'ra_detail'
context['edit'] = 'ra_edit'
context['review'] = 'ra_review'
context['perm'] = 'perms.RIGS.review_riskassessment'
return context
class EventRiskAssessmentReview(generic.View):
def get(self, *args, **kwargs):
rpk = kwargs.get('pk')
ra = models.RiskAssessment.objects.get(pk=rpk)
with reversion.create_revision():
reversion.set_user(self.request.user)
ra.reviewed_by = self.request.user
ra.reviewed_at = timezone.now()
ra.save()
return HttpResponseRedirect(reverse_lazy('ra_list'))
class EventChecklistDetail(generic.DetailView):
model = models.EventChecklist
template_name = 'hs/event_checklist_detail.html'
@@ -130,7 +114,7 @@ class EventChecklistEdit(generic.UpdateView):
ec.reviewed_by = None
ec.reviewed_at = None
ec.save()
return reverse_lazy('ec_detail', kwargs={'pk': self.object.pk})
return reverse('ec_detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super(EventChecklistEdit, self).get_context_data(**kwargs)
@@ -143,7 +127,7 @@ class EventChecklistEdit(generic.UpdateView):
return context
class EventChecklistCreate(generic.CreateView):
class EventChecklistCreate(HSCreateView):
model = models.EventChecklist
template_name = 'hs/event_checklist_form.html'
form_class = forms.EventChecklistForm
@@ -158,57 +142,67 @@ class EventChecklistCreate(generic.CreateView):
if ra is None:
messages.error(self.request, f'A Risk Assessment must exist prior to creating any Event Checklists for {event}! Please create one now.')
return HttpResponseRedirect(reverse_lazy('event_ra', kwargs={'pk': epk}))
return HttpResponseRedirect(reverse('event_ra', kwargs={'pk': epk}))
return super(EventChecklistCreate, self).get(self)
def get_form(self, **kwargs):
form = super(EventChecklistCreate, self).get_form(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
form.instance.event = event
return form
def get_success_url(self):
return reverse('ec_detail', kwargs={'pk': self.object.pk})
class PowerTestDetail(generic.DetailView):
model = models.PowerTestRecord
template_name = 'hs/power_detail.html'
def get_context_data(self, **kwargs):
context = super(EventChecklistCreate, self).get_context_data(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
context['event'] = event
context['page_title'] = f'Create Event Checklist for Event {event.display_id}'
context = super().get_context_data(**kwargs)
context['page_title'] = f"Power Test Record for Event <a href='{self.object.event.get_absolute_url()}'>{self.object.event.display_id} {self.object.event.name}</a>"
return context
class PowerTestEdit(generic.UpdateView):
model = models.PowerTestRecord
template_name = 'hs/power_form.html'
form_class = forms.PowerTestRecordForm
def get_success_url(self):
return reverse_lazy('ec_detail', kwargs={'pk': self.object.pk})
class EventChecklistList(generic.ListView):
paginate_by = 20
model = models.EventChecklist
template_name = 'hs/hs_object_list.html'
def get_queryset(self):
return self.model.objects.exclude(event__status=models.Event.CANCELLED).order_by('reviewed_at').select_related('event')
ec = self.get_object()
ec.reviewed_by = None
ec.reviewed_at = None
ec.save()
return reverse('ec_detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super(EventChecklistList, self).get_context_data(**kwargs)
context['title'] = 'Event Checklist'
context['view'] = 'ec_detail'
context['edit'] = 'ec_edit'
context['review'] = 'ec_review'
context['perm'] = 'perms.RIGS.review_eventchecklist'
context = super().get_context_data(**kwargs)
pk = self.kwargs.get('pk')
ec = models.PowerTestRecord.objects.get(pk=pk)
context['event'] = ec.event
context['edit'] = True
context['page_title'] = f'Edit Power Test Record for Event {ec.event.display_id}'
# get_related(context['form'], context)
return context
class EventChecklistReview(generic.View):
class PowerTestCreate(HSCreateView):
model = models.PowerTestRecord
template_name = 'hs/power_form.html'
form_class = forms.PowerTestRecordForm
def get(self, *args, **kwargs):
rpk = kwargs.get('pk')
ec = models.EventChecklist.objects.get(pk=rpk)
with reversion.create_revision():
reversion.set_user(self.request.user)
ec.reviewed_by = self.request.user
ec.reviewed_at = timezone.now()
ec.save()
return HttpResponseRedirect(reverse_lazy('ec_list'))
epk = kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
# Check if RA exists
ra = models.RiskAssessment.objects.filter(event=event).first()
if ra is None:
messages.error(self.request, f'A Risk Assessment must exist prior to creating any Power Test Records for {event}! Please create one now.')
return HttpResponseRedirect(reverse('event_ra', kwargs={'pk': epk}))
return super().get(self)
def get_success_url(self):
return reverse('pt_detail', kwargs={'pk': self.object.pk})
class HSList(generic.ListView):
@@ -233,3 +227,64 @@ class RAPrint(PrintView):
context = super().get_context_data(**kwargs)
context['filename'] = f"EventSpecificRiskAssessment_for_{context['object'].event.display_id}.pdf"
return context
class EventCheckIn(generic.CreateView, ModalURLMixin):
model = models.EventCheckIn
template_name = 'hs/eventcheckin_form.html'
form_class = forms.EventCheckInForm
def get_success_url(self):
return self.get_close_url('event_detail', 'event_detail') # Well, that's one way of doing that...!
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['event'] = models.Event.objects.get(pk=self.kwargs.get('pk'))
context['page_title'] = f'Check In to Event {context["event"].display_id}'
# get_related(context['form'], context)
return context
class EventCheckInOverride(generic.CreateView):
model = models.EventCheckIn
template_name = 'hs/eventcheckin_form.html'
form_class = forms.EditCheckInForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['event'] = models.Event.objects.get(pk=self.kwargs.get('pk'))
context['page_title'] = f'Manually add Check In to Event {context["event"].display_id}'
context['manual'] = True
return context
class EventCheckInEdit(generic.UpdateView, ModalURLMixin):
model = models.EventCheckIn
template_name = 'hs/eventcheckin_form.html'
form_class = forms.EditCheckInForm
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
if not obj.person == self.request.user and not obj.event.mic == self.request.user:
return redirect(self.request.META.get('HTTP_REFERER', '/'))
return super().dispatch(request)
def get_success_url(self):
return self.get_close_url('event_detail', 'event_detail') # Well, that's one way of doing that...!
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['event'] = self.object.event
context['page_title'] = f'Edit Check In for Event {context["event"].display_id}'
context['edit'] = True
# get_related(context['form'], context)
return context
class EventCheckOut(generic.RedirectView):
def get_redirect_url(self, *args, **kwargs):
checkin = self.request.user.current_event()
if checkin:
checkin.end_time = timezone.now()
checkin.save()
return self.request.META.get('HTTP_REFERER', '/')

12
package-lock.json generated
View File

@@ -7904,9 +7904,9 @@
"dev": true
},
"node_modules/socket.io-parser": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz",
"integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==",
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz",
"integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==",
"dev": true,
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
@@ -15233,9 +15233,9 @@
}
},
"socket.io-parser": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz",
"integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==",
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz",
"integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==",
"dev": true,
"requires": {
"@socket.io/component-emitter": "~3.1.0",

View File

@@ -73,7 +73,7 @@ function initPicker(obj) {
return array;
}
};
console.log(obj.data);
//console.log(obj.data);
if (!obj.data('noclear')) {
obj.prepend($("<option></option>")
.attr("value",'')

View File

@@ -30,6 +30,9 @@
<body>
<a class="skip-link" href='#main'>Skip to content</a>
{% block navbar %}
{% if request.user.current_event %}
<div class="bg-primary d-flex justify-content-between align-items-center"><span class="ml-2">You are currently checked in to <a href="{{request.user.current_event.event.get_absolute_url}}" class="text-white">{{request.user.current_event.event}}</a></span><a href="{% url 'event_checkout'%}" class="btn btn-warning">Check Out</a></div>
{% endif %}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark" role="navigation">
<div class="container">
<a class="navbar-brand" style="position: absolute; left:0.5em; top: 2px;" href="{% if request.user.is_authenticated %}https://rigs.nottinghamtec.co.uk{%else%}https://nottinghamtec.co.uk{%endif%}">

View File

@@ -8,6 +8,13 @@
<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>
{% if now %}
<div class="col-sm-12 alert alert-primary rounded-0 mx-auto">
{% for event in now %}
Event {{ event }} is happening now! <a href="{% url 'event_checkin' event.pk %}" class="btn btn-success btn-sm modal-href align-baseline {% if request.user.current_event %}disabled{%endif%}"><span class="fas fa-user-clock"></span> <span class="d-none d-sm-inline">Check In</span></a><br/>
{% endfor %}
</div>
{% endif %}
<div class="col-sm-4 mb-3">
<div class="card">
<img class="card-img-top d-none d-sm-block" src="{% static 'imgs/rigs.jpg' %}" alt="Some lights and haze, very purple" style="height: 150px; object-fit: cover;">
@@ -24,7 +31,6 @@
</div>
<div class="col-sm-4 mb-3">
<div class="card">
{% now "m-d" as todays_date %}
<img class="card-img-top d-none d-sm-block" src="{% if todays_date == '04-01' %}{% static 'imgs/tappytaptap.gif' %}{%else%}{% static 'imgs/assets.jpg' %}{%endif%}" alt="M32 sound desk close up of the faders" style="height: 150px; object-fit: cover;">
<h4 class="card-header">Asset Database</h4>
<div class="list-group list-group-flush">

View File

@@ -8,10 +8,10 @@
<p>Please Note:</p>
<ul>
<li>Technical Assistant status is automatically valid 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>Technician status is also automatic. Notification of completion should be made at the next general meeting.</li>
<li>Supervisor status is <em>not automatically valid</em> and until signed off at a general meeting, does not count.</li>
</ul>
<sup>Correct as of 3rd September 2021, check the Training Policy.</sup>
<sup>Correct as of 24th May 2023, check the Training Policy.</sup>
</div>
{% endif %}
{% for level in object_list %}

View File

@@ -50,10 +50,7 @@ class Command(BaseCommand):
"add_supplier", "view_cabletype", "change_cabletype",
"add_cabletype", "view_eventchecklist", "change_eventchecklist",
"add_eventchecklist", "view_riskassessment", "change_riskassessment",
"add_riskassessment", "add_eventchecklistcrew", "change_eventchecklistcrew",
"delete_eventchecklistcrew", "view_eventchecklistcrew", "add_eventchecklistvehicle",
"change_eventchecklistvehicle",
"delete_eventchecklistvehicle", "view_eventchecklistvehicle", ]
"add_riskassessment"]
finance_perms = keyholder_perms + ["add_invoice", "change_invoice", "view_invoice",
"add_payment", "change_payment", "delete_payment"]
hs_perms = keyholder_perms + ["review_riskassessment", "review_eventchecklist"]