diff --git a/Pipfile b/Pipfile index dd276645..35f92973 100644 --- a/Pipfile +++ b/Pipfile @@ -12,19 +12,19 @@ bcrypt = "==4.1.2" boto3 = "==1.17.112" botocore = "==1.20.112" certifi = "==2024.2.2" -cffi = "==1.15.1" +cffi = "==1.16.0" click = ">=7.1.2" distlib = "==0.3.8" filelock = "==3.13.1" flask-cors = "==4.0.0" flask-restful = "==0.3.10" flask = "==3.0.2" -greenlet = "==3.0.1" +greenlet = "==3.0.3" ics = "==0.7.2" itsdangerous = ">=1.1.0" jinja2 = ">=2.11.3" jmespath = "==0.10.0" -mako = "==1.2.4" +mako = "==1.3.2" markupsafe = ">=1.1.1" minizinc = "==0.9.0" mysqlclient = "==2.2.4" @@ -32,7 +32,7 @@ pipenv = "*" pycparser = "==2.21" pyjwt = "==2.8.0" python-dateutil = "==2.9.0.post0" -python-dotenv = "==1.0.0" +python-dotenv = "==1.0.1" python-editor = "==1.0.4" python-http-client = "==3.3.7" pytz = "==2024.1" diff --git a/Pipfile.lock b/Pipfile.lock index d3f9734b..3ddb0453 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "52fcc8fa5b66698ab780b1081c4f67af952c0dee9e37c41bf59aa5f1ec05e3af" + "sha256": "4bcc030abc00a2dbd4224365e6cc5f42105cb928b69cd3893b750d5443697f1a" }, "pipfile-spec": 6, "requires": { @@ -129,73 +129,62 @@ }, "cffi": { "hashes": [ - "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", - "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", - "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", - "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", - "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", - "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", - "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", - "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", - "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", - "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", - "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", - "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", - "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", - "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", - "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", - "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", - "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", - "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", - "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", - "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", - "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", - "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", - "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", - "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", - "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", - "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", - "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", - "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", - "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", - "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", - "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", - "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", - "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", - "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", - "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", - "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", - "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", - "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", - "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", - "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", - "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", - "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", - "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", - "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", - "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", - "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", - "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", - "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", - "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", - "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", - "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", - "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", - "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", - "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", - "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", - "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", - "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", - "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", - "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", - "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", - "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", - "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", - "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", - "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" - ], - "index": "pypi", - "version": "==1.15.1" + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.16.0" }, "click": { "hashes": [ @@ -258,67 +247,68 @@ }, "greenlet": { "hashes": [ - "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174", - "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd", - "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa", - "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a", - "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec", - "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565", - "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d", - "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c", - "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234", - "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d", - "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546", - "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2", - "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74", - "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de", - "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd", - "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9", - "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3", - "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846", - "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2", - "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353", - "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8", - "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166", - "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206", - "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b", - "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d", - "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe", - "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997", - "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445", - "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0", - "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96", - "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884", - "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6", - "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1", - "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619", - "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94", - "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4", - "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1", - "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63", - "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd", - "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a", - "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376", - "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57", - "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16", - "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e", - "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc", - "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a", - "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c", - "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5", - "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a", - "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72", - "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9", - "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9", - "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e", - "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8", - "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65", - "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", - "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" + "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", + "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", + "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", + "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", + "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", + "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", + "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", + "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", + "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", + "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", + "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", + "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", + "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", + "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", + "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", + "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", + "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", + "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", + "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", + "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", + "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", + "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", + "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", + "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", + "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", + "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", + "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", + "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", + "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", + "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", + "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", + "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", + "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", + "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", + "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", + "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", + "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", + "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", + "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", + "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", + "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", + "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", + "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", + "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", + "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", + "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", + "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", + "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", + "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", + "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", + "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", + "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", + "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", + "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", + "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", + "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", + "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", + "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==3.0.1" + "version": "==3.0.3" }, "ics": { "hashes": [ @@ -382,12 +372,12 @@ }, "mako": { "hashes": [ - "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818", - "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34" + "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e", + "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.2.4" + "markers": "python_version >= '3.8'", + "version": "==1.3.2" }, "markupsafe": { "hashes": [ @@ -587,12 +577,12 @@ }, "python-dotenv": { "hashes": [ - "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba", - "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a" + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.0.0" + "version": "==1.0.1" }, "python-editor": { "hashes": [ diff --git a/application.py b/application.py index 0943da89..eb20c537 100644 --- a/application.py +++ b/application.py @@ -1,56 +1,53 @@ from flask import Flask from flask_cors import CORS - from controllers import * -# Register the application - -application = app = Flask(__name__) - -# TODO: Tech Debt -# - CORS Should be specified at the host level per environment, not a global free-for-all. We do this to stop -# cross site scripting (XSS) attacks. -CORS(app) - -# Register all controllers individually -app.register_blueprint(existing_requests_bp) -app.register_blueprint(new_request_bp) -app.register_blueprint(recommendation_bp) -app.register_blueprint(shift_request_bp) -app.register_blueprint(vehicle_request_bp) -app.register_blueprint(volunteer_bp) -app.register_blueprint(volunteer_all_bp) -app.register_blueprint(volunteer_availability_bp) -app.register_blueprint(volunteer_preferred_hours_bp) -app.register_blueprint(volunteer_shifts_bp) -app.register_blueprint(volunteer_status_bp) -app.register_blueprint(authentication_bp) -app.register_blueprint(reference_bp) -app.register_blueprint(user_role_bp) -app.register_blueprint(asset_type_role_bp) -app.register_blueprint(user_type_bp) -app.register_blueprint(tenancy_config_bp) -app.register_blueprint(tutorial_quiz_bp) -app.register_blueprint(email_bp) -app.register_blueprint(profile_bp) -app.register_blueprint(v2_bp) -app.register_blueprint(volunteer_unavailability_bp) -app.register_blueprint(user_bp) -app.register_blueprint(chatbot_bp) -app.register_blueprint(diet_requirement_retrieval_bp) - - -@app.route('/') -def main(): - return { - 'status': 'OK', - } - +def create_app(): + # Create the Flask application + app = Flask(__name__) + + # Apply CORS settings to the application + CORS(app) + + # Register all controllers individually + app.register_blueprint(existing_requests_bp) + app.register_blueprint(new_request_bp) + app.register_blueprint(recommendation_bp) + app.register_blueprint(shift_request_bp) + app.register_blueprint(vehicle_request_bp) + app.register_blueprint(volunteer_bp) + app.register_blueprint(volunteer_all_bp) + app.register_blueprint(volunteer_availability_bp) + app.register_blueprint(volunteer_preferred_hours_bp) + app.register_blueprint(volunteer_shifts_bp) + app.register_blueprint(volunteer_status_bp) + app.register_blueprint(authentication_bp) + app.register_blueprint(reference_bp) + app.register_blueprint(user_role_bp) + app.register_blueprint(asset_type_role_bp) + app.register_blueprint(user_type_bp) + app.register_blueprint(tenancy_config_bp) + app.register_blueprint(tutorial_quiz_bp) + app.register_blueprint(email_bp) + app.register_blueprint(profile_bp) + app.register_blueprint(v2_bp) + app.register_blueprint(volunteer_unavailability_bp) + app.register_blueprint(user_bp) + app.register_blueprint(chatbot_bp) + app.register_blueprint(diet_requirement_retrieval_bp) + + # Define the main route + @app.route('/') + def main(): + return { + 'status': 'OK', + } + + return app if __name__ == '__main__': import logging logging.basicConfig(filename='error.log', level=logging.DEBUG) + app = create_app() # Create an app by calling the factory method app.run(host='0.0.0.0') - - diff --git a/controllers/v2/unavailability/api.py b/controllers/v2/unavailability/api.py index 23a223a4..2f71d942 100644 --- a/controllers/v2/unavailability/api.py +++ b/controllers/v2/unavailability/api.py @@ -1,9 +1,14 @@ +import json +import uuid + from flask import jsonify from flask_restful import reqparse, Resource, marshal_with, inputs +from domain.entity import unavailability_time from .response_models import volunteer_unavailability_time from domain import session_scope, UserType from repository.volunteer_unavailability_v2 import * +from repository.unavailability_repository import * from services.jwk import requires_auth, is_user_or_has_role from controllers.v2.v2_blueprint import v2_api @@ -43,7 +48,7 @@ def delete(self, user_id, event_id): return {"message": "Unavailability event not found."}, 404 except Exception as e: # HTTP 500 Internal Server Error - return {"description": "Internal server error", "error": str(e)}, 500 + return {"message": "Internal server error", "error": str(e)}, 500 class VolunteerUnavailabilityV2(Resource): @@ -64,7 +69,26 @@ def get(self, user_id): def post(self, user_id): try: args = edit_parser.parse_args() + # Check if start time is earlier than end time. + if args['start'] >= args['end']: + return {"message": "Start time must be earlier than end time"}, 400 # HTTP 400 Bad Request + with session_scope() as session: + # checks if new time frame overlaps with any existing in the database for specific userId + overlapping_events = session.query(UnavailabilityTime).filter( + UnavailabilityTime.userId == user_id, + UnavailabilityTime.start < args['end'], + UnavailabilityTime.end > args['start'], + UnavailabilityTime.periodicity == args['periodicity'] + ).all() + if overlapping_events: + overlapping_details = [] + for event in overlapping_events: + overlapping_details.append({ + "eventId": event.eventId}) + return {"message": "Time frames overlap with existing events", + "overlapping_events": overlapping_details}, 400 # HTTP 400 Bad Request + eventId = create_event( session, user_id, @@ -86,3 +110,4 @@ def post(self, user_id): v2_api.add_resource(VolunteerUnavailabilityV2, '/v2/volunteers/', '/v2/volunteers//unavailability') + diff --git a/requirements.txt b/requirements.txt index e5eaa21d..15991adb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ blinker==1.7.0; python_version >= '3.8' boto3==1.17.112; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' botocore==1.20.112; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' certifi==2024.2.2; python_version >= '3.6' -cffi==1.15.1 +cffi==1.16.0; python_version >= '3.8' click==8.1.7; python_version >= '3.7' distlib==0.3.8 exceptiongroup==1.2.0; python_version < '3.11' @@ -17,7 +17,7 @@ filelock==3.13.1; python_version >= '3.8' flask==3.0.2; python_version >= '3.8' flask-cors==4.0.0 flask-restful==0.3.10 -greenlet==3.0.1; python_version >= '3.7' +greenlet==3.0.3; python_version >= '3.7' ics==0.7.2 importlib-metadata==7.0.1; python_version < '3.9' importlib-resources==6.1.1; python_version < '3.9' @@ -25,7 +25,7 @@ iniconfig==2.0.0; python_version >= '3.7' itsdangerous==2.1.2; python_version >= '3.7' jinja2==3.1.3; python_version >= '3.7' jmespath==0.10.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' -mako==1.2.4; python_version >= '3.7' +mako==1.3.2; python_version >= '3.8' markupsafe==2.1.5; python_version >= '3.7' minizinc==0.9.0; python_version >= '3.7' and python_version < '4.0' mysqlclient==2.2.4; python_version >= '3.8' @@ -36,9 +36,9 @@ platformdirs==4.2.0; python_version >= '3.8' pluggy==1.4.0; python_version >= '3.8' pycparser==2.21; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' pyjwt==2.8.0; python_version >= '3.7' -pytest==8.0.2; python_version >= '3.8' +pytest==8.0.1; python_version >= '3.8' python-dateutil==2.9.0.post0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -python-dotenv==1.0.0; python_version >= '3.8' +python-dotenv==1.0.1; python_version >= '3.8' python-editor==1.0.4 python-http-client==3.3.7; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' pytz==2024.1 diff --git a/services/optimiser/input_processing.py b/services/optimiser/input_processing.py index 5f515c71..b3d3bd28 100644 --- a/services/optimiser/input_processing.py +++ b/services/optimiser/input_processing.py @@ -348,30 +348,3 @@ def get_position_qualification(session, position_id): qualification_id = qualification[0] return qualification_id - -# This can be called by api from postman, to test easily -def test_vehicle_list(session, request_id): - v_l = get_vehicle_list(session, request_id) - for v in v_l: - get_position_list(session, v) - - # print("get_postion_role", get_position_role(session,720)) - # print("get_APR_matrix", get_input_rolerequirements(session,361)) - # get_position_qualification(session,756) - # print("get_APQ_matrix",get_input_qualrequirements(session,361)) - # print("get_position_list_all",get_position_list_all(session,360)) - # print("get_position_vehicle",get_position_vehicle(session,863)) - # print("get_posrequirements_matrix",get_input_posrequirements(session,360)) - # get_input_qualability(session) - # print(get_qualification_list(session)) - # print(get_input_qualability(session)) - # get_role_list(session) - # print(get_input_roleability(session)) - # print(get_vehicle_time(session,369)) - # print(get_input_availability(session,360)) - # print(time_unavailability_list(session,31)) - # print(get_input_availability(session,360)) - # print(get_input_clashes(session,323)) - - - return 1 \ No newline at end of file diff --git a/services/optimiser/testing/test_shift.py b/services/optimiser/testing/test_shift.py deleted file mode 100644 index e1e88af5..00000000 --- a/services/optimiser/testing/test_shift.py +++ /dev/null @@ -1,64 +0,0 @@ -import pytest as pytest - -from domain.base import Session, Engine -from services.optimiser.input_processing import * - -# Todo: Mock session - -@pytest.fixture -def session(): - new_session = Session(bind=Engine) - yield new_session - - new_session.close() - - -@pytest.fixture -def request_id(): - return '356' - - -# Todo: using the mock database to test. - -# Input: The database and the vehicle id. -# Expected output: A matrix of (A, P, Q). Indicates the qualifications for the vehicle on all shifts. -# A-The number of shifts for this vehicle attend. P-The number of position. Q-The number of qualifications. -def test_get_input_qualrequirements(session, request_id): - print(get_input_qualrequirements(session, request_id)) - - -# Input: The database and the vehicle id. -# Expected output: A matrix of (A, P, R). Indicates the roles for the vehicle on all shifts. -# A-The number of shifts for this vehicle attend. P-The number of position. R-The number of roles. -def test_get_input_rolerequirements(session, request_id): - print(get_input_rolerequirements(session, request_id)) - - -# Input: The database and the vehicle id. -# Expected output: A matrix of (P, A). Indicates the number of positions of the vehicle on all shifts. -# P-The number of position. A-The number of shifts for this vehicle attend. -def test_get_input_posrequirements(session, request_id): - print(get_input_posrequirements(session, request_id)) - - -# Input: The database and the vehicle id. -# Expected output: A matrix of (V, Q). Indicates the qualifications for all volunteers. -# V-The number of volunteers. Q-The number of qualifications. -def test_get_input_qualability(session): - print(get_input_qualability(session)) - - -# Input: The database and the vehicle id. -# Expected output: A matrix of (V, R). Indicates the roles for all volunteers. -# V-The number of volunteers. R-The number of roles. -def test_get_input_roleability(session): - print(get_input_roleability(session)) - - -# Input: The database and the vehicle id. -# Expected output: A matrix of (V, A). Indicates the volunteer's availability during the scheduled shift time. -# V-The number of volunteers. A-The number of shifts for this vehicle attend. -def test_get_input_availability(session, request_id): - print(get_input_availability(session, request_id)) - - diff --git a/services/optimiser/testing_for_scheduler.py b/services/optimiser/testing_for_scheduler.py deleted file mode 100644 index 2827bc53..00000000 --- a/services/optimiser/testing_for_scheduler.py +++ /dev/null @@ -1,61 +0,0 @@ -import pytest -from domain.base import Session, Engine -from services.optimiser.input_processing import ( - get_input_clashes, - get_vehicle_time, - get_vehicle_list, - get_position_list, - get_position_list_all, -) - -@pytest.fixture -def session(): - new_session = Session(bind=Engine) - yield new_session - - new_session.close() - -@pytest.fixture -def request_id(): - return '322' - -@pytest.fixture -def vehicle_id(): - return '369' - -# Input: The database and the request id. -# Output: A 2-dimensional matrix of size [A, A] that contains boolean values. -# A- represent whether each asset shift clashes with other asset shifts -def test_get_input_clashes(session,request_id): - print(get_input_clashes(session,request_id)) - -# Input: The database and the vehicle id. -# Output: A 6-dimensional matrix of size [year,Month,Day,Hour,Minute,Second] means the time of vehicle . -def test_get_vehicle_time(session,vehicle_id): - print(get_vehicle_time(session,vehicle_id)) - -# Input: The database and the request id. -# Output: A 2-dimensional matrix of size [vehicle_id, vehicle_id] that contains vehicle_id that request_id order. -# output: The dimensionality of the output matrix depends on how many vehicles are required for this request id -# Tips: This data is stored in table[asset_request_vehicle] and the "id" filed represent the vehicle_id -def test_get_vehicle_list(session,request_id): - print(get_vehicle_list(session,request_id)) - -# Input: The database and the vehicle id. -# Output: A 3-dimensional matrix of size [position id,position id,position id] -# output: The dimensionality of the output matrix depends on how many position are related to this vehicle id -def test_get_position_list(session,vehicle_id): - print(get_position_list(session,vehicle_id)) - -# Input: The database and the vehicle id. -# Output: A 3-dimensional matrix of size [position id,position id,position id]. -# This matrix will be sorted based on the size of the numbers -def test_get_position_list_all(session,request_id): - print(get_position_list_all(session,request_id)) - - - - - - - diff --git a/services/optimiser/testing/__init__.py b/tests/__init__.py similarity index 100% rename from services/optimiser/testing/__init__.py rename to tests/__init__.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..f22e3429 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,39 @@ +import os +import pytest +from application import create_app + +os.environ.setdefault('username', 'user') +os.environ.setdefault('password', 'password') +os.environ.setdefault('host', '127.0.0.1') +os.environ.setdefault('port', '3306') +os.environ.setdefault('dbname', 'db') + + +@pytest.fixture(scope='module') +def test_client(): + # Set the Testing configuration prior to creating the Flask application + + with create_app().test_client() as testing_client: + # Establish an application context + with create_app().app_context(): + yield testing_client + + +@pytest.fixture(scope='module') +def auth_token(test_client): + # Login with predefined admin credentials + login_payload = { # admin account details + 'email': 'admin', + 'password': 'admin' + } + response = test_client.post('/authentication/login', json=login_payload) + assert response.status_code == 200, "Failed to log in with the given credential" + print(response.json) + token = response.json['access_token'] + return token + + +# append jwt token to header of all the testing requests +@pytest.fixture(autouse=True) +def set_auth_header(test_client, auth_token): + test_client.environ_base['HTTP_AUTHORIZATION'] = f'Bearer {auth_token}' diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/functional/test_get_unavailability.py b/tests/functional/test_get_unavailability.py new file mode 100644 index 00000000..2c2f1e46 --- /dev/null +++ b/tests/functional/test_get_unavailability.py @@ -0,0 +1,40 @@ +def test_get_volunteer_unavailability_success(test_client): + user_id = 49 # Assuming this user has at least one unavailability record + response = test_client.get(f"/v2/volunteers/{user_id}/unavailability") + assert response.status_code == 200 + assert "unavailability" in response.json # Assuming the response includes an 'unavailability' key + assert isinstance(response.json["unavailability"], + list) # Assuming the unavailability records are returned as a list + + +def test_get_volunteer_unavailability_no_records(test_client): + user_id = 50 # Assuming this user has no unavailability records + response = test_client.get(f"/v2/volunteers/{user_id}/unavailability") + assert response.status_code == 400 # Assuming the endpoint returns a 400 status for no records found + assert response.json == {'userID': user_id, 'success': False} # Expected response body for no records + + +def test_get_volunteer_unavailability_invalid_user(test_client): + user_id = -1 # Non-existent user ID + response = test_client.get(f"/v2/volunteers/{user_id}/unavailability") + # Assuming the system treats requests for non-existent users as bad requests or not found + assert response.status_code == 404 + assert response.json == {"message": "User not found"} # Assuming this is the response for an invalid user ID + + +# This is not yet implemented in the get method +def test_get_volunteer_unavailability_unauthorized(test_client): + user_id = 30 + # This test assumes that the endpoint requires authentication and that the test client is not authenticated + response = test_client.get(f"/v2/volunteers/{user_id}/unavailability") + assert response.status_code == 401 # Unauthorized access + assert "error" in response.json # Checking for an error message in the response + + +# This is not yet implemented in the get method +def test_get_volunteer_unavailability_forbidden(test_client): + user_id = 31 + # Assuming the test client is authenticated as a different user without permissions to access this user's data + response = test_client.get(f"/v2/volunteers/{user_id}/unavailability") + assert response.status_code == 403 # Forbidden access + assert "error" in response.json # Checking for an error message indicating lack of permissions diff --git a/tests/functional/test_unavailability.py b/tests/functional/test_unavailability.py new file mode 100644 index 00000000..060ac788 --- /dev/null +++ b/tests/functional/test_unavailability.py @@ -0,0 +1,137 @@ +def test_create_unavailability_consecutive_event_id(test_client): + user_id = 49 # admin id + payload_1 = { + "title": "All Day Event", + "periodicity": 0, + "start": "2024-03-02T00:00:00Z", + "end": "2024-03-02T23:59:59Z" + } + payload_2 = { + "title": "All Day Event", + "periodicity": 0, + "start": "2024-03-03T00:00:00Z", + "end": "2024-03-03T23:59:59Z" + } + response = test_client.post(f"/v2/volunteers/{user_id}/unavailability", + json=payload_1 + ) + response_2 = test_client.post(f"/v2/volunteers/{user_id}/unavailability", + json=payload_2 + ) + assert response.status_code == 200 + assert response_2.status_code == 200 + assert response_2.json["eventId"] - response.json["eventId"] == 1 + + +def test_create_unavailability_same_time_interval(test_client): + user_id = 49 + payload = { + "title": "All Day Event", + "periodicity": 0, + "start": "2024-03-02T00:00:00Z", + "end": "2024-03-02T23:59:59Z" + } + response = test_client.post(f"/v2/volunteers/{user_id}/unavailability", + json=payload + ) + response_2 = test_client.post(f"/v2/volunteers/{user_id}/unavailability", + json=payload + ) + assert response.status_code == 200 + assert response_2.status_code == 400 + + +def test_create_unavailability_nonexistent_user_id(test_client): + user_id = -1 + payload = { + "title": "All Day Event", + "periodicity": 0, + "start": "2024-03-02T00:00:00Z", + "end": "2024-03-02T23:59:59Z" + } + response = test_client.post(f"/v2/volunteers/{user_id}/unavailability", + json=payload + ) + response.status_code = 404 + + +def test_create_unavailability_end_before_start(test_client): + user_id = 49 + payload = { + "title": "All Day Event", + "periodicity": 0, + "start": "2024-03-02T00:00:00Z", + "end": "2024-03-01T23:59:59Z" + } + response = test_client.post(f"/v2/volunteers/{user_id}/unavailability", + json=payload + ) + assert response.status_code == 400 + + +def test_create_unavailability_overlapped_time(test_client): + user_id = 49 + payload_1 = { + "title": "All Day Event", + "periodicity": 0, + "start": "2024-03-03T00:00:00Z", + "end": "2024-03-04T23:59:59Z" + } + payload_2 = { + "title": "All Day Event", + "periodicity": 0, + "start": "2024-03-01T00:00:00Z", + "end": "2024-03-05T23:59:59Z" + } + response_1 = test_client.post(f"/v2/volunteers/{user_id}/unavailability", + json=payload_1 + ) + response_2 = test_client.post(f"/v2/volunteers/{user_id}/unavailability", + json=payload_2 + ) + assert response_1.status_code == 200 + assert response_2.status_code == 400 + + +def test_merge_overlapping_unavailability_intervals(test_client): + user_id = 49 + payload_1 = { + "title": "Morning Event", + "periodicity": 0, + "start": "2024-03-05T08:00:00Z", + "end": "2024-03-05T12:00:00Z" + } + payload_2 = { + "title": "Afternoon Event", + "periodicity": 0, + "start": "2024-03-05T11:00:00Z", + "end": "2024-03-05T15:00:00Z" + } + test_client.post(f"/v2/volunteers/{user_id}/unavailability", json=payload_1) + response = test_client.post(f"/v2/volunteers/{user_id}/unavailability", json=payload_2) + assert response.status_code == 200 + assert len(response.json["mergedIntervals"]) == 1 # json response must have mergedIntervals field if it is merged + assert response.json["mergedIntervals"][0]["start"] == "2024-03-05T08:00:00Z" + assert response.json["mergedIntervals"][0]["end"] == "2024-03-05T15:00:00Z" + + +def test_merge_adjacent_unavailability_intervals(test_client): + user_id = 49 + payload_1 = { + "title": "Morning Shift", + "periodicity": 0, + "start": "2024-03-06T08:00:00Z", + "end": "2024-03-06T12:00:00Z" + } + payload_2 = { + "title": "Afternoon Shift", + "periodicity": 0, + "start": "2024-03-06T12:00:00Z", + "end": "2024-03-06T16:00:00Z" + } + test_client.post(f"/v2/volunteers/{user_id}/unavailability", json=payload_1) + response = test_client.post(f"/v2/volunteers/{user_id}/unavailability", json=payload_2) + assert response.status_code == 200 + assert len(response.json["mergedIntervals"]) == 1 # json response must have mergedIntervals field if it is merged + assert response.json["mergedIntervals"][0]["start"] == "2024-03-06T08:00:00Z" + assert response.json["mergedIntervals"][0]["end"] == "2024-03-06T16:00:00Z" diff --git a/tests/functional/test_vehicle_list.py b/tests/functional/test_vehicle_list.py new file mode 100644 index 00000000..1779b73b --- /dev/null +++ b/tests/functional/test_vehicle_list.py @@ -0,0 +1,28 @@ +from services.optimiser.input_processing import get_vehicle_list, get_position_list + +# This can be called by api from postman, to tests easily +def test_vehicle_list(session, request_id): + v_l = get_vehicle_list(session, request_id) + for v in v_l: + get_position_list(session, v) + + # print("get_postion_role", get_position_role(session,720)) + # print("get_APR_matrix", get_input_rolerequirements(session,361)) + # get_position_qualification(session,756) + # print("get_APQ_matrix",get_input_qualrequirements(session,361)) + # print("get_position_list_all",get_position_list_all(session,360)) + # print("get_position_vehicle",get_position_vehicle(session,863)) + # print("get_posrequirements_matrix",get_input_posrequirements(session,360)) + # get_input_qualability(session) + # print(get_qualification_list(session)) + # print(get_input_qualability(session)) + # get_role_list(session) + # print(get_input_roleability(session)) + # print(get_vehicle_time(session,369)) + # print(get_input_availability(session,360)) + # print(time_unavailability_list(session,31)) + # print(get_input_availability(session,360)) + # print(get_input_clashes(session,323)) + + + return 1 \ No newline at end of file diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b