Add and apply black formatter

This commit is contained in:
Adam Goldsmith 2022-01-24 23:37:04 -05:00
parent 0d34d4832a
commit ecdd2f861c
19 changed files with 289 additions and 206 deletions

View File

@ -15,6 +15,7 @@ django-recurrence = "~=1.10"
[dev-packages] [dev-packages]
djlint = "~=0.7" djlint = "~=0.7"
black = "*"
[requires] [requires]
python_version = "3.9" python_version = "3.9"

211
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "38925606631e4d558d3539d27170aaaa686a4dc20fe8ee68f435b93c3ef7c12f" "sha256": "4e1b5c33531624fa18bd7683e300a081a273442b3b1893f89f688ea3aed31da7"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -253,11 +253,11 @@
}, },
"uvicorn": { "uvicorn": {
"hashes": [ "hashes": [
"sha256:0b89c91bb8fe84c4bded9996af13c4b8c0de799d29bffeaa0c8ad298f2be0934", "sha256:60a149248181920a73b2e97aec1dacec5501618867f041a228b2519d91a62a91",
"sha256:192c2422b056a3beb512c6c260bf77a7a884204a4ae41856719c1913ead63bbb" "sha256:fa166e6c3d58e23ff5a1a3543b079c7b28aa057ab1388201e4b34a49ec05da72"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.17.0" "version": "==0.17.0.post1"
}, },
"webencodings": { "webencodings": {
"hashes": [ "hashes": [
@ -276,6 +276,14 @@
} }
}, },
"develop": { "develop": {
"black": {
"hashes": [
"sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3",
"sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"
],
"index": "pypi",
"version": "==21.12b0"
},
"click": { "click": {
"hashes": [ "hashes": [
"sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3",
@ -294,11 +302,18 @@
}, },
"djlint": { "djlint": {
"hashes": [ "hashes": [
"sha256:95715103781a4258ef91bdcb3702e1f5707ca075ceb49b58f0a8ce6b568115c6", "sha256:68aad9ddfef883cc9d9e0d177387b74840af5ca12dcce6e4629eb7075c97dc05",
"sha256:d03cc3d6cd51db9f942211ffbdc85009d33f31e28ea1edbd22954af80d28141a" "sha256:714ed457e022047149c8bff57d5be00ce30f8846b60e866791c66d27e7d11e7f"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.7.2" "version": "==0.7.3"
},
"mypy-extensions": {
"hashes": [
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
],
"version": "==0.4.3"
}, },
"pathspec": { "pathspec": {
"hashes": [ "hashes": [
@ -307,6 +322,14 @@
], ],
"version": "==0.9.0" "version": "==0.9.0"
}, },
"platformdirs": {
"hashes": [
"sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca",
"sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"
],
"markers": "python_version >= '3.7'",
"version": "==2.4.1"
},
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
@ -348,90 +371,98 @@
}, },
"regex": { "regex": {
"hashes": [ "hashes": [
"sha256:0416f7399e918c4b0e074a0f66e5191077ee2ca32a0f99d4c187a62beb47aa05", "sha256:04611cc0f627fc4a50bc4a9a2e6178a974c6a6a4aa9c1cca921635d2c47b9c87",
"sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f", "sha256:0b5d6f9aed3153487252d00a18e53f19b7f52a1651bc1d0c4b5844bc286dfa52",
"sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc", "sha256:0d2f5c3f7057530afd7b739ed42eb04f1011203bc5e4663e1e1d01bb50f813e3",
"sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4", "sha256:11772be1eb1748e0e197a40ffb82fb8fd0d6914cd147d841d9703e2bef24d288",
"sha256:0f594b96fe2e0821d026365f72ac7b4f0b487487fb3d4aaf10dd9d97d88a9737", "sha256:1333b3ce73269f986b1fa4d5d395643810074dc2de5b9d262eb258daf37dc98f",
"sha256:139a23d1f5d30db2cc6c7fd9c6d6497872a672db22c4ae1910be22d4f4b2068a", "sha256:16f81025bb3556eccb0681d7946e2b35ff254f9f888cff7d2120e8826330315c",
"sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4", "sha256:1a171eaac36a08964d023eeff740b18a415f79aeb212169080c170ec42dd5184",
"sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8", "sha256:1d6301f5288e9bdca65fab3de6b7de17362c5016d6bf8ee4ba4cbe833b2eda0f",
"sha256:2409b5c9cef7054dde93a9803156b411b677affc84fca69e908b1cb2c540025d", "sha256:1e031899cb2bc92c0cf4d45389eff5b078d1936860a1be3aa8c94fa25fb46ed8",
"sha256:2fee3ed82a011184807d2127f1733b4f6b2ff6ec7151d83ef3477f3b96a13d03", "sha256:1f8c0ae0a0de4e19fddaaff036f508db175f6f03db318c80bbc239a1def62d02",
"sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f", "sha256:2245441445099411b528379dee83e56eadf449db924648e5feb9b747473f42e3",
"sha256:3598893bde43091ee5ca0a6ad20f08a0435e93a69255eeb5f81b85e81e329264", "sha256:22709d701e7037e64dae2a04855021b62efd64a66c3ceed99dfd684bfef09e38",
"sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a", "sha256:24c89346734a4e4d60ecf9b27cac4c1fee3431a413f7aa00be7c4d7bbacc2c4d",
"sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef", "sha256:25716aa70a0d153cd844fe861d4f3315a6ccafce22b39d8aadbf7fcadff2b633",
"sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f", "sha256:2dacb3dae6b8cc579637a7b72f008bff50a94cde5e36e432352f4ca57b9e54c4",
"sha256:42b50fa6666b0d50c30a990527127334d6b96dd969011e843e726a64011485da", "sha256:34316bf693b1d2d29c087ee7e4bb10cdfa39da5f9c50fa15b07489b4ab93a1b5",
"sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc", "sha256:36b2d700a27e168fa96272b42d28c7ac3ff72030c67b32f37c05616ebd22a202",
"sha256:473e67837f786404570eae33c3b64a4b9635ae9f00145250851a1292f484c063", "sha256:37978254d9d00cda01acc1997513f786b6b971e57b778fbe7c20e30ae81a97f3",
"sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50", "sha256:38289f1690a7e27aacd049e420769b996826f3728756859420eeee21cc857118",
"sha256:50a7ddf3d131dc5633dccdb51417e2d1910d25cbcf842115a3a5893509140a3a", "sha256:385ccf6d011b97768a640e9d4de25412204fbe8d6b9ae39ff115d4ff03f6fe5d",
"sha256:529801a0d58809b60b3531ee804d3e3be4b412c94b5d267daa3de7fadef00f49", "sha256:3c7ea86b9ca83e30fa4d4cd0eaf01db3ebcc7b2726a25990966627e39577d729",
"sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d", "sha256:49810f907dfe6de8da5da7d2b238d343e6add62f01a15d03e2195afc180059ed",
"sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d", "sha256:519c0b3a6fbb68afaa0febf0d28f6c4b0a1074aefc484802ecb9709faf181607",
"sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733", "sha256:51f02ca184518702975b56affde6c573ebad4e411599005ce4468b1014b4786c",
"sha256:563d5f9354e15e048465061509403f68424fef37d5add3064038c2511c8f5e00", "sha256:552a39987ac6655dad4bf6f17dd2b55c7b0c6e949d933b8846d2e312ee80005a",
"sha256:5d408a642a5484b9b4d11dea15a489ea0928c7e410c7525cd892f4d04f2f617b", "sha256:596f5ae2eeddb79b595583c2e0285312b2783b0ec759930c272dbf02f851ff75",
"sha256:61600a7ca4bcf78a96a68a27c2ae9389763b5b94b63943d5158f2a377e09d29a", "sha256:6014038f52b4b2ac1fa41a58d439a8a00f015b5c0735a0cd4b09afe344c94899",
"sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36", "sha256:61ebbcd208d78658b09e19c78920f1ad38936a0aa0f9c459c46c197d11c580a0",
"sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345", "sha256:6213713ac743b190ecbf3f316d6e41d099e774812d470422b3a0f137ea635832",
"sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0", "sha256:637e27ea1ebe4a561db75a880ac659ff439dec7f55588212e71700bb1ddd5af9",
"sha256:6e1d2cc79e8dae442b3fa4a26c5794428b98f81389af90623ffcc650ce9f6732", "sha256:6aa427c55a0abec450bca10b64446331b5ca8f79b648531138f357569705bc4a",
"sha256:74cbeac0451f27d4f50e6e8a8f3a52ca074b5e2da9f7b505c4201a57a8ed6286", "sha256:6ca45359d7a21644793de0e29de497ef7f1ae7268e346c4faf87b421fea364e6",
"sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12", "sha256:6db1b52c6f2c04fafc8da17ea506608e6be7086715dab498570c3e55e4f8fbd1",
"sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646", "sha256:752e7ddfb743344d447367baa85bccd3629c2c3940f70506eb5f01abce98ee68",
"sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667", "sha256:760c54ad1b8a9b81951030a7e8e7c3ec0964c1cb9fee585a03ff53d9e531bb8e",
"sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244", "sha256:768632fd8172ae03852e3245f11c8a425d95f65ff444ce46b3e673ae5b057b74",
"sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29", "sha256:7a0b9f6a1a15d494b35f25ed07abda03209fa76c33564c09c9e81d34f4b919d7",
"sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec", "sha256:7e070d3aef50ac3856f2ef5ec7214798453da878bb5e5a16c16a61edf1817cc3",
"sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf", "sha256:7e12949e5071c20ec49ef00c75121ed2b076972132fc1913ddf5f76cae8d10b4",
"sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4", "sha256:7e26eac9e52e8ce86f915fd33380f1b6896a2b51994e40bb094841e5003429b4",
"sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449", "sha256:85ffd6b1cb0dfb037ede50ff3bef80d9bf7fa60515d192403af6745524524f3b",
"sha256:96fc32c16ea6d60d3ca7f63397bff5c75c5a562f7db6dec7d412f7c4d2e78ec0", "sha256:8618d9213a863c468a865e9d2ec50221015f7abf52221bc927152ef26c484b4c",
"sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a", "sha256:8acef4d8a4353f6678fd1035422a937c2170de58a2b29f7da045d5249e934101",
"sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d", "sha256:8d2f355a951f60f0843f2368b39970e4667517e54e86b1508e76f92b44811a8a",
"sha256:a955b747d620a50408b7fdf948e04359d6e762ff8a85f5775d907ceced715129", "sha256:90b6840b6448203228a9d8464a7a0d99aa8fa9f027ef95fe230579abaf8a6ee1",
"sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb", "sha256:9187500d83fd0cef4669385cbb0961e227a41c0c9bc39219044e35810793edf7",
"sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e", "sha256:93c20777a72cae8620203ac11c4010365706062aa13aaedd1a21bb07adbb9d5d",
"sha256:b9ed0b1e5e0759d6b7f8e2f143894b2a7f3edd313f38cf44e1e15d360e11749b", "sha256:93cce7d422a0093cfb3606beae38a8e47a25232eea0f292c878af580a9dc7605",
"sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83", "sha256:94c623c331a48a5ccc7d25271399aff29729fa202c737ae3b4b28b89d2b0976d",
"sha256:ca49e1ab99593438b204e00f3970e7a5f70d045267051dfa6b5f4304fcfa1dbf", "sha256:97f32dc03a8054a4c4a5ab5d761ed4861e828b2c200febd4e46857069a483916",
"sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e", "sha256:9a2bf98ac92f58777c0fafc772bf0493e67fcf677302e0c0a630ee517a43b949",
"sha256:cd410a1cbb2d297c67d8521759ab2ee3f1d66206d2e4328502a487589a2cb21b", "sha256:a602bdc8607c99eb5b391592d58c92618dcd1537fdd87df1813f03fed49957a6",
"sha256:ce298e3d0c65bd03fa65ffcc6db0e2b578e8f626d468db64fdf8457731052942", "sha256:a9d24b03daf7415f78abc2d25a208f234e2c585e5e6f92f0204d2ab7b9ab48e3",
"sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a", "sha256:abfcb0ef78df0ee9df4ea81f03beea41849340ce33a4c4bd4dbb99e23ec781b6",
"sha256:d5fd67df77bab0d3f4ea1d7afca9ef15c2ee35dfb348c7b57ffb9782a6e4db6e", "sha256:b013f759cd69cb0a62de954d6d2096d648bc210034b79b1881406b07ed0a83f9",
"sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94", "sha256:b02e3e72665cd02afafb933453b0c9f6c59ff6e3708bd28d0d8580450e7e88af",
"sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc", "sha256:b52cc45e71657bc4743a5606d9023459de929b2a198d545868e11898ba1c3f59",
"sha256:dc07f021ee80510f3cd3af2cad5b6a3b3a10b057521d9e6aaeb621730d320c5a", "sha256:ba37f11e1d020969e8a779c06b4af866ffb6b854d7229db63c5fdddfceaa917f",
"sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e", "sha256:bb804c7d0bfbd7e3f33924ff49757de9106c44e27979e2492819c16972ec0da2",
"sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965", "sha256:bf594cc7cc9d528338d66674c10a5b25e3cde7dd75c3e96784df8f371d77a298",
"sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0", "sha256:c38baee6bdb7fe1b110b6b3aaa555e6e872d322206b7245aa39572d3fc991ee4",
"sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36", "sha256:c73d2166e4b210b73d1429c4f1ca97cea9cc090e5302df2a7a0a96ce55373f1c",
"sha256:e6096b0688e6e14af6a1b10eaad86b4ff17935c49aa774eac7c95a57a4e8c296", "sha256:c9099bf89078675c372339011ccfc9ec310310bf6c292b413c013eb90ffdcafc",
"sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec", "sha256:cf0db26a1f76aa6b3aa314a74b8facd586b7a5457d05b64f8082a62c9c49582a",
"sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23", "sha256:d19a34f8a3429bd536996ad53597b805c10352a8561d8382e05830df389d2b43",
"sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7", "sha256:da80047524eac2acf7c04c18ac7a7da05a9136241f642dd2ed94269ef0d0a45a",
"sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe", "sha256:de2923886b5d3214be951bc2ce3f6b8ac0d6dfd4a0d0e2a4d2e5523d8046fdfb",
"sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6", "sha256:defa0652696ff0ba48c8aff5a1fac1eef1ca6ac9c660b047fc8e7623c4eb5093",
"sha256:f5be7805e53dafe94d295399cfbe5227f39995a997f4fd8539bf3cbdc8f47ca8", "sha256:e54a1eb9fd38f2779e973d2f8958fd575b532fe26013405d1afb9ee2374e7ab8",
"sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b", "sha256:e5c31d70a478b0ca22a9d2d76d520ae996214019d39ed7dd93af872c7f301e52",
"sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb", "sha256:ebaeb93f90c0903233b11ce913a7cb8f6ee069158406e056f884854c737d2442",
"sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b", "sha256:ecfe51abf7f045e0b9cdde71ca9e153d11238679ef7b5da6c82093874adf3338",
"sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30", "sha256:f99112aed4fb7cee00c7f77e8b964a9b10f69488cdff626ffd797d02e2e4484f",
"sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e" "sha256:fd914db437ec25bfa410f8aa0aa2f3ba87cdfc04d9919d608d02330947afaeab"
], ],
"version": "==2021.11.10" "version": "==2022.1.18"
},
"tomli": {
"hashes": [
"sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f",
"sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"
],
"markers": "python_version >= '3.6'",
"version": "==1.2.3"
}, },
"tomlkit": { "tomlkit": {
"hashes": [ "hashes": [
"sha256:173ad840fa5d2aac140528ca1933c29791b79a374a0861a80347f42ec9328117", "sha256:29e84a855712dfe0e88a48f6d05c21118dbafb283bb2eed614d46f80deb8e9a1",
"sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754" "sha256:b824e3466f1d475b2b5f1c392954c6cb7ea04d64354ff7300dc7c14257dc85db"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "markers": "python_version >= '3.6' and python_version < '4.0'",
"version": "==0.7.2" "version": "==0.8.0"
}, },
"tqdm": { "tqdm": {
"hashes": [ "hashes": [
@ -440,6 +471,14 @@
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==4.62.3" "version": "==4.62.3"
},
"typing-extensions": {
"hashes": [
"sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e",
"sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"
],
"markers": "python_version >= '3.6'",
"version": "==4.0.1"
} }
} }
} }

View File

@ -6,7 +6,7 @@ import sys
def main(): def main():
"""Run administrative tasks.""" """Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'recmaint.settings.dev') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "recmaint.settings.dev")
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:
@ -18,5 +18,5 @@ def main():
execute_from_command_line(sys.argv) execute_from_command_line(sys.argv)
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View File

@ -11,6 +11,6 @@ import os
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'recmaint.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "recmaint.settings")
application = get_asgi_application() application = get_asgi_application()

View File

@ -22,61 +22,61 @@ ALLOWED_HOSTS = []
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'tasks.apps.TasksConfig', "tasks.apps.TasksConfig",
'rentals.apps.RentalsConfig', "rentals.apps.RentalsConfig",
'widget_tweaks', "widget_tweaks",
'markdownx', "markdownx",
'markdownify', "markdownify",
'recurrence', "recurrence",
'django.contrib.admin', "django.contrib.admin",
'django.contrib.auth', "django.contrib.auth",
'django.contrib.contenttypes', "django.contrib.contenttypes",
'django.contrib.sessions', "django.contrib.sessions",
'django.contrib.messages', "django.contrib.messages",
'django.contrib.staticfiles', "django.contrib.staticfiles",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
] ]
ROOT_URLCONF = 'recmaint.urls' ROOT_URLCONF = "recmaint.urls"
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [os.path.join(BASE_DIR, 'templates')], "DIRS": [os.path.join(BASE_DIR, "templates")],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
], ],
}, },
}, },
] ]
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
WSGI_APPLICATION = 'recmaint.wsgi.application' WSGI_APPLICATION = "recmaint.wsgi.application"
# Default URL to redirect to after authentication # Default URL to redirect to after authentication
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = "/"
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/ # https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = "en-us"
TIME_ZONE = 'America/New_York' TIME_ZONE = "America/New_York"
USE_I18N = False USE_I18N = False
USE_L10N = True USE_L10N = True
USE_TZ = True USE_TZ = True
@ -85,4 +85,4 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/ # https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/' STATIC_URL = "/static/"

View File

@ -4,7 +4,7 @@ from .base import *
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'g&zqf68eks@z$ctb%3_5+retmt%_i0=)7-y$i^gvc6p#s1+*ng' SECRET_KEY = "g&zqf68eks@z$ctb%3_5+retmt%_i0=)7-y$i^gvc6p#s1+*ng"
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
@ -13,8 +13,8 @@ DEBUG = True
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases # https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { "default": {
'ENGINE': 'django.db.backends.sqlite3', "ENGINE": "django.db.backends.sqlite3",
'NAME': BASE_DIR / 'db.sqlite3', "NAME": BASE_DIR / "db.sqlite3",
} }
} }

View File

@ -11,26 +11,26 @@ DEBUG = False
# "AUTH_LDAP_SERVER_URI", "AUTH_LDAP_BIND_DN", and "AUTH_LDAP_BIND_PASSWORD" set in prod.py # "AUTH_LDAP_SERVER_URI", "AUTH_LDAP_BIND_DN", and "AUTH_LDAP_BIND_PASSWORD" set in prod.py
AUTHENTICATION_BACKENDS = [ AUTHENTICATION_BACKENDS = [
'django_auth_ldap.backend.LDAPBackend', "django_auth_ldap.backend.LDAPBackend",
'django.contrib.auth.backends.ModelBackend', "django.contrib.auth.backends.ModelBackend",
] ]
AUTH_LDAP_USER_SEARCH = LDAPSearch( AUTH_LDAP_USER_SEARCH = LDAPSearch(
'cn=users,dc=sawtooth,dc=claremontmakerspace,dc=org', "cn=users,dc=sawtooth,dc=claremontmakerspace,dc=org",
ldap.SCOPE_SUBTREE, ldap.SCOPE_SUBTREE,
'(uid=%(user)s)', "(uid=%(user)s)",
) )
AUTH_LDAP_USER_ATTR_MAP = { AUTH_LDAP_USER_ATTR_MAP = {
'first_name': 'givenName', "first_name": "givenName",
'last_name': 'sn', "last_name": "sn",
'email': 'mail', "email": "mail",
} }
AUTH_LDAP_GROUP_SEARCH = LDAPSearch( AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
'cn=groups,dc=sawtooth,dc=claremontmakerspace,dc=org', "cn=groups,dc=sawtooth,dc=claremontmakerspace,dc=org",
ldap.SCOPE_SUBTREE, ldap.SCOPE_SUBTREE,
'(objectClass=posixGroup)', "(objectClass=posixGroup)",
) )
AUTH_LDAP_GROUP_TYPE = PosixGroupType() AUTH_LDAP_GROUP_TYPE = PosixGroupType()
AUTH_LDAP_MIRROR_GROUPS = True AUTH_LDAP_MIRROR_GROUPS = True

View File

@ -19,17 +19,30 @@ from django.urls import include, path
from django.contrib.auth.views import LoginView, LogoutView from django.contrib.auth.views import LoginView, LogoutView
urlpatterns = [ urlpatterns = [
path('', lambda request: redirect('/tasks/')), path("", lambda request: redirect("/tasks/")),
path('tasks/', include('tasks.urls')), path("tasks/", include("tasks.urls")),
path('rentals/', include('rentals.urls')), path("rentals/", include("rentals.urls")),
path('admin/', admin.site.urls), path("admin/", admin.site.urls),
path('auth/', include([ path(
path('login/', LoginView.as_view( "auth/",
template_name="auth/login.dj.html", include(
redirect_authenticated_user=True), name='login'), [
path('logout/', LogoutView.as_view(template_name="auth/logout.dj.html"), name='logout'), path(
])), "login/",
path('markdownx/', include('markdownx.urls')), LoginView.as_view(
template_name="auth/login.dj.html",
redirect_authenticated_user=True,
),
name="login",
),
path(
"logout/",
LogoutView.as_view(template_name="auth/logout.dj.html"),
name="logout",
),
]
),
),
path("markdownx/", include("markdownx.urls")),
] ]

View File

@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'recmaint.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "recmaint.settings")
application = get_wsgi_application() application = get_wsgi_application()

View File

@ -2,13 +2,16 @@ from django.contrib import admin
from .models import LockerBank, LockerRental from .models import LockerBank, LockerRental
class LockerRentalInline(admin.TabularInline): class LockerRentalInline(admin.TabularInline):
model = LockerRental model = LockerRental
extra = 0 extra = 0
@admin.register(LockerBank) @admin.register(LockerBank)
class LockerBankAdmin(admin.ModelAdmin): class LockerBankAdmin(admin.ModelAdmin):
inlines = [LockerRentalInline] inlines = [LockerRentalInline]
prepopulated_fields = {"slug": ("name",)} prepopulated_fields = {"slug": ("name",)}
admin.site.register(LockerRental) admin.site.register(LockerRental)

View File

@ -2,5 +2,5 @@ from django.apps import AppConfig
class RentalsConfig(AppConfig): class RentalsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = "django.db.models.BigAutoField"
name = 'rentals' name = "rentals"

View File

@ -22,11 +22,15 @@ class LockerBank(models.Model):
for row in range(self.rows): for row in range(self.rows):
for column in range(self.columns): for column in range(self.columns):
# TODO: filter could be optimized # TODO: filter could be optimized
yield chr(row + ord('A')), column + 1, self.rentals.filter(row=row, column=column) yield chr(row + ord("A")), column + 1, self.rentals.filter(
row=row, column=column
)
class LockerRental(models.Model): class LockerRental(models.Model):
locker_bank = models.ForeignKey(LockerBank, on_delete=models.CASCADE, related_name="rentals") locker_bank = models.ForeignKey(
LockerBank, on_delete=models.CASCADE, related_name="rentals"
)
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
row = models.PositiveIntegerField() row = models.PositiveIntegerField()
column = models.PositiveIntegerField() column = models.PositiveIntegerField()

View File

@ -2,4 +2,4 @@ from django.apps import AppConfig
class TasksConfig(AppConfig): class TasksConfig(AppConfig):
name = 'tasks' name = "tasks"

View File

@ -5,20 +5,21 @@ from markdownx.widgets import MarkdownxWidget
from .models import Event from .models import Event
class EventForm(forms.ModelForm): class EventForm(forms.ModelForm):
class Meta: class Meta:
model = Event model = Event
fields = ['date', 'user', 'notes'] fields = ["date", "user", "notes"]
widgets = { widgets = {
'notes': MarkdownxWidget(attrs={'rows': 2}), "notes": MarkdownxWidget(attrs={"rows": 2}),
} }
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
user = kwargs.pop('user') user = kwargs.pop("user")
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['date'].initial = datetime.now() self.fields["date"].initial = datetime.now()
self.fields['user'].initial = user self.fields["user"].initial = user
if not user.is_staff: if not user.is_staff:
print(list(self.fields['user'].choices)) print(list(self.fields["user"].choices))
self.fields['user'].choices = [(user.id, user)] self.fields["user"].choices = [(user.id, user)]

View File

@ -4,13 +4,13 @@ from django_auth_ldap.backend import LDAPBackend
class Command(BaseCommand): class Command(BaseCommand):
help = 'Import a user from LDAP by username' help = "Import a user from LDAP by username"
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('username', type=str) parser.add_argument("username", type=str)
def handle(self, *args, **options): def handle(self, *args, **options):
username = options['username'] username = options["username"]
user = LDAPBackend().populate_user(username) user = LDAPBackend().populate_user(username)
if user is None: if user is None:
raise Exception(f"No user named {username}") raise Exception(f"No user named {username}")

View File

@ -8,7 +8,7 @@ from tasks.models import Tool, Task, Event, GroupToolSubscription, GroupTaskSubs
class Command(BaseCommand): class Command(BaseCommand):
help = 'Sends any notifications for upcoming and overdue tasks' help = "Sends any notifications for upcoming and overdue tasks"
def _active_task_subscriptions(self): def _active_task_subscriptions(self):
for subscription in GroupTaskSubscription.objects.all(): for subscription in GroupTaskSubscription.objects.all():
@ -34,29 +34,40 @@ class Command(BaseCommand):
} }
def handle(self, *args, **options): def handle(self, *args, **options):
template = loader.get_template('tasks/notificationEmail.txt.dtl') template = loader.get_template("tasks/notificationEmail.txt.dtl")
group_subscriptions = self._active_task_subscriptions() group_subscriptions = self._active_task_subscriptions()
subscriptions_per_user = self._expand_group_subscriptions_to_users(group_subscriptions) subscriptions_per_user = self._expand_group_subscriptions_to_users(
group_subscriptions
)
for user, subscriptions in subscriptions_per_user.items(): for user, subscriptions in subscriptions_per_user.items():
if not user.email: if not user.email:
self.stdout.write(self.style.ERROR( self.stdout.write(
f"Can't send email, user '{user}' is missing an email address")) self.style.ERROR(
f"Can't send email, user '{user}' is missing an email address"
)
)
continue continue
self.stdout.write(self.style.SUCCESS( self.stdout.write(
f'Sending notification for {len(subscriptions)} task(s) to {user}')) self.style.SUCCESS(
f"Sending notification for {len(subscriptions)} task(s) to {user}"
)
)
try: try:
send_mail( send_mail(
subject=f'[CMS Tool Maintenance] {len(subscriptions)} tasks are upcoming or overdue!', subject=f"[CMS Tool Maintenance] {len(subscriptions)} tasks are upcoming or overdue!",
message=template.render({'subscriptions': subscriptions}).strip(), message=template.render({"subscriptions": subscriptions}).strip(),
from_email='adam@adamgoldsmith.name', from_email="adam@adamgoldsmith.name",
recipient_list=[user.email], recipient_list=[user.email],
fail_silently=False fail_silently=False,
) )
except Exception as e: except Exception as e:
self.stdout.write(self.style.ERROR( self.stdout.write(
f"Failed to send mail for user '{user}': {type(e).__name__}: {e}")) self.style.ERROR(
f"Failed to send mail for user '{user}': {type(e).__name__}: {e}"
)
)

View File

@ -7,6 +7,7 @@ from django.urls import reverse
from markdownx.models import MarkdownxField from markdownx.models import MarkdownxField
from recurrence.fields import RecurrenceField from recurrence.fields import RecurrenceField
class Tool(models.Model): class Tool(models.Model):
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
slug = models.SlugField(unique=True) slug = models.SlugField(unique=True)
@ -16,7 +17,7 @@ class Tool(models.Model):
return f"{self.name}{(' - ' + self.asset_tag) if self.asset_tag else ''}" return f"{self.name}{(' - ' + self.asset_tag) if self.asset_tag else ''}"
def get_absolute_url(self): def get_absolute_url(self):
return reverse('toolDetail', args=[self.slug]) return reverse("toolDetail", args=[self.slug])
class Task(models.Model): class Task(models.Model):
@ -34,21 +35,23 @@ class Task(models.Model):
return f"{self.tool.name}: {self.name}" return f"{self.tool.name}: {self.name}"
def get_absolute_url(self): def get_absolute_url(self):
return reverse('taskDetail', args=[self.tool.slug, self.slug]) return reverse("taskDetail", args=[self.tool.slug, self.slug])
@property @property
def last_event(self): def last_event(self):
return self.event_set.latest('date') return self.event_set.latest("date")
@property @property
def next_recurrence(self): def next_recurrence(self):
def _date_to_datetime(date): def _date_to_datetime(date):
return datetime.datetime.combine(date, datetime.time.min) return datetime.datetime.combine(date, datetime.time.min)
if self.recurrence_base is None: # relative date if self.recurrence_base is None: # relative date
try: try:
return self.recurrence.after( return self.recurrence.after(
_date_to_datetime(self.last_event.date), _date_to_datetime(self.last_event.date),
dtstart=_date_to_datetime(self.last_event.date)) dtstart=_date_to_datetime(self.last_event.date),
)
except Event.DoesNotExist: except Event.DoesNotExist:
return None return None
else: # absolute date else: # absolute date
@ -83,9 +86,8 @@ class GroupToolSubscription(SubscriptionSettings):
def get_task_subscriptions(self): def get_task_subscriptions(self):
for task in self.tool.task_set.all(): for task in self.tool.task_set.all():
yield GroupTaskSubscription( yield GroupTaskSubscription(
days_before=self.days_before, days_before=self.days_before, group=self.group, task=task
group=self.group, )
task=task)
class Meta: class Meta:
unique_together = (("group", "tool"),) unique_together = (("group", "tool"),)

View File

@ -4,11 +4,14 @@ from django.urls import path
from . import views from . import views
urlpatterns = [ urlpatterns = [
path('', views.index, name='index'), path("", views.index, name="index"),
# ex: /CMS00001/ # ex: /CMS00001/
path('<str:tool_slug>/', views.toolDetail, name='toolDetail'), path("<str:tool_slug>/", views.toolDetail, name="toolDetail"),
# ex: /CMS00001/tasks/ # ex: /CMS00001/tasks/
path('<str:tool_slug>/tasks/', lambda request, tool_slug: redirect('toolDetail', tool_slug)), path(
"<str:tool_slug>/tasks/",
lambda request, tool_slug: redirect("toolDetail", tool_slug),
),
# ex: /CMS00001/tasks/task_name/ # ex: /CMS00001/tasks/task_name/
path('<str:tool_slug>/tasks/<str:task_slug>', views.taskDetail, name='taskDetail'), path("<str:tool_slug>/tasks/<str:task_slug>", views.taskDetail, name="taskDetail"),
] ]

View File

@ -7,10 +7,10 @@ from .forms import EventForm
def index(request): def index(request):
context = { context = {
'tools': Tool.objects.all(), "tools": Tool.objects.all(),
'tasks': Task.objects.all(), "tasks": Task.objects.all(),
} }
return render(request, 'tasks/index.dj.html', context) return render(request, "tasks/index.dj.html", context)
def toolDetail(request, tool_slug): def toolDetail(request, tool_slug):
@ -18,11 +18,11 @@ def toolDetail(request, tool_slug):
tasks = tool.task_set.all() tasks = tool.task_set.all()
events = Event.objects.filter(task__tool=tool) events = Event.objects.filter(task__tool=tool)
context = { context = {
'tool': tool, "tool": tool,
'tasks': tasks, "tasks": tasks,
'events': events, "events": events,
} }
return render(request, 'tasks/toolDetail.dj.html', context) return render(request, "tasks/toolDetail.dj.html", context)
def taskDetail(request, tool_slug, task_slug): def taskDetail(request, tool_slug, task_slug):
@ -30,7 +30,7 @@ def taskDetail(request, tool_slug, task_slug):
task = get_object_or_404(tool.task_set, slug=task_slug) task = get_object_or_404(tool.task_set, slug=task_slug)
events = task.event_set.all() events = task.event_set.all()
if request.method == 'POST': if request.method == "POST":
if request.user.is_authenticated: if request.user.is_authenticated:
event = Event(task=task) event = Event(task=task)
form = EventForm(request.POST, user=request.user, instance=event) form = EventForm(request.POST, user=request.user, instance=event)
@ -44,15 +44,21 @@ def taskDetail(request, tool_slug, task_slug):
form = EventForm(user=request.user) form = EventForm(user=request.user)
context = { context = {
'tool': tool, "tool": tool,
'task': task, "task": task,
'events': events, "events": events,
'form': form, "form": form,
} }
if request.user.is_authenticated: if request.user.is_authenticated:
context.update({ context.update(
'task_subs': task.grouptasksubscription_set.filter(group__user=request.user), {
'tool_subs': tool.grouptoolsubscription_set.filter(group__user=request.user), "task_subs": task.grouptasksubscription_set.filter(
}) group__user=request.user
return render(request, 'tasks/taskDetail.dj.html', context) ),
"tool_subs": tool.grouptoolsubscription_set.filter(
group__user=request.user
),
}
)
return render(request, "tasks/taskDetail.dj.html", context)