Compare commits

..

15 Commits

Author SHA1 Message Date
2f5e7b8366 Automagically clear and focus ID field when audit modal closes
Closes #533
2023-05-22 11:48:55 +01:00
36b9e40a8c Add "ex VAT" tooltips to asset purchase price and replacement cost 2023-05-22 11:31:38 +01:00
3667256245 Exclude dry hires from H&S overview list 2023-05-22 11:23:33 +01:00
a3458cea3d Prevent checking in to cancelled events and dry hires
Will close #539
2023-05-22 11:19:08 +01:00
90ae87b1b2 Implement some suggestions from the Doctor 2023-05-20 17:02:33 +01:00
0b22669a29 Default power MIC to MIC 2023-05-20 16:41:35 +01:00
020d43d5f8 Do not set power plan field to required on RA
"This might be a problem if the risk assessment is being done by one person and the power plan by another."
2023-05-20 16:39:21 +01:00
b4c1ada05c Add link to create power test from EC detail 2023-05-20 16:28:04 +01:00
aff911493f Fix population of initial venue values for EC/PT 2023-05-20 16:25:48 +01:00
020b08f9b0 Default venue to event venue in EC/PT 2023-05-20 15:24:24 +01:00
aae9a45d82 Allow a higher value in PSSC fields 2023-05-20 15:20:34 +01:00
773c07af81 Add units to power test record detail and form
I'm a bad scientist (coz I'm an engineer)
2023-05-20 10:40:22 +00:00
0cbba9d436 Return user to current page when clicking 'mark reviewed' 2023-05-19 18:20:49 +00:00
d47322a959 Update to target Python 3.10 2023-05-19 18:20:44 +00:00
74b8948daa Add absolute URL to power tests 2023-05-19 18:08:35 +00:00
49 changed files with 1278 additions and 1317 deletions

View File

@@ -12,7 +12,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PYTHONDONTWRITEBYTECODE: 1
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Set up Python - name: Set up Python
@@ -42,8 +41,8 @@ jobs:
pipenv run python3 manage.py makemigrations --check --dry-run pipenv run python3 manage.py makemigrations --check --dry-run
pipenv run python3 manage.py collectstatic --noinput pipenv run python3 manage.py collectstatic --noinput
- name: Run Tests - name: Run Tests
run: pipenv run pytest -n auto --cov run: pipenv run pytest -n auto -vv --cov
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v2
if: failure() if: failure()
with: with:
name: failure-screenshots ${{ matrix.test-group }} name: failure-screenshots ${{ matrix.test-group }}

View File

@@ -39,7 +39,7 @@ premailer = "~=3.7.0"
progress = "~=1.5" progress = "~=1.5"
psutil = "~=5.8.0" psutil = "~=5.8.0"
psycopg2 = "~=2.8.6" psycopg2 = "~=2.8.6"
Pygments = "~=2.15.0" Pygments = "~=2.7.4"
pyparsing = "~=2.4.7" pyparsing = "~=2.4.7"
PyPDF2 = "~=1.27.5" PyPDF2 = "~=1.27.5"
PyPOM = "~=2.2.4" PyPOM = "~=2.2.4"
@@ -47,7 +47,7 @@ python-dateutil = "~=2.8.1"
pytoml = "~=0.1.21" pytoml = "~=0.1.21"
pytz = "~=2020.5" pytz = "~=2020.5"
reportlab = "*" reportlab = "*"
requests = "~=2.31.0" requests = "~=2.25.1"
retrying = "~=1.3.3" retrying = "~=1.3.3"
simplejson = "~=3.17.2" simplejson = "~=3.17.2"
six = "~=1.15.0" six = "~=1.15.0"
@@ -56,7 +56,7 @@ sqlparse = "~=0.4.2"
static3 = "~=0.7.0" static3 = "~=0.7.0"
svg2rlg = "~=0.3" svg2rlg = "~=0.3"
tini = "~=3.0.1" tini = "~=3.0.1"
tornado = "~=6.3" tornado = "~=6.1"
urllib3 = "~=1.26.5" urllib3 = "~=1.26.5"
whitenoise = "~=5.2.0" whitenoise = "~=5.2.0"
yolk = "~=0.4.3" yolk = "~=0.4.3"

