diff --git a/Pipfile b/Pipfile index 2edd129..1256167 100644 --- a/Pipfile +++ b/Pipfile @@ -15,6 +15,7 @@ django-recurrence = "~=1.10" [dev-packages] djlint = "~=0.7" +black = "*" [requires] python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock index e5b004d..c049d41 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "38925606631e4d558d3539d27170aaaa686a4dc20fe8ee68f435b93c3ef7c12f" + "sha256": "4e1b5c33531624fa18bd7683e300a081a273442b3b1893f89f688ea3aed31da7" }, "pipfile-spec": 6, "requires": { @@ -253,11 +253,11 @@ }, "uvicorn": { "hashes": [ - "sha256:0b89c91bb8fe84c4bded9996af13c4b8c0de799d29bffeaa0c8ad298f2be0934", - "sha256:192c2422b056a3beb512c6c260bf77a7a884204a4ae41856719c1913ead63bbb" + "sha256:60a149248181920a73b2e97aec1dacec5501618867f041a228b2519d91a62a91", + "sha256:fa166e6c3d58e23ff5a1a3543b079c7b28aa057ab1388201e4b34a49ec05da72" ], "index": "pypi", - "version": "==0.17.0" + "version": "==0.17.0.post1" }, "webencodings": { "hashes": [ @@ -276,6 +276,14 @@ } }, "develop": { + "black": { + "hashes": [ + "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3", + "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f" + ], + "index": "pypi", + "version": "==21.12b0" + }, "click": { "hashes": [ "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", @@ -294,11 +302,18 @@ }, "djlint": { "hashes": [ - "sha256:95715103781a4258ef91bdcb3702e1f5707ca075ceb49b58f0a8ce6b568115c6", - "sha256:d03cc3d6cd51db9f942211ffbdc85009d33f31e28ea1edbd22954af80d28141a" + "sha256:68aad9ddfef883cc9d9e0d177387b74840af5ca12dcce6e4629eb7075c97dc05", + "sha256:714ed457e022047149c8bff57d5be00ce30f8846b60e866791c66d27e7d11e7f" ], "index": "pypi", - "version": "==0.7.2" + "version": "==0.7.3" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" }, "pathspec": { "hashes": [ @@ -307,6 +322,14 @@ ], "version": "==0.9.0" }, + "platformdirs": { + "hashes": [ + "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca", + "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda" + ], + "markers": "python_version >= '3.7'", + "version": "==2.4.1" + }, "pyyaml": { "hashes": [ "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", @@ -348,90 +371,98 @@ }, "regex": { "hashes": [ - "sha256:0416f7399e918c4b0e074a0f66e5191077ee2ca32a0f99d4c187a62beb47aa05", - "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f", - "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc", - "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4", - "sha256:0f594b96fe2e0821d026365f72ac7b4f0b487487fb3d4aaf10dd9d97d88a9737", - "sha256:139a23d1f5d30db2cc6c7fd9c6d6497872a672db22c4ae1910be22d4f4b2068a", - "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4", - "sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8", - "sha256:2409b5c9cef7054dde93a9803156b411b677affc84fca69e908b1cb2c540025d", - "sha256:2fee3ed82a011184807d2127f1733b4f6b2ff6ec7151d83ef3477f3b96a13d03", - "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f", - "sha256:3598893bde43091ee5ca0a6ad20f08a0435e93a69255eeb5f81b85e81e329264", - "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a", - "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef", - "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f", - "sha256:42b50fa6666b0d50c30a990527127334d6b96dd969011e843e726a64011485da", - "sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc", - "sha256:473e67837f786404570eae33c3b64a4b9635ae9f00145250851a1292f484c063", - "sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50", - "sha256:50a7ddf3d131dc5633dccdb51417e2d1910d25cbcf842115a3a5893509140a3a", - "sha256:529801a0d58809b60b3531ee804d3e3be4b412c94b5d267daa3de7fadef00f49", - "sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d", - "sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d", - "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733", - "sha256:563d5f9354e15e048465061509403f68424fef37d5add3064038c2511c8f5e00", - "sha256:5d408a642a5484b9b4d11dea15a489ea0928c7e410c7525cd892f4d04f2f617b", - "sha256:61600a7ca4bcf78a96a68a27c2ae9389763b5b94b63943d5158f2a377e09d29a", - "sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36", - "sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345", - "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0", - "sha256:6e1d2cc79e8dae442b3fa4a26c5794428b98f81389af90623ffcc650ce9f6732", - "sha256:74cbeac0451f27d4f50e6e8a8f3a52ca074b5e2da9f7b505c4201a57a8ed6286", - "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12", - "sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646", - "sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667", - "sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244", - "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29", - "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec", - "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf", - "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4", - "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449", - "sha256:96fc32c16ea6d60d3ca7f63397bff5c75c5a562f7db6dec7d412f7c4d2e78ec0", - "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a", - "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d", - "sha256:a955b747d620a50408b7fdf948e04359d6e762ff8a85f5775d907ceced715129", - "sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb", - "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e", - "sha256:b9ed0b1e5e0759d6b7f8e2f143894b2a7f3edd313f38cf44e1e15d360e11749b", - "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83", - "sha256:ca49e1ab99593438b204e00f3970e7a5f70d045267051dfa6b5f4304fcfa1dbf", - "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e", - "sha256:cd410a1cbb2d297c67d8521759ab2ee3f1d66206d2e4328502a487589a2cb21b", - "sha256:ce298e3d0c65bd03fa65ffcc6db0e2b578e8f626d468db64fdf8457731052942", - "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a", - "sha256:d5fd67df77bab0d3f4ea1d7afca9ef15c2ee35dfb348c7b57ffb9782a6e4db6e", - "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94", - "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc", - "sha256:dc07f021ee80510f3cd3af2cad5b6a3b3a10b057521d9e6aaeb621730d320c5a", - "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e", - "sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965", - "sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0", - "sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36", - "sha256:e6096b0688e6e14af6a1b10eaad86b4ff17935c49aa774eac7c95a57a4e8c296", - "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec", - "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23", - "sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7", - "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe", - "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6", - "sha256:f5be7805e53dafe94d295399cfbe5227f39995a997f4fd8539bf3cbdc8f47ca8", - "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b", - "sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb", - "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b", - "sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30", - "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e" + "sha256:04611cc0f627fc4a50bc4a9a2e6178a974c6a6a4aa9c1cca921635d2c47b9c87", + "sha256:0b5d6f9aed3153487252d00a18e53f19b7f52a1651bc1d0c4b5844bc286dfa52", + "sha256:0d2f5c3f7057530afd7b739ed42eb04f1011203bc5e4663e1e1d01bb50f813e3", + "sha256:11772be1eb1748e0e197a40ffb82fb8fd0d6914cd147d841d9703e2bef24d288", + "sha256:1333b3ce73269f986b1fa4d5d395643810074dc2de5b9d262eb258daf37dc98f", + "sha256:16f81025bb3556eccb0681d7946e2b35ff254f9f888cff7d2120e8826330315c", + "sha256:1a171eaac36a08964d023eeff740b18a415f79aeb212169080c170ec42dd5184", + "sha256:1d6301f5288e9bdca65fab3de6b7de17362c5016d6bf8ee4ba4cbe833b2eda0f", + "sha256:1e031899cb2bc92c0cf4d45389eff5b078d1936860a1be3aa8c94fa25fb46ed8", + "sha256:1f8c0ae0a0de4e19fddaaff036f508db175f6f03db318c80bbc239a1def62d02", + "sha256:2245441445099411b528379dee83e56eadf449db924648e5feb9b747473f42e3", + "sha256:22709d701e7037e64dae2a04855021b62efd64a66c3ceed99dfd684bfef09e38", + "sha256:24c89346734a4e4d60ecf9b27cac4c1fee3431a413f7aa00be7c4d7bbacc2c4d", + "sha256:25716aa70a0d153cd844fe861d4f3315a6ccafce22b39d8aadbf7fcadff2b633", + "sha256:2dacb3dae6b8cc579637a7b72f008bff50a94cde5e36e432352f4ca57b9e54c4", + "sha256:34316bf693b1d2d29c087ee7e4bb10cdfa39da5f9c50fa15b07489b4ab93a1b5", + "sha256:36b2d700a27e168fa96272b42d28c7ac3ff72030c67b32f37c05616ebd22a202", + "sha256:37978254d9d00cda01acc1997513f786b6b971e57b778fbe7c20e30ae81a97f3", + "sha256:38289f1690a7e27aacd049e420769b996826f3728756859420eeee21cc857118", + "sha256:385ccf6d011b97768a640e9d4de25412204fbe8d6b9ae39ff115d4ff03f6fe5d", + "sha256:3c7ea86b9ca83e30fa4d4cd0eaf01db3ebcc7b2726a25990966627e39577d729", + "sha256:49810f907dfe6de8da5da7d2b238d343e6add62f01a15d03e2195afc180059ed", + "sha256:519c0b3a6fbb68afaa0febf0d28f6c4b0a1074aefc484802ecb9709faf181607", + "sha256:51f02ca184518702975b56affde6c573ebad4e411599005ce4468b1014b4786c", + "sha256:552a39987ac6655dad4bf6f17dd2b55c7b0c6e949d933b8846d2e312ee80005a", + "sha256:596f5ae2eeddb79b595583c2e0285312b2783b0ec759930c272dbf02f851ff75", + "sha256:6014038f52b4b2ac1fa41a58d439a8a00f015b5c0735a0cd4b09afe344c94899", + "sha256:61ebbcd208d78658b09e19c78920f1ad38936a0aa0f9c459c46c197d11c580a0", + "sha256:6213713ac743b190ecbf3f316d6e41d099e774812d470422b3a0f137ea635832", + "sha256:637e27ea1ebe4a561db75a880ac659ff439dec7f55588212e71700bb1ddd5af9", + "sha256:6aa427c55a0abec450bca10b64446331b5ca8f79b648531138f357569705bc4a", + "sha256:6ca45359d7a21644793de0e29de497ef7f1ae7268e346c4faf87b421fea364e6", + "sha256:6db1b52c6f2c04fafc8da17ea506608e6be7086715dab498570c3e55e4f8fbd1", + "sha256:752e7ddfb743344d447367baa85bccd3629c2c3940f70506eb5f01abce98ee68", + "sha256:760c54ad1b8a9b81951030a7e8e7c3ec0964c1cb9fee585a03ff53d9e531bb8e", + "sha256:768632fd8172ae03852e3245f11c8a425d95f65ff444ce46b3e673ae5b057b74", + "sha256:7a0b9f6a1a15d494b35f25ed07abda03209fa76c33564c09c9e81d34f4b919d7", + "sha256:7e070d3aef50ac3856f2ef5ec7214798453da878bb5e5a16c16a61edf1817cc3", + "sha256:7e12949e5071c20ec49ef00c75121ed2b076972132fc1913ddf5f76cae8d10b4", + "sha256:7e26eac9e52e8ce86f915fd33380f1b6896a2b51994e40bb094841e5003429b4", + "sha256:85ffd6b1cb0dfb037ede50ff3bef80d9bf7fa60515d192403af6745524524f3b", + "sha256:8618d9213a863c468a865e9d2ec50221015f7abf52221bc927152ef26c484b4c", + "sha256:8acef4d8a4353f6678fd1035422a937c2170de58a2b29f7da045d5249e934101", + "sha256:8d2f355a951f60f0843f2368b39970e4667517e54e86b1508e76f92b44811a8a", + "sha256:90b6840b6448203228a9d8464a7a0d99aa8fa9f027ef95fe230579abaf8a6ee1", + "sha256:9187500d83fd0cef4669385cbb0961e227a41c0c9bc39219044e35810793edf7", + "sha256:93c20777a72cae8620203ac11c4010365706062aa13aaedd1a21bb07adbb9d5d", + "sha256:93cce7d422a0093cfb3606beae38a8e47a25232eea0f292c878af580a9dc7605", + "sha256:94c623c331a48a5ccc7d25271399aff29729fa202c737ae3b4b28b89d2b0976d", + "sha256:97f32dc03a8054a4c4a5ab5d761ed4861e828b2c200febd4e46857069a483916", + "sha256:9a2bf98ac92f58777c0fafc772bf0493e67fcf677302e0c0a630ee517a43b949", + "sha256:a602bdc8607c99eb5b391592d58c92618dcd1537fdd87df1813f03fed49957a6", + "sha256:a9d24b03daf7415f78abc2d25a208f234e2c585e5e6f92f0204d2ab7b9ab48e3", + "sha256:abfcb0ef78df0ee9df4ea81f03beea41849340ce33a4c4bd4dbb99e23ec781b6", + "sha256:b013f759cd69cb0a62de954d6d2096d648bc210034b79b1881406b07ed0a83f9", + "sha256:b02e3e72665cd02afafb933453b0c9f6c59ff6e3708bd28d0d8580450e7e88af", + "sha256:b52cc45e71657bc4743a5606d9023459de929b2a198d545868e11898ba1c3f59", + "sha256:ba37f11e1d020969e8a779c06b4af866ffb6b854d7229db63c5fdddfceaa917f", + "sha256:bb804c7d0bfbd7e3f33924ff49757de9106c44e27979e2492819c16972ec0da2", + "sha256:bf594cc7cc9d528338d66674c10a5b25e3cde7dd75c3e96784df8f371d77a298", + "sha256:c38baee6bdb7fe1b110b6b3aaa555e6e872d322206b7245aa39572d3fc991ee4", + "sha256:c73d2166e4b210b73d1429c4f1ca97cea9cc090e5302df2a7a0a96ce55373f1c", + "sha256:c9099bf89078675c372339011ccfc9ec310310bf6c292b413c013eb90ffdcafc", + "sha256:cf0db26a1f76aa6b3aa314a74b8facd586b7a5457d05b64f8082a62c9c49582a", + "sha256:d19a34f8a3429bd536996ad53597b805c10352a8561d8382e05830df389d2b43", + "sha256:da80047524eac2acf7c04c18ac7a7da05a9136241f642dd2ed94269ef0d0a45a", + "sha256:de2923886b5d3214be951bc2ce3f6b8ac0d6dfd4a0d0e2a4d2e5523d8046fdfb", + "sha256:defa0652696ff0ba48c8aff5a1fac1eef1ca6ac9c660b047fc8e7623c4eb5093", + "sha256:e54a1eb9fd38f2779e973d2f8958fd575b532fe26013405d1afb9ee2374e7ab8", + "sha256:e5c31d70a478b0ca22a9d2d76d520ae996214019d39ed7dd93af872c7f301e52", + "sha256:ebaeb93f90c0903233b11ce913a7cb8f6ee069158406e056f884854c737d2442", + "sha256:ecfe51abf7f045e0b9cdde71ca9e153d11238679ef7b5da6c82093874adf3338", + "sha256:f99112aed4fb7cee00c7f77e8b964a9b10f69488cdff626ffd797d02e2e4484f", + "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": { "hashes": [ - "sha256:173ad840fa5d2aac140528ca1933c29791b79a374a0861a80347f42ec9328117", - "sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754" + "sha256:29e84a855712dfe0e88a48f6d05c21118dbafb283bb2eed614d46f80deb8e9a1", + "sha256:b824e3466f1d475b2b5f1c392954c6cb7ea04d64354ff7300dc7c14257dc85db" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.7.2" + "markers": "python_version >= '3.6' and python_version < '4.0'", + "version": "==0.8.0" }, "tqdm": { "hashes": [ @@ -440,6 +471,14 @@ ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==4.62.3" + }, + "typing-extensions": { + "hashes": [ + "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e", + "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b" + ], + "markers": "python_version >= '3.6'", + "version": "==4.0.1" } } } diff --git a/manage.py b/manage.py index 56684bf..49af7a9 100755 --- a/manage.py +++ b/manage.py @@ -6,7 +6,7 @@ import sys def main(): """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'recmaint.settings.dev') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "recmaint.settings.dev") try: from django.core.management import execute_from_command_line except ImportError as exc: @@ -18,5 +18,5 @@ def main(): execute_from_command_line(sys.argv) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/recmaint/asgi.py b/recmaint/asgi.py index 86a7bc7..e1b8287 100644 --- a/recmaint/asgi.py +++ b/recmaint/asgi.py @@ -11,6 +11,6 @@ import os 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() diff --git a/recmaint/settings/base.py b/recmaint/settings/base.py index 4a38b1f..7df22b2 100644 --- a/recmaint/settings/base.py +++ b/recmaint/settings/base.py @@ -22,61 +22,61 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ - 'tasks.apps.TasksConfig', - 'rentals.apps.RentalsConfig', - 'widget_tweaks', - 'markdownx', - 'markdownify', - 'recurrence', - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "tasks.apps.TasksConfig", + "rentals.apps.RentalsConfig", + "widget_tweaks", + "markdownx", + "markdownify", + "recurrence", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'recmaint.urls' +ROOT_URLCONF = "recmaint.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [os.path.join(BASE_DIR, "templates")], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "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 -LOGIN_REDIRECT_URL = '/' +LOGIN_REDIRECT_URL = "/" # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/ -LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'America/New_York' +LANGUAGE_CODE = "en-us" +TIME_ZONE = "America/New_York" USE_I18N = False USE_L10N = True USE_TZ = True @@ -85,4 +85,4 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.1/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" diff --git a/recmaint/settings/dev.py b/recmaint/settings/dev.py index fe8fe13..260aa89 100644 --- a/recmaint/settings/dev.py +++ b/recmaint/settings/dev.py @@ -4,7 +4,7 @@ from .base import * # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # 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! DEBUG = True @@ -13,8 +13,8 @@ DEBUG = True # https://docs.djangoproject.com/en/3.1/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", } } diff --git a/recmaint/settings/prod_base.py b/recmaint/settings/prod_base.py index 63c720f..772c78d 100644 --- a/recmaint/settings/prod_base.py +++ b/recmaint/settings/prod_base.py @@ -11,26 +11,26 @@ DEBUG = False # "AUTH_LDAP_SERVER_URI", "AUTH_LDAP_BIND_DN", and "AUTH_LDAP_BIND_PASSWORD" set in prod.py AUTHENTICATION_BACKENDS = [ - 'django_auth_ldap.backend.LDAPBackend', - 'django.contrib.auth.backends.ModelBackend', + "django_auth_ldap.backend.LDAPBackend", + "django.contrib.auth.backends.ModelBackend", ] AUTH_LDAP_USER_SEARCH = LDAPSearch( - 'cn=users,dc=sawtooth,dc=claremontmakerspace,dc=org', + "cn=users,dc=sawtooth,dc=claremontmakerspace,dc=org", ldap.SCOPE_SUBTREE, - '(uid=%(user)s)', + "(uid=%(user)s)", ) AUTH_LDAP_USER_ATTR_MAP = { - 'first_name': 'givenName', - 'last_name': 'sn', - 'email': 'mail', + "first_name": "givenName", + "last_name": "sn", + "email": "mail", } AUTH_LDAP_GROUP_SEARCH = LDAPSearch( - 'cn=groups,dc=sawtooth,dc=claremontmakerspace,dc=org', + "cn=groups,dc=sawtooth,dc=claremontmakerspace,dc=org", ldap.SCOPE_SUBTREE, - '(objectClass=posixGroup)', + "(objectClass=posixGroup)", ) AUTH_LDAP_GROUP_TYPE = PosixGroupType() AUTH_LDAP_MIRROR_GROUPS = True diff --git a/recmaint/urls.py b/recmaint/urls.py index 1178cfa..4803d59 100644 --- a/recmaint/urls.py +++ b/recmaint/urls.py @@ -19,17 +19,30 @@ from django.urls import include, path from django.contrib.auth.views import LoginView, LogoutView - urlpatterns = [ - path('', lambda request: redirect('/tasks/')), - path('tasks/', include('tasks.urls')), - path('rentals/', include('rentals.urls')), - path('admin/', admin.site.urls), - path('auth/', include([ - path('login/', 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')), + path("", lambda request: redirect("/tasks/")), + path("tasks/", include("tasks.urls")), + path("rentals/", include("rentals.urls")), + path("admin/", admin.site.urls), + path( + "auth/", + include( + [ + path( + "login/", + 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")), ] diff --git a/recmaint/wsgi.py b/recmaint/wsgi.py index c6caae7..2e36b0c 100644 --- a/recmaint/wsgi.py +++ b/recmaint/wsgi.py @@ -11,6 +11,6 @@ import os 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() diff --git a/rentals/admin.py b/rentals/admin.py index 3d09307..0bd4bf5 100644 --- a/rentals/admin.py +++ b/rentals/admin.py @@ -2,13 +2,16 @@ from django.contrib import admin from .models import LockerBank, LockerRental + class LockerRentalInline(admin.TabularInline): model = LockerRental extra = 0 + @admin.register(LockerBank) class LockerBankAdmin(admin.ModelAdmin): inlines = [LockerRentalInline] prepopulated_fields = {"slug": ("name",)} + admin.site.register(LockerRental) diff --git a/rentals/apps.py b/rentals/apps.py index 8f49d49..d4c1323 100644 --- a/rentals/apps.py +++ b/rentals/apps.py @@ -2,5 +2,5 @@ from django.apps import AppConfig class RentalsConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'rentals' + default_auto_field = "django.db.models.BigAutoField" + name = "rentals" diff --git a/rentals/models.py b/rentals/models.py index 86aad63..9563886 100644 --- a/rentals/models.py +++ b/rentals/models.py @@ -22,11 +22,15 @@ class LockerBank(models.Model): for row in range(self.rows): for column in range(self.columns): # 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): - 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) row = models.PositiveIntegerField() column = models.PositiveIntegerField() diff --git a/tasks/apps.py b/tasks/apps.py index 2054722..5aadae0 100644 --- a/tasks/apps.py +++ b/tasks/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class TasksConfig(AppConfig): - name = 'tasks' + name = "tasks" diff --git a/tasks/forms.py b/tasks/forms.py index 14e7c93..5d581d0 100644 --- a/tasks/forms.py +++ b/tasks/forms.py @@ -5,20 +5,21 @@ from markdownx.widgets import MarkdownxWidget from .models import Event + class EventForm(forms.ModelForm): class Meta: model = Event - fields = ['date', 'user', 'notes'] + fields = ["date", "user", "notes"] widgets = { - 'notes': MarkdownxWidget(attrs={'rows': 2}), + "notes": MarkdownxWidget(attrs={"rows": 2}), } def __init__(self, *args, **kwargs): - user = kwargs.pop('user') + user = kwargs.pop("user") super().__init__(*args, **kwargs) - self.fields['date'].initial = datetime.now() - self.fields['user'].initial = user + self.fields["date"].initial = datetime.now() + self.fields["user"].initial = user if not user.is_staff: - print(list(self.fields['user'].choices)) - self.fields['user'].choices = [(user.id, user)] + print(list(self.fields["user"].choices)) + self.fields["user"].choices = [(user.id, user)] diff --git a/tasks/management/commands/import_ldap_user.py b/tasks/management/commands/import_ldap_user.py index c3c1551..598019f 100644 --- a/tasks/management/commands/import_ldap_user.py +++ b/tasks/management/commands/import_ldap_user.py @@ -4,13 +4,13 @@ from django_auth_ldap.backend import LDAPBackend class Command(BaseCommand): - help = 'Import a user from LDAP by username' + help = "Import a user from LDAP by username" def add_arguments(self, parser): - parser.add_argument('username', type=str) + parser.add_argument("username", type=str) def handle(self, *args, **options): - username = options['username'] + username = options["username"] user = LDAPBackend().populate_user(username) if user is None: raise Exception(f"No user named {username}") diff --git a/tasks/management/commands/sendNotifications.py b/tasks/management/commands/sendNotifications.py index 3a173fb..aed570b 100644 --- a/tasks/management/commands/sendNotifications.py +++ b/tasks/management/commands/sendNotifications.py @@ -8,7 +8,7 @@ from tasks.models import Tool, Task, Event, GroupToolSubscription, GroupTaskSubs 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): for subscription in GroupTaskSubscription.objects.all(): @@ -34,29 +34,40 @@ class Command(BaseCommand): } 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() - 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(): if not user.email: - self.stdout.write(self.style.ERROR( - f"Can't send email, user '{user}' is missing an email address")) + self.stdout.write( + self.style.ERROR( + f"Can't send email, user '{user}' is missing an email address" + ) + ) continue - self.stdout.write(self.style.SUCCESS( - f'Sending notification for {len(subscriptions)} task(s) to {user}')) + self.stdout.write( + self.style.SUCCESS( + f"Sending notification for {len(subscriptions)} task(s) to {user}" + ) + ) try: send_mail( - subject=f'[CMS Tool Maintenance] {len(subscriptions)} tasks are upcoming or overdue!', - message=template.render({'subscriptions': subscriptions}).strip(), - from_email='adam@adamgoldsmith.name', + subject=f"[CMS Tool Maintenance] {len(subscriptions)} tasks are upcoming or overdue!", + message=template.render({"subscriptions": subscriptions}).strip(), + from_email="adam@adamgoldsmith.name", recipient_list=[user.email], - fail_silently=False + fail_silently=False, ) except Exception as e: - self.stdout.write(self.style.ERROR( - f"Failed to send mail for user '{user}': {type(e).__name__}: {e}")) + self.stdout.write( + self.style.ERROR( + f"Failed to send mail for user '{user}': {type(e).__name__}: {e}" + ) + ) diff --git a/tasks/models.py b/tasks/models.py index 45c0564..e4e8bfd 100644 --- a/tasks/models.py +++ b/tasks/models.py @@ -7,6 +7,7 @@ from django.urls import reverse from markdownx.models import MarkdownxField from recurrence.fields import RecurrenceField + class Tool(models.Model): name = models.CharField(max_length=200) 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 ''}" def get_absolute_url(self): - return reverse('toolDetail', args=[self.slug]) + return reverse("toolDetail", args=[self.slug]) class Task(models.Model): @@ -34,21 +35,23 @@ class Task(models.Model): return f"{self.tool.name}: {self.name}" def get_absolute_url(self): - return reverse('taskDetail', args=[self.tool.slug, self.slug]) + return reverse("taskDetail", args=[self.tool.slug, self.slug]) @property def last_event(self): - return self.event_set.latest('date') + return self.event_set.latest("date") @property def next_recurrence(self): def _date_to_datetime(date): return datetime.datetime.combine(date, datetime.time.min) + if self.recurrence_base is None: # relative date try: return self.recurrence.after( _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: return None else: # absolute date @@ -83,9 +86,8 @@ class GroupToolSubscription(SubscriptionSettings): def get_task_subscriptions(self): for task in self.tool.task_set.all(): yield GroupTaskSubscription( - days_before=self.days_before, - group=self.group, - task=task) + days_before=self.days_before, group=self.group, task=task + ) class Meta: unique_together = (("group", "tool"),) diff --git a/tasks/urls.py b/tasks/urls.py index a3ee87d..96054dc 100644 --- a/tasks/urls.py +++ b/tasks/urls.py @@ -4,11 +4,14 @@ from django.urls import path from . import views urlpatterns = [ - path('', views.index, name='index'), + path("", views.index, name="index"), # ex: /CMS00001/ - path('/', views.toolDetail, name='toolDetail'), + path("/", views.toolDetail, name="toolDetail"), # ex: /CMS00001/tasks/ - path('/tasks/', lambda request, tool_slug: redirect('toolDetail', tool_slug)), + path( + "/tasks/", + lambda request, tool_slug: redirect("toolDetail", tool_slug), + ), # ex: /CMS00001/tasks/task_name/ - path('/tasks/', views.taskDetail, name='taskDetail'), + path("/tasks/", views.taskDetail, name="taskDetail"), ] diff --git a/tasks/views.py b/tasks/views.py index 5154db1..8f7c006 100644 --- a/tasks/views.py +++ b/tasks/views.py @@ -7,10 +7,10 @@ from .forms import EventForm def index(request): context = { - 'tools': Tool.objects.all(), - 'tasks': Task.objects.all(), + "tools": Tool.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): @@ -18,11 +18,11 @@ def toolDetail(request, tool_slug): tasks = tool.task_set.all() events = Event.objects.filter(task__tool=tool) context = { - 'tool': tool, - 'tasks': tasks, - 'events': events, + "tool": tool, + "tasks": tasks, + "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): @@ -30,7 +30,7 @@ def taskDetail(request, tool_slug, task_slug): task = get_object_or_404(tool.task_set, slug=task_slug) events = task.event_set.all() - if request.method == 'POST': + if request.method == "POST": if request.user.is_authenticated: event = Event(task=task) 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) context = { - 'tool': tool, - 'task': task, - 'events': events, - 'form': form, + "tool": tool, + "task": task, + "events": events, + "form": form, } if request.user.is_authenticated: - context.update({ - 'task_subs': task.grouptasksubscription_set.filter(group__user=request.user), - 'tool_subs': tool.grouptoolsubscription_set.filter(group__user=request.user), - }) - return render(request, 'tasks/taskDetail.dj.html', context) + context.update( + { + "task_subs": task.grouptasksubscription_set.filter( + group__user=request.user + ), + "tool_subs": tool.grouptoolsubscription_set.filter( + group__user=request.user + ), + } + ) + return render(request, "tasks/taskDetail.dj.html", context)