Merge commit 'c835f1e0e0e98a06dbec3984f2c966b9ce48aa6e' into feature-NEWDWH2021-1072-fix-webapp

This commit is contained in:
shimoda.m@nds-tyo.co.jp 2023-06-02 11:54:40 +09:00
commit 20f64139b8
54 changed files with 1027 additions and 512 deletions

View File

@ -23,3 +23,4 @@ AUTHORIZE_ENDPOINT=oauth2/authorize
TOKEN_ENDPOINT=oauth2/token
BIO_SEARCH_RESULT_MAX_COUNT=35000
SESSION_EXPIRE_MINUTE=20
LOG_LEVEL=DEBUG

View File

@ -9,6 +9,7 @@ app = "uvicorn src.main:app --reload --no-server-header"
[packages]
fastapi = "*"
uvicorn = "*"
"uvicorn[standard]" = "*"
gunicorn = "*"
boto3 = "*"
jinja2 = "*"

View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "d78a6bf1a96aa14c45431185961cae6d54ca1da8ea0319e1976bad4c2bebd673"
"sha256": "3fc09dcad05f44b119f92f9955a7731128d9f9b1829240b7689102fb14f82edc"
},
"pipfile-spec": 6,
"requires": {
@ -18,35 +18,35 @@
"default": {
"anyio": {
"hashes": [
"sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421",
"sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"
"sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce",
"sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0"
],
"markers": "python_full_version >= '3.6.2'",
"version": "==3.6.2"
"markers": "python_version >= '3.7'",
"version": "==3.7.0"
},
"boto3": {
"hashes": [
"sha256:278d896e9090a976f41ec68da5c572bc4e5b7cb1e515f1898fee8cb2fadfb50d",
"sha256:3ce2225a61832d69831d669d912424ea3863268ca1cfa2a82203bb90952acefa"
"sha256:30f8ab1cf89d5864a80ba2d5eb5316dbd2a63c9469877e0cffb522630438aa85",
"sha256:77e8fa7c257f9ed8bfe0c3ffc2ccc47b1cfa27058f99415b6003699d1202e0c0"
],
"index": "pypi",
"version": "==1.26.91"
"version": "==1.26.145"
},
"botocore": {
"hashes": [
"sha256:4ed6a488aee1b42367eace71f7d0993dda05b02eebd7dcdd78db5c9ce3d80da5",
"sha256:a8a800a2a945da807758cace539fc5b5ec1d5082ce363799d3a3870c2c4ed6fc"
"sha256:264a3f19ed280d80711b7e278be09acff7ed379a96432fdf179b4e6e3a687e6a",
"sha256:65e2a2b1cc70583225f87d6d63736215f93c6234721967bdab872270ba7a1f45"
],
"markers": "python_version >= '3.7'",
"version": "==1.29.91"
"version": "==1.29.145"
},
"certifi": {
"hashes": [
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
"sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
"sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
],
"markers": "python_version >= '3.6'",
"version": "==2022.12.7"
"version": "==2023.5.7"
},
"cffi": {
"hashes": [
@ -195,7 +195,7 @@
"sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df",
"sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"
],
"markers": "python_version >= '3.7'",
"markers": "python_full_version >= '3.7.0'",
"version": "==3.1.0"
},
"click": {
@ -208,31 +208,27 @@
},
"cryptography": {
"hashes": [
"sha256:103e8f7155f3ce2ffa0049fe60169878d47a4364b277906386f8de21c9234aa1",
"sha256:23df8ca3f24699167daf3e23e51f7ba7334d504af63a94af468f468b975b7dd7",
"sha256:2725672bb53bb92dc7b4150d233cd4b8c59615cd8288d495eaa86db00d4e5c06",
"sha256:30b1d1bfd00f6fc80d11300a29f1d8ab2b8d9febb6ed4a38a76880ec564fae84",
"sha256:35d658536b0a4117c885728d1a7032bdc9a5974722ae298d6c533755a6ee3915",
"sha256:50cadb9b2f961757e712a9737ef33d89b8190c3ea34d0fb6675e00edbe35d074",
"sha256:5f8c682e736513db7d04349b4f6693690170f95aac449c56f97415c6980edef5",
"sha256:6236a9610c912b129610eb1a274bdc1350b5df834d124fa84729ebeaf7da42c3",
"sha256:788b3921d763ee35dfdb04248d0e3de11e3ca8eb22e2e48fef880c42e1f3c8f9",
"sha256:8bc0008ef798231fac03fe7d26e82d601d15bd16f3afaad1c6113771566570f3",
"sha256:8f35c17bd4faed2bc7797d2a66cbb4f986242ce2e30340ab832e5d99ae60e011",
"sha256:b49a88ff802e1993b7f749b1eeb31134f03c8d5c956e3c125c75558955cda536",
"sha256:bc0521cce2c1d541634b19f3ac661d7a64f9555135e9d8af3980965be717fd4a",
"sha256:bc5b871e977c8ee5a1bbc42fa8d19bcc08baf0c51cbf1586b0e87a2694dde42f",
"sha256:c43ac224aabcbf83a947eeb8b17eaf1547bce3767ee2d70093b461f31729a480",
"sha256:d15809e0dbdad486f4ad0979753518f47980020b7a34e9fc56e8be4f60702fac",
"sha256:d7d84a512a59f4412ca8549b01f94be4161c94efc598bf09d027d67826beddc0",
"sha256:e029b844c21116564b8b61216befabca4b500e6816fa9f0ba49527653cae2108",
"sha256:e8a0772016feeb106efd28d4a328e77dc2edae84dfbac06061319fdb669ff828",
"sha256:e944fe07b6f229f4c1a06a7ef906a19652bdd9fd54c761b0ff87e83ae7a30354",
"sha256:eb40fe69cfc6f5cdab9a5ebd022131ba21453cf7b8a7fd3631f45bbf52bed612",
"sha256:fa507318e427169ade4e9eccef39e9011cdc19534f55ca2f36ec3f388c1f70f3",
"sha256:ffd394c7896ed7821a6d13b24657c6a34b6e2650bd84ae063cf11ccffa4f1a97"
"sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db",
"sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a",
"sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039",
"sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c",
"sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3",
"sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485",
"sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c",
"sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca",
"sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5",
"sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5",
"sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3",
"sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb",
"sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43",
"sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31",
"sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc",
"sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b",
"sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006",
"sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a",
"sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699"
],
"version": "==39.0.2"
"version": "==41.0.1"
},
"et-xmlfile": {
"hashes": [
@ -242,13 +238,21 @@
"markers": "python_version >= '3.6'",
"version": "==1.1.0"
},
"exceptiongroup": {
"hashes": [
"sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e",
"sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"
],
"markers": "python_version < '3.11'",
"version": "==1.1.1"
},
"fastapi": {
"hashes": [
"sha256:451387550c2d25a972193f22e408a82e75a8e7867c834a03076704fe20df3256",
"sha256:4a75936dbf9eb74be5eb0d41a793adefe9f3fc6ba66dbdabd160120fd3c2d9cd"
"sha256:4d9d3e8c71c73f11874bcf5e33626258d143252e329a01002f767306c64fb982",
"sha256:d374dbc4ef2ad9b803899bd3360d34c534adc574546e25314ab72c0c4411749f"
],
"index": "pypi",
"version": "==0.94.1"
"version": "==0.95.2"
},
"greenlet": {
"hashes": [
@ -332,6 +336,52 @@
"markers": "python_version >= '3.7'",
"version": "==0.14.0"
},
"httptools": {
"hashes": [
"sha256:0297822cea9f90a38df29f48e40b42ac3d48a28637368f3ec6d15eebefd182f9",
"sha256:1af91b3650ce518d226466f30bbba5b6376dbd3ddb1b2be8b0658c6799dd450b",
"sha256:1f90cd6fd97c9a1b7fe9215e60c3bd97336742a0857f00a4cb31547bc22560c2",
"sha256:24bb4bb8ac3882f90aa95403a1cb48465de877e2d5298ad6ddcfdebec060787d",
"sha256:295874861c173f9101960bba332429bb77ed4dcd8cdf5cee9922eb00e4f6bc09",
"sha256:3625a55886257755cb15194efbf209584754e31d336e09e2ffe0685a76cb4b60",
"sha256:3a47a34f6015dd52c9eb629c0f5a8a5193e47bf2a12d9a3194d231eaf1bc451a",
"sha256:3cb8acf8f951363b617a8420768a9f249099b92e703c052f9a51b66342eea89b",
"sha256:4b098e4bb1174096a93f48f6193e7d9aa7071506a5877da09a783509ca5fff42",
"sha256:4d9ebac23d2de960726ce45f49d70eb5466725c0087a078866043dad115f850f",
"sha256:50d4613025f15f4b11f1c54bbed4761c0020f7f921b95143ad6d58c151198142",
"sha256:5230a99e724a1bdbbf236a1b58d6e8504b912b0552721c7c6b8570925ee0ccde",
"sha256:54465401dbbec9a6a42cf737627fb0f014d50dc7365a6b6cd57753f151a86ff0",
"sha256:550059885dc9c19a072ca6d6735739d879be3b5959ec218ba3e013fd2255a11b",
"sha256:557be7fbf2bfa4a2ec65192c254e151684545ebab45eca5d50477d562c40f986",
"sha256:5b65be160adcd9de7a7e6413a4966665756e263f0d5ddeffde277ffeee0576a5",
"sha256:64eba6f168803a7469866a9c9b5263a7463fa8b7a25b35e547492aa7322036b6",
"sha256:72ad589ba5e4a87e1d404cc1cb1b5780bfcb16e2aec957b88ce15fe879cc08ca",
"sha256:7d0c1044bce274ec6711f0770fd2d5544fe392591d204c68328e60a46f88843b",
"sha256:7e5eefc58d20e4c2da82c78d91b2906f1a947ef42bd668db05f4ab4201a99f49",
"sha256:850fec36c48df5a790aa735417dca8ce7d4b48d59b3ebd6f83e88a8125cde324",
"sha256:85b392aba273566c3d5596a0a490978c085b79700814fb22bfd537d381dd230c",
"sha256:8c2a56b6aad7cc8f5551d8e04ff5a319d203f9d870398b94702300de50190f63",
"sha256:8f470c79061599a126d74385623ff4744c4e0f4a0997a353a44923c0b561ee51",
"sha256:8ffce9d81c825ac1deaa13bc9694c0562e2840a48ba21cfc9f3b4c922c16f372",
"sha256:9423a2de923820c7e82e18980b937893f4aa8251c43684fa1772e341f6e06887",
"sha256:9b571b281a19762adb3f48a7731f6842f920fa71108aff9be49888320ac3e24d",
"sha256:a04fe458a4597aa559b79c7f48fe3dceabef0f69f562daf5c5e926b153817281",
"sha256:aa47ffcf70ba6f7848349b8a6f9b481ee0f7637931d91a9860a1838bfc586901",
"sha256:bede7ee075e54b9a5bde695b4fc8f569f30185891796b2e4e09e2226801d09bd",
"sha256:c1d2357f791b12d86faced7b5736dea9ef4f5ecdc6c3f253e445ee82da579449",
"sha256:c6eeefd4435055a8ebb6c5cc36111b8591c192c56a95b45fe2af22d9881eee25",
"sha256:ca1b7becf7d9d3ccdbb2f038f665c0f4857e08e1d8481cbcc1a86a0afcfb62b2",
"sha256:e67d4f8734f8054d2c4858570cc4b233bf753f56e85217de4dfb2495904cf02e",
"sha256:e8a34e4c0ab7b1ca17b8763613783e2458e77938092c18ac919420ab8655c8c1",
"sha256:e90491a4d77d0cb82e0e7a9cb35d86284c677402e4ce7ba6b448ccc7325c5421",
"sha256:ef1616b3ba965cd68e6f759eeb5d34fbf596a79e84215eeceebf34ba3f61fdc7",
"sha256:f222e1e9d3f13b68ff8a835574eda02e67277d51631d69d7cf7f8e07df678c86",
"sha256:f5e3088f4ed33947e16fd865b8200f9cfae1144f41b64a8cf19b599508e096bc",
"sha256:f659d7a48401158c59933904040085c200b4be631cb5f23a7d561fbae593ec1f",
"sha256:fe9c766a0c35b7e3d6b6939393c8dfdd5da3ac5dec7f971ec9134f284c6c36d6"
],
"version": "==0.5.0"
},
"idna": {
"hashes": [
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
@ -414,37 +464,34 @@
},
"numpy": {
"hashes": [
"sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22",
"sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f",
"sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9",
"sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96",
"sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0",
"sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a",
"sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281",
"sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04",
"sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468",
"sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253",
"sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756",
"sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a",
"sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb",
"sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d",
"sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0",
"sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910",
"sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978",
"sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5",
"sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f",
"sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a",
"sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5",
"sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2",
"sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d",
"sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95",
"sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5",
"sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d",
"sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780",
"sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa"
"sha256:04847257662eef90599a1beca30c757d8e562aa8c7d64e91ea465f299469075d",
"sha256:06bae17a3629416eb5bae3a429655dc075561206b6d3c1ddfa38b51f273bae5c",
"sha256:1365157813810cfda2be9518806bf32f6b5f56e5e501d8299e3b681d53e405e2",
"sha256:165b0fb4d5b6349eef7b909be2d61a673bc6e75e0eec43776eea3222385a9d11",
"sha256:1bed69508b3b97dd3fb8c439352881c1bd232a0c8dd1e11d8df4e68046d434cf",
"sha256:20dd2352806eb229bc79c4fa308431eaf1721c66f7928950ee0381df98a2d269",
"sha256:224e8862a1cd357eede831b270b9e6c51d2cbc2bb5cc2e2b8d0c76d52cbd1edc",
"sha256:236c8ff573c02677b873e0934419c8e9873bd2b35aaba885170b7b43cb26d5da",
"sha256:416da35914d2fecc3afd31127b1eb1a283df33292cfcb453e1c8fb46d52611a1",
"sha256:472bdc3ade289d3efa331738b1daa5a529eef0550650f5d5d2eadb936a2f83a5",
"sha256:48e33b46b7db13de75dd0c1c919b8b297b5d7a4dc50b181066977ee17bed7cc3",
"sha256:5e7ba92ad63ffded03400d5038af89f7788843794c77ad1a37522fa69762b06f",
"sha256:7261d100c9bf722057fd5b9cd5b48f2973b17792b41e689eeaf9b55843cd1afd",
"sha256:763fca81a8d8beb6bf4b9a9bbf4045b0c134c15ea66c81d26e5b8683b1861293",
"sha256:7cf92c2bfbaf7bd52df1a21e56e8d34cff711594498ecbd02a39df3aaada763b",
"sha256:8aad2f86d2036622af1e1eb9db94e26618f42a571e02583fa72d5b1983782bf8",
"sha256:9a18d2c173a44e48e72614748df5624875439af2d352a416b9f3840583ad9efb",
"sha256:a0dab69ef25ccabf6f066a4902e238767cbbe52bc5ff90aa99514f87812ba76a",
"sha256:a4a9f1eaa63b5e35e23e5465ed59746b0a680eb5b5da06f2d432f828d32b26c1",
"sha256:b1a22ae597ee1d0e2336044854b33965fd92e731efe3c2ab965826e02cca2a8c",
"sha256:bd1de5d7ab75cdf56f2247aace7940dfd0a8fd048e07808358d8fca604f1d102",
"sha256:dfe2e3845c3b630f6617f9e8a15c8a1cbaf452c9fa32c71ec0a77d09548cd662",
"sha256:f59080829bbfe46660a201fc17315a4e8ec6e4499ee745bab3df61866f63e771",
"sha256:f64b730004e500f836f6405ad5cd36d309b6ac065366a0855860155f23eb2ad5",
"sha256:f8cbdb428d848f03a4f6f534284cf7fb168a6ec0e742357bf65ad268316906ea"
],
"markers": "python_version < '3.10'",
"version": "==1.24.2"
"version": "==1.25.0rc1"
},
"openpyxl": {
"hashes": [
@ -456,34 +503,34 @@
},
"pandas": {
"hashes": [
"sha256:008aa9843e92753d1345353e643c51017d8a9e303041db3165b683fc16a4d380",
"sha256:1f060ae468cb24e1ab42c6344b097375b24a902d3cefb5524f93ef0cd0db5f4b",
"sha256:2379d66055592480aab24cda5b1543539302e0f85e9a33538e9e4fd309b3063e",
"sha256:26a507e14dc9a5ef29239b85d0ef5f01a7e308b88781b451a415d9d15e2d1a61",
"sha256:314bc00a0575151d3ec3124af23bf2ef7533b0e160fb138007a4ef1b3c6a0e63",
"sha256:3935c394e1b10d5c311bd9378018a468283adfe8469dc8084e21d55ca06be979",
"sha256:47f116fcb3aa533ab6661ca391136a643e25d1387dae989ed3e5b9248b98e2e9",
"sha256:4e99adf0a3b4e040fad8823567b52eacfd48db50d11024244a60197430ec74b8",
"sha256:67a5251a821b5af1c5aefe5a610a7758fae04693434fb98b2ebad10349cd727a",
"sha256:7bb2d670c1f7de9bcef0986ae9f832fbd99acc43db1d5fe22f2f06bda8a67d43",
"sha256:7fc7c85fcf27726633751d064f4d115dbccb202b0b6ea2909b6d89ca071115e3",
"sha256:8010e4c988c2c2ed1f5763a6e579448a13a7c87b810400124bb872121c9ca3f9",
"sha256:867fd5c3325c302e8feaaa7ec2d99c224be38551d8a9e1ae5d15be7e04424172",
"sha256:8cb4789c8b1f361d7b07a25002e871546b108519af9c176f8a5ca66316c09d90",
"sha256:8ce8603f8cf07044458914b81bb7445b6cc31d381657e0fac21b3eee40f404d0",
"sha256:adc1e91f282426d37830837f108747f0628e7635b1e83b2401b4f7e2a0068a82",
"sha256:b72ba4e9553645c0bfd688a4e89efe9694fb2936adb5c6295d31626233cb674a",
"sha256:c3c3be69e186d12a94004b0c76bb390e26b48e4b444f3adc86d2cf6506c71d99",
"sha256:cf960fc1f2545114b9ed1a0f025d6de63c891df31640e454e333e3b38504d36b",
"sha256:dc45eb7f23c92e0aa5278bb210fb30136e6e0b760636cf18874cdf2d6448df0f",
"sha256:e5ebb19a66d8c4a4563e6cb628a23ee6898dc50e5dfe8b73c692cd7ea81def0a",
"sha256:e817d97597be5c21b1a66cbecadd0d0242482b72f6f5b60129fce5cec329e274",
"sha256:e829b927b156f85432390580d8799dfee59db0be3954235cf5f5df8a42eaaacd",
"sha256:ebc301fb34185275d9ad57838f533d5413a02b434174d1be89785141f785b226",
"sha256:f082e075aeac904db0e69d8b8acc1d610362e3d823ace3af029622b24b105900"
"sha256:02755de164da6827764ceb3bbc5f64b35cb12394b1024fdf88704d0fa06e0e2f",
"sha256:0a1e0576611641acde15c2322228d138258f236d14b749ad9af498ab69089e2d",
"sha256:1eb09a242184092f424b2edd06eb2b99d06dc07eeddff9929e8667d4ed44e181",
"sha256:30a89d0fec4263ccbf96f68592fd668939481854d2ff9da709d32a047689393b",
"sha256:50e451932b3011b61d2961b4185382c92cc8c6ee4658dcd4f320687bb2d000ee",
"sha256:51a93d422fbb1bd04b67639ba4b5368dffc26923f3ea32a275d2cc450f1d1c86",
"sha256:598e9020d85a8cdbaa1815eb325a91cfff2bb2b23c1442549b8a3668e36f0f77",
"sha256:66d00300f188fa5de73f92d5725ced162488f6dc6ad4cecfe4144ca29debe3b8",
"sha256:69167693cb8f9b3fc060956a5d0a0a8dbfed5f980d9fd2c306fb5b9c855c814c",
"sha256:6d6d10c2142d11d40d6e6c0a190b1f89f525bcf85564707e31b0a39e3b398e08",
"sha256:713f2f70abcdade1ddd68fc91577cb090b3544b07ceba78a12f799355a13ee44",
"sha256:7376e13d28eb16752c398ca1d36ccfe52bf7e887067af9a0474de6331dd948d2",
"sha256:77550c8909ebc23e56a89f91b40ad01b50c42cfbfab49b3393694a50549295ea",
"sha256:7b21cb72958fc49ad757685db1919021d99650d7aaba676576c9e88d3889d456",
"sha256:9ebb9f1c22ddb828e7fd017ea265a59d80461d5a79154b49a4207bd17514d122",
"sha256:a18e5c72b989ff0f7197707ceddc99828320d0ca22ab50dd1b9e37db45b010c0",
"sha256:a6b5f14cd24a2ed06e14255ff40fe2ea0cfaef79a8dd68069b7ace74bd6acbba",
"sha256:b42b120458636a981077cfcfa8568c031b3e8709701315e2bfa866324a83efa8",
"sha256:c4af689352c4fe3d75b2834933ee9d0ccdbf5d7a8a7264f0ce9524e877820c08",
"sha256:c7319b6e68de14e6209460f72a8d1ef13c09fb3d3ef6c37c1e65b35d50b5c145",
"sha256:cf3f0c361a4270185baa89ec7ab92ecaa355fe783791457077473f974f654df5",
"sha256:dd46bde7309088481b1cf9c58e3f0e204b9ff9e3244f441accd220dd3365ce7c",
"sha256:dd5476b6c3fe410ee95926873f377b856dbc4e81a9c605a0dc05aaccc6a7c6c6",
"sha256:e69140bc2d29a8556f55445c15f5794490852af3de0f609a24003ef174528b79",
"sha256:f908a77cbeef9bbd646bd4b81214cbef9ac3dda4181d5092a4aa9797d1bc7774"
],
"index": "pypi",
"version": "==2.0.0rc0"
"version": "==2.0.2"
},
"pycparser": {
"hashes": [
@ -494,72 +541,70 @@
},
"pydantic": {
"hashes": [
"sha256:012c99a9c0d18cfde7469aa1ebff922e24b0c706d03ead96940f5465f2c9cf62",
"sha256:0abd9c60eee6201b853b6c4be104edfba4f8f6c5f3623f8e1dba90634d63eb35",
"sha256:12e837fd320dd30bd625be1b101e3b62edc096a49835392dcf418f1a5ac2b832",
"sha256:163e79386c3547c49366e959d01e37fc30252285a70619ffc1b10ede4758250a",
"sha256:189318051c3d57821f7233ecc94708767dd67687a614a4e8f92b4a020d4ffd06",
"sha256:1c84583b9df62522829cbc46e2b22e0ec11445625b5acd70c5681ce09c9b11c4",
"sha256:3091d2eaeda25391405e36c2fc2ed102b48bac4b384d42b2267310abae350ca6",
"sha256:32937835e525d92c98a1512218db4eed9ddc8f4ee2a78382d77f54341972c0e7",
"sha256:3a2be0a0f32c83265fd71a45027201e1278beaa82ea88ea5b345eea6afa9ac7f",
"sha256:3ac1cd4deed871dfe0c5f63721e29debf03e2deefa41b3ed5eb5f5df287c7b70",
"sha256:3ce13a558b484c9ae48a6a7c184b1ba0e5588c5525482681db418268e5f86186",
"sha256:415a3f719ce518e95a92effc7ee30118a25c3d032455d13e121e3840985f2efd",
"sha256:43cdeca8d30de9a897440e3fb8866f827c4c31f6c73838e3a01a14b03b067b1d",
"sha256:476f6674303ae7965730a382a8e8d7fae18b8004b7b69a56c3d8fa93968aa21c",
"sha256:4c19eb5163167489cb1e0161ae9220dadd4fc609a42649e7e84a8fa8fff7a80f",
"sha256:4ca83739c1263a044ec8b79df4eefc34bbac87191f0a513d00dd47d46e307a65",
"sha256:528dcf7ec49fb5a84bf6fe346c1cc3c55b0e7603c2123881996ca3ad79db5bfc",
"sha256:53de12b4608290992a943801d7756f18a37b7aee284b9ffa794ee8ea8153f8e2",
"sha256:587d92831d0115874d766b1f5fddcdde0c5b6c60f8c6111a394078ec227fca6d",
"sha256:60184e80aac3b56933c71c48d6181e630b0fbc61ae455a63322a66a23c14731a",
"sha256:6195ca908045054dd2d57eb9c39a5fe86409968b8040de8c2240186da0769da7",
"sha256:61f1f08adfaa9cc02e0cbc94f478140385cbd52d5b3c5a657c2fceb15de8d1fb",
"sha256:72cb30894a34d3a7ab6d959b45a70abac8a2a93b6480fc5a7bfbd9c935bdc4fb",
"sha256:751f008cd2afe812a781fd6aa2fb66c620ca2e1a13b6a2152b1ad51553cb4b77",
"sha256:89f15277d720aa57e173954d237628a8d304896364b9de745dcb722f584812c7",
"sha256:8c32b6bba301490d9bb2bf5f631907803135e8085b6aa3e5fe5a770d46dd0160",
"sha256:acc6783751ac9c9bc4680379edd6d286468a1dc8d7d9906cd6f1186ed682b2b0",
"sha256:b1eb6610330a1dfba9ce142ada792f26bbef1255b75f538196a39e9e90388bf4",
"sha256:b243b564cea2576725e77aeeda54e3e0229a168bc587d536cd69941e6797543d",
"sha256:b41822064585fea56d0116aa431fbd5137ce69dfe837b599e310034171996084",
"sha256:bbd5c531b22928e63d0cb1868dee76123456e1de2f1cb45879e9e7a3f3f1779b",
"sha256:cf95adb0d1671fc38d8c43dd921ad5814a735e7d9b4d9e437c088002863854fd",
"sha256:e277bd18339177daa62a294256869bbe84df1fb592be2716ec62627bb8d7c81d",
"sha256:ea4e2a7cb409951988e79a469f609bba998a576e6d7b9791ae5d1e0619e1c0f2",
"sha256:f9289065611c48147c1dd1fd344e9d57ab45f1d99b0fb26c51f1cf72cd9bcd31",
"sha256:fd9b9e98068fa1068edfc9eabde70a7132017bdd4f362f8b4fd0abed79c33083"
"sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375",
"sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277",
"sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d",
"sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4",
"sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca",
"sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c",
"sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01",
"sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18",
"sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68",
"sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887",
"sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459",
"sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4",
"sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5",
"sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e",
"sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1",
"sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33",
"sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a",
"sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56",
"sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108",
"sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2",
"sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4",
"sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878",
"sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0",
"sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e",
"sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6",
"sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f",
"sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800",
"sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea",
"sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f",
"sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b",
"sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1",
"sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd",
"sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319",
"sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab",
"sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85",
"sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f"
],
"markers": "python_version >= '3.7'",
"version": "==1.10.6"
"version": "==1.10.8"
},
"pyjwt": {
"extras": [
"crypto"
],
"extras": [],
"hashes": [
"sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd",
"sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"
"sha256:ba2b425b15ad5ef12f200dc67dd56af4e26de2331f965c5439994dad075876e1",
"sha256:bd6ca4a3c4285c1a2d4349e5a035fdf8fb94e04ccd0fcbe6ba289dae9cc3e074"
],
"index": "pypi",
"version": "==2.6.0"
"version": "==2.7.0"
},
"pymysql": {
"hashes": [
"sha256:41fc3a0c5013d5f039639442321185532e3e2c8924687abe6537de157d403641",
"sha256:816927a350f38d56072aeca5dfb10221fe1dc653745853d30a216637f5d7ad36"
"sha256:5072fb2637f8bfff0e7a15a9c02a0f4ba98f97800e12432e1d6d95936ec6d496",
"sha256:5cc02f2f60936c5d2d6122ffaff27783bd29ba7683ea45a8ab75c5083f00dc20"
],
"index": "pypi",
"version": "==1.0.2"
"version": "==1.1.0rc1"
},
"pynamodb": {
"hashes": [
"sha256:3c4d10867d59e6d7a2b54ee4ae213f1021d6f50ff93145e3909784bfc2b7560e",
"sha256:e09c39880560e10251778185b3d0c7a97ee8f42ab363a940c674e9330b61bf9d"
"sha256:82f77bb0c21a12756e6781df735ca841f543337847d8522a4ab8db6df7bbfc9f",
"sha256:a44fb486fc3e66a25b58d921e07f016f62416e323b381b96c1b725105868dacf"
],
"index": "pypi",
"version": "==5.4.1"
"version": "==5.5.0"
},
"python-dateutil": {
"hashes": [
@ -569,6 +614,13 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.2"
},
"python-dotenv": {
"hashes": [
"sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba",
"sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"
],
"version": "==1.0.0"
},
"python-multipart": {
"hashes": [
"sha256:e9925a80bb668529f1b67c7fdb0a5dacdd7cbfc6fb0bff3ea443fe22bdd62132",
@ -579,34 +631,79 @@
},
"pytz": {
"hashes": [
"sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0",
"sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"
"sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588",
"sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"
],
"version": "==2022.7.1"
"version": "==2023.3"
},
"pyyaml": {
"hashes": [
"sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf",
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
"sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
"sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
"sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
"sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
"sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
"sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782",
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
"sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1",
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
"sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d",
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
"sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7",
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
"sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358",
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
"sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f",
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
],
"version": "==6.0"
},
"requests": {
"hashes": [
"sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa",
"sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"
"sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
"sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"
],
"index": "pypi",
"version": "==2.28.2"
"version": "==2.31.0"
},
"s3transfer": {
"hashes": [
"sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd",
"sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"
"sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346",
"sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9"
],
"markers": "python_version >= '3.7'",
"version": "==0.6.0"
"version": "==0.6.1"
},
"setuptools": {
"hashes": [
"sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077",
"sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"
"sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f",
"sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"
],
"markers": "python_version >= '3.7'",
"version": "==67.6.0"
"version": "==67.8.0"
},
"six": {
"hashes": [
@ -626,82 +723,227 @@
},
"sqlalchemy": {
"hashes": [
"sha256:1df00f280fcf7628379c6838d47ac6abd2319848cb02984af313de9243994db8",
"sha256:1fd154847f2c77128e16757e3fd2028151aa8208dd3b9a5978918ea786a15312",
"sha256:20f36bff3b6c9fa94e40114fda4dc5048d40fd665390f5547b456a28e8059ee8",
"sha256:224c817e880359d344a462fc4dd94a233804f371aa290b024b6b976a2f5ade36",
"sha256:2ad44f45526411bebbf427cf858955a35f3a6bfd7db8f4314b12da4c0d1a4fd2",
"sha256:2c4c64f321080c83a3f0eed11cc9b73fe2a574f6b8339c402861274165c24cf6",
"sha256:3625a52fae744cff6f9beb6ed0775468b9eb7e6e8f6730676dfc49aa77d98b4e",
"sha256:3be54b3825512b3de5698ae04bf4aad6ea60442ac0f6b91ee4b8fa4db5c2dccd",
"sha256:4100c80070a66b042f1010b29b29a88d1d151c27a5e522c95ec07518b361a7a3",
"sha256:47e96be3e8c9c0f2c71ec87599be4bb8409d61841b66964a36b2447bec510b3b",
"sha256:483712fce53e2f7ec95ed7d106cd463f9fc122c28a7df4aaf2bc873d0d2a901f",
"sha256:48824b989a0e4340cd099dd4539702ddb1a5ce449f8a7355124e40a4935a95fa",
"sha256:4d653962da384a1d99795dbd8aac4a7516071b2f2984ed2aa25545fae670b808",
"sha256:5b067b2eaf3d97a49f3f6217981efa7b45d5726c2142f103712b020dd250fd98",
"sha256:5c35175b74cbcfe9af077bd13e87cfab13239e075c0e1e920095082f9377f0ed",
"sha256:61abff42e44e5daf17372cb8baa90e970dc647fc5f747e2caa9f9768acf17be8",
"sha256:6987f658389ad8bb6257db91551e7fde3e904974eef6f323856260907ef311d7",
"sha256:709f1ecb5dcea59f36fa0f485e09e41ff313b2d62c83a6f99b36870b0d6e42fa",
"sha256:7635cd38e3ea8522729b14451157104fce2117c44e7ba6a14684ed153d71b567",
"sha256:778db814cc21eff200c8bd42b4ffe976fa3378d10fb84d2c164d3c6a30bb38ee",
"sha256:81d4fc8f5c966677a3a2f39eb8e496442269d8c7d285b28145f7745fcc089d63",
"sha256:82691d3539023c3cee5ae055c47bf873728cd6b33bfaa7b916bea5a99b92f700",
"sha256:8ef7c56c74f4420b2c4a148d2531ba7f99b946cbf438a2bbcb2435fb4938a08d",
"sha256:9310666251385e4374c6f0bae6d69e62bc422021298ceb8669bf6ff56957ff37",
"sha256:ac6274dd530b684cca8cbb774e348afac6846f15d1694a56954413be6e2e8dcd",
"sha256:b7be0e6a4061d28b66ca4b4eb24558dd8c6386d3bcd2d6d7ef247be27cf1281b",
"sha256:bea2c1341abe9bc6f30071b8ada1a3c44f24ec0fe1b9418e9c1112ed32057c9e",
"sha256:bfcadfb8f0a9d26a76a5e2488cedd2e7cf8e70fe76d58aeb1c85eb83b33cbc5c",
"sha256:bfce790746d059af6d0bc68b578ba20d50a63c71a3db16edce7aa8eccdd73796",
"sha256:bfde1d7cf8b9aa6bbd0d53946cd508d76db7689afd442e2289642cdc8908b7b7",
"sha256:c343f0b546495f5d7a239c70bf50a99a48d7321c165b82afafa8483b9ebebf6e",
"sha256:c5d754665edea1ecdc79e3023659cb5594372e10776f3b3734d75c2c3ce95013",
"sha256:c76caced0c8e9129810895f71954c72f478e30bea7d0bba7130bade396be5048",
"sha256:ca147d9cde38b481085408e1d4277ee834cb88bcc31bc01933bc6513340071bc",
"sha256:d7bd001a40997f0c9a9ac10a57663a9397959966a5a365bb24a4d1a17aa60175",
"sha256:db91fe985f2264ab49b3450ab7e2a59c34f7eaf3bf283d6b9e2f9ee02b29e533",
"sha256:e0e270a4f5b42c67362d9c6af648cb86f6a00b20767553cfd734c914e1e2a5e0",
"sha256:ed714b864349704a7a719ec7199eec3f9cd15c190ecf6e10c34b5a0c549c5c18",
"sha256:edc16c8e24605d0a7925afaf99dbcbdc3f98a2cdda4622f1ea34482cb3b91940",
"sha256:f47709c98544384d390aed34046f0573df5725d22861c0cd0a5c151bc22eedff",
"sha256:ff10ad2d74a9a79c2984a2c709943e5362a1c898d8f3414815ea57515ae80c84"
"sha256:1a0754c2d9f0c7982bec0a31138e495ed1f6b8435d7e677c45be60ec18370acf",
"sha256:1d6320a1d175447dce63618ec997a53836de48ed3b44bbe952f0b4b399b19941",
"sha256:1e885dacb167077df15af2f9ccdacbd7f5dd0d538a6d74b94074f2cefc7bb589",
"sha256:201a99f922ac8c780b3929128fbd9df901418877c70e160e19adb05665e51c31",
"sha256:21c89044fc48a25c2184eba332edeffbbf9367913bb065cd31538235d828f06f",
"sha256:256b2b9660e51ad7055a9835b12717416cf7288afcf465107413917b6bb2316f",
"sha256:2e940a8659ef870ae10e0d9e2a6d5aaddf0ff6e91f7d0d7732afc9e8c4be9bbc",
"sha256:3fb5d09f1d51480f711b69fe28ad42e4f8b08600a85ab2473baee669e1257800",
"sha256:435f6807fa6a0597d84741470f19db204a7d34625ea121abd63e8d95f673f0c4",
"sha256:4670ce853cb25f72115a1bbe366ae13cf3f28fc5c87222df14f8d3d55d51816e",
"sha256:4a75fdb9a84072521bb2ebd31eefe1165d4dccea3039dda701a864f4b5daa17f",
"sha256:4d61731a35eddb0f667774fe15e5a4831e444d066081d1e809e1b8a0e3f97cae",
"sha256:51b19887c96d405599880da6a7cbdf8545a7e78ec5683e46a43bac8885e32d0f",
"sha256:536c86ec81ca89291d533ff41a3a05f9e4e88e01906dcee0751fc7082f3e8d6c",
"sha256:55ec62ddc0200b4fee94d11abbec7aa25948d5d21cb8df8807f4bdd3c51ba44b",
"sha256:5cc48a7fda2b5c5b8860494d6c575db3a101a68416492105fed6591dc8a2728a",
"sha256:670ecf74ee2e70b917028a06446ad26ff9b1195e84b09c3139c215123d57dc30",
"sha256:6a3f8020e013e9b3b7941dcf20b0fc8f7429daaf7158760846731cbd8caa5e45",
"sha256:6b42913a0259267e9ee335da0c36498077799e59c5e332d506e72b4f32de781d",
"sha256:6f5784dfb2d45c19cde03c45c04a54bf47428610106197ed6e6fa79f33bc63d3",
"sha256:6f80a9c9a9af0e4bd5080cc0955ce70274c28e9b931ad7e0fb07021afcd32af6",
"sha256:78303719c6f72af97814b0072ad18bee72e70adca8d95cf8fecd59c5e1ddb040",
"sha256:788d1772fb8dcd12091ca82809eef504ce0f2c423e45284bc351b872966ff554",
"sha256:79bfe728219239bdc493950ea4a4d15b02138ecb304771f9024d0d6f5f4e3706",
"sha256:810199d1c5b43603a9e815ae9487aef3ab1ade7ed9c0c485e12519358929fbfe",
"sha256:88ab245ed2c96265441ed2818977be28c840cfa5204ba167425d6c26eb67b7e7",
"sha256:933d30273861fe61f014ce2a7e3c364915f5efe9ed250ec1066ca6ea5942c0bd",
"sha256:994a75b197662e0608b6a76935d7c345f7fd874eac0b7093d561033db61b0e8c",
"sha256:9b31ebde27575b3b0708673ec14f0c305c4564d995b545148ab7ac0f4d9b847a",
"sha256:9d810b4aacd5ef4e293aa4ea01f19fca53999e9edcfc4a8ef1146238b30bdc28",
"sha256:ae1d8deb391ab39cc8f0d5844e588a115ae3717e607d91482023917f920f777f",
"sha256:bc5c2b0da46c26c5f73f700834f871d0723e1e882641932468d56833bab09775",
"sha256:cea7c4a3dfc2ca61f88a2b1ddd6b0bfbd116c9b1a361b3b66fd826034b833142",
"sha256:d14282bf5b4de87f922db3c70858953fd081ef4f05dba6cca3dd705daffe1cc9",
"sha256:d6b17cb86908e7f88be14007d6afe7d2ab11966e373044137f96a6a4d83eb21c",
"sha256:da7381a883aee20b7d2ffda17d909b38134b6a625920e65239a1c681881df800",
"sha256:db269f67ed17b07e80aaa8fba1f650c0d84aa0bdd9d5352e4ac38d5bf47ac568",
"sha256:df25052b92bd514357a9b370d74f240db890ea79aaa428fb893520e10ee5bc18",
"sha256:e17fdcb8971e77c439113642ca8861f9465e21fc693bd3916654ceef3ac26883",
"sha256:f6fd3c88ea4b170d13527e93be1945e69facd917661d3725a63470eb683fbffe",
"sha256:f7f994a53c0e6b44a2966fd6bfc53e37d34b7dca34e75b6be295de6db598255e"
],
"index": "pypi",
"version": "==2.0.6"
"version": "==2.0.15"
},
"starlette": {
"hashes": [
"sha256:41da799057ea8620e4667a3e69a5b1923ebd32b1819c8fa75634bbe8d8bea9bd",
"sha256:e87fce5d7cbdde34b76f0ac69013fd9d190d581d80681493016666e6f96c6d5e"
"sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75",
"sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"
],
"markers": "python_version >= '3.7'",
"version": "==0.26.1"
"version": "==0.27.0"
},
"typing-extensions": {
"hashes": [
"sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb",
"sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"
"sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26",
"sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"
],
"markers": "python_version >= '3.7'",
"version": "==4.5.0"
"version": "==4.6.3"
},
"tzdata": {
"hashes": [
"sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a",
"sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"
],
"markers": "python_version >= '2'",
"version": "==2023.3"
},
"urllib3": {
"hashes": [
"sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305",
"sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"
"sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f",
"sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.26.15"
"version": "==1.26.16"
},
"uvicorn": {
"hashes": [
"sha256:8635a388062222082f4b06225b867b74a7e4ef942124453d4d1d1a5cb3750932",
"sha256:e69e955cb621ae7b75f5590a814a4fcbfb14cb8f44a36dfe3c5c75ab8aee3ad5"
"sha256:79277ae03db57ce7d9aa0567830bbb51d7a612f54d6e1e3e92da3ef24c2c8ed8",
"sha256:e9434d3bbf05f310e762147f769c9f21235ee118ba2d2bf1155a7196448bd996"
],
"index": "pypi",
"version": "==0.21.0"
"version": "==0.22.0"
},
"uvloop": {
"hashes": [
"sha256:0949caf774b9fcefc7c5756bacbbbd3fc4c05a6b7eebc7c7ad6f825b23998d6d",
"sha256:0ddf6baf9cf11a1a22c71487f39f15b2cf78eb5bde7e5b45fbb99e8a9d91b9e1",
"sha256:1436c8673c1563422213ac6907789ecb2b070f5939b9cbff9ef7113f2b531595",
"sha256:23609ca361a7fc587031429fa25ad2ed7242941adec948f9d10c045bfecab06b",
"sha256:2a6149e1defac0faf505406259561bc14b034cdf1d4711a3ddcdfbaa8d825a05",
"sha256:2deae0b0fb00a6af41fe60a675cec079615b01d68beb4cc7b722424406b126a8",
"sha256:307958f9fc5c8bb01fad752d1345168c0abc5d62c1b72a4a8c6c06f042b45b20",
"sha256:30babd84706115626ea78ea5dbc7dd8d0d01a2e9f9b306d24ca4ed5796c66ded",
"sha256:3378eb62c63bf336ae2070599e49089005771cc651c8769aaad72d1bd9385a7c",
"sha256:3d97672dc709fa4447ab83276f344a165075fd9f366a97b712bdd3fee05efae8",
"sha256:3db8de10ed684995a7f34a001f15b374c230f7655ae840964d51496e2f8a8474",
"sha256:3ebeeec6a6641d0adb2ea71dcfb76017602ee2bfd8213e3fcc18d8f699c5104f",
"sha256:45cea33b208971e87a31c17622e4b440cac231766ec11e5d22c76fab3bf9df62",
"sha256:6708f30db9117f115eadc4f125c2a10c1a50d711461699a0cbfaa45b9a78e376",
"sha256:68532f4349fd3900b839f588972b3392ee56042e440dd5873dfbbcd2cc67617c",
"sha256:6aafa5a78b9e62493539456f8b646f85abc7093dd997f4976bb105537cf2635e",
"sha256:7d37dccc7ae63e61f7b96ee2e19c40f153ba6ce730d8ba4d3b4e9738c1dccc1b",
"sha256:864e1197139d651a76c81757db5eb199db8866e13acb0dfe96e6fc5d1cf45fc4",
"sha256:8887d675a64cfc59f4ecd34382e5b4f0ef4ae1da37ed665adba0c2badf0d6578",
"sha256:8efcadc5a0003d3a6e887ccc1fb44dec25594f117a94e3127954c05cf144d811",
"sha256:9b09e0f0ac29eee0451d71798878eae5a4e6a91aa275e114037b27f7db72702d",
"sha256:a4aee22ece20958888eedbad20e4dbb03c37533e010fb824161b4f05e641f738",
"sha256:a5abddb3558d3f0a78949c750644a67be31e47936042d4f6c888dd6f3c95f4aa",
"sha256:c092a2c1e736086d59ac8e41f9c98f26bbf9b9222a76f21af9dfe949b99b2eb9",
"sha256:c686a47d57ca910a2572fddfe9912819880b8765e2f01dc0dd12a9bf8573e539",
"sha256:cbbe908fda687e39afd6ea2a2f14c2c3e43f2ca88e3a11964b297822358d0e6c",
"sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718",
"sha256:dbbaf9da2ee98ee2531e0c780455f2841e4675ff580ecf93fe5c48fe733b5667",
"sha256:f1e507c9ee39c61bfddd79714e4f85900656db1aec4d40c6de55648e85c2799c",
"sha256:ff3d00b70ce95adce264462c930fbaecb29718ba6563db354608f37e49e09024"
],
"version": "==0.17.0"
},
"watchfiles": {
"hashes": [
"sha256:0089c6dc24d436b373c3c57657bf4f9a453b13767150d17284fc6162b2791911",
"sha256:09ea3397aecbc81c19ed7f025e051a7387feefdb789cf768ff994c1228182fda",
"sha256:176a9a7641ec2c97b24455135d58012a5be5c6217fc4d5fef0b2b9f75dbf5154",
"sha256:18b28f6ad871b82df9542ff958d0c86bb0d8310bb09eb8e87d97318a3b5273af",
"sha256:20b44221764955b1e703f012c74015306fb7e79a00c15370785f309b1ed9aa8d",
"sha256:3d7d267d27aceeeaa3de0dd161a0d64f0a282264d592e335fff7958cc0cbae7c",
"sha256:5471582658ea56fca122c0f0d0116a36807c63fefd6fdc92c71ca9a4491b6b48",
"sha256:5569fc7f967429d4bc87e355cdfdcee6aabe4b620801e2cf5805ea245c06097c",
"sha256:68dce92b29575dda0f8d30c11742a8e2b9b8ec768ae414b54f7453f27bdf9545",
"sha256:79c533ff593db861ae23436541f481ec896ee3da4e5db8962429b441bbaae16e",
"sha256:7f3920b1285a7d3ce898e303d84791b7bf40d57b7695ad549dc04e6a44c9f120",
"sha256:91633e64712df3051ca454ca7d1b976baf842d7a3640b87622b323c55f3345e7",
"sha256:945be0baa3e2440151eb3718fd8846751e8b51d8de7b884c90b17d271d34cae8",
"sha256:9afd0d69429172c796164fd7fe8e821ade9be983f51c659a38da3faaaaac44dc",
"sha256:9c75eff897786ee262c9f17a48886f4e98e6cfd335e011c591c305e5d083c056",
"sha256:b538014a87f94d92f98f34d3e6d2635478e6be6423a9ea53e4dd96210065e193",
"sha256:b6577b8c6c8701ba8642ea9335a129836347894b666dd1ec2226830e263909d3",
"sha256:c0376deac92377817e4fb8f347bf559b7d44ff556d9bc6f6208dd3f79f104aaf",
"sha256:cae3dde0b4b2078f31527acff6f486e23abed307ba4d3932466ba7cdd5ecec79",
"sha256:cb5d45c4143c1dd60f98a16187fd123eda7248f84ef22244818c18d531a249d1",
"sha256:d9b073073e048081e502b6c6b0b88714c026a1a4c890569238d04aca5f9ca74b",
"sha256:fac19dc9cbc34052394dbe81e149411a62e71999c0a19e1e09ce537867f95ae0"
],
"version": "==0.19.0"
},
"websockets": {
"hashes": [
"sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd",
"sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f",
"sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998",
"sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82",
"sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788",
"sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa",
"sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f",
"sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4",
"sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7",
"sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f",
"sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd",
"sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69",
"sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb",
"sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b",
"sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016",
"sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac",
"sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4",
"sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb",
"sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99",
"sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e",
"sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54",
"sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf",
"sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007",
"sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3",
"sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6",
"sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86",
"sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1",
"sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61",
"sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11",
"sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8",
"sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f",
"sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931",
"sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526",
"sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016",
"sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae",
"sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd",
"sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b",
"sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311",
"sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af",
"sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152",
"sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288",
"sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de",
"sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97",
"sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d",
"sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d",
"sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca",
"sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0",
"sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9",
"sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b",
"sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e",
"sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128",
"sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d",
"sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c",
"sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5",
"sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6",
"sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b",
"sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b",
"sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280",
"sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c",
"sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c",
"sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f",
"sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20",
"sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8",
"sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb",
"sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602",
"sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf",
"sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0",
"sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74",
"sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0",
"sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"
],
"version": "==11.0.3"
},
"xlrd": {
"hashes": [

View File

@ -23,7 +23,7 @@
- Merck_NewDWH開発2021のWiki、[Python環境構築](https://nds-tyo.backlog.com/alias/wiki/1874930)を参照
- 「Pipenvの導入」までを行っておくこと
- 構築完了後、プロジェクト配下で以下のコマンドを実行し、Pythonの仮想環境を作成する
- `pipenv install --python <pyenvでインストールしたpythonバージョン>`
- `pipenv install --python <pyenvでインストールしたpythonバージョン> --dev`
- この手順で出力される仮想環境のパスは、後述するVSCodeの設定手順で使用するため、控えておく
- MySQLの環境構築

View File

@ -7,9 +7,9 @@ from src.system_var import environment
class CognitoClient(AWSAPIClient):
def __init__(self) -> None:
self.__client = boto3.client('cognito-idp')
def login_by_user_password_flow(self, username: str, password: str, secret_hash: str):
auth_response = self.__client.admin_initiate_auth(
UserPoolId=environment.COGNITO_USER_POOL_ID,
ClientId=environment.COGNITO_CLIENT_ID,
@ -21,5 +21,5 @@ class CognitoClient(AWSAPIClient):
},
)
authentication_result = auth_response['AuthenticationResult']
return authentication_result['IdToken'], authentication_result['RefreshToken'],

View File

@ -7,16 +7,16 @@ from src.aws.aws_api_client import AWSAPIClient
class S3Client(AWSAPIClient):
__s3_client = boto3.client('s3')
def upload_file(self, local_file_path: str, bucket_name: str, file_key: str):
self.__s3_client.upload_file(
local_file_path,
Bucket=bucket_name,
Key=file_key
)
local_file_path,
Bucket=bucket_name,
Key=file_key
)
def generate_presigned_url(self, bucket_name: str, file_key: str, download_filename: str=''):
# presigned_urlを生成
def generate_presigned_url(self, bucket_name: str, file_key: str, download_filename: str = ''):
# presigned_urlを生成
presigned_url = self.__s3_client.generate_presigned_url(
'get_object',
Params={
@ -28,5 +28,5 @@ class S3Client(AWSAPIClient):
# 有効期限20分
ExpiresIn=1200
)
return presigned_url

View File

@ -1,10 +1,10 @@
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.exceptions import HTTPException
from starlette import status
from src.depends.services import get_service
from src.logging.get_logger import get_logger
from src.model.internal.session import UserSession
from src.model.request.bio import BioModel
from src.model.view.bio_view_model import BioViewModel
@ -17,20 +17,25 @@ from src.templates import templates
router = APIRouter()
router.route_class = AuthenticatedRoute
logger = get_logger('生物由来参照')
#########################
# Views #
#########################
@router.get('/BioSearchList')
def bio_view(
request: Request,
batch_status_service:BatchStatusService=Depends(get_service(BatchStatusService)),
bio_service: BioViewService=Depends(get_service(BioViewService))
batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService)),
bio_service: BioViewService = Depends(get_service(BioViewService))
):
session: UserSession = request.session
# バッチ処理中の場合、機能を利用させない
if batch_status_service.is_batch_processing():
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=constants.LOGOUT_REASON_BATCH_PROCESSING)
logger.debug(f'UserId: {session.user_id}')
# 検索項目の取得
bio = bio_service.prepare_bio_view(session)
# セッション書き換え
@ -50,14 +55,14 @@ def bio_view(
)
return templates_response
@router.post('/BioSearchList')
def search_bio(
request: Request,
bio_form: Optional[BioModel] = Depends(BioModel.as_form),
bio_service: BioViewService=Depends(get_service(BioViewService)),
batch_status_service:BatchStatusService=Depends(get_service(BatchStatusService))
bio_service: BioViewService = Depends(get_service(BioViewService)),
batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService))
):
# error_log(date("Y/m/d H:i:s") . " [INFO] UserId:" . $UserId . "\r\n", 3, "$execLog");
session: UserSession = request.session
# バッチ処理中の場合、機能を利用させない
if batch_status_service.is_batch_processing():

View File

@ -2,94 +2,76 @@
from datetime import datetime
from typing import Union
import pandas as pd
from fastapi import APIRouter, Depends, HTTPException
from fastapi.exceptions import HTTPException
from fastapi.responses import JSONResponse
from starlette import status
from src.depends.auth import verify_session
from src.depends.services import get_service
from src.error.exceptions import DBException
from src.logging.get_logger import get_logger
from src.model.internal.session import UserSession
from src.model.request.bio import BioModel
from src.model.request.bio_download import BioDownloadModel
from src.services.batch_status_service import BatchStatusService
from src.services.bio_view_service import BioViewService
from src.services.session_service import set_session
from src.system_var import constants
from src.system_var import constants, environment
logger = get_logger('生物由来参照')
router = APIRouter()
#########################
# APIs #
#########################
@router.post('/download')
async def download_bio_data(
search_param: BioModel=Depends(BioModel.as_body),
download_param: BioDownloadModel=Depends(BioDownloadModel.as_body),
search_param: BioModel = Depends(BioModel.as_body),
download_param: BioDownloadModel = Depends(BioDownloadModel.as_body),
bio_service: BioViewService = Depends(get_service(BioViewService)),
batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService)),
session: Union[UserSession, None]=Depends(verify_session)
):
session: Union[UserSession, None] = Depends(verify_session)
):
# 通常のビューとはルーティングの扱いを変えるために、個別のルーターで登録する
# error_log(date("Y/m/d H:i:s") . " [INFO] getBioData start" . "\r\n", 3, "$execLog");
# 改修後のパラメータを打ち出すようにする
# いらない error_log(date("Y/m/d H:i:s") . " [INFO] param:szConditions=" . htmlspecialchars($_POST["szConditions"], ENT_QUOTES) . "\r\n", 3, "$execLog");
# いらない error_log(date("Y/m/d H:i:s") . " [INFO] param:pageNum=" . htmlspecialchars($_POST["pageNum"], ENT_QUOTES) . "\r\n", 3, "$execLog");
# いらない error_log(date("Y/m/d H:i:s") . " [INFO] szUser=" . htmlspecialchars($_POST["szUser"], ENT_QUOTES) . "\r\n", 3, "$execLog");
# いらない error_log(date("Y/m/d H:i:s") . " [INFO] szfilename=" . htmlspecialchars($_POST["szfilename"], ENT_QUOTES) . "\r\n", 3, "$execLog");
# いらない error_log(date("Y/m/d H:i:s") . " [INFO] extension=" . htmlspecialchars($_POST["extension"], ENT_QUOTES) . "\r\n", 3, "$execLog");
# いらない error_log(date("Y/m/d H:i:s") . " [INFO] sql=" . htmlspecialchars($_POST["sql"], ENT_QUOTES) . "\r\n", 3, "$execLog");
# いらない error_log(date("Y/m/d H:i:s") . " [INFO] arrayPrepare=" . $_POST["arrayPrepare"] . "\r\n", 3, "$execLog");
logger.info('生物由来データダウンロード開始')
logger.info(f'ユーザーID: {download_param.user_id}')
logger.info(f'拡張子: {download_param.ext}')
# ファイル名に使用するタイムスタンプを初期化しておく
now = datetime.now()
current_timestamp = datetime.now()
# 出力ファイル名
download_file_name = f'Result_{download_param.user_id}_{current_timestamp:%Y%m%d%H%M%S%f}.{download_param.ext}'
if session is None:
return {'status': 'session_expired'}
# バッチ処理中の場合、機能を利用させない
if batch_status_service.is_batch_processing():
return {'status': 'batch_processing'}
try:
# 生物由来データを検索
search_result_df = bio_service.search_download_bio_data(search_param)
except DBException as e:
# error_log(date("Y/m/d H:i:s") . " [ERROR] " . "\r\n", 3, "$execLog");
print('DB Error', e.args)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail={'error': 'db_error', 'message': e.args}
)
# 生物由来データを検索
# 検索に使用したクエリも取得
search_result_df, query = _search_bio_data(bio_service, search_param, download_param.user_id)
# アクセスログを記録
bio_service.write_access_log(query, search_param, download_param.user_id, current_timestamp, download_file_name)
if search_result_df.size < 1:
# 検索結果が0件の場合、download_urlを返さない
print('Bio data not found')
return {'status': 'ok', 'download_url': None}
# ファイルに打ち出すカラムを抽出
extract_df = search_result_df[constants.BIO_EXTRACT_COLUMNS]
# TODO: SQLクエリを修正するため、この処理は不要になる
extract_df = _extract_output_df(search_result_df)
# 値を変換
# データ種別の正式名を設定
extract_df.loc[:, 'slip_org_kbn'] = extract_df['slip_org_kbn'].apply(lambda key: constants.SLIP_ORG_KBN_FULL_NAME.get(key))
# データ区分の区分の日本語名を設定
extract_df.loc[:, 'data_kbn'] = extract_df['data_kbn'].apply(lambda key: constants.DATA_KBN_JP_NAME.get(key))
# ロット番号エラーフラグの日本語名を設定
extract_df.loc[:, 'lot_no_err_flg'] = extract_df['lot_no_err_flg'].apply(lambda key: constants.LOT_NO_ERR_FLG_JP_NAME.get(key))
# 訂正前伝票管理番号がセットされているときのみ修正日時、修正者、エラー詳細種別をセット
extract_df.loc[:, 'ins_dt'] = extract_df['bef_slip_mgt_no'].apply(lambda bef_slip_mgt_no:extract_df['ins_dt'] if bef_slip_mgt_no is not None else '')
extract_df.loc[:, 'ins_usr'] = extract_df['bef_slip_mgt_no'].apply(lambda bef_slip_mgt_no:extract_df['ins_usr'] if bef_slip_mgt_no is not None else '')
# 種別によって出力を変える
local_file_path = ''
if download_param.kind == 'xlsx':
# error_log(date("Y/m/d H:i:s") . " [INFO] 今回はExcelファイルに出力する" . "\r\n", 3, "$execLog");
local_file_path = bio_service.write_excel_file(extract_df, download_param.user_id, timestamp=now)
elif download_param.kind == 'csv':
# error_log(date("Y/m/d H:i:s") . " [INFO] 今回はCSVファイルに出力する" . "\r\n", 3, "$execLog");
local_file_path = bio_service.write_csv_file(extract_df, download_param.user_id, header=constants.BIO_CSV_HEADER, timestamp=now)
# ファイルを書き出し(Excel or CSV)
local_file_path = _write_bio_data_to_file(bio_service, download_param, extract_df, download_file_name)
# ローカルファイルからS3にアップロードし、ダウンロード用URLを取得する
try:
bio_service.upload_bio_data_file(local_file_path)
download_file_url = bio_service.generate_download_file_url(local_file_path, download_param.user_id, download_param.kind)
download_file_url = bio_service.generate_download_file_url(
local_file_path, download_param.user_id, download_param.ext)
except Exception as e:
print('S3 access error', e.args)
raise HTTPException(
@ -114,8 +96,64 @@ async def download_bio_data(
json_response.set_cookie(
key='session',
value=session.session_key,
max_age=20*60,
max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける
secure=True,
httponly=True
)
return json_response
def _search_bio_data(bio_service: BioViewService, search_param: BioModel, user_id: str) -> pd.DataFrame:
try:
# 生物由来データを検索
search_result_df, query = bio_service.search_download_bio_data(search_param)
# TODO: ファイルにも出力する
except DBException as e:
logger.exception(f'DB Error: {e}')
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail={'error': 'db_error', 'message': e.args}
)
return search_result_df, query
def _extract_output_df(search_result_df: pd.DataFrame) -> pd.DataFrame:
extract_df = search_result_df[constants.BIO_EXTRACT_COLUMNS]
# 値を変換
# データ種別の正式名を設定
extract_df.loc[:, 'slip_org_kbn'] = extract_df['slip_org_kbn'].apply(
lambda key: constants.SLIP_ORG_KBN_FULL_NAME.get(key))
# データ区分の区分の日本語名を設定
extract_df.loc[:, 'data_kbn'] = extract_df['data_kbn'].apply(lambda key: constants.DATA_KBN_JP_NAME.get(key))
# ロット番号エラーフラグの日本語名を設定
extract_df.loc[:, 'lot_num_err_flg'] = extract_df['lot_num_err_flg'].apply(
lambda key: constants.LOT_NO_ERR_FLG_JP_NAME.get(key))
# 訂正前伝票管理番号がセットされているときのみ修正日時、修正者、エラー詳細種別をセット
extract_df.loc[:, 'ins_dt'] = extract_df['bef_slip_mgt_num'].apply(
lambda bef_slip_mgt_num: extract_df['ins_dt'] if bef_slip_mgt_num is not None else '')
extract_df.loc[:, 'ins_usr'] = extract_df['bef_slip_mgt_num'].apply(
lambda bef_slip_mgt_num: extract_df['ins_usr'] if bef_slip_mgt_num is not None else '')
return extract_df
def _write_bio_data_to_file(
bio_service: BioViewService,
download_param: BioDownloadModel,
df: pd.DataFrame,
download_file_name: str
) -> str:
# 種別によって出力を変える
local_file_path = ''
if download_param.ext == 'xlsx':
logger.info('今回はExcelファイルに出力する')
local_file_path = bio_service.write_excel_file(
df, download_param.user_id, download_file_name=download_file_name)
elif download_param.ext == 'csv':
logger.info('今回はCSVファイルに出力する')
local_file_path = bio_service.write_csv_file(
df, download_param.user_id, header=constants.BIO_CSV_HEADER, download_file_name=download_file_name)
return local_file_path

View File

@ -5,6 +5,8 @@ router = APIRouter()
#########################
# Views #
#########################
@router.get('/')
def healthcheck():
return {'status': 'OK'}

View File

@ -1,4 +1,3 @@
import os.path as path
import secrets
import urllib.parse as parse
from typing import Union
@ -25,6 +24,8 @@ router.route_class = AfterSetCookieSessionRoute
#########################
# Views #
#########################
@router.get('/userlogin')
def login_user_redirect_view():
auth_query_string = parse.urlencode(
@ -39,6 +40,7 @@ def login_user_redirect_view():
return RedirectResponse(url=authorize_endpoint_url, status_code=status.HTTP_303_SEE_OTHER)
@router.get('/maintlogin')
def login_maintenance_view(request: Request):
mainte_login = MainteLoginViewModel()
@ -53,20 +55,22 @@ def login_maintenance_view(request: Request):
#########################
# APIs #
#########################
@router.post('/maintlogin')
def login(
response: Response,
request: LoginModel = Depends(LoginModel.as_form),
login_service: LoginService = Depends(get_service(LoginService))
):
response: Response,
request: LoginModel = Depends(LoginModel.as_form),
login_service: LoginService = Depends(get_service(LoginService))
):
try:
jwt_token = login_service.login(request.username, request.password)
except NotAuthorizeException as e:
print(e)
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_LOGIN_ERROR)
except JWTTokenVerifyException as e:
except JWTTokenVerifyException:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_SESSION_EXPIRED)
verified_token = jwt_token.verify_token()
# 普通の認証だと、`cognito:username`に入る。
user_id = verified_token.user_id
@ -92,9 +96,9 @@ def login(
user_flg=user_record.mntuser_flg
)
session_key = set_session(session_model)
response = RedirectResponse(
url='/menu',
url='/menu/',
status_code=status.HTTP_303_SEE_OTHER,
headers={'session_key': session_key}
)
@ -103,9 +107,9 @@ def login(
@router.get('/authorize')
def sso_authorize(
code:Union[str, None]=Depends(code_security),
login_service: LoginService=Depends(get_service(LoginService))
) -> Response:
code: Union[str, None] = Depends(code_security),
login_service: LoginService = Depends(get_service(LoginService))
) -> Response:
if not code:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=constants.LOGOUT_REASON_NOT_LOGIN)
@ -114,7 +118,7 @@ def sso_authorize(
try:
# トークン検証
verified_token = jwt_token.verify_token()
except JWTTokenVerifyException as e:
except JWTTokenVerifyException:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_SESSION_EXPIRED)
# トークンからユーザーIDを取得
@ -143,7 +147,7 @@ def sso_authorize(
)
session_key = set_session(session_model)
response = RedirectResponse(
url='/menu',
url='/menu/',
status_code=status.HTTP_303_SEE_OTHER,
headers={'session_key': session_key}
)

View File

@ -14,12 +14,14 @@ router = APIRouter()
#########################
# Views #
#########################
@router.get('/', response_class=HTMLResponse)
def logout_view(
request: Request,
reason: Optional[str] = None,
session: Union[UserSession, None]=Depends(verify_session)
):
request: Request,
reason: Optional[str] = None,
session: Union[UserSession, None] = Depends(verify_session)
):
redirect_to = '/login/userlogin'
link_text = 'MeDaCA機能メニューへ'
if session is not None and session.user_flg == '1':

View File

@ -2,6 +2,7 @@ from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from src.depends.services import get_service
from src.logging.get_logger import get_logger
from src.model.internal.session import UserSession
from src.model.view.menu_view_model import MenuViewModel
from src.model.view.user_view_model import UserViewModel
@ -10,18 +11,23 @@ from src.services.batch_status_service import BatchStatusService
from src.services.session_service import set_session
from src.templates import templates
logger = get_logger('MeDaCA機能メニュー')
router = APIRouter()
router.route_class = AuthenticatedRoute
#########################
# Views #
#########################
@router.get('/', response_class=HTMLResponse)
def menu_view(
request: Request,
batch_status_service:BatchStatusService=Depends(get_service(BatchStatusService))
):
request: Request,
batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService))
):
session: UserSession = request.session
logger.info(f'UserID: {session.user_id}')
# 日付マスターからバッチ情報を取得する
hdke_tbl_record = batch_status_service.hdke_table_record

View File

@ -0,0 +1,11 @@
from fastapi import APIRouter
from fastapi.responses import RedirectResponse
from starlette import status
router = APIRouter()
@router.get('/')
def redirect_to_user_login():
# ルートパスへのアクセスは、顧客ユーザーログイン画面にリダイレクトさせる
return RedirectResponse(url='/login/userlogin', status_code=status.HTTP_303_SEE_OTHER)

View File

@ -0,0 +1,44 @@
from typing import Annotated
from fastapi import APIRouter, File, Form, Request, UploadFile
from src.templates import templates
router = APIRouter()
@router.get('/')
def get_view(request: Request):
return templates.TemplateResponse(
'sample_send_file.html',
{
'request': request
}
)
@router.post('/')
# file.readがCoroutineが返ってくるため、必ずasync関数にする
async def post_view(
# formからファイルを受け取る。formタグにenctype="multipart/form-data"を指定すること)
file: Annotated[UploadFile, File()],
message: str = Form()
):
# ファイルを読み込むCoroutineが取れるため、必ずawaitする
file_bytes = await file.read()
# 閉じとく
await file.close()
# 読み込んだファイルはbytesで返ってくるので、デコードする
file_content = file_bytes.decode()
print(file_content)
try:
return {
# ファイル名
"file_name": file.filename,
# ファイルのバイト数
"file_size": file.size,
# Content-Type
"file_content_type": file.content_type
}
except Exception:
return {'code': 'fail'}

View File

@ -18,4 +18,4 @@ def create_stop_app_handler(app: FastAPI) -> Callable:
def stop_app() -> None:
close_db(app)
return stop_app
return stop_app

View File

@ -77,7 +77,10 @@ class Database:
Raises:
DBException: 接続失敗
"""
self.__connection = self.__engine.connect()
try:
self.__connection = self.__engine.connect()
except Exception as e:
raise DBException(e)
def execute_select(self, select_query: str, parameters=None) -> list[dict]:
"""SELECTクエリを実行します。

View File

@ -16,13 +16,14 @@ class SQLCondition:
self.column = column
self.operator = operator
self.param = param
self.literal=literal
self.literal = literal
def apply(self):
# literalがFalseならプレースホルダー。Trueだったならは固定値。
param = f':{self.param}' if self.literal is False else self.param
return f' {self.column} {self.operator} {param}'
# 定数
EQ = '='
NE = '<>'
@ -32,4 +33,4 @@ GE = '>='
LE = '<='
LIKE = 'LIKE'
IS = 'IS'
IS_NOT = 'IS NOT'
IS_NOT = 'IS NOT'

View File

@ -17,26 +17,28 @@ code_security = APIKeyQuery(name='code', auto_error=False)
def get_current_session(session_key=Depends(cookie_security)):
if session_key is None:
return None
session = get_session(session_key)
# sessionが存在しない場合はNoneが返る
return session
def check_session_expired(session:Union[UserSession, None]=Depends(get_current_session)):
def check_session_expired(session: Union[UserSession, None] = Depends(get_current_session)):
"""セッションの最後にアクセスした時間が、セッション有効期限切れであるかどうかをチェックする"""
if session is None:
return None
last_access_time = session.last_access_time
session_expired_period = datetime.datetime.fromtimestamp(last_access_time) + datetime.timedelta(minutes=environment.SESSION_EXPIRE_MINUTE)
session_expired_period = datetime.datetime.fromtimestamp(
last_access_time) + datetime.timedelta(minutes=environment.SESSION_EXPIRE_MINUTE)
if session_expired_period < datetime.datetime.now():
return None
return session
def verify_session(session:Union[UserSession, None]=Depends(check_session_expired)):
def verify_session(session: Union[UserSession, None] = Depends(check_session_expired)):
if session is None:
return None
jwt_token = JWTToken(session.id_token, session.refresh_token)

View File

@ -14,4 +14,4 @@ def get_database(request: Request) -> Database:
def get_repository(Repo_type: Type[BaseRepository]) -> Callable:
def get_repo(db: Database = Depends(get_database)) -> Type[BaseRepository]:
return Repo_type(db)
return get_repo
return get_repo

View File

@ -1,7 +1,6 @@
from typing import Callable, Type
from fastapi import Depends
from starlette.requests import Request
from src.db.database import Database
from src.depends.database import get_database
@ -9,8 +8,8 @@ from src.services.base_service import BaseService
def get_service(Service_type: Type[BaseService]) -> Callable:
def get_service(db: Database=Depends(get_database)) -> Type[BaseService]:
def get_service(db: Database = Depends(get_database)) -> Type[BaseService]:
repositories = {key: repository(db) for key, repository in Service_type.REPOSITORIES.items()}
clients = {key: client() for key, client in Service_type.CLIENTS.items()}
return Service_type(repositories=repositories, clients=clients)
return get_service
return get_service

View File

@ -7,18 +7,22 @@ class MeDaCaException(Exception):
"""Webアプリの共通例外"""
pass
class NotAuthorizeException(MeDaCaException):
"""認証失敗の例外"""
pass
class JWTTokenVerifyException(MeDaCaException):
"""トークン検証失敗の例外"""
pass
class DBException(MeDaCaException):
"""DB関連の例外"""
pass
class UnexpectedException(MeDaCaException):
"""予期しない例外"""

View File

@ -0,0 +1,37 @@
import logging
from src.system_var.environment import LOG_LEVEL
# boto3関連モジュールのログレベルを事前に個別指定し、モジュール内のDEBUGログの表示を抑止する
for name in ["boto3", "botocore", "s3transfer", "urllib3"]:
logging.getLogger(name).setLevel(logging.WARNING)
def get_logger(log_name: str) -> logging.Logger:
"""一意のログ出力モジュールを取得します。
Args:
log_name (str): ロガー名
Returns:
_type_: _description_
"""
logger = logging.getLogger(log_name)
level = logging.getLevelName(LOG_LEVEL)
if not isinstance(level, int):
level = logging.INFO
logger.setLevel(level)
if not logger.hasHandlers():
handler = logging.StreamHandler()
logger.addHandler(handler)
formatter = logging.Formatter(
'%(name)s\t[%(levelname)s]\t%(asctime)s\t%(message)s',
'%Y-%m-%d %H:%M:%S'
)
for handler in logger.handlers:
handler.setFormatter(formatter)
return logger

View File

@ -5,7 +5,9 @@ from fastapi.staticfiles import StaticFiles
from starlette import status
import src.static as static
from src.controller import bio, bio_download, healthcheck, login, logout, menu
from src.controller import (bio, bio_download, healthcheck, login, logout,
menu, root)
from src.controller.sample_send_file import router as sample_router
from src.core import tasks
from src.error.exception_handler import http_exception_handler
from src.error.exceptions import UnexpectedException
@ -14,6 +16,8 @@ app = FastAPI()
# 静的ファイルをマウント
app.mount('/static', StaticFiles(directory=path.dirname(static.__file__)), name='static')
# ルートパス。顧客ログイン画面にリダイレクトさせる
app.include_router(root.router)
# ログイン関連のルーター
app.include_router(login.router, prefix='/login')
# ログアウト関連のルーター
@ -28,6 +32,9 @@ app.include_router(bio_download.router, prefix='/bio')
# ヘルスチェック用のルーター
app.include_router(healthcheck.router, prefix='/healthcheck')
# サンプル実装、ファイル送信ルーター
app.include_router(sample_router, prefix='/sample')
# エラー発生時にログアウト画面に遷移させるハンドラー
app.add_exception_handler(status.HTTP_401_UNAUTHORIZED, http_exception_handler)
app.add_exception_handler(status.HTTP_403_FORBIDDEN, http_exception_handler)

View File

@ -15,34 +15,34 @@ class BioSalesViewModel(BaseDBModel):
rec_tran_kbn: Optional[str]
rev_hsdnymd_wrk: Optional[str]
rev_hsdnymd_srk: Optional[str]
rec_urag_no: Optional[str]
rec_comm_nm: Optional[str]
rec_nnskfcl_nm: Optional[str]
rec_nnsk_fcl_addr: Optional[str]
rec_urag_num: Optional[str]
rec_comm_name: Optional[str]
rec_nonyu_fcl_name: Optional[str]
rec_nonyu_fcl_addr: Optional[str]
rec_lot_num: Optional[str]
rec_amt: Optional[str]
rec_qty: Optional[str]
rec_ymd: Optional[str]
sale_data_cat: Optional[str]
slip_file_nm: Optional[str]
slip_mgt_no: Optional[str]
slip_file_name: Optional[str]
slip_mgt_num: Optional[str]
row_num: Optional[int]
hsdn_ymd: Optional[str]
exec_dt: Optional[str]
v_tran_cd: Optional[int]
tran_kbn_nm: Optional[str]
tran_kbn_name: Optional[str]
whs_org_cd: Optional[str]
v_whsorg_cd: Optional[str]
whs_org_nm: Optional[str]
whs_org_name: Optional[str]
whs_org_kn: Optional[str]
v_whs_cd: Optional[int]
whs_nm: Optional[str]
nnsk_cd: Optional[str]
whs_name: Optional[str]
nonyu_fcl_cd: Optional[str]
v_inst_cd: Optional[str]
v_inst_kn: Optional[str]
v_inst_nm: Optional[str]
v_inst_addr: Optional[str]
comm_cd: Optional[str]
comm_nm: Optional[str]
product_name: Optional[str]
whs_rep_comm_nm: Optional[str]
whs_rep_nnskfcl_nm: Optional[str]
whs_rep_nnsk_fcl_addr: Optional[str]
@ -53,7 +53,7 @@ class BioSalesViewModel(BaseDBModel):
fcl_exis_kbn: Optional[str]
amt: Optional[int]
slip_org_kbn: Optional[str]
bef_slip_mgt_no: Optional[str]
bef_slip_mgt_num: Optional[str]
lot_no_err_flg: Optional[str]
iko_flg: Optional[str]
kjyo_ym: Optional[str]

View File

@ -1,4 +1,3 @@
from datetime import datetime
from typing import Optional
from src.model.db.base_db_model import BaseDBModel

View File

@ -4,4 +4,4 @@ from src.model.db.base_db_model import BaseDBModel
class PharmacyProductMasterModel(BaseDBModel):
mkr_cd_nm: Optional[str]
mkr_cd_name: Optional[str]

View File

@ -28,9 +28,9 @@ class UserMasterModel(BaseDBModel):
def is_enable_user(self):
return self.enabled_flg == 'Y'
def is_maintenance_user(self):
return self.mntuser_flg == '1'
def is_groupware_user(self):
return self.mntuser_flg == '0'

View File

@ -6,5 +6,5 @@ from src.model.db.base_db_model import BaseDBModel
class WholesalerMasterModel(BaseDBModel):
rec_whs_cd: Optional[str]
rec_whs_sub_cd: Optional[str]
nm: Optional[str]
whs_nm: Optional[str]
name: Optional[str]
whs_name: Optional[str]

View File

@ -15,11 +15,11 @@ class JWTToken:
refresh_token: str
verified_jwt: Optional[dict]
def __init__(self, id_token: str, refresh_token: str, verified_jwt: dict=None) -> None:
def __init__(self, id_token: str, refresh_token: str, verified_jwt: dict = None) -> None:
self.id_token = id_token
self.refresh_token = refresh_token
self.verified_jwt = verified_jwt
@property
def verified_token(self):
if self.verified_jwt is None:
@ -29,7 +29,7 @@ class JWTToken:
@property
def user_id(self):
verified_token = self.verified_token
user_id: str = None
identities: dict = verified_token.get('identities')
if identities is not None:
@ -63,7 +63,7 @@ class JWTToken:
'code': code,
'redirect_uri': environment.COGNITO_REDIRECT_URI
}
message = bytes(f'{environment.COGNITO_CLIENT_ID}:{environment.COGNITO_CLIENT_SECRET}', 'utf8')
auth_header_value = base64.b64encode(message).decode()
request_headers = {
@ -76,7 +76,7 @@ class JWTToken:
raise JWTTokenVerifyException(res.text)
token_response = json.loads(res.text)
return cls(id_token=token_response['id_token'], refresh_token=token_response['refresh_token'])
@classmethod
@ -99,7 +99,7 @@ class JWTToken:
'refresh_token': refresh_token,
'redirect_uri': environment.COGNITO_REDIRECT_URI
}
message = bytes(f'{environment.COGNITO_CLIENT_ID}:{environment.COGNITO_CLIENT_SECRET}', 'utf8')
auth_header_value = base64.b64encode(message).decode()
request_headers = {
@ -117,13 +117,13 @@ class JWTToken:
def verify_token(self):
if self.id_token is None:
raise Exception('アクセストークンがない')
issuer = f'https://cognito-idp.{environment.AWS_REGION}.amazonaws.com/{environment.COGNITO_USER_POOL_ID}'
jwks_url = f'{issuer}/.well-known/jwks.json'
jwks_client = jwt.PyJWKClient(jwks_url)
signing_key = jwks_client.get_signing_key_from_jwt(self.id_token)
try:
verified_jwt = jwt.decode(
self.id_token,

View File

@ -27,7 +27,7 @@ class UserSession(DynamoDBTableModel):
@classmethod
def new_last_access_time(cls):
return datetime.datetime.now().timestamp()
@classmethod
def new_record_expiration_time(cls, expire=environment.SESSION_EXPIRE_MINUTE):
last_access_time = datetime.datetime.fromtimestamp(cls.new_last_access_time())
@ -35,8 +35,8 @@ class UserSession(DynamoDBTableModel):
@classmethod
def new(
cls, user_id, id_token, refresh_token, csrf_token, doc_flg, inst_flg, bio_flg, master_mainte_flg, user_flg
):
cls, user_id, id_token, refresh_token, csrf_token, doc_flg, inst_flg, bio_flg, master_mainte_flg, user_flg
):
return cls(
session_key=str(uuid.uuid4()),
user_id=user_id,
@ -47,7 +47,7 @@ class UserSession(DynamoDBTableModel):
inst_flg=inst_flg,
bio_flg=bio_flg,
master_mainte_flg=master_mainte_flg,
user_flg=user_flg,
user_flg=user_flg,
last_access_time=cls.new_last_access_time(),
record_expiration_time=cls.new_record_expiration_time()
)

View File

@ -9,18 +9,18 @@ from src.util.string_util import is_not_empty
@sanitize
class BioModel(BaseModel):
wholesaler_code: Optional[str]
wholesaler_sub_code: Optional[str]
wholesaler_name: Optional[str]
org_kbn: Optional[str]
rec_whs_cd: Optional[str]
rec_whs_sub_cd: Optional[str]
whs_name: Optional[str]
slip_org_kbn: Optional[str]
rec_ymd_from: Optional[str]
rec_ymd_to: Optional[str]
rec_lot_num: Optional[str]
data_kbn: Optional[str]
maker_cd: Optional[str]
mkr_cd: Optional[str]
rev_hsdnymd_srk_from: Optional[str]
rev_hsdnymd_srk_to: Optional[str]
ikoFlg: Optional[str]
iko_flg: Optional[str]
@classmethod
def as_form(
@ -50,7 +50,7 @@ class BioModel(BaseModel):
ctrl_rev_hsdnymd_srk_to,
ikoFlg
)
@classmethod
def as_body(
cls,
@ -79,7 +79,6 @@ class BioModel(BaseModel):
ctrl_rev_hsdnymd_srk_to,
ikoFlg
)
def __convert_request_param(
cls,
@ -122,16 +121,16 @@ class BioModel(BaseModel):
rev_hsdnymd_srk_to = ctrl_rev_hsdnymd_srk_to.replace('/', '')
return cls(
wholesaler_code=wholesaler_code,
wholesaler_sub_code=wholesaler_sub_code,
wholesaler_name=wholesaler_name,
org_kbn=ctrl_org_kbn,
rec_whs_cd=wholesaler_code,
rec_whs_sub_cd=wholesaler_sub_code,
whs_name=wholesaler_name,
slip_org_kbn=ctrl_org_kbn,
rec_ymd_from=rec_ymd_from,
rec_ymd_to=rec_ymd_to,
rec_lot_num=ctrl_rec_lot_num,
data_kbn=ctrl_data_kbn,
maker_cd=ctrl_maker_cd,
mkr_cd=ctrl_maker_cd,
rev_hsdnymd_srk_from=rev_hsdnymd_srk_from,
rev_hsdnymd_srk_to=rev_hsdnymd_srk_to,
ikoFlg=ikoFlg
)
iko_flg=ikoFlg
)

View File

@ -1,20 +1,18 @@
from typing import Optional
from fastapi import Body
from pydantic import BaseModel
class BioDownloadModel(BaseModel):
user_id: str
kind: str
ext: str
@classmethod
def as_body(
cls,
user_id: str = Body(),
kind: str = Body()
ext: str = Body()
):
return cls(
user_id=user_id,
kind=kind
ext=ext
)

View File

@ -5,7 +5,7 @@ from pydantic import BaseModel
class LoginModel(BaseModel):
username: str
password: str
@classmethod
def as_form(
cls,

View File

@ -7,13 +7,13 @@ from src.util.sanitize import sanitize
class BisDisplayModel(BioSalesViewModel):
def __init__(self, param: BioSalesViewModel) -> None:
super().__init__(**param.dict())
# 区分・フラグの正式名称を設定
self.slip_org_kbn = constants.SLIP_ORG_KBN_FULL_NAME.get(self.slip_org_kbn)
self.data_kbn = constants.DATA_KBN_JP_NAME.get(self.data_kbn)
self.lot_no_err_flg = constants.LOT_NO_ERR_FLG_JP_NAME.get(self.lot_no_err_flg)
# 訂正前伝票管理番号がセットされているときのみ修正日時、修正者、エラー詳細種別をセット
if (self.bef_slip_mgt_no is None):
if (self.bef_slip_mgt_num is None):
self.ins_dt = ""
self.ins_usr = ""

View File

@ -20,10 +20,10 @@ class BioViewModel(BaseModel):
phm_models: list[PharmacyProductMasterModel]
bio_data: Optional[list[BisDisplayModel]] = []
form_data: Optional[BioModel]
def display_wholesaler_names(self):
display_names = [
f'{whs_model.rec_whs_cd}-{whs_model.rec_whs_sub_cd}:{whs_model.nm}'
f'{whs_model.rec_whs_cd}-{whs_model.rec_whs_sub_cd}:{whs_model.name}'
for whs_model in self.whs_models
]
return display_names
@ -41,7 +41,7 @@ class BioViewModel(BaseModel):
def display_data_kbn(self):
return OrderedDict(
{
'' : '',
'': '',
'0': '正常',
'1': 'ロットエラー',
'3': 'ロット不明',
@ -59,14 +59,16 @@ class BioViewModel(BaseModel):
if not self.is_form_submitted():
return ''
form_wholesaler_full_name = f'{self.form_data.wholesaler_code}-{self.form_data.wholesaler_sub_code}:{self.form_data.wholesaler_name}'
form_wholesaler_full_name = \
f'{self.form_data.rec_whs_cd}-{self.form_data.rec_whs_sub_cd}:{self.form_data.whs_name}'
return self._selected_value(form_wholesaler_full_name, selected_wholesaler)
def is_selected_org_kbn(self, selected_org_kbn):
if not self.is_form_submitted():
return ''
return self._selected_value(self.form_data.org_kbn, selected_org_kbn)
return self._selected_value(self.form_data.slip_org_kbn, selected_org_kbn)
def is_input_rec_ymd_from(self):
if not self.is_form_submitted():
return ''
@ -84,18 +86,18 @@ class BioViewModel(BaseModel):
return ''
return self.form_data.rec_lot_num or ''
def is_selected_data_kbn(self, selected_data_kbn):
if not self.is_form_submitted():
return ''
return self._selected_value(self.form_data.data_kbn, selected_data_kbn)
def is_selected_maker_cd(self, selected_maker_cd):
if not self.is_form_submitted():
return ''
return self._selected_value(self.form_data.maker_cd, selected_maker_cd)
return self._selected_value(self.form_data.mkr_cd, selected_maker_cd)
def is_input_rev_hsdnymd_srk_from(self):
if not self.is_form_submitted():
@ -108,13 +110,13 @@ class BioViewModel(BaseModel):
return ''
return self._format_date_string(self.form_data.rev_hsdnymd_srk_to)
def is_checked_iko_flg(self):
if not self.is_form_submitted():
return ''
return 'checked' if self.form_data.ikoFlg else ''
return 'checked' if self.form_data.iko_flg else ''
def disabled_button(self):
return 'disabled' if self.is_data_empty() or self.is_data_overflow_max_length() else ''
@ -123,7 +125,7 @@ class BioViewModel(BaseModel):
def is_data_empty(self):
return len(self.bio_data) == 0
def is_data_overflow_max_length(self):
return len(self.bio_data) >= environment.BIO_SEARCH_RESULT_MAX_COUNT

View File

@ -7,4 +7,4 @@ class LogoutViewModel(BaseModel):
subtitle: str = 'MeDaCA Logout'
redirect_to: Optional[str]
reason: Optional[str]
link_text:Optional[str]
link_text: Optional[str]

View File

@ -7,9 +7,9 @@ class UserViewModel(BaseModel):
bio_flg: str # AUTH_FLG1
doc_flg: str # AUTH_FLG2
inst_flg: str # AUTH_FLG3
master_mainte_flg: str # AUTH_FLG4
master_mainte_flg: str # AUTH_FLG4
user_flg: Optional[str] # MNTUSER_FLG
def has_ult_doctor_permission(self):
return self.doc_flg == '1'
@ -21,6 +21,6 @@ class UserViewModel(BaseModel):
def has_master_maintenance_permission(self):
return self.master_mainte_flg == '1'
def is_maintenance_user(self):
return self.user_flg == '1'

View File

@ -8,8 +8,9 @@ from src.model.db.base_db_model import BaseDBModel
class BaseRepository(metaclass=ABCMeta):
_database: Database
def __init__(self, db: Database) -> None:
self._database = db
@ -29,10 +30,9 @@ class BaseRepository(metaclass=ABCMeta):
"""DBの取得結果をデータフレームにして返す"""
pass
def _to_data_frame(self, query, parameter: BaseDBModel):
"""DBの取得結果をデータフレームに変換する"""
params = params=parameter.dict()
params = params = parameter.dict()
sql_query = pd.read_sql(
text(query),

View File

@ -1,10 +1,13 @@
from src.db import sql_condition as condition
from src.db.sql_condition import SQLCondition
from src.logging.get_logger import get_logger
from src.model.db.bio_sales_view import BioSalesViewModel
from src.model.request.bio import BioModel
from src.repositories.base_repository import BaseRepository
from src.util.string_util import is_not_empty
logger = get_logger('生物由来参照')
class BioSalesViewRepository(BaseRepository):
FETCH_SQL = """\
@ -12,19 +15,19 @@ class BioSalesViewRepository(BaseRepository):
(
CASE
WHEN LEFT(bs.v_tran_cd, 1) = 2
AND bs.amt >= 1 THEN CONCAT('-', bs.amt)
ELSE bs.amt
AND bs.qty >= 1 THEN CONCAT('-', bs.qty)
ELSE bs.qty
END
) AS amt_fugo,
bs.*,
ln.ser_no,
ln.ser_num,
ln.lot_num,
ln.expr_dt
FROM
src05.bio_sales_view bs
LEFT OUTER JOIN
src05.lot_num_mst ln
ON bs.mkr_cd = ln.ser_no
ON bs.mkr_cd = ln.ser_num
AND bs.rec_lot_num = ln.lot_num
WHERE
{where_clause}
@ -32,45 +35,40 @@ class BioSalesViewRepository(BaseRepository):
bs.rec_whs_cd,
bs.rec_whs_sub_cd,
bs.rev_hsdnymd_srk,
bs.slip_mgt_no
bs.slip_mgt_num
ASC\
"""
def fetch_many(self, parameter: BioModel) -> list[BioSalesViewModel]:
try:
self._database.connect()
logger.debug('DB参照実行')
where_clause = self.__build_condition(parameter)
# error_log(date("Y/m/d H:i:s") . " [INFO] DB Return=" . $result . "\r\n", 3, "$execLog");
# error_log(date("Y/m/d H:i:s") . " [INFO] DB参照実行" . "\r\n", 3, "$execLog");
query = self.FETCH_SQL.format(where_clause=where_clause)
# error_log(date("Y/m/d H:i:s") . " [INFO] SQL: " . $query . "\r\n", 3, "$execLog");
logger.debug(f'SQL: {query}')
result = self._database.execute_select(query, parameter.dict())
logger.debug(f'count= {len(result)}')
models = [BioSalesViewModel(**r) for r in result]
# error_log(date("Y/m/d H:i:s") . " [INFO] count=" . $count . "\r\n", 3, "$execLog");
return models
except Exception as e:
# TODO: ファイルへの書き出しはloggerでやる
print(f"[ERROR] DB Error : Exception={e.args}")
logger.exception(f"DB Error : Exception={e.args}")
raise e
finally:
self._database.disconnect()
def fetch_as_data_frame(self, parameter: BioModel):
try:
self._database.connect()
logger.debug('DB参照実行')
where_clause = self.__build_condition(parameter)
# error_log(date("Y/m/d H:i:s") . " [INFO] DB Return=" . $result . "\r\n", 3, "$execLog");
# error_log(date("Y/m/d H:i:s") . " [INFO] DB参照実行" . "\r\n", 3, "$execLog");
query = self.FETCH_SQL.format(where_clause=where_clause)
# error_log(date("Y/m/d H:i:s") . " [INFO] SQL: " . $query . "\r\n", 3, "$execLog");
# models = [BioSalesViewModel(**r) for r in result]
# error_log(date("Y/m/d H:i:s") . " [INFO] count=" . $count . "\r\n", 3, "$execLog");
logger.debug(f'SQL: {query}')
df = self._to_data_frame(query, parameter)
return df
logger.debug(f'count= {len(df.index)}')
# ログ出力のため、クエリも返却
return df, query
except Exception as e:
# TODO: ファイルへの書き出しはloggerでやる
print(f"[ERROR] DB Error : Exception={e.args}")
logger.exception(f"DB Error : Exception={e.args}")
raise e
finally:
self._database.disconnect()
@ -79,12 +77,12 @@ class BioSalesViewRepository(BaseRepository):
where_clauses: list[SQLCondition] = []
# 卸(コード/サブコード)
if is_not_empty(parameter.wholesaler_code) and is_not_empty(parameter.wholesaler_sub_code):
where_clauses.append(SQLCondition('rec_whs_cd', condition.EQ, 'wholesaler_code'))
where_clauses.append(SQLCondition('rec_whs_sub_cd', condition.EQ, 'wholesaler_sub_code'))
if is_not_empty(parameter.rec_whs_cd) and is_not_empty(parameter.rec_whs_sub_cd):
where_clauses.append(SQLCondition('rec_whs_cd', condition.EQ, 'rec_whs_cd'))
where_clauses.append(SQLCondition('rec_whs_sub_cd', condition.EQ, 'rec_whs_sub_cd'))
# データ種別
if is_not_empty(parameter.org_kbn):
where_clauses.append(SQLCondition('slip_org_kbn', condition.EQ, 'org_kbn'))
if is_not_empty(parameter.slip_org_kbn):
where_clauses.append(SQLCondition('slip_org_kbn', condition.EQ, 'slip_org_kbn'))
# 処理日 開始日
if is_not_empty(parameter.rec_ymd_from):
where_clauses.append(SQLCondition('rec_ymd', condition.GE, 'rec_ymd_from'))
@ -95,14 +93,14 @@ class BioSalesViewRepository(BaseRepository):
if is_not_empty(parameter.rec_lot_num):
rec_lot_num = parameter.rec_lot_num
# あいまい検索文字列('%')が含まれる場合は'LIKE'、でなければ'='で検索
rec_lot_num_comparator = condition.LIKE if rec_lot_num in '%' else condition.EQ
rec_lot_num_comparator = condition.LIKE if rec_lot_num in '%' else condition.EQ
where_clauses.append(SQLCondition('rec_lot_num', rec_lot_num_comparator, 'rec_lot_num'))
# データ区分
if is_not_empty(parameter.data_kbn):
where_clauses.append(SQLCondition('data_kbn', condition.EQ, 'data_kbn'))
# 製品
if is_not_empty(parameter.maker_cd):
where_clauses.append(SQLCondition('mkr_cd', condition.EQ, 'maker_cd'))
if is_not_empty(parameter.mkr_cd):
where_clauses.append(SQLCondition('mkr_cd', condition.EQ, 'mkr_cd'))
# 発伝年月日 開始日
if is_not_empty(parameter.rev_hsdnymd_srk_from):
where_clauses.append(SQLCondition('rev_hsdnymd_srk', condition.GE, 'rev_hsdnymd_srk_from'))
@ -111,12 +109,12 @@ class BioSalesViewRepository(BaseRepository):
where_clauses.append(SQLCondition('rev_hsdnymd_srk', condition.LE, 'rev_hsdnymd_srk_to'))
# 移行フラグ
# チェックが入っていない場合、移行対象(IKO_FLG = '*')を省く
if parameter.ikoFlg is None:
where_clauses.append(SQLCondition('iko_flg', condition.IS, 'NULL', literal=True))
if parameter.iko_flg is None:
where_clauses.append(SQLCondition('iko_flg', condition.IS, 'NULL', literal=True))
# 固定条件
# Viewで返されるロット番号9件をNull以外で抽出
where_clauses.append(SQLCondition('LENGTH(TRIM(rec_lot_num))', condition.GT, '0', literal=True))
where_clauses_str = ' AND '.join([condition.apply() for condition in where_clauses])
# error_log(date("Y/m/d H:i:s") . " [INFO] 条件設定終了:" . $szConditions . "\r\n", 3, "$execLog");
logger.debug(f'条件設定終了:{where_clauses_str}')
return where_clauses_str

View File

@ -1,5 +1,4 @@
from src.model.db.hdke_tbl import HdkeTblModel
from src.model.request.bio import BioModel
from src.repositories.base_repository import BaseRepository

View File

@ -3,24 +3,24 @@ from src.repositories.base_repository import BaseRepository
class PharmacyProductMasterRepository(BaseRepository):
FETCH_SQL = """\
SELECT
CONCAT(IFNULL(mkr_cd, ''), ' ', IFNULL(mkr_inf_1, '')) AS mkr_cd_nm
SELECT
CONCAT(IFNULL(t1.mkr_cd, ''), ' ', IFNULL(t1.mkr_inf_1, '')) AS mkr_cd_name
FROM
src05.phm_prd_mst_v t1
INNER JOIN
INNER JOIN
(
SELECT
prd_cd,MAX(sub_no) AS sno
SELECT
prd_cd, MAX(sub_num) AS sno
FROM
src05.phm_prd_mst_v
WHERE rec_sts_kbn <> '9'
GROUP BY prd_cd
) fmv2
ON t1.prd_cd = fmv2.prd_cd AND t1.sub_no = fmv2.sno
ON t1.prd_cd = fmv2.prd_cd AND t1.sub_num = fmv2.sno
WHERE
mkr_cd IS NOT NULL
t1.mkr_cd IS NOT NULL
ORDER BY mkr_cd
"""

View File

@ -1,5 +1,4 @@
from src.model.db.user_master import UserMasterModel
from src.model.request.bio import BioModel
from src.repositories.base_repository import BaseRepository
@ -27,4 +26,4 @@ class UserMasterRepository(BaseRepository):
print(f"[ERROR] DB Error : Exception={e.args}")
raise e
finally:
self._database.disconnect()
self._database.disconnect()

View File

@ -3,23 +3,27 @@ from src.repositories.base_repository import BaseRepository
class WholesalerMasterRepository(BaseRepository):
FETCH_SQL = """\
SELECT DISTINCT
b.rec_whs_cd,
b.rec_whs_sub_cd,
v2.nm,
b.whs_nm
v2.name,
b.whs_name
FROM src05.bio_sales b
LEFT OUTER JOIN
(
SELECT sub_no, nm, v_whs_cd, rec_sts_kbn
SELECT
sub_num,
name,
v_whs_cd,
rec_sts_kbn
FROM src05.whs_mst_v
WHERE (SELECT STR_TO_DATE(syor_date, '%Y%m%d') FROM src05.hdke_tbl) BETWEEN start_date AND end_date
) v2
ON b.v_whs_cd = v2.v_whs_cd
ON b.v_whs_cd = v2.v_whs_cd
AND v2.rec_sts_kbn <> '9'
ORDER BY b.rec_whs_cd, b.rec_whs_sub_cd , b.whs_nm DESC
ORDER BY b.rec_whs_cd, b.rec_whs_sub_cd , b.whs_name DESC
"""
def fetch_all(self) -> list[WholesalerMasterModel]:

View File

@ -1,4 +1,3 @@
import logging
from typing import Callable
from fastapi import Request, Response
@ -9,9 +8,11 @@ from starlette import status
from src.depends.auth import (check_session_expired, get_current_session,
verify_session)
from src.error.exceptions import UnexpectedException
from src.logging.get_logger import get_logger
from src.system_var import constants, environment
logger = logging.getLogger('uvicorn')
logger = get_logger('medaca_router')
class MeDaCaRoute(APIRoute):
"""アプリケーションのカスタムルーター
@ -19,6 +20,7 @@ class MeDaCaRoute(APIRoute):
Args:
APIRoute (APIRoute): FastAPIの標準APIRoute
"""
def get_route_handler(self) -> Callable:
"""前後処理を付加するルートハンドラーを返す
@ -34,14 +36,11 @@ class MeDaCaRoute(APIRoute):
# 返却するルートハンドラーを定義。必ず非同期関数にする必要がある。
async def custom_route_handler(request: Request) -> Response:
try:
logger.info('pre routing process')
# 事前処理
request = await self.pre_process_route(request)
# 本来のルーティング処理
logger.info('routing process')
response = await original_route_handler(request)
# 事後処理
logger.info('post routing process')
return await self.post_process_route(request, response)
except HTTPException as e:
raise e
@ -60,6 +59,7 @@ class MeDaCaRoute(APIRoute):
Request: 加工後のRequestインスタンス
"""
return request
async def post_process_route(self, request: Request, response: Response) -> Response:
"""ルートハンドラーの事後処理
@ -71,6 +71,7 @@ class MeDaCaRoute(APIRoute):
"""
return response
class BeforeCheckSessionRoute(MeDaCaRoute):
"""事前処理として、セッションチェックを行うルートハンドラー
@ -86,12 +87,14 @@ class BeforeCheckSessionRoute(MeDaCaRoute):
verified_session = verify_session(checked_session)
# セッションが有効でない場合、エラーにする
if verified_session is None:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_SESSION_EXPIRED)
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
detail=constants.LOGOUT_REASON_SESSION_EXPIRED)
scope = request.scope
scope['session'] = verified_session
session_request = Request(receive=request.receive, scope=scope)
return session_request
class AfterSetCookieSessionRoute(MeDaCaRoute):
"""事後処理として、セッションキーをcookieに設定するカスタムルートハンドラー
@ -110,16 +113,18 @@ class AfterSetCookieSessionRoute(MeDaCaRoute):
response.set_cookie(
key='session',
value=session_key,
max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける
max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける
secure=True,
httponly=True
)
return response
class AuthenticatedRoute(BeforeCheckSessionRoute, AfterSetCookieSessionRoute):
async def pre_process_route(self, request: Request):
request = await super().pre_process_route(request)
return request
async def post_process_route(self, request: Request, response: Response):
response = await super().post_process_route(request, response)
return response

View File

@ -9,5 +9,6 @@ class BaseService(metaclass=ABCMeta):
REPOSITORIES: dict[str, BaseRepository] = {}
# 各サービスが依存するAWS APIクライアントクラスのマップ
CLIENTS: dict[str, AWSAPIClient] = {}
def __init__(self, repositories: dict[str, BaseRepository], clients: dict[str, AWSAPIClient]) -> None:
pass

View File

@ -13,6 +13,7 @@ class BatchStatusService(BaseService):
}
hdke_table_repository: HdkeTblRepository
__hdke_table_record: list[HdkeTblModel] = []
def __init__(self, repositories: dict[str, BaseRepository], clients: dict[str, AWSAPIClient]) -> None:
super().__init__(repositories, clients)
self.hdke_table_repository = repositories['hdke_table_repository']
@ -38,4 +39,3 @@ class BatchStatusService(BaseService):
# 日付マスタのレコードがない場合は例外とする
if len(self.__hdke_table_record) == 0:
raise DBException('日付テーブルのレコードが存在しません')

View File

@ -1,4 +1,4 @@
import os.path as path
import os
import shutil
from datetime import datetime
@ -6,6 +6,7 @@ import pandas as pd
from src.aws.aws_api_client import AWSAPIClient
from src.aws.s3 import S3Client
from src.logging.get_logger import get_logger
from src.model.internal.session import UserSession
from src.model.request.bio import BioModel
from src.model.view.bio_disp_model import BisDisplayModel
@ -19,6 +20,8 @@ from src.repositories.wholesaler_master_repository import \
from src.services.base_service import BaseService
from src.system_var import constants, environment
logger = get_logger('生物由来参照')
class BioViewService(BaseService):
REPOSITORIES = {
@ -26,15 +29,16 @@ class BioViewService(BaseService):
'phm_repository': PharmacyProductMasterRepository,
'bio_sales_repository': BioSalesViewRepository
}
CLIENTS = {
's3_client': S3Client
}
whs_repository: WholesalerMasterRepository
phm_repository: PharmacyProductMasterRepository
bio_sales_repository: BioSalesViewRepository
s3_client: S3Client
def __init__(self, repositories: dict[str, BaseRepository], clients: dict[str, AWSAPIClient]) -> None:
super().__init__(repositories, clients)
self.whs_repository = repositories['whs_repository']
@ -45,7 +49,7 @@ class BioViewService(BaseService):
def prepare_bio_view(
self,
session: UserSession
) ->BioViewModel:
) -> BioViewModel:
# 卸リストを取得
wholesalers = self.whs_repository.fetch_all()
# 製品リストを取得
@ -67,12 +71,60 @@ class BioViewService(BaseService):
def search_download_bio_data(self, search_params: BioModel):
# 生物由来データをダウンロードするために、DBから検索した結果をデータフレームに変換
bio_sales_data_frame = self.bio_sales_repository.fetch_as_data_frame(parameter=search_params)
return bio_sales_data_frame
bio_sales_data_frame, query = self.bio_sales_repository.fetch_as_data_frame(parameter=search_params)
return bio_sales_data_frame, query
def write_excel_file(self, data_frame: pd.DataFrame, user_id: str, timestamp: datetime):
def write_access_log(
self,
query: str,
parameters: BioModel,
user_id: str,
timestamp: datetime,
download_file_name: str
):
# アクセスログを書き出し、S3に保管する
access_log_file_name = f'BioAccessLog_{user_id}_{timestamp:%Y%m%d%H%M%S%f}.log'
# アクセスログファイル出力用のロガーを生成
import logging
access_logger = logging.getLogger(access_log_file_name)
level = logging.getLevelName(environment.LOG_LEVEL)
if not isinstance(level, int):
level = logging.INFO
access_logger.setLevel(level)
access_log_file_path = os.path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, access_log_file_name)
if not access_logger.hasHandlers():
access_log_handler = logging.FileHandler(access_log_file_path)
access_logger.addHandler(access_log_handler)
formatter = logging.Formatter(
'[%(levelname)s]\t%(asctime)s\t%(message)s',
'%Y-%m-%d %H:%M:%S'
)
for handler in logger.handlers:
handler.setFormatter(formatter)
# SQL文を出力
sql_message = f'ユーザーID: {user_id} SQL: {query}\t{download_file_name}'
access_logger.info(sql_message)
# 標準出力にも書き出す
logger.info(sql_message)
# 検索パラメータを1行ずつ書き出す
for param_key, param_value in parameters.dict().items():
if param_value is None or len(param_value) == 0:
continue
parameter_message = f'ユーザーID: {user_id} Value: {param_key} = {param_value}\t{download_file_name}'
logger.info(parameter_message)
access_logger.info(parameter_message)
# S3にアップロード
self.upload_bio_access_log_file(access_log_file_path)
def write_excel_file(self, data_frame: pd.DataFrame, user_id: str, download_file_name: str):
# Excelに書き込み
output_file_path = path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, f'Result_{user_id}_{timestamp:%Y%m%d%H%M%S%f}.xlsx')
output_file_path = os.path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, download_file_name)
# テンプレートファイルをコピーして出力ファイルの枠だけを作る
shutil.copyfile(
@ -87,12 +139,12 @@ class BioViewService(BaseService):
# DF内のヘッダと連番を書き込みたくない場合、`header`と`index`をFalseに指定する。
# `startrow`と`startcol`で、Excelの書き込み位置を決定する。省略した場合はA1セルから書く。
data_frame.to_excel(writer, header=False, index=False, startrow=1, startcol=0)
return output_file_path
def write_csv_file(self, data_frame: pd.DataFrame, user_id: str, header: list[str], timestamp: datetime):
def write_csv_file(self, data_frame: pd.DataFrame, user_id: str, header: list[str], download_file_name: str):
# csvに書き込み
output_file_path = path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, f'Result_{user_id}_{timestamp:%Y%m%d%H%M%S%f}.csv')
output_file_path = os.path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, download_file_name)
# 横長のDataFrameとするため、ヘッダーの加工処理
header_data = {}
for df_column, header_column in zip(data_frame.columns, header):
@ -107,13 +159,28 @@ class BioViewService(BaseService):
def upload_bio_data_file(self, local_file_path: str) -> None:
bucket_name = environment.BIO_ACCESS_LOG_BUCKET
# TODO: フォルダを変える
file_key =f'bio/{path.basename(local_file_path)}'
# TODO: ファイルパスにYYYY/MM/DDを加える
file_key = f'data/{os.path.basename(local_file_path)}'
self.s3_client.upload_file(local_file_path, bucket_name, file_key)
def generate_download_file_url(self, local_file_path:str, user_id: str, kind: str) -> str:
# アップロード後、ローカルからは削除する
self.delete_local_file(local_file_path)
def upload_bio_access_log_file(self, local_file_path: str) -> None:
bucket_name = environment.BIO_ACCESS_LOG_BUCKET
# TODO: フォルダを変える
file_key = f'bio/{path.basename(local_file_path)}'
# TODO: ファイルパスにYYYY/MM/DDを加える
file_key = f'log/{os.path.basename(local_file_path)}'
self.s3_client.upload_file(local_file_path, bucket_name, file_key)
# アップロード後、ローカルからは削除する
self.delete_local_file(local_file_path)
def generate_download_file_url(self, local_file_path: str, user_id: str, kind: str) -> str:
bucket_name = environment.BIO_ACCESS_LOG_BUCKET
# TODO: ファイルパスにYYYY/MM/DDを加える
file_key = f'data/{os.path.basename(local_file_path)}'
download_filename = f'{user_id}_生物由来卸販売データ.{kind}'
return self.s3_client.generate_presigned_url(bucket_name, file_key, download_filename)
def delete_local_file(self, local_file_path: str):
os.remove(local_file_path)

View File

@ -17,7 +17,7 @@ class LoginService(BaseService):
REPOSITORIES = {
'user_repository': UserMasterRepository
}
CLIENTS = {
'cognito_client': CognitoClient
}
@ -41,10 +41,10 @@ class LoginService(BaseService):
raise e
return JWTToken(id_token, refresh_token)
def login_with_security_code(self, code: str) -> JWTToken:
return JWTToken.request(code)
def logged_in_user(self, user_id):
user_record: UserMasterModel = self.user_repository.fetch_one({'user_id': user_id})
return user_record

View File

@ -6,6 +6,7 @@ def set_session(session: UserSession) -> str:
session.save()
return session.session_key
def get_session(key: str) -> UserSession:
try:
session = UserSession.get(hash_key=key, consistent_read=True)

View File

@ -5,40 +5,40 @@ BIO_EXCEL_TEMPLATE_FILE_PATH = path.join(BIO_TEMPORARY_FILE_DIR_PATH, 'BioData_t
BIO_EXTRACT_COLUMNS = [
'slip_org_kbn',
'slip_mgt_no',
'slip_mgt_num',
'rec_ymd',
'rec_whs_cd',
'rec_whs_sub_cd',
'whs_nm',
'whs_name',
'rec_whs_org_cd',
'rec_urag_no',
'rec_urag_num',
'rev_hsdnymd_srk',
'rec_tran_kbn',
'tran_kbn_nm',
'tran_kbn_name',
'mkr_cd',
'rec_comm_cd',
'comm_nm',
'whs_rep_comm_nm',
'nnsk_cd',
'rec_nnskfcl_nm',
'whs_rep_nnskfcl_nm',
'rec_nnsk_fcl_addr',
'whs_rep_nnsk_fcl_addr',
'product_name',
'whs_rep_comm_name',
'nonyu_fcl_cd',
'rec_nonyu_fcl_name',
'whs_rep_nonyu_fcl_name',
'rec_nonyu_fcl_addr',
'whs_rep_nonyu_fcl_addr',
'rec_lot_num',
'amt_fugo',
'expr_dt',
'data_kbn',
'lot_no_err_flg',
'bef_slip_mgt_no',
'lot_num_err_flg',
'bef_slip_mgt_num',
'ins_usr',
'ins_dt',
'inst_cd',
'inst_name_form',
'address',
'tel_no',
'tel_num',
'v_whs_cd',
'v_whsorg_cd',
'whs_org_nm',
'whs_org_name',
'v_tran_cd',
'iko_flg'
]

View File

@ -19,4 +19,6 @@ DB_PASSWORD = os.environ['DB_PASSWORD']
DB_SCHEMA = os.environ['DB_SCHEMA']
BIO_SEARCH_RESULT_MAX_COUNT = int(os.environ['BIO_SEARCH_RESULT_MAX_COUNT'])
SESSION_EXPIRE_MINUTE=int(os.environ['SESSION_EXPIRE_MINUTE'])
SESSION_EXPIRE_MINUTE = int(os.environ['SESSION_EXPIRE_MINUTE'])
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')

View File

@ -89,8 +89,8 @@
<option value=""></option>
{% for phm in bio.phm_models %}
<option
value="{{phm['mkr_cd_nm']}}" {{bio.is_selected_maker_cd(phm['mkr_cd_nm'])}}>
{{phm['mkr_cd_nm']}}
value="{{phm['mkr_cd_name']}}" {{bio.is_selected_maker_cd(phm['mkr_cd_name'])}}>
{{phm['mkr_cd_name']}}
</option>
{% endfor %}
</select>
@ -192,7 +192,7 @@
<!-- CSV/Excelダウンロードボタン。ここはajaxでやってる -->
<script type="text/javascript">
function download(filename, kind) {
function download(filename, ext) {
$(`#loading`).toggle()
// 検索パラメータを取得
@ -205,7 +205,7 @@
// ダウンロード固有のパラメータを設定
const downloadRequestParams = {
user_id: '{{bio.user_id}}',
kind: kind,
ext: ext,
}
$.extend(downloadRequestParams, searchParams)
@ -231,14 +231,14 @@
/**if (data.download_url === '') {
// 予期せぬエラーが発生した場合
$(`#loading`).toggle();
$(`#modal_${kind}`).modal('toggle');
$(`#modal_${ext}`).modal('toggle');
$(`#ErrorModal_Unexpected`).modal('toggle');
}
*/
// S3の期限付き署名URLがレスポンスされる
window.location.href = data.download_url;
$(`#loading`).toggle();
$(`#modal_${kind}`).modal('toggle');
$(`#modal_${ext}`).modal('toggle');
} catch (e) {
alert("エラーが発生しました。:" + e.message);
}
@ -247,20 +247,20 @@
const responseJson = jqXHR.responseJSON
if (responseJson?.detail?.error === 'db_error') {
$(`#loading`).toggle();
$(`#modal_${kind}`).modal('toggle');
$(`#modal_${ext}`).modal('toggle');
$(`#ErrorModal_DB`).modal('toggle');
return
}
if (responseJson?.detail?.error === 'aws_error') {
$(`#loading`).toggle();
$(`#modal_${kind}`).modal('toggle');
$(`#modal_${ext}`).modal('toggle');
$(`#ErrorModal_AWS`).modal('toggle');
return
}
// 予期せぬエラーが発生した場合
$(`#loading`).toggle();
$(`#modal_${kind}`).modal('toggle');
$(`#modal_${ext}`).modal('toggle');
$(`#ErrorModal_Unexpected`).modal('toggle');
return
}
@ -278,7 +278,6 @@
return
}
$(".pagination").pagination({
// 以下はテスト用コード
dataSource: function(done) {
done(searchResultData)
},
@ -299,31 +298,31 @@
function pagination_content(datas) {
const display_keys = [
'slip_org_kbn',
'slip_mgt_no',
'slip_mgt_num',
'rec_ymd',
'rec_whs_cd',
'rec_whs_sub_cd',
'whs_nm',
'whs_name',
'rec_whs_org_cd',
'rec_urag_no',
'rec_urag_num',
'rev_hsdnymd_srk',
'rec_tran_kbn',
'tran_kbn_nm',
'tran_kbn_name',
'mkr_cd',
'rec_comm_cd',
'comm_nm',
'product_name',
'whs_rep_comm_nm',
'nnsk_cd',
'rec_nnskfcl_nm',
'nonyu_fcl_cd',
'rec_nonyu_fcl_name',
'whs_rep_nnskfcl_nm',
'rec_nnsk_fcl_addr',
'rec_nonyu_fcl_addr',
'whs_rep_nnsk_fcl_addr',
'rec_lot_num',
'amt_fugo',
'expr_dt',
'data_kbn',
'lot_no_err_flg',
'bef_slip_mgt_no',
'bef_slip_mgt_num',
'ins_usr',
'ins_dt',
'inst_cd',
@ -332,7 +331,7 @@
'tel_no',
'v_whs_cd',
'v_whsorg_cd',
'whs_org_nm',
'whs_org_name',
'v_tran_cd',
'iko_flg',
];

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="ja">
<head>
{% with subtitle = 'サンプルファイル送信' %}
{% include '_header.html' %}
{% endwith %}
<link href="/static/css/menuStyle.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid text-center background">
<h1>サンプルファイル送信</h1>
<br><br>
<form name="login" class="text-center" method="post" action="/sample/" enctype="multipart/form-data">
<div class="form-group">
<label for="file">Choose File.</label>
<input type="file" name="file" class="form-control-file" required>
</div>
<div class="form-group">
<label for="message">メッセージ</label>
<input type="text" name="message" class="form-control">
</div>
<input type="submit" id="login_button" name="login" class="btn btn-info btn-lg btn_width" id="submit" value="送信">
</form>
</div>
</body>
</html>

View File

@ -4,11 +4,18 @@ import html
def sanitize(cls):
class SanitizedClass(cls):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for prop_name, prop_value in self.__dict__.items():
if isinstance(prop_value, str):
sanitized_value = html.escape(prop_value, quote=True)
setattr(self, prop_name, sanitized_value)
return SanitizedClass
original_init = cls.__init__
def new_init(self, *args, **kwargs):
# オリジナルの __init__ メソッドを呼び出してインスタンスを初期化
sanitized_kwargs = {**kwargs}
for key, value in kwargs.items():
if isinstance(value, str):
# 文字列の場合はサニタイズ処理を行うHTMLタグをエスケープ
sanitized_value = html.escape(value, quote=True)
sanitized_kwargs[key] = sanitized_value
original_init(self, *args, **sanitized_kwargs)
cls.__init__ = new_init
return cls