684
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "db73ee1e1b3aaf5abe57ca59314cf73ba81f0e8c96aa8aca99e4462571adf9d1" "sha256": "1ce9d1bf1bc9962178445d164d7dee6ef1105b8eae9e6cd6721d020ddf01e9c2"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@@ -32,6 +32,14 @@
"index": "pypi", "index": "pypi",
"version": "==3.3.4" "version": "==3.3.4"
}, },
"async-generator": {
"hashes": [
"sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b",
"sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"
],
"markers": "python_version >= '3.5'",
"version": "==1.10"
},
"attrs": { "attrs": {
"hashes": [ "hashes": [
"sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04",
@@ -163,11 +171,11 @@
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
"sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2023.7.22" "version": "==2023.5.7"
}, },
"chardet": { "chardet": {
"hashes": [ "hashes": [
@@ -177,87 +185,6 @@
"index": "pypi", "index": "pypi",
"version": "==4.0.0" "version": "==4.0.0"
}, },
"charset-normalizer": {
"hashes": [
"sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96",
"sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c",
"sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710",
"sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706",
"sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020",
"sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252",
"sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad",
"sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329",
"sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a",
"sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f",
"sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6",
"sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4",
"sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a",
"sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46",
"sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2",
"sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23",
"sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace",
"sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd",
"sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982",
"sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10",
"sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2",
"sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea",
"sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09",
"sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5",
"sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149",
"sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489",
"sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9",
"sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80",
"sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592",
"sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3",
"sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6",
"sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed",
"sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c",
"sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200",
"sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a",
"sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e",
"sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d",
"sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6",
"sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623",
"sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669",
"sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3",
"sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa",
"sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9",
"sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2",
"sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f",
"sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1",
"sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4",
"sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a",
"sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8",
"sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3",
"sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029",
"sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f",
"sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959",
"sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22",
"sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7",
"sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952",
"sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346",
"sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e",
"sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d",
"sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299",
"sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd",
"sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a",
"sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3",
"sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037",
"sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94",
"sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c",
"sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858",
"sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a",
"sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449",
"sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c",
"sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918",
"sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1",
"sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c",
"sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac",
"sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"
],
"markers": "python_full_version >= '3.7.0'",
"version": "==3.2.0"
},
"configparser": { "configparser": {
"hashes": [ "hashes": [
"sha256:85d5de102cfe6d14a5172676f09d19c465ce63d6019cf0a4ef13385fc535e828", "sha256:85d5de102cfe6d14a5172676f09d19c465ce63d6019cf0a4ef13385fc535e828",
@@ -330,11 +257,11 @@
}, },
"django": { "django": {
"hashes": [ "hashes": [
"sha256:a5de4c484e7b7418e6d3e52a5b8794f0e6b9f9e4ce3c037018cf1c489fa87f3c", "sha256:031365bae96814da19c10706218c44dff3b654cc4de20a98bd2d29b9bde469f0",
"sha256:d31b06c58aa2cd73998ca5966bc3001243d3c4e77ee2d0c479bced124765fd99" "sha256:21cc991466245d659ab79cb01204f9515690f8dae00e5eabde307f14d24d4d7d"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.2.21" "version": "==3.2.19"
}, },
"django-debug-toolbar": { "django-debug-toolbar": {
"hashes": [ "hashes": [
@@ -377,11 +304,11 @@
}, },
"django-mass-edit": { "django-mass-edit": {
"hashes": [ "hashes": [
"sha256:4da202ef31862d1978088c38ca7dfb98d0ee7456e031e81d83885978065068b6", "sha256:03e5adabfc9bf89ae4edee80d63957e86a18e0f4564076779750d30e4b3650d6",
"sha256:ac1be9b519d2464111e859299fa7081793cf7fa431c21f546471c3cc9183cb1f" "sha256:e05ef51e02b952f9ca517964d302ab75991886332d212d46697ad9debb38dfd6"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.5.0" "version": "==3.4.1"
}, },
"django-queryable-properties": { "django-queryable-properties": {
"hashes": [ "hashes": [
@@ -432,24 +359,24 @@
}, },
"exceptiongroup": { "exceptiongroup": {
"hashes": [ "hashes": [
"sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9", "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e",
"sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3" "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"
], ],
"markers": "python_version < '3.11'", "markers": "python_version < '3.11'",
"version": "==1.1.3" "version": "==1.1.1"
}, },
"freetype-py": { "freetype-py": {
"hashes": [ "hashes": [
"sha256:3e0f5a91bc812f42d98a92137e86bac4ed037a29e43dafdb76d716d5732189e8", "sha256:3a552265b06c2cb3fa54f86ed6fcbf045d8dc8176f9475bedddf9a1b31f5402f",
"sha256:8ad81195d2f8f339aba61700cebfbd77defad149c51f59b75a2a5e37833ae12e", "sha256:89cee8f4e7cf0a37b73a43a08c88703d84e3b9f9243fc665d8dc0b72a5d206a8",
"sha256:9614f68876e9c62e821dfa811dd6160e00279d9d98cf60118cb264be48da1472", "sha256:b95ccd52ff7e9bef34505f8af724cee114a3c3cc9cf13e0fd406fa0cc92b988a",
"sha256:a2620788d4f0c00bd75fee2dfca61635ab0da856131598c96e2355d5257f70e5", "sha256:c8f17c3ac35dc7cc9571ac37a00a6daa428a1a6d0fe6926a77d16066865ed5ef",
"sha256:c6276d92ac401c8ce02ea391fc854de413b01a8d835fb394ee5eb6f04fc947f5", "sha256:ca7155de937af6f26bfd9f9089a6e9b01fa8f9d3040a3ddc0aeb3a53cf88f428",
"sha256:c9a3abc277f5f6d21575c0093c0c6139c161bf05b91aa6258505ab27c5001c5e", "sha256:ccdb1616794a8ad48beaa9e29d3494e6643d24d8e925cc39263de21c062ea5a7",
"sha256:ce931f581d5038c4fea1f3d314254e0264e92441a5fdaef6817fe77b7bb888d3" "sha256:f9b64ce3272a5c358dcee824800a32d70997fb872a0965a557adca20fce7a5d0"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==2.4.0" "version": "==2.3.0"
}, },
"gunicorn": { "gunicorn": {
"hashes": [ "hashes": [
@@ -493,109 +420,94 @@
}, },
"importlib-metadata": { "importlib-metadata": {
"hashes": [ "hashes": [
"sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb", "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed",
"sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743" "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"
], ],
"index": "pypi", "index": "pypi",
"version": "==6.8.0" "version": "==6.6.0"
}, },
"lxml": { "lxml": {
"hashes": [ "hashes": [
"sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3", "sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7",
"sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d", "sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726",
"sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a", "sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03",
"sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120", "sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140",
"sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305", "sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a",
"sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287", "sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05",
"sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23", "sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03",
"sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52", "sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419",
"sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f", "sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4",
"sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4", "sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e",
"sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584", "sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67",
"sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f", "sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50",
"sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693", "sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894",
"sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef", "sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf",
"sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5", "sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947",
"sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02", "sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1",
"sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc", "sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd",
"sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7", "sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3",
"sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da", "sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92",
"sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a", "sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3",
"sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40", "sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457",
"sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8", "sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74",
"sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd", "sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf",
"sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601", "sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1",
"sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c", "sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4",
"sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be", "sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975",
"sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2", "sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5",
"sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c", "sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe",
"sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129", "sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7",
"sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc", "sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1",
"sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2", "sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2",
"sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1", "sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409",
"sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7", "sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f",
"sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d", "sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f",
"sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477", "sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5",
"sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d", "sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24",
"sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e", "sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e",
"sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7", "sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4",
"sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2", "sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a",
"sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574", "sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c",
"sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf", "sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de",
"sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b", "sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f",
"sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98", "sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b",
"sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12", "sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5",
"sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42", "sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7",
"sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35", "sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a",
"sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d", "sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c",
"sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce", "sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9",
"sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d", "sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e",
"sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f", "sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab",
"sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db", "sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941",
"sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4", "sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5",
"sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694", "sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45",
"sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac", "sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7",
"sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2", "sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892",
"sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7", "sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746",
"sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96", "sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c",
"sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d", "sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53",
"sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b", "sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe",
"sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a", "sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184",
"sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13", "sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38",
"sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340", "sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df",
"sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6", "sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9",
"sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458", "sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b",
"sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c", "sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2",
"sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c", "sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0",
"sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9", "sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda",
"sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432", "sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b",
"sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991", "sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5",
"sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69", "sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380",
"sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf", "sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33",
"sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb", "sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8",
"sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b", "sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1",
"sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833", "sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889",
"sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76", "sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9",
"sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85", "sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f",
"sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e", "sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c"
"sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50",
"sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8",
"sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4",
"sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b",
"sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5",
"sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190",
"sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7",
"sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa",
"sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0",
"sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9",
"sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0",
"sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b",
"sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5",
"sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7",
"sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.9.3" "version": "==4.9.2"
}, },
"markdown": { "markdown": {
"hashes": [ "hashes": [
@@ -700,44 +612,42 @@
}, },
"pikepdf": { "pikepdf": {
"hashes": [ "hashes": [
"sha256:08c3ae63dee9c66f42463ecece3fb2bccc875b6bb5032dc999be4ed95c90aead", "sha256:0e1607fda03a53a29a4a8e3fbacbde788804c78167ff251e1c1006f89539f306",
"sha256:0c9adf0cc48c84ac2cbabdc1a2e0b727e6e6f9dc65fd28473110117087481206", "sha256:1cc8d0be5a62ed9011bb519abc34907b5965b392995043719effc4b6a00e2052",
"sha256:1739edcf9227346ba501f251644fbe204e8e0d783b6ec67af94a8cf0fb7d15a0", "sha256:26b9bfb99265dfb6deb72574f8cd30e7ffbc2f53237988bb4e167e18d813f510",
"sha256:174777cdd93254a1116190ef4732599a139abf94f4691487b115f2b26f8115e4", "sha256:371eb23ac14e6c9947e59e5cea15ea93e61a5714c6b1f99fba948927809605ea",
"sha256:1e23b988a3ead19ae43ca09ef142ff6ada421cdcc2d4c1e8a0d4e1442cdce9bc", "sha256:3c30776791fad8d57a43c392d8e190afded857c61e49dac471ab74e9e716c441",
"sha256:1ed3fadaad5a14fe737e4c875f6bef110d8be755e3488a49d7894a0a4d38a956", "sha256:3efe4dd2cb417f42865b11e6fc9adb1b6252241bd7a8d891afcaeb2191c285c2",
"sha256:1fcc52e38a3ce8fdb63d51222cb714a7ac3539bbbf6e65fc4805e04b153b50ef", "sha256:5155e1127dfe3aacf77a33552f128e9c04e8b61bf585ab2b155d062a524bfd06",
"sha256:1fd7cb6ba007f2244beb9ae82771a1749ad8684021cdd67336f81b39b9afded5", "sha256:6670efe5b9c1548d60348cdd5ce84ca363a3cde22e9cf695f1ce3b3f818e498d",
"sha256:2547d4715bd6a55ac81d7dfb6f146ce0bdb0b9f7c2b9aac2613306ec0cae0070", "sha256:677f04f02535ac398806970544c43c1e2b120d82b027437c467923a16c81d528",
"sha256:2e13a6383ebb24d755bba4fb75dde31eb419a6437877fce2f010f45c149d92b5", "sha256:6790cf10da642d72703cbe887afb923daa2e0f7cb9467a79fe449dbe228f8942",
"sha256:353f1dbbb6ca2b47de80d768bcf3e8d7eea0f51f37cef1b834ca143315b3f450", "sha256:6a150160f7ed97769f3c0e60de5cb031bce04e1f6708916ac1c936774a65cc0c",
"sha256:47b176dac8a93060e8d5efbcf2b48c4731b81dcb548f148a30414258fa0ed96b", "sha256:6dbe2b62e12ff2b47d4e56ebbe16697d0e25bc2c608f4ee5230cf179ebd2a8ab",
"sha256:48d674d812aa7ae2154b0b2ac4140de45cfbb6073fc4dbea86cc64bfa029ba1b", "sha256:70f1161dd22ccfcbfd1c460873c95b68b79cf234f0a4e9f37cb565bf436fd85e",
"sha256:584914e698a6e971cc5b7966476c498e6adc6957dc3756fb81653c6d93ffcfcc", "sha256:7246789dd9071ebcf8c90baebe1eee34ac627e2ea22bf241eff31d32f5ca5df9",
"sha256:5c791163b86b924b1b6f14e4908143c0210c500a7ee888bff5bf304f1701acc6", "sha256:7a07f73f2aac48af46a546e285360d6e595b499075ab78c3b8ca9f5f13d9e876",
"sha256:6fb8769a7dbf5f03acc4c3cc538bd4fe469ce99b4f9e449153c331735e9f4964", "sha256:99e483e037f6991be3c4c655454d57324c10ccf41960acd1edd899ebe9a314dd",
"sha256:859cd42b8c2f8ccbec27092a4e4ab55384605750226bfb4a8840aa996628fdd2", "sha256:9db8270c940f94d0049420f1a6c05b1f7d326d2abb20493c83f64fde3949404e",
"sha256:9094976d332b0189420cf8ea2dd563b9f0eee121caa8c83ae490183624383c03", "sha256:9fadd1a99754dcd925e37721485d4d1259c7ad3c9073c6b3b0ed12c6e2d2234c",
"sha256:a87d4cc42d0adb436d108387274c2618afed73fca1eb3505a636d051344c6abe", "sha256:ad82b836faed0376c725e19d0f8a7c7bef389e8c46683c11bbfc70410bc2e3ee",
"sha256:b5669fe3b0f9d2d3d9c90a1944be683806f98f18d257834b81ac25dd5a84ee37", "sha256:b148959f1ad51d236cf6bbc5343beef72c4c60569151221ec06b1d787909222e",
"sha256:b60027aa6c9cfd60bd89a9147bd1a3be9bae1aa5abadf81c8b090e929b960b2e", "sha256:b24b7520c7f40bba4f437ba5111dca99dbd0cca9d3cab0f0a33afa6150091ee3",
"sha256:b90538b4c88117971b11d1fa5870600019432360f3461786fb1560d0d2881cf6", "sha256:c99838e86c0fc5a215a0588011344477e8f6ec2c5faf48ee3a9da1ba2c2cac5b",
"sha256:bc40d17229fd03e3980599d81650b4c13503896abc65f0220f79c5a15ee08948", "sha256:d0698a429948e613f810b487318ce88112bc71a67fd76645be140532130e6c86",
"sha256:bf8c4636241e53212bad1b8253193cb8c4e6e9706e1bea34b8a507f8d2cb54aa", "sha256:d410028ef3435a459e55de520d29010ee91e4a40872d9eb2dab86e6730a24e9d",
"sha256:c12b1ff205e585857b4a2713ce55c474bc8ca75c1f5a40a6ae5814b3b012ce35", "sha256:dc6a653f0f98076a1e86e4fe58fe36dcf403963ae55c65bfcd28aa1d2d9b1b18",
"sha256:c9055f9f0430f957511aa794ccf2303a202bb8bd87add6437128ecffc3e6e5e1", "sha256:e21bce4760c6e64c90b17601a8ce000219677adb264a3c038d2522de032169ca",
"sha256:d54ccb9a69ce3f17818bd88316e8f55a9d9f07ddb8e1d2d8eb868ee883f266c3", "sha256:e4559941b359beb0e90b7d0b8016397ab4700f075f7aa11f2561958a7ce0f8ea",
"sha256:dace484b7c31851bd1e4905aaae71ca0a283f1de168d596970bea03565fa6a63", "sha256:ebc1b30d646ded58721a5594a5ca457e098fedfd9bcab28de79ad79a119e3537",
"sha256:dc8d8ee81aa392bfd2e1af3211c3c2dc1d9db877bc2cfd6bd86ab33d882b0572", "sha256:f27a1aea4cae5484cffe4fc5fa761af11f384ca0fd4b2f9114f9ba9717fe4746",
"sha256:dcaa4e27bc277b0ebcbde918970148b2fa29fbcd847e608423bea78ec2d8f19a", "sha256:f325af78bfb63e305be7f31a3afea47fd33ba035ccb08e89d608d2e88b367349",
"sha256:dd4dd627606a385249bce5be3471b0065dd49fa6b2279f54b2b73745df59eb31", "sha256:f3c97acce9b66a41b2759dc30ef57de8f38c7239c9b0e7a5febc196b764a2567",
"sha256:dfcf3d26d4f5dcdfa765094304881b38dc0451789368f4f13acb1eebcca465cc", "sha256:f458c4161e76a882a15ade4125a2f92faa7e5ce120d2e6530dd995aa3308971c",
"sha256:e2d71896f02d544e7588997d50b968b9b86e7a4ce80faa32ed3240d7ab2caabd", "sha256:f7451f176eb9828d8dd7cb3d4e00d4e0aa7f7d7d00331fe640bc20cf3328deb5"
"sha256:f24c6948f8610553fbe6a3008db579f8bbfe811d12b650ca0a4512a0f98057d5",
"sha256:fe054eb9f1e6f89c1e53fcbcf8ffe1296abaf34c3e516e0e485563e6328f29d4"
], ],
"index": "pypi", "index": "pypi",
"version": "==8.4.0" "version": "==7.2.0"
}, },
"pillow": { "pillow": {
"hashes": [ "hashes": [
@@ -808,11 +718,11 @@
}, },
"pluggy": { "pluggy": {
"hashes": [ "hashes": [
"sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.6'",
"version": "==1.3.0" "version": "==1.0.0"
}, },
"premailer": { "premailer": {
"hashes": [ "hashes": [
@@ -886,28 +796,28 @@
}, },
"pycairo": { "pycairo": {
"hashes": [ "hashes": [
"sha256:031f5ef2c80792673f2c54ee285f2a31779a44d7521a27a7f82e4a5ecfafc26e", "sha256:1a6d8e0f353062ad92954784e33dbbaf66c880c9c30e947996c542ed9748aaaf",
"sha256:1444d52f1bb4cc79a4a0c0fe2ccec4bd78ff885ab01ebe1c0f637d8392bcafb6", "sha256:2dec5378133778961993fb59d66df16070e03f4d491b67eb695ca9ad7a696008",
"sha256:220742f187d3940d695c1af1a0c1646e26dc2199d65b7bafaa527e15c3520fd3", "sha256:3a71f758e461180d241e62ef52e85499c843bd2660fd6d87cec99c9833792bfa",
"sha256:26fe2c32ba24caae524d855f26f3ee1a0c1ea3291da2a19604264ed29f64d834", "sha256:564601e5f528531c6caec1c0177c3d0709081e1a2a5cccc13561f715080ae535",
"sha256:3199d6a0538d6482c71efb816bd330515e98bb06f182e23572c77d92be98f536", "sha256:82e335774a17870bc038e0c2fb106c1e5e7ad0c764662023886dfcfce5bb5a52",
"sha256:4631ed794a3376ec314ce47826c3e51940b54695f4ef7d5b3245b203037ae760", "sha256:87efd62a7b7afad9a0a420f05b6008742a6cfc59077697be65afe8dc73ae15ad",
"sha256:6ad5c3425408ebb0dfaad2cca4a80e7524d7a309305acdb2569638b5cc988fe7", "sha256:9b61ac818723adc04367301317eb2e814a83522f07bbd1f409af0dada463c44c",
"sha256:afccac552386ab628e8ae658185fa363e8d15a5afe96d1de43f97027dd78bdd6", "sha256:a4b1f525bbdf637c40f4d91378de36c01ec2b7f8ecc585b700a079b9ff83298e",
"sha256:c5f6efdd86fe13d36a6b004c64d8e97cde9854d599cf6cca962afb1966fe533d", "sha256:d6bacff15d688ed135b4567965a4b664d9fb8de7417a7865bb138ad612043c9f",
"sha256:c7c79e748ec849811241d29553184c3ad93c857558dbe9954a49327680b8d356", "sha256:e7cde633986435d87a86b6118b7b6109c384266fd719ef959883e2729f6eafae",
"sha256:ed0622a0ccffb873ffe7fee1699d60779f1260fba143390e5366d55f1d1739f5" "sha256:ec305fc7f2f0299df78aadec0eaf6eb9accb90eda242b5d3492544d3f2b28027"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.7'",
"version": "==1.24.0" "version": "==1.23.0"
}, },
"pygments": { "pygments": {
"hashes": [ "hashes": [
"sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c", "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435",
"sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1" "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.15.1" "version": "==2.7.4"
}, },
"pyparsing": { "pyparsing": {
"hashes": [ "hashes": [
@@ -943,11 +853,11 @@
}, },
"python-barcode": { "python-barcode": {
"hashes": [ "hashes": [
"sha256:057636fba37369c22852410c8535b36adfbeb965ddfd4e5b6924455d692e0886", "sha256:241b34aa5c5cb6a9889882f9409b0182903a2c5d19b4218be3609cdbbd5ffdf9",
"sha256:3b1825fbdb11e597466dff4286b4ea9b1e86a57717b59e563ae679726fc854de" "sha256:eefbb2583ba7bdb09baba6f8663129883109c61df7e23c9b9b473087521c926f"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.15.1" "version": "==0.14.0"
}, },
"python-dateutil": { "python-dateutil": {
"hashes": [ "hashes": [
@@ -975,19 +885,19 @@
}, },
"reportlab": { "reportlab": {
"hashes": [ "hashes": [
"sha256:3dcda79ce04baf70721e2ec54854722644262cac2feec3d5c4c5e77015504cb0", "sha256:3ea3b2954cb434b024dac61e9f270f2a4c0f9e0cc8b2cf2e310273307b2ba05c",
"sha256:7f70b3b56aff5f11cb4136c51a0f5a56fe6e4c8fbbac7b903076db99a8ef31c1" "sha256:a1433a24cee3119fdc142487c6594d72621dd1d5d33df2d032c559aa0bb8b115"
], ],
"index": "pypi", "index": "pypi",
"version": "==4.0.4" "version": "==4.0.0"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.31.0" "version": "==2.25.1"
}, },
"retrying": { "retrying": {
"hashes": [ "hashes": [
@@ -999,10 +909,11 @@
}, },
"rlpycairo": { "rlpycairo": {
"hashes": [ "hashes": [
"sha256:f6ec712a76914f78c1491351e1d79eeb1a40d072accf5d36075e399963c17b17" "sha256:7cd1eac30fe69d98f75d67a54892f9c10534a047b9a959ef17bb3926a196e50a",
"sha256:a88bce206c45d2180f944b8754c6e2e9245f80506c90fdfb94c7fbdd27805c25"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==0.3.0" "version": "==0.2.0"
}, },
"selenium": { "selenium": {
"hashes": [ "hashes": [
@@ -1014,19 +925,19 @@
}, },
"sentry-sdk": { "sentry-sdk": {
"hashes": [ "hashes": [
"sha256:2e53ad63f96bb9da6570ba2e755c267e529edcf58580a2c0d2a11ef26e1e678b", "sha256:0300fbe7a07b3865b3885929fb863a68ff01f59e3bcfb4e7953d0bf7fd19c67f",
"sha256:7dc873b87e1faf4d00614afd1058bfa1522942f33daef8a59f90de8ed75cd10c" "sha256:a884e2478e0b055776ea2b9234d5de9339b4bae0b3a5e74ae43d131db8ded27e"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.30.0" "version": "==1.23.1"
}, },
"setuptools": { "setuptools": {
"hashes": [ "hashes": [
"sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d", "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b",
"sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b" "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.7'",
"version": "==68.1.2" "version": "==67.7.2"
}, },
"simplejson": { "simplejson": {
"hashes": [ "hashes": [
@@ -1120,11 +1031,11 @@
}, },
"soupsieve": { "soupsieve": {
"hashes": [ "hashes": [
"sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8",
"sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.5" "version": "==2.4.1"
}, },
"sqlparse": { "sqlparse": {
"hashes": [ "hashes": [
@@ -1181,44 +1092,44 @@
}, },
"tornado": { "tornado": {
"hashes": [ "hashes": [
"sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f", "sha256:05615096845cf50a895026f749195bf0b10b8909f9be672f50b0fe69cba368e4",
"sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5", "sha256:0c325e66c8123c606eea33084976c832aa4e766b7dff8aedd7587ea44a604cdf",
"sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d", "sha256:29e71c847a35f6e10ca3b5c2990a52ce38b233019d8e858b755ea6ce4dcdd19d",
"sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3", "sha256:4b927c4f19b71e627b13f3db2324e4ae660527143f9e1f2e2fb404f3a187e2ba",
"sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2", "sha256:5b17b1cf5f8354efa3d37c6e28fdfd9c1c1e5122f2cb56dac121ac61baa47cbe",
"sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a", "sha256:6a0848f1aea0d196a7c4f6772197cbe2abc4266f836b0aac76947872cd29b411",
"sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16", "sha256:7efcbcc30b7c654eb6a8c9c9da787a851c18f8ccd4a5a3a95b05c7accfa068d2",
"sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a", "sha256:834ae7540ad3a83199a8da8f9f2d383e3c3d5130a328889e4cc991acc81e87a0",
"sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17", "sha256:b46a6ab20f5c7c1cb949c72c1994a4585d2eaa0be4853f50a03b5031e964fc7c",
"sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0", "sha256:c2de14066c4a38b4ecbbcd55c5cc4b5340eb04f1c5e81da7451ef555859c833f",
"sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe" "sha256:c367ab6c0393d71171123ca5515c61ff62fe09024fa6bf299cd1339dc9456829"
], ],
"index": "pypi", "index": "pypi",
"version": "==6.3.3" "version": "==6.3.2"
}, },
"trio": { "trio": {
"hashes": [ "hashes": [
"sha256:3887cf18c8bcc894433420305468388dac76932e9668afa1c49aa3806b6accb3", "sha256:ce68f1c5400a47b137c5a4de72c7c901bd4e7a24fbdebfe9b41de8c6c04eaacf",
"sha256:f43da357620e5872b3d940a2e3589aa251fd3f881b65a608d742e00809b1ec38" "sha256:f1dd0780a89bfc880c7c7994519cb53f62aacb2c25ff487001c0052bd721cdf0"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==0.22.2" "version": "==0.22.0"
}, },
"trio-websocket": { "trio-websocket": {
"hashes": [ "hashes": [
"sha256:1a748604ad906a7dcab9a43c6eb5681e37de4793ba0847ef0bc9486933ed027b", "sha256:0908435e4eecc49d830ae1c4d6c47b978a75f00594a2be2104d58b61a04cdb53",
"sha256:a9937d48e8132ebf833019efde2a52ca82d223a30a7ea3e8d60a7d28f75a4e3a" "sha256:af13e9393f9051111300287947ec595d601758ce3d165328e7d36325135a8d62"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==0.10.3" "version": "==0.10.2"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f", "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305",
"sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14" "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.26.16" "version": "==1.26.15"
}, },
"webencodings": { "webencodings": {
"hashes": [ "hashes": [
@@ -1252,11 +1163,11 @@
}, },
"z3c.rml": { "z3c.rml": {
"hashes": [ "hashes": [
"sha256:3593e37ee3fcfb261d25d9a8546a062f9fcebe9df5803d876329bbc4b08570fb", "sha256:100b62a3978a9acbd2d1a043d4c5b1e2e29601dc687a19b604a95570da6af94f",
"sha256:be5bbe2315f37c8fc1052f5d141e7f01e9fa1545deebf4c793887e73b24e2fd8" "sha256:a8064fd06581e45fa208ff02707834744b3b776d4e399bc1fcd5f2217b8ecdd1"
], ],
"index": "pypi", "index": "pypi",
"version": "==4.4.0" "version": "==4.3.0"
}, },
"zipp": { "zipp": {
"hashes": [ "hashes": [
@@ -1458,6 +1369,14 @@
} }
}, },
"develop": { "develop": {
"async-generator": {
"hashes": [
"sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b",
"sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"
],
"markers": "python_version >= '3.5'",
"version": "==1.10"
},
"attrs": { "attrs": {
"hashes": [ "hashes": [
"sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04",
@@ -1468,92 +1387,19 @@
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
"sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2023.7.22" "version": "==2023.5.7"
}, },
"charset-normalizer": { "chardet": {
"hashes": [ "hashes": [
"sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
"sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710",
"sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706",
"sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020",
"sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252",
"sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad",
"sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329",
"sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a",
"sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f",
"sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6",
"sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4",
"sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a",
"sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46",
"sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2",
"sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23",
"sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace",
"sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd",
"sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982",
"sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10",
"sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2",
"sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea",
"sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09",
"sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5",
"sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149",
"sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489",
"sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9",
"sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80",
"sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592",
"sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3",
"sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6",
"sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed",
"sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c",
"sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200",
"sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a",
"sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e",
"sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d",
"sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6",
"sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623",
"sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669",
"sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3",
"sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa",
"sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9",
"sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2",
"sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f",
"sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1",
"sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4",
"sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a",
"sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8",
"sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3",
"sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029",
"sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f",
"sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959",
"sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22",
"sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7",
"sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952",
"sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346",
"sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e",
"sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d",
"sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299",
"sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd",
"sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a",
"sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3",
"sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037",
"sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94",
"sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c",
"sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858",
"sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a",
"sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449",
"sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c",
"sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918",
"sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1",
"sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c",
"sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac",
"sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"
], ],
"markers": "python_full_version >= '3.7.0'", "index": "pypi",
"version": "==3.2.0" "version": "==4.0.0"
}, },
"coverage": { "coverage": {
"hashes": [ "hashes": [
@@ -1621,11 +1467,11 @@
}, },
"django-coverage-plugin": { "django-coverage-plugin": {
"hashes": [ "hashes": [
"sha256:223d34bf92bebadcb8b7b89932480e41c7bd98b44a8156934488fbe7f4a71f99", "sha256:245ecd6e91e5be7a66e0f811fd57091c46b55c0eb85c7fe1a1e4aebca9842a5f",
"sha256:eb0ea8ffdb0db11a02994fc99be6500550efb496c350d709f418ff3d8e553a67" "sha256:c063d8d49ba2da30fe95d91cf3f0f9f659b55c3f80d4a029d619b2b3144b1206"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.1.0" "version": "==3.0.0"
}, },
"docopt": { "docopt": {
"hashes": [ "hashes": [
@@ -1635,19 +1481,19 @@
}, },
"exceptiongroup": { "exceptiongroup": {
"hashes": [ "hashes": [
"sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9", "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e",
"sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3" "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"
], ],
"markers": "python_version < '3.11'", "markers": "python_version < '3.11'",
"version": "==1.1.3" "version": "==1.1.1"
}, },
"execnet": { "execnet": {
"hashes": [ "hashes": [
"sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5",
"sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.0.2" "version": "==1.9.0"
}, },
"h11": { "h11": {
"hashes": [ "hashes": [
@@ -1691,11 +1537,11 @@
}, },
"pluggy": { "pluggy": {
"hashes": [ "hashes": [
"sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.6'",
"version": "==1.3.0" "version": "==1.0.0"
}, },
"psutil": { "psutil": {
"hashes": [ "hashes": [
@@ -1757,19 +1603,19 @@
}, },
"pytest": { "pytest": {
"hashes": [ "hashes": [
"sha256:2f2301e797521b23e4d2585a0a3d7b5e50fdddaaf7e7d6773ea26ddb17c213ab", "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362",
"sha256:460c9a59b14e27c602eb5ece2e47bec99dc5fc5f6513cf924a7d03a578991b1f" "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"
], ],
"index": "pypi", "index": "pypi",
"version": "==7.4.1" "version": "==7.3.1"
}, },
"pytest-cov": { "pytest-cov": {
"hashes": [ "hashes": [
"sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b",
"sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a" "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"
], ],
"index": "pypi", "index": "pypi",
"version": "==4.1.0" "version": "==4.0.0"
}, },
"pytest-django": { "pytest-django": {
"hashes": [ "hashes": [
@@ -1781,11 +1627,11 @@
}, },
"pytest-reverse": { "pytest-reverse": {
"hashes": [ "hashes": [
"sha256:37e83daac57eea3fb1cb718aa9ccdf9ca2ea8ac3645cb5bccf1c7ae25a8ad1d2", "sha256:71952dbcc09d0bbed88af33dab2d540298325cc6193a00b4bb037f380041d680",
"sha256:f943e5b9d253267569fd7ad237afc56b3e98ce9f6d2f6f3bb487b8c759e214fe" "sha256:78ec5c8bab341403abf43a1e59e0f7499b7b0b897a54606f67e4f4d1ffc8b4cc"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.7.0" "version": "==1.5.0"
}, },
"pytest-splinter": { "pytest-splinter": {
"hashes": [ "hashes": [
@@ -1808,11 +1654,11 @@
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.31.0" "version": "==2.25.1"
}, },
"selenium": { "selenium": {
"hashes": [ "hashes": [
@@ -1824,11 +1670,11 @@
}, },
"setuptools": { "setuptools": {
"hashes": [ "hashes": [
"sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d", "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b",
"sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b" "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.7'",
"version": "==68.1.2" "version": "==67.7.2"
}, },
"sniffio": { "sniffio": {
"hashes": [ "hashes": [
@@ -1862,27 +1708,27 @@
}, },
"trio": { "trio": {
"hashes": [ "hashes": [
"sha256:3887cf18c8bcc894433420305468388dac76932e9668afa1c49aa3806b6accb3", "sha256:ce68f1c5400a47b137c5a4de72c7c901bd4e7a24fbdebfe9b41de8c6c04eaacf",
"sha256:f43da357620e5872b3d940a2e3589aa251fd3f881b65a608d742e00809b1ec38" "sha256:f1dd0780a89bfc880c7c7994519cb53f62aacb2c25ff487001c0052bd721cdf0"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==0.22.2" "version": "==0.22.0"
}, },
"trio-websocket": { "trio-websocket": {
"hashes": [ "hashes": [
"sha256:1a748604ad906a7dcab9a43c6eb5681e37de4793ba0847ef0bc9486933ed027b", "sha256:0908435e4eecc49d830ae1c4d6c47b978a75f00594a2be2104d58b61a04cdb53",
"sha256:a9937d48e8132ebf833019efde2a52ca82d223a30a7ea3e8d60a7d28f75a4e3a" "sha256:af13e9393f9051111300287947ec595d601758ce3d165328e7d36325135a8d62"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==0.10.3" "version": "==0.10.2"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f", "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305",
"sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14" "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.26.16" "version": "==1.26.15"
}, },
"wsproto": { "wsproto": {
"hashes": [ "hashes": [

View File

@@ -121,7 +121,3 @@ def nottinghamtec_address_required(function):
return function(request, *args, **kwargs) return function(request, *args, **kwargs)
return wrap return wrap
def not_estates():
return user_passes_test_with_403(lambda u: not u.email.endswith('@nottingham.ac.uk'))

0
PyRIGS/forms.py Normal file
View File

View File

@@ -35,9 +35,6 @@ if DEBUG:
ALLOWED_HOSTS.append('localhost') ALLOWED_HOSTS.append('localhost')
ALLOWED_HOSTS.append('example.com') ALLOWED_HOSTS.append('example.com')
ALLOWED_HOSTS.append('127.0.0.1') ALLOWED_HOSTS.append('127.0.0.1')
ALLOWED_HOSTS.append('.github.dev')
CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
if not DEBUG: if not DEBUG:

View File

@@ -6,8 +6,6 @@ from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import path from django.urls import path
from django.views.generic import TemplateView from django.views.generic import TemplateView
from PyRIGS.decorators import not_estates
from PyRIGS import views from PyRIGS import views
urlpatterns = [ urlpatterns = [
@@ -16,17 +14,17 @@ urlpatterns = [
path('assets/', include('assets.urls')), path('assets/', include('assets.urls')),
path('training/', include('training.urls')), path('training/', include('training.urls')),
path('', not_estates()(views.Index.as_view()), name='index'), path('', login_required(views.Index.as_view()), name='index'),
# API # API
path('api/<str:model>/', not_estates()(views.SecureAPIRequest.as_view()), path('api/<str:model>/', login_required(views.SecureAPIRequest.as_view()),
name="api_secure"), name="api_secure"),
path('api/<str:model>/<int:pk>/', not_estates()(views.SecureAPIRequest.as_view()), path('api/<str:model>/<int:pk>/', login_required(views.SecureAPIRequest.as_view()),
name="api_secure"), name="api_secure"),
path('closemodal/', views.CloseModal.as_view(), name='closemodal'), path('closemodal/', views.CloseModal.as_view(), name='closemodal'),
path('search/', not_estates()(views.Search.as_view()), name='search'), path('search/', login_required(views.Search.as_view()), name='search'),
path('search_help/', not_estates()(views.SearchHelp.as_view()), name='search_help'), path('search_help/', login_required(views.SearchHelp.as_view()), name='search_help'),
path('', include('users.urls')), path('', include('users.urls')),

View File

@@ -48,7 +48,7 @@ class Index(generic.TemplateView): # Displays the current rig count along with
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['rig_count'] = models.Event.objects.rig_count() context['rig_count'] = models.Event.objects.rig_count()
context['now'] = models.Event.objects.events_in_bounds(timezone.now(), timezone.now()).exclude(status=models.Event.CANCELLED).filter(is_rig=True, dry_hire=False) context['now'] = models.Event.objects.events_in_bounds(timezone.now(), timezone.now()).exclude(dry_hire=True).exclude(status=models.Event.CANCELLED)
return context return context
@@ -134,15 +134,11 @@ class SecureAPIRequest(generic.View):
results = [] results = []
query = reduce(operator.and_, queries) query = reduce(operator.and_, queries)
objects = self.models[model].objects.filter(query) objects = self.models[model].objects.filter(query)
# Returning unactivated or unapproved users when they are elsewhere filtered out of the default queryset leads to some *very* unexpected results
if model == "profile":
objects = objects.filter(is_active=True, is_approved=True)
for o in objects: for o in objects:
name = o.display_name if hasattr(o, 'display_name') else o.name
data = { data = {
'pk': o.pk, 'pk': o.pk,
'value': o.pk, 'value': o.pk,
'text': name, 'text': o.name,
} }
try: # See if there is a valid update URL try: # See if there is a valid update URL
data['update'] = reverse(f"{model}_update", kwargs={'pk': o.pk}) data['update'] = reverse(f"{model}_update", kwargs={'pk': o.pk})
@@ -187,7 +183,7 @@ class ModalURLMixin:
url = reverse_lazy('closemodal') url = reverse_lazy('closemodal')
update_url = str(reverse_lazy(update, kwargs={'pk': self.object.pk})) update_url = str(reverse_lazy(update, kwargs={'pk': self.object.pk}))
messages.info(self.request, "modalobject=" + serializers.serialize("json", [self.object])) messages.info(self.request, "modalobject=" + serializers.serialize("json", [self.object]))
messages.info(self.request, f"modalobject[0]['update_url']='{update_url}'") messages.info(self.request, "modalobject[0]['update_url']='" + update_url + "'")
else: else:
url = reverse_lazy(detail, kwargs={ url = reverse_lazy(detail, kwargs={
'pk': self.object.pk, 'pk': self.object.pk,

View File

@@ -11,9 +11,8 @@ For setup information and other such helpful stuff check the [Wiki](https://gith
- PyRIGS: Base app, stores 'global' information - PyRIGS: Base app, stores 'global' information
- RIGS: Rigboard stuff - event calendar etc - RIGS: Rigboard stuff - event calendar etc
- assets: Database of our kit, testing data etc - assets: Database of our kit, testing data etc
- training: Logs in-house training within various "departments" (sound, lighting etc).
- versioning: Our custom logic built on top of django-reversion. Semi-modular. - versioning: Our custom logic built on top of django-reversion. Semi-modular.
- users: Our custom logic for registration and profiles. Semi-modular. - users: Our custom logic for registration and profiles. Semi-modular.
- training: SoonTM
[![forthebadge](https://forthebadge.com/images/badges/built-with-resentment.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/contains-technical-debt.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/built-with-resentment.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/contains-technical-debt.svg)](https://forthebadge.com)

View File

@@ -154,9 +154,8 @@ class AssociateAdmin(VersionAdmin):
@admin.register(models.Profile) @admin.register(models.Profile)
class ProfileAdmin(UserAdmin, AssociateAdmin): class ProfileAdmin(UserAdmin, AssociateAdmin):
list_display = ('username', 'name', 'is_approved', 'is_superuser', 'is_supervisor', 'number_of_events', 'last_login') list_display = ('username', 'name', 'is_approved', 'is_staff', 'is_superuser', 'is_supervisor', 'number_of_events')
list_display_links = ['username'] list_display_links = ['username']
list_filter = UserAdmin.list_filter + ('is_approved',)
fieldsets = ( fieldsets = (
(None, {'fields': ('username', 'password')}), (None, {'fields': ('username', 'password')}),
(_('Personal info'), { (_('Personal info'), {

View File

@@ -121,7 +121,7 @@ class EventForm(forms.ModelForm):
fields = ['is_rig', 'name', 'venue', 'start_time', 'end_date', 'start_date', fields = ['is_rig', 'name', 'venue', 'start_time', 'end_date', 'start_date',
'end_time', 'meet_at', 'access_at', 'description', 'notes', 'mic', 'end_time', 'meet_at', 'access_at', 'description', 'notes', 'mic',
'person', 'organisation', 'dry_hire', 'checked_in_by', 'status', 'person', 'organisation', 'dry_hire', 'checked_in_by', 'status',
'purchase_order', 'collector', 'forum_url'] 'purchase_order', 'collector']
class BaseClientEventAuthorisationForm(forms.ModelForm): class BaseClientEventAuthorisationForm(forms.ModelForm):

View File

@@ -1,4 +1,4 @@
# Generated by Django 3.2.19 on 2023-05-29 10:23 # Generated by Django 3.2.19 on 2023-05-20 10:36
from django.db import migrations, models from django.db import migrations, models
@@ -13,7 +13,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='powertestrecord', model_name='powertestrecord',
name='fd_earth_fault', name='fd_earth_fault',
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=6, null=True, verbose_name='Earth Fault Loop Impedance'), field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='powertestrecord', model_name='powertestrecord',
@@ -23,7 +23,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='powertestrecord', model_name='powertestrecord',
name='w1_earth_fault', name='w1_earth_fault',
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=6, null=True, verbose_name='Earth Fault Loop Impedance'), field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='powertestrecord', model_name='powertestrecord',
@@ -33,7 +33,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='powertestrecord', model_name='powertestrecord',
name='w2_earth_fault', name='w2_earth_fault',
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=6, null=True, verbose_name='Earth Fault Loop Impedance'), field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='powertestrecord', model_name='powertestrecord',
@@ -43,7 +43,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='powertestrecord', model_name='powertestrecord',
name='w3_earth_fault', name='w3_earth_fault',
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=6, null=True, verbose_name='Earth Fault Loop Impedance'), field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='powertestrecord', model_name='powertestrecord',

View File

@@ -0,0 +1,33 @@
# Generated by Django 3.2.19 on 2023-05-20 14:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0049_auto_20230520_1136'),
]
operations = [
migrations.AlterField(
model_name='powertestrecord',
name='fd_earth_fault',
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=6, null=True, verbose_name='Earth Fault Loop Impedance'),
),
migrations.AlterField(
model_name='powertestrecord',
name='w1_earth_fault',
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=6, null=True, verbose_name='Earth Fault Loop Impedance'),
),
migrations.AlterField(
model_name='powertestrecord',
name='w2_earth_fault',
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=6, null=True, verbose_name='Earth Fault Loop Impedance'),
),
migrations.AlterField(
model_name='powertestrecord',
name='w3_earth_fault',
field=models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>) / Ω', max_digits=6, null=True, verbose_name='Earth Fault Loop Impedance'),
),
]

View File

@@ -1,19 +0,0 @@
# Generated by Django 3.2.19 on 2023-06-27 11:28
import RIGS.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0049_auto_20230529_1123'),
]
operations = [
migrations.AddField(
model_name='event',
name='forum_url',
field=models.URLField(blank=True, default='', validators=[RIGS.models.validate_forum_url]),
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.2.19 on 2023-07-09 21:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0050_event_forum_url'),
]
operations = [
migrations.AlterField(
model_name='payment',
name='method',
field=models.CharField(blank=True, choices=[('C', 'Cash'), ('I', 'Internal'), ('E', 'External'), ('T', 'TEC Adjustment')], default='', max_length=2),
),
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.2.21 on 2023-09-05 22:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0051_alter_payment_method'),
]
operations = [
migrations.AddField(
model_name='venue',
name='on_campus',
field=models.BooleanField(default=False, verbose_name='Is this venue on a UoN campus?'),
),
]

View File

@@ -76,8 +76,7 @@ class Profile(AbstractUser):
@classmethod @classmethod
def users_awaiting_approval_count(cls): def users_awaiting_approval_count(cls):
# last_login = None ensures we only pick up genuinely new users, not those that have been deactivated for inactivity return Profile.objects.filter(models.Q(is_approved=False)).count()
return Profile.objects.filter(is_approved=False, last_login=None).count()
def __str__(self): def __str__(self):
return self.name return self.name
@@ -213,7 +212,6 @@ class Venue(models.Model, RevisionMixin):
phone = models.CharField(max_length=15, blank=True, default='') phone = models.CharField(max_length=15, blank=True, default='')
email = models.EmailField(blank=True, default='') email = models.EmailField(blank=True, default='')
three_phase_available = models.BooleanField(default=False) three_phase_available = models.BooleanField(default=False)
on_campus = models.BooleanField(default=False, verbose_name="Is this venue on a UoN campus?")
notes = models.TextField(blank=True, default='') notes = models.TextField(blank=True, default='')
address = models.TextField(blank=True, default='') address = models.TextField(blank=True, default='')
@@ -310,14 +308,6 @@ class EventManager(models.Manager):
return qs return qs
def validate_forum_url(value):
if not value:
return # Required error is done the field
obj = urlparse(value)
if obj.hostname not in ('forum.nottinghamtec.co.uk'):
raise ValidationError('URL must point to a location on the TEC Forum')
@reversion.register(follow=['items']) @reversion.register(follow=['items'])
class Event(models.Model, RevisionMixin): class Event(models.Model, RevisionMixin):
# Done to make it much nicer on the database # Done to make it much nicer on the database
@@ -367,8 +357,6 @@ class Event(models.Model, RevisionMixin):
auth_request_at = models.DateTimeField(null=True, blank=True) auth_request_at = models.DateTimeField(null=True, blank=True)
auth_request_to = models.EmailField(blank=True, default='') auth_request_to = models.EmailField(blank=True, default='')
forum_url = models.URLField(default='', blank=True, validators=[validate_forum_url])
@property @property
def display_id(self): def display_id(self):
if self.pk: if self.pk:
@@ -517,7 +505,7 @@ class Event(models.Model, RevisionMixin):
return reverse('event_detail', kwargs={'pk': self.pk}) return reverse('event_detail', kwargs={'pk': self.pk})
def __str__(self): def __str__(self):
return f"{self.display_id} | {self.name}" return f"{self.display_id}: {self.name}"
def clean(self): def clean(self):
errdict = {} errdict = {}
@@ -689,11 +677,13 @@ class Payment(models.Model, RevisionMixin):
CASH = 'C' CASH = 'C'
INTERNAL = 'I' INTERNAL = 'I'
EXTERNAL = 'E' EXTERNAL = 'E'
SUCORE = 'SU'
ADJUSTMENT = 'T' ADJUSTMENT = 'T'
METHODS = ( METHODS = (
(CASH, 'Cash'), (CASH, 'Cash'),
(INTERNAL, 'Internal'), (INTERNAL, 'Internal'),
(EXTERNAL, 'External'), (EXTERNAL, 'External'),
(SUCORE, 'SU Core'),
(ADJUSTMENT, 'TEC Adjustment'), (ADJUSTMENT, 'TEC Adjustment'),
) )

View File

@@ -1,5 +0,0 @@
{% extends 'base_client.html' %}
{% block content %}
{% include 'estates/estates_event_table.html' %}
{% endblock %}

View File

@@ -1,78 +0,0 @@
{% load namewithnotes from filters %}
{% load markdown_tags %}
<div class="table-responsive">
<table class="table mb-0" id="event_table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Dates & Times</th>
<th scope="col">Event Details</th>
<th scope="col">Status</th>
<th scope="col">Member In Charge</th>
<th scope="col">Power Plan</th>
</tr>
</thead>
<tbody>
{% for event in events %}
<tr {% if event.cancelled %}style="opacity: 50% !important;"{% endif %} id="event_row">
<!---Number-->
<th scope="row" id="event_number">{{ event.display_id }}</th>
<!--Dates & Times-->
<td id="event_dates" style="text-align: justify;">
<span class="text-nowrap">Start: <strong>{{ event.start_date|date:"D d/m/Y" }}
{% if event.has_start_time %}
{{ event.start_time|date:"H:i" }}
{% endif %}</strong>
</span>
{% if event.end_date %}
<br>
<span class="text-nowrap">End: {% if event.end_date != event.start_date %}<strong>{{ event.end_date|date:"D d/m/Y" }}{% endif %}
{% if event.has_end_time %}
{{ event.end_time|date:"H:i" }}
{% endif %}</strong>
</span>
{% endif %}
</td>
<!---Details-->
<td id="event_details" class="w-100">
<h4>
{{ event.name }}
{% if event.venue %}
<small>at {{ event.venue }}</small>
{% endif %}
</h4>
{% if event.is_rig and not event.cancelled %}
<h5>
{{ event.person.name }}
{% if event.organisation %}
for {{ event.organisation.name }}
{% endif %}
</h5>
{% endif %}
{% if not event.cancelled and event.description %}
<p>{{ event.description|markdown }}</p>
{% endif %}
</td>
<td>
{{ event.get_status_display }}
</td>
<!---MIC-->
<td id="event_mic" class="text-nowrap">
{% if event.mic %}
{{ event.mic }}
{% elif event.is_rig %}
<span class="fas fa-user-slash"></span>
{% endif %}
</td>
<td>
{{ event.riskassessment.power_plan|default:"Pending" }}
</td>
</tr>
{% empty %}
<tr class="bg-warning">
<td colspan="4">No events found</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

View File

@@ -231,7 +231,7 @@
<label for="{{ form.start_date.id_for_label }}" <label for="{{ form.start_date.id_for_label }}"
class="col-sm-4 col-form-label">{{ form.start_date.label }}</label> class="col-sm-4 col-form-label">{{ form.start_date.label }}</label>
<div class="col-sm-10"> <div class="col-sm-8">
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-7" data-toggle="tooltip" title="Start date for event, required"> <div class="col-sm-12 col-md-7" data-toggle="tooltip" title="Start date for event, required">
{% render_field form.start_date class+="form-control" %} {% render_field form.start_date class+="form-control" %}
@@ -246,7 +246,7 @@
<label for="{{ form.end_date.id_for_label }}" <label for="{{ form.end_date.id_for_label }}"
class="col-sm-4 col-form-label">{{ form.end_date.label }}</label> class="col-sm-4 col-form-label">{{ form.end_date.label }}</label>
<div class="col-sm-10"> <div class="col-sm-8">
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-7" data-toggle="tooltip" title="End date of event, leave blank if unknown or same as start date"> <div class="col-sm-12 col-md-7" data-toggle="tooltip" title="End date of event, leave blank if unknown or same as start date">
{% render_field form.end_date class+="form-control" %} {% render_field form.end_date class+="form-control" %}
@@ -334,26 +334,12 @@
<div class="form-group" data-toggle="tooltip" title="The purchase order number (for external clients)"> <div class="form-group" data-toggle="tooltip" title="The purchase order number (for external clients)">
<label for="{{ form.purchase_order.id_for_label }}" <label for="{{ form.purchase_order.id_for_label }}"
class="col-sm-4 col-form-label">{{ form.purchase_order.label }}</label> class="col-sm-4 col-fitem_tableorm-label">{{ form.purchase_order.label }}</label>
<div class="col-sm-8"> <div class="col-sm-8">
{% render_field form.purchase_order class+="form-control" %} {% render_field form.purchase_order class+="form-control" %}
</div> </div>
</div> </div>
<div class="form-group" data-toggle="tooltip" title="The thread for this event on the TEC Forum">
<label for="{{ form.forum_url.id_for_label }}"
class="col-sm-4 col-form-label">Forum Thread</label>
<div class="col-sm-12">
<p class="small mb-0">Paste URL</p>
{% render_field form.forum_url class+="form-control" %}
{% if object.pk %}
<p class="small mb-0">or</p>
<a href="{% url 'event_thread' object.pk %}" class="btn btn-primary" title="Create Forum Thread" target="_blank">
<span class="fas fa-plus"></span> <span class="hidden-xs">Create Forum Thread</span></a>
{% endif %}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -165,11 +165,11 @@
</div> </div>
</div> </div>
<div class="col-12 text-right"> <div class="col-12 text-right">
{% button 'edit' url='pt_edit' pk=object.pk %} {% button 'edit' url='ec_edit' pk=object.pk %}
{% button 'view' url='event_detail' pk=object.event.pk text="Event" %} {% button 'view' url='event_detail' pk=object.pk text="Event" %}
{% include 'partials/review_status.html' with perm=perms.RIGS.review_power review='pt_review' %} {% include 'partials/review_status.html' with perm=perms.RIGS.review_eventchecklist review='ec_review' %}
</div> </div>
<div class="col-12 text-right"> <div class="col-12 text-right">
{% include 'partials/last_edited.html' with target="powertestrecord_history" %} {% include 'partials/last_edited.html' with target="eventchecklist_history" %}
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -77,15 +77,6 @@
<dt class="col-sm-6">PO</dt> <dt class="col-sm-6">PO</dt>
<dd class="col-sm-6">{{ object.purchase_order }}</dd> <dd class="col-sm-6">{{ object.purchase_order }}</dd>
{% endif %} {% endif %}
<dt class="col-6">Forum Thread</dt>
{% if object.forum_url %}
<dd class="col-6"><a href="{{object.forum_url}}">{{object.forum_url}}</a></dd>
{% else %}
<a href="{% url 'event_thread' object.pk %}" class="btn btn-primary" title="Create Forum Thread" target="_blank"><span
class="fas fa-plus"></span> <span
class="hidden-xs">Create Forum Thread</span></a>
{% endif %}
</dl> </dl>
</div> </div>
</div> </div>

View File

@@ -29,15 +29,7 @@
</div> </div>
<div class="row pt-3"> <div class="row pt-3">
<label class="col-sm-4 col-form-label" <label class="col-sm-4 col-form-label"
for="{{ form.method.id_for_label }}">{{ form.method.label }} for="{{ form.method.id_for_label }}">{{ form.method.label }}</label>
<span class="fas fa-info-circle text-info" data-toggle="collapse" data-target="#collapse" aria-expanded="false" aria-controls="collapse"></span>
<ul class="collapse" id="collapse">
<li>Cash - Self Explanatory</li>
<li>Internal - Transfers within the Students' Union only</li>
<li>External - All other transfers (<em>including</em> the University)</li>
<li>TEC Adjustment - Manual corrections</li>
</ul>
</label>
<div class="col-sm-8"> <div class="col-sm-8">
{% render_field form.method class+="form-control" %} {% render_field form.method class+="form-control" %}
</div> </div>

View File

@@ -372,7 +372,7 @@ def test_ra_redirect(admin_client, admin_user, ra):
class TestMarkdownTemplateTags(TestCase): class TestMarkdownTemplateTags(TestCase):
with open(os.path.join(settings.BASE_DIR, "RIGS/tests/sample.md"), encoding="utf-8") as f: with open(os.path.join(settings.BASE_DIR, "RIGS/tests/sample.md")) as f:
markdown = f.read() markdown = f.read()
def test_html_safe(self): def test_html_safe(self):

View File

@@ -4,7 +4,7 @@ from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import RedirectView from django.views.generic import RedirectView
from PyRIGS.decorators import (api_key_required, has_oembed, from PyRIGS.decorators import (api_key_required, has_oembed,
permission_required_with_403, not_estates) permission_required_with_403)
from . import views from . import views
urlpatterns = [ urlpatterns = [
@@ -42,22 +42,21 @@ urlpatterns = [
name='venue_update'), name='venue_update'),
# Rigboard # Rigboard
path('rigboard/', not_estates()(views.RigboardIndex.as_view()), name='rigboard'), path('rigboard/', login_required(views.RigboardIndex.as_view()), name='rigboard'),
path('rigboard/calendar/', not_estates()(views.WebCalendar.as_view()), path('rigboard/calendar/', login_required()(views.WebCalendar.as_view()),
name='web_calendar'), name='web_calendar'),
re_path(r'^rigboard/calendar/(?P<view>(month|week|day))/$', re_path(r'^rigboard/calendar/(?P<view>(month|week|day))/$',
not_estates()(views.WebCalendar.as_view()), name='web_calendar'), login_required()(views.WebCalendar.as_view()), name='web_calendar'),
re_path(r'^rigboard/calendar/(?P<view>(month|week|day))/(?P<date>(\d{4}-\d{2}-\d{2}))/$', re_path(r'^rigboard/calendar/(?P<view>(month|week|day))/(?P<date>(\d{4}-\d{2}-\d{2}))/$',
not_estates()(views.WebCalendar.as_view()), name='web_calendar'), login_required()(views.WebCalendar.as_view()), name='web_calendar'),
path('rigboard/archive/', RedirectView.as_view(permanent=True, pattern_name='event_archive')), path('rigboard/archive/', RedirectView.as_view(permanent=True, pattern_name='event_archive')),
path('estates/', login_required()(views.EstatesEventList.as_view()), name='estates'),
path('event/<int:pk>/', has_oembed(oembed_view="event_oembed")(views.EventDetail.as_view()), path('event/<int:pk>/', has_oembed(oembed_view="event_oembed")(views.EventDetail.as_view()),
name='event_detail'), name='event_detail'),
path('event/create/', permission_required_with_403('RIGS.add_event')(views.EventCreate.as_view()), path('event/create/', permission_required_with_403('RIGS.add_event')(views.EventCreate.as_view()),
name='event_create'), name='event_create'),
path('event/archive/', not_estates()(views.EventArchive.as_view()), path('event/archive/', login_required()(views.EventArchive.as_view()),
name='event_archive'), name='event_archive'),
path('event/<int:pk>/embed/', path('event/<int:pk>/embed/',
xframe_options_exempt(login_required(login_url='/user/login/embed/')(views.EventEmbed.as_view())), xframe_options_exempt(login_required(login_url='/user/login/embed/')(views.EventEmbed.as_view())),
@@ -76,7 +75,7 @@ urlpatterns = [
path('event/<int:pk>/ra/', permission_required_with_403('RIGS.add_riskassessment')(views.EventRiskAssessmentCreate.as_view()), path('event/<int:pk>/ra/', permission_required_with_403('RIGS.add_riskassessment')(views.EventRiskAssessmentCreate.as_view()),
name='event_ra'), name='event_ra'),
path('event/ra/<int:pk>/', not_estates()(views.EventRiskAssessmentDetail.as_view()), path('event/ra/<int:pk>/', login_required(views.EventRiskAssessmentDetail.as_view()),
name='ra_detail'), name='ra_detail'),
path('event/ra/<int:pk>/edit/', permission_required_with_403('RIGS.change_riskassessment')(views.EventRiskAssessmentEdit.as_view()), path('event/ra/<int:pk>/edit/', permission_required_with_403('RIGS.change_riskassessment')(views.EventRiskAssessmentEdit.as_view()),
name='ra_edit'), name='ra_edit'),
@@ -86,7 +85,7 @@ urlpatterns = [
path('event/<int:pk>/checklist/', permission_required_with_403('RIGS.add_eventchecklist')(views.EventChecklistCreate.as_view()), path('event/<int:pk>/checklist/', permission_required_with_403('RIGS.add_eventchecklist')(views.EventChecklistCreate.as_view()),
name='event_ec'), name='event_ec'),
path('event/checklist/<int:pk>/', not_estates()(views.EventChecklistDetail.as_view()), path('event/checklist/<int:pk>/', login_required(views.EventChecklistDetail.as_view()),
name='ec_detail'), name='ec_detail'),
path('event/checklist/<int:pk>/edit/', permission_required_with_403('RIGS.change_eventchecklist')(views.EventChecklistEdit.as_view()), path('event/checklist/<int:pk>/edit/', permission_required_with_403('RIGS.change_eventchecklist')(views.EventChecklistEdit.as_view()),
name='ec_edit'), name='ec_edit'),
@@ -95,25 +94,22 @@ urlpatterns = [
path('event/<int:pk>/power/', permission_required_with_403('RIGS.add_powertestrecord')(views.PowerTestCreate.as_view()), path('event/<int:pk>/power/', permission_required_with_403('RIGS.add_powertestrecord')(views.PowerTestCreate.as_view()),
name='event_pt'), name='event_pt'),
path('event/power/<int:pk>/', not_estates()(views.PowerTestDetail.as_view()), path('event/power/<int:pk>/', login_required(views.PowerTestDetail.as_view()),
name='pt_detail'), name='pt_detail'),
path('event/power/<int:pk>/edit/', permission_required_with_403('RIGS.change_powertestrecord')(views.PowerTestEdit.as_view()), path('event/power/<int:pk>/edit/', permission_required_with_403('RIGS.change_powertestrecord')(views.PowerTestEdit.as_view()),
name='pt_edit'), name='pt_edit'),
path('event/power/<int:pk>/review/', permission_required_with_403('RIGS.review_power')(views.MarkReviewed.as_view()), path('event/power/<int:pk>/review/', permission_required_with_403('RIGS.review_power')(views.MarkReviewed.as_view()),
name='pt_review', kwargs={'model': 'PowerTestRecord'}), name='pt_review', kwargs={'model': 'PowerTestRecord'}),
path('event/<int:pk>/checkin/', not_estates()(views.EventCheckIn.as_view()), path('event/<int:pk>/checkin/', login_required(views.EventCheckIn.as_view()),
name='event_checkin'), name='event_checkin'),
path('event/checkout/', not_estates()(views.EventCheckOut.as_view()), path('event/checkout/', login_required(views.EventCheckOut.as_view()),
name='event_checkout'), name='event_checkout'),
path('event/<int:pk>/checkin/edit/', not_estates()(views.EventCheckInEdit.as_view()), path('event/<int:pk>/checkin/edit/', login_required(views.EventCheckInEdit.as_view()),
name='edit_checkin'), name='edit_checkin'),
path('event/<int:pk>/checkin/add/', not_estates()(views.EventCheckInOverride.as_view()), path('event/<int:pk>/checkin/add/', login_required(views.EventCheckInOverride.as_view()),
name='event_checkin_override'), name='event_checkin_override'),
path('event/<int:pk>/thread/', permission_required_with_403('RIGS.change_event')(views.CreateForumThread.as_view()), name='event_thread'),
path('event/webhook/', views.RecieveForumWebhook.as_view(), name='webhook_recieve'),
# Finance # Finance
path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceIndex.as_view()), path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceIndex.as_view()),
name='invoice_list'), name='invoice_list'),

View File

@@ -115,7 +115,7 @@ class VenueDetail(GenericDetailView):
class VenueCreate(GenericCreateView, ModalURLMixin): class VenueCreate(GenericCreateView, ModalURLMixin):
model = models.Venue model = models.Venue
fields = ['name', 'phone', 'email', 'address', 'notes', 'three_phase_available', 'on_campus'] fields = ['name', 'phone', 'email', 'address', 'notes', 'three_phase_available']
def get_success_url(self): def get_success_url(self):
return self.get_close_url('venue_update', 'venue_detail') return self.get_close_url('venue_update', 'venue_detail')
@@ -123,7 +123,7 @@ class VenueCreate(GenericCreateView, ModalURLMixin):
class VenueUpdate(GenericUpdateView, ModalURLMixin): class VenueUpdate(GenericUpdateView, ModalURLMixin):
model = models.Venue model = models.Venue
fields = ['name', 'phone', 'email', 'address', 'notes', 'three_phase_available', 'on_campus'] fields = ['name', 'phone', 'email', 'address', 'notes', 'three_phase_available']
def get_success_url(self): def get_success_url(self):
return self.get_close_url('venue_update', 'venue_detail') return self.get_close_url('venue_update', 'venue_detail')

View File

@@ -53,6 +53,12 @@ class EventRiskAssessmentCreate(HSCreateView):
def get_success_url(self): def get_success_url(self):
return reverse('ra_detail', kwargs={'pk': self.object.pk}) return reverse('ra_detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if context['event'].mic:
context['power_mic'] = context['event'].mic
return context
class EventRiskAssessmentEdit(generic.UpdateView): class EventRiskAssessmentEdit(generic.UpdateView):
model = models.RiskAssessment model = models.RiskAssessment
@@ -166,7 +172,7 @@ class PowerTestEdit(generic.UpdateView):
ec.reviewed_by = None ec.reviewed_by = None
ec.reviewed_at = None ec.reviewed_at = None
ec.save() ec.save()
return reverse('pt_detail', kwargs={'pk': self.object.pk}) return reverse('ec_detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)

View File

@@ -3,12 +3,6 @@ import datetime
import re import re
import premailer import premailer
import simplejson import simplejson
import urllib
import hmac
import hashlib
from envparse import env
from bs4 import BeautifulSoup
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
@@ -25,8 +19,6 @@ from django.urls import reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views import generic from django.views import generic
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.mixins import UserPassesTestMixin
from PyRIGS import decorators from PyRIGS import decorators
from PyRIGS.views import OEmbedView, is_ajax, ModalURLMixin, PrintView, get_related from PyRIGS.views import OEmbedView, is_ajax, ModalURLMixin, PrintView, get_related
@@ -385,55 +377,3 @@ class EventAuthoriseRequestEmailPreview(generic.DetailView):
context['to_name'] = self.request.GET.get('to_name', None) context['to_name'] = self.request.GET.get('to_name', None)
context['target'] = 'event_authorise_form_preview' context['target'] = 'event_authorise_form_preview'
return context return context
class CreateForumThread(generic.base.RedirectView):
permanent = False
def get_redirect_url(self, *args, **kwargs):
event = get_object_or_404(models.Event, pk=kwargs['pk'])
if event.forum_url:
return event.forum_url
params = {
'title': str(event),
'body': f'https://rigs.nottinghamtec.co.uk/event/{event.pk}',
'category': 'rig-info'
}
return f'https://forum.nottinghamtec.co.uk/new-topic?{urllib.parse.urlencode(params)}'
class RecieveForumWebhook(generic.View):
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
computed = f"sha256={hmac.new(env('FORUM_WEBHOOK_SECRET').encode(), request.body, hashlib.sha256).hexdigest()}"
if not hmac.compare_digest(request.headers.get('X-Discourse-Event-Signature'), computed):
return HttpResponseForbidden('Invalid signature header')
# Check if this is the right kind of event. The webhook filters by category on the forum side
if request.headers.get('X-Discourse-Event') == "topic_created":
body = simplejson.loads(request.body.decode('utf-8'))
event_id = int(body['topic']['title'][1:6]) # find the ID, force convert it to an int to eliminate leading zeros
event = models.Event.objects.filter(pk=event_id).first()
if event:
event.forum_url = f"https://forum.nottinghamtec.co.uk/t/{body['topic']['slug']}"
event.save()
return HttpResponse(status=202)
return HttpResponse(status=204)
class EstatesEventList(UserPassesTestMixin, generic.TemplateView):
template_name = 'estates/estates_event_list.html'
def get_context_data(self, **kwargs):
# get super context
context = super().get_context_data(**kwargs)
# call out method to get current events
context['events'] = models.Event.objects.current_events().filter(venue__on_campus=True, dry_hire=False, is_rig=True)
context['page_title'] = "Upcoming Campus Events"
return context
def test_func(self):
return self.request.user.email.endswith('@nottingham.ac.uk')

View File

@@ -1,18 +0,0 @@
# Generated by Django 3.2.19 on 2023-05-24 22:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0027_asset_nickname'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='length',
field=models.DecimalField(blank=True, decimal_places=2, help_text='m', max_digits=10, null=True),
),
]

View File

@@ -135,7 +135,7 @@ class Asset(models.Model, RevisionMixin):
# Cable assets # Cable assets
is_cable = models.BooleanField(default=False) is_cable = models.BooleanField(default=False)
cable_type = models.ForeignKey(to=CableType, blank=True, null=True, on_delete=models.SET_NULL) cable_type = models.ForeignKey(to=CableType, blank=True, null=True, on_delete=models.SET_NULL)
length = models.DecimalField(decimal_places=2, max_digits=10, length = models.DecimalField(decimal_places=1, max_digits=10,
blank=True, null=True, help_text='m') blank=True, null=True, help_text='m')
csa = models.DecimalField(decimal_places=2, max_digits=10, csa = models.DecimalField(decimal_places=2, max_digits=10,
blank=True, null=True, help_text='mm²') blank=True, null=True, help_text='mm²')
@@ -192,5 +192,5 @@ class Asset(models.Model, RevisionMixin):
return str(self.asset_id) return str(self.asset_id)
@property @property
def display_name(self): def name(self):
return f"{self.display_id} | {self.description}" return f"{self.display_id} | {self.description}"

View File

@@ -12,6 +12,7 @@
{{ block.super }} {{ block.super }}
<script src="{% static 'js/selects.js' %}"></script> <script src="{% static 'js/selects.js' %}"></script>
<script src="{% static 'js/easymde.min.js' %}"></script> <script src="{% static 'js/easymde.min.js' %}"></script>
<script src="{% static 'js/interaction.js' %}"></script>
{% endblock %} {% endblock %}
{% block js %} {% block js %}
@@ -34,6 +35,11 @@
$(document).find(".selectpicker").selectpicker().each(function(){initPicker($(this))}); $(document).find(".selectpicker").selectpicker().each(function(){initPicker($(this))});
}); });
</script> </script>
<script>
$(document).ready(function () {
setupMDE('#id_comments');
});
</script>
<script src="{% static "js/tooltip.js" %}"></script> <script src="{% static "js/tooltip.js" %}"></script>
<script> <script>
$(function () { $(function () {

View File

@@ -38,17 +38,3 @@ def test_asset(db, category, status):
asset, created = models.Asset.objects.get_or_create(asset_id="91991", description="Spaceflower", status=status, category=category, date_acquired=datetime.date(1991, 12, 26), replacement_cost=100) asset, created = models.Asset.objects.get_or_create(asset_id="91991", description="Spaceflower", status=status, category=category, date_acquired=datetime.date(1991, 12, 26), replacement_cost=100)
yield asset yield asset
asset.delete() asset.delete()
@pytest.fixture
def test_status_2(db):
status = models.AssetStatus.objects.create(name="Lost", should_show=False)
yield status
status.delete()
@pytest.fixture
def test_asset_2(db, category, test_status_2):
asset, created = models.Asset.objects.get_or_create(asset_id="10", description="Working Mic", status=test_status_2, category=category, date_acquired=datetime.date(2001, 10, 20), replacement_cost=1000)
yield asset
asset.delete()

View File

@@ -77,7 +77,7 @@ class AssetForm(FormPage):
'description': (regions.TextBox, (By.ID, 'id_description')), 'description': (regions.TextBox, (By.ID, 'id_description')),
'is_cable': (regions.CheckBox, (By.ID, 'id_is_cable')), 'is_cable': (regions.CheckBox, (By.ID, 'id_is_cable')),
'serial_number': (regions.TextBox, (By.ID, 'id_serial_number')), 'serial_number': (regions.TextBox, (By.ID, 'id_serial_number')),
'comments': (regions.TextBox, (By.ID, 'id_comments')), 'comments': (regions.SimpleMDETextArea, (By.ID, 'id_comments')),
'purchase_price': (regions.TextBox, (By.ID, 'id_purchase_price')), 'purchase_price': (regions.TextBox, (By.ID, 'id_purchase_price')),
'replacement_cost': (regions.TextBox, (By.ID, 'id_replacement_cost')), 'replacement_cost': (regions.TextBox, (By.ID, 'id_replacement_cost')),
'date_acquired': (regions.DatePicker, (By.ID, 'id_date_acquired')), 'date_acquired': (regions.DatePicker, (By.ID, 'id_date_acquired')),

View File

@@ -1,6 +1,5 @@
import time import time
import datetime import datetime
import pytest
from django.utils import timezone from django.utils import timezone
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
@@ -54,45 +53,45 @@ class TestAssetList(AutoLoginTest):
self.assertEqual("10", asset_ids[2]) self.assertEqual("10", asset_ids[2])
self.assertEqual("C1", asset_ids[3]) self.assertEqual("C1", asset_ids[3])
def test_search(self):
self.page.set_query("10")
self.page.search()
self.assertTrue(len(self.page.assets) == 1)
self.assertEqual("Working Mic", self.page.assets[0].description)
self.assertEqual("10", self.page.assets[0].id)
@pytest.mark.xfail(reason="Fails on CI for unknown reason", raises=AssertionError) self.page.set_query("light")
def test_search(logged_in_browser, admin_user, live_server, test_asset, test_asset_2, category, status, cable_type): self.page.search()
page = pages.AssetList(logged_in_browser.driver, live_server.url).open() self.assertTrue(len(self.page.assets) == 1)
page.set_query(test_asset.asset_id) self.assertEqual("A light", self.page.assets[0].description)
page.search()
assert len(page.assets) == 1
assert page.assets[0].description == test_asset.description
assert page.assets[0].id == test_asset.asset_id
page.set_query(test_asset.description) self.page.set_query("Random string")
page.search() self.page.search()
assert len(page.assets) == 1 self.assertTrue(len(self.page.assets) == 0)
assert page.assets[0].description == test_asset.description
page.set_query("Random string") self.page.set_query("")
page.search() self.page.search()
assert len(page.assets) == 0
page.set_query("")
page.search()
# Only working stuff shown by default # Only working stuff shown by default
assert len(page.assets) == 1 self.assertTrue(len(self.page.assets) == 2)
page.status_selector.toggle() self.page.status_selector.toggle()
assert page.status_selector.is_open self.assertTrue(self.page.status_selector.is_open)
page.status_selector.select_all() self.page.status_selector.select_all()
page.status_selector.toggle() self.page.status_selector.toggle()
assert not page.status_selector.is_open self.assertFalse(self.page.status_selector.is_open)
page.filter() self.page.filter()
assert len(page.assets) == 2 self.assertTrue(len(self.page.assets) == 4)
page.category_selector.toggle() self.page.category_selector.toggle()
assert page.category_selector.is_open self.assertTrue(self.page.category_selector.is_open)
page.category_selector.set_option(category.name, True) self.page.category_selector.set_option("Sound", True)
page.category_selector.close() self.page.category_selector.close()
assert not page.category_selector.is_open self.assertFalse(self.page.category_selector.is_open)
page.filter() self.page.filter()
assert len(page.assets) == 2 self.assertTrue(len(self.page.assets) == 2)
asset_ids = list(map(lambda x: x.id, self.page.assets))
self.assertEqual("1", asset_ids[0])
self.assertEqual("10", asset_ids[1])
def test_cable_create(logged_in_browser, admin_user, live_server, test_asset, category, status, cable_type): def test_cable_create(logged_in_browser, admin_user, live_server, test_asset, category, status, cable_type):

View File

@@ -2,7 +2,7 @@ from django.contrib.auth.decorators import login_required
from django.urls import path, register_converter from django.urls import path, register_converter
from django.views.decorators.clickjacking import xframe_options_exempt from django.views.decorators.clickjacking import xframe_options_exempt
from PyRIGS.decorators import has_oembed, permission_required_with_403, not_estates from PyRIGS.decorators import has_oembed, permission_required_with_403
from PyRIGS.views import OEmbedView from PyRIGS.views import OEmbedView
from . import views, converters from . import views, converters
@@ -10,8 +10,8 @@ register_converter(converters.AssetIDConverter, 'asset')
register_converter(converters.ListConverter, 'list') register_converter(converters.ListConverter, 'list')
urlpatterns = [ urlpatterns = [
path('', not_estates()(views.AssetList.as_view()), name='asset_index'), path('', login_required(views.AssetList.as_view()), name='asset_index'),
path('asset/list/', not_estates()(views.AssetList.as_view()), name='asset_list'), path('asset/list/', login_required(views.AssetList.as_view()), name='asset_list'),
path('asset/id/<asset:pk>/', has_oembed(oembed_view="asset_oembed")(views.AssetDetail.as_view()), name='asset_detail'), path('asset/id/<asset:pk>/', has_oembed(oembed_view="asset_oembed")(views.AssetDetail.as_view()), name='asset_detail'),
path('asset/create/', permission_required_with_403('assets.add_asset') path('asset/create/', permission_required_with_403('assets.add_asset')
(views.AssetCreate.as_view()), name='asset_create'), (views.AssetCreate.as_view()), name='asset_create'),
@@ -19,14 +19,14 @@ urlpatterns = [
(views.AssetEdit.as_view()), name='asset_update'), (views.AssetEdit.as_view()), name='asset_update'),
path('asset/id/<asset:pk>/duplicate/', permission_required_with_403('assets.add_asset') path('asset/id/<asset:pk>/duplicate/', permission_required_with_403('assets.add_asset')
(views.AssetDuplicate.as_view()), name='asset_duplicate'), (views.AssetDuplicate.as_view()), name='asset_duplicate'),
path('asset/id/<asset:pk>/label', not_estates()(views.GenerateLabel.as_view()), name='generate_label'), path('asset/id/<asset:pk>/label', login_required(views.GenerateLabel.as_view()), name='generate_label'),
path('asset/<list:ids>/list/label', views.GenerateLabels.as_view(), name='generate_labels'), path('asset/<list:ids>/list/label', views.GenerateLabels.as_view(), name='generate_labels'),
path('cables/list/', not_estates()(views.CableList.as_view()), name='cable_list'), path('cables/list/', login_required(views.CableList.as_view()), name='cable_list'),
path('cabletype/list/', not_estates()(views.CableTypeList.as_view()), name='cable_type_list'), path('cabletype/list/', login_required(views.CableTypeList.as_view()), name='cable_type_list'),
path('cabletype/create/', permission_required_with_403('assets.add_cable_type')(views.CableTypeCreate.as_view()), name='cable_type_create'), path('cabletype/create/', permission_required_with_403('assets.add_cable_type')(views.CableTypeCreate.as_view()), name='cable_type_create'),
path('cabletype/<int:pk>/update/', permission_required_with_403('assets.change_cable_type')(views.CableTypeUpdate.as_view()), name='cable_type_update'), path('cabletype/<int:pk>/update/', permission_required_with_403('assets.change_cable_type')(views.CableTypeUpdate.as_view()), name='cable_type_update'),
path('cabletype/<int:pk>/detail/', not_estates()(views.CableTypeDetail.as_view()), name='cable_type_detail'), path('cabletype/<int:pk>/detail/', login_required(views.CableTypeDetail.as_view()), name='cable_type_detail'),
path('asset/id/<str:pk>/embed/', path('asset/id/<str:pk>/embed/',
xframe_options_exempt( xframe_options_exempt(
@@ -37,8 +37,8 @@ urlpatterns = [
path('asset/audit/', permission_required_with_403('assets.change_asset')(views.AssetAuditList.as_view()), name='asset_audit_list'), path('asset/audit/', permission_required_with_403('assets.change_asset')(views.AssetAuditList.as_view()), name='asset_audit_list'),
path('asset/id/<str:pk>/audit/', permission_required_with_403('assets.change_asset')(views.AssetAudit.as_view()), name='asset_audit'), path('asset/id/<str:pk>/audit/', permission_required_with_403('assets.change_asset')(views.AssetAudit.as_view()), name='asset_audit'),
path('supplier/list/', not_estates()(views.SupplierList.as_view()), name='supplier_list'), path('supplier/list/', login_required(views.SupplierList.as_view()), name='supplier_list'),
path('supplier/<int:pk>/', not_estates()(views.SupplierDetail.as_view()), name='supplier_detail'), path('supplier/<int:pk>/', login_required(views.SupplierDetail.as_view()), name='supplier_detail'),
path('supplier/create/', permission_required_with_403('assets.add_supplier') path('supplier/create/', permission_required_with_403('assets.add_supplier')
(views.SupplierCreate.as_view()), name='supplier_create'), (views.SupplierCreate.as_view()), name='supplier_create'),
path('supplier/<int:pk>/edit/', permission_required_with_403('assets.change_supplier') path('supplier/<int:pk>/edit/', permission_required_with_403('assets.change_supplier')

1294
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@
"jquery": "^3.6.0", "jquery": "^3.6.0",
"konami": "^1.6.3", "konami": "^1.6.3",
"moment": "^2.29.4", "moment": "^2.29.4",
"node-sass": "^9.0.0", "node-sass": "^7.0.3",
"popper.js": "^1.16.1", "popper.js": "^1.16.1",
"postcss": "^8.4.5", "postcss": "^8.4.5",
"uglify-js": "^3.14.5" "uglify-js": "^3.14.5"

View File

@@ -6,6 +6,11 @@ function setupItemTable(items_json) {
newitem = -1; newitem = -1;
} }
function nl2br(str, is_xhtml) {
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
}
function escapeHtml(str) { function escapeHtml(str) {
return $('<div/>').text(str).html(); return $('<div/>').text(str).html();
} }

View File

@@ -1,7 +1,6 @@
{% extends override|default:"base_rigs.html" %} {% extends override|default:"base_rigs.html" %}
{% load widget_tweaks %} {% load widget_tweaks %}
{% load button from filters %} {% load button from filters %}
{% load verbose_name from filters %}
{% load markdown_tags %} {% load markdown_tags %}
{% block content %} {% block content %}
@@ -31,11 +30,6 @@
<dd>{{ object.three_phase_available|yesno|capfirst }}</dd> <dd>{{ object.three_phase_available|yesno|capfirst }}</dd>
{% endif%} {% endif%}
{% if object.on_campus is not None %}
<dt>{{ object|verbose_name:"on_campus" }}</dt>
<dd>{{ object.on_campus|yesno|capfirst }}</dd>
{% endif%}
{% if object.union_account is not None %} {% if object.union_account is not None %}
<dt>Union Account</dt> <dt>Union Account</dt>
<dd>{{ object.union_account|yesno|capfirst }}</dd> <dd>{{ object.union_account|yesno|capfirst }}</dd>

View File

@@ -78,20 +78,6 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% if form.on_campus is not None %}
<div class="form-group form-row">
<div class="col-sm-10 col-sm-offset-2">
<div class="checkbox">
<label>
{% render_field form.on_campus %} {{ form.on_campus.label }}
</label>
</div>
</div>
<div class="alert alert-danger">
<span class="fas fa-exclamation"></span> Selecting this option will add <em>all</em> events at this venue to the calendar viewable by UoN Estates.
</div>
</div>
{% endif %}
{% if form.union_account is not None %} {% if form.union_account is not None %}
<div class="form-group form-row"> <div class="form-group form-row">
<div class="col-sm-10 col-sm-offset-2"> <div class="col-sm-10 col-sm-offset-2">

View File

@@ -11,7 +11,7 @@
{% if now %} {% if now %}
<div class="col-sm-12 alert alert-primary rounded-0 mx-auto"> <div class="col-sm-12 alert alert-primary rounded-0 mx-auto">
{% for event in now %} {% for event in now %}
Event {{ event }} is happening today! <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/> 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 %} {% endfor %}
</div> </div>
{% endif %} {% endif %}

View File

@@ -105,10 +105,6 @@ class TrainingItem(models.Model):
def display_id(self): def display_id(self):
return f"{self.category.reference_number}.{self.reference_number}" return f"{self.category.reference_number}.{self.reference_number}"
@property
def display_name(self):
return f"{self.display_id} | {self.name}"
@display_id.filter @display_id.filter
@classmethod @classmethod
def display_id(cls, lookup, value): def display_id(cls, lookup, value):

View File

@@ -78,6 +78,11 @@
</tr> </tr>
{% endfor %} {% endfor %}
<tr><th colspan="3" class="text-center">{{object}}</th></tr> <tr><th colspan="3" class="text-center">{{object}}</th></tr>
<tr>
<td><ul class="list-unstyled">{% for req in object.started_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 0 %} {% if request.user.is_supervisor %}<a type="button" class="btn btn-link tn-sm p-0 align-baseline" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-trash-alt text-danger"></span></a>{%endif%}</li>{% endfor %}</ul></td>
<td><ul class="list-unstyled">{% for req in object.complete_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 1 %} {% if request.user.is_supervisor %}<a type="button" class="btn btn-link tn-sm p-0 align-baseline" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-trash-alt text-danger"></span></a>{%endif%}</li>{% endfor %}</ul></td>
<td><ul class="list-unstyled">{% for req in object.passed_out_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 2 %} {% if request.user.is_supervisor %}<a type="button" class="btn btn-link tn-sm p-0 align-baseline"" href="{% url 'remove_requirement' pk=req.pk %}" title="Delete requirement"><span class="fas fa-trash-alt text-danger"></span></a>{%endif%}</li>{% endfor %}</ul></td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@@ -8,10 +8,10 @@
<p>Please Note:</p> <p>Please Note:</p>
<ul> <ul>
<li>Technical Assistant status is automatically valid when the item requirements are met.</li> <li>Technical Assistant status is automatically valid when the item requirements are met.</li>
<li>Technician status is also automatic. Notification of completion should be made at the next general meeting.</li> <li>Technician status is also automatic, but notification of status should be made at the next general meeting, at which point 'approval' should be granted on the system.</li>
<li>Supervisor status is <em>not automatically valid</em> and until signed off at a general meeting, does not count.</li> <li>Supervisor status is <em>not automatically valid</em> and until signed off at a general meeting, does not count.</li>
</ul> </ul>
<sup>Correct as of 24th May 2023, check the Training Policy.</sup> <sup>Correct as of 3rd September 2021, check the Training Policy.</sup>
</div> </div>
{% endif %} {% endif %}
{% for level in object_list %} {% for level in object_list %}

View File

@@ -29,7 +29,7 @@ def test_add_qualification_reversion(admin_client, trainee, training_item, super
assert response.status_code == 302 assert response.status_code == 302
qual = models.TrainingItemQualification.objects.last() qual = models.TrainingItemQualification.objects.last()
assert qual is not None assert qual is not None
assert training_item.pk == qual.item_id assert training_itempk == qual.item_id
# Ensure only one revision has been created # Ensure only one revision has been created
assert Revision.objects.count() == 1 assert Revision.objects.count() == 1
response = admin_client.post(url, {'date': date, 'supervisor': supervisor.pk, 'trainee': trainee.pk, 'item': training_item.pk, 'depth': 1}) response = admin_client.post(url, {'date': date, 'supervisor': supervisor.pk, 'trainee': trainee.pk, 'item': training_item.pk, 'depth': 1})

View File

@@ -1,34 +1,33 @@
from django.urls import path from django.urls import path
from django.contrib.auth.decorators import login_required
from training.decorators import is_supervisor from training.decorators import is_supervisor
from training import views, models from training import views, models
from versioning.views import VersionHistory from versioning.views import VersionHistory
from PyRIGS.decorators import not_estates
urlpatterns = [ urlpatterns = [
path('items/', not_estates()(views.ItemList.as_view()), name='item_list'), path('items/', login_required(views.ItemList.as_view()), name='item_list'),
path('items/export/', not_estates()(views.ItemListExport.as_view()), name='item_list_export'), path('items/export/', login_required(views.ItemListExport.as_view()), name='item_list_export'),
path('item/<int:pk>/qualified_users/', not_estates()(views.ItemQualifications.as_view()), name='item_qualification'), path('item/<int:pk>/qualified_users/', login_required(views.ItemQualifications.as_view()), name='item_qualification'),
path('trainee/list/', not_estates()(views.TraineeList.as_view()), name='trainee_list'), path('trainee/list/', login_required(views.TraineeList.as_view()), name='trainee_list'),
path('trainee/<int:pk>/', not_estates()(views.TraineeDetail.as_view()), path('trainee/<int:pk>/', login_required(views.TraineeDetail.as_view()),
name='trainee_detail'), name='trainee_detail'),
path('trainee/<int:pk>/history', not_estates()(VersionHistory.as_view()), name='trainee_history', kwargs={'model': models.Trainee, 'app': 'training'}), # Not picked up automatically because proxy model (I think) path('trainee/<int:pk>/history', login_required(VersionHistory.as_view()), name='trainee_history', kwargs={'model': models.Trainee, 'app': 'training'}), # Not picked up automatically because proxy model (I think)
path('trainee/<int:pk>/add_qualification/', is_supervisor()(views.AddQualification.as_view()), path('trainee/<int:pk>/add_qualification/', is_supervisor()(views.AddQualification.as_view()),
name='add_qualification'), name='add_qualification'),
path('trainee/edit_qualification/<int:pk>/', is_supervisor()(views.EditQualification.as_view()), path('trainee/edit_qualification/<int:pk>/', is_supervisor()(views.EditQualification.as_view()),
name='edit_qualification'), name='edit_qualification'),
path('levels/', not_estates()(views.LevelList.as_view()), name='level_list'), path('levels/', login_required(views.LevelList.as_view()), name='level_list'),
path('level/<int:pk>/', not_estates()(views.LevelDetail.as_view()), name='level_detail'), path('level/<int:pk>/', login_required(views.LevelDetail.as_view()), name='level_detail'),
path('level/<int:pk>/user/<int:u>/', not_estates()(views.LevelDetail.as_view()), name='level_detail'), path('level/<int:pk>/user/<int:u>/', login_required(views.LevelDetail.as_view()), name='level_detail'),
path('level/<int:pk>/add_requirement/', is_supervisor()(views.AddLevelRequirement.as_view()), name='add_requirement'), path('level/<int:pk>/add_requirement/', is_supervisor()(views.AddLevelRequirement.as_view()), name='add_requirement'),
path('level/remove_requirement/<int:pk>/', is_supervisor()(views.RemoveRequirement.as_view()), name='remove_requirement'), path('level/remove_requirement/<int:pk>/', is_supervisor()(views.RemoveRequirement.as_view()), name='remove_requirement'),
path('trainee/<int:pk>/level/<int:level_pk>/confirm', is_supervisor()(views.ConfirmLevel.as_view()), name='confirm_level'), path('trainee/<int:pk>/level/<int:level_pk>/confirm', is_supervisor()(views.ConfirmLevel.as_view()), name='confirm_level'),
path('trainee/<int:pk>/item_record', not_estates()(views.TraineeItemDetail.as_view()), name='trainee_item_detail'), path('trainee/<int:pk>/item_record', login_required(views.TraineeItemDetail.as_view()), name='trainee_item_detail'),
path('session_log', is_supervisor()(views.SessionLog.as_view()), name='session_log'), path('session_log', is_supervisor()(views.SessionLog.as_view()), name='session_log'),
] ]

View File

@@ -265,5 +265,5 @@ class ItemQualifications(generic.ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["page_title"] = f"People Qualified In {models.TrainingItem.objects.get(pk=self.kwargs['pk'])}" context["page_title"] = f"People Qualified In {self.object_list[0].item}"
return context return context

View File

@@ -5,7 +5,7 @@ from django.urls import path
from django.views.decorators.clickjacking import xframe_options_exempt from django.views.decorators.clickjacking import xframe_options_exempt
from registration.backends.default.views import RegistrationView from registration.backends.default.views import RegistrationView
from PyRIGS.decorators import permission_required_with_403, not_estates from PyRIGS.decorators import permission_required_with_403
from users import forms, views from users import forms, views
urlpatterns = [ urlpatterns = [
@@ -14,11 +14,11 @@ urlpatterns = [
path('user/login/', LoginView.as_view(authentication_form=forms.CheckApprovedForm), name='login'), path('user/login/', LoginView.as_view(authentication_form=forms.CheckApprovedForm), name='login'),
path('user/login/embed/', xframe_options_exempt(views.LoginEmbed.as_view()), name='login_embed'), path('user/login/embed/', xframe_options_exempt(views.LoginEmbed.as_view()), name='login_embed'),
# User editing # User editing
path('user/edit/', not_estates()(views.ProfileUpdateSelf.as_view()), path('user/edit/', login_required(views.ProfileUpdateSelf.as_view()),
name='profile_update_self'), name='profile_update_self'),
path('user/reset_api_key', not_estates()(views.ResetApiKey.as_view(permanent=False)), path('user/reset_api_key', login_required(views.ResetApiKey.as_view(permanent=False)),
name='reset_api_key'), name='reset_api_key'),
path('user/', not_estates()(views.ProfileDetail.as_view()), name='profile_detail'), path('user/', login_required(views.ProfileDetail.as_view()), name='profile_detail'),
path('user/<int:pk>/', path('user/<int:pk>/',
permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()), permission_required_with_403('RIGS.view_profile')(views.ProfileDetail.as_view()),
name='profile_detail'), name='profile_detail'),

View File

@@ -1,4 +1,3 @@
import logging
from diff_match_patch import diff_match_patch from diff_match_patch import diff_match_patch
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
@@ -148,9 +147,9 @@ class ModelComparison:
@cached_property @cached_property
def item_changes(self): def item_changes(self):
if self.follow and self.version.object is not None:
from RIGS.models import EventAuthorisation from RIGS.models import EventAuthorisation
from training.models import TrainingLevelQualification, TrainingItemQualification from training.models import TrainingLevelQualification, TrainingItemQualification
if self.follow and self.version.object is not None:
item_type = ContentType.objects.get_for_model(self.version.object) item_type = ContentType.objects.get_for_model(self.version.object)
old_item_versions = self.version.parent.revision.version_set.exclude(content_type=item_type).exclude(content_type=ContentType.objects.get_for_model(TrainingItemQualification)) \ old_item_versions = self.version.parent.revision.version_set.exclude(content_type=item_type).exclude(content_type=ContentType.objects.get_for_model(TrainingItemQualification)) \
.exclude(content_type=ContentType.objects.get_for_model(TrainingLevelQualification)) .exclude(content_type=ContentType.objects.get_for_model(TrainingLevelQualification))
@@ -161,14 +160,10 @@ class ModelComparison:
# Build some dicts of what we have # Build some dicts of what we have
item_dict = {} # build a list of items, key is the item_pk item_dict = {} # build a list of items, key is the item_pk
for version in old_item_versions: # put all the old versions in a list for version in old_item_versions: # put all the old versions in a list
if version._model is None:
continue
compare = ModelComparison(old=version._object_version.object, **comparisonParams) compare = ModelComparison(old=version._object_version.object, **comparisonParams)
item_dict[version.object_id] = compare item_dict[version.object_id] = compare
for version in new_item_versions: # go through the new versions for version in new_item_versions: # go through the new versions
if version._model is None:
continue
try: try:
compare = item_dict[version.object_id] # see if there's a matching old version compare = item_dict[version.object_id] # see if there's a matching old version
compare.new = version._object_version.object # then add the new version to the dictionary compare.new = version._object_version.object # then add the new version to the dictionary