feat: 実消化&アルトマーク Webアプリケーションの試作品を登録
This commit is contained in:
parent
314342af6f
commit
f0e7aec6d3
12
ecs/jskult-webapp/.dockerignore
Normal file
12
ecs/jskult-webapp/.dockerignore
Normal file
@ -0,0 +1,12 @@
|
||||
tests/*
|
||||
.coverage
|
||||
.env
|
||||
.env.example
|
||||
.report/*
|
||||
.vscode/*
|
||||
.pytest_cache/*
|
||||
*/__pychache__/*
|
||||
Dockerfile
|
||||
pytest.ini
|
||||
README.md
|
||||
*.sql
|
||||
24
ecs/jskult-webapp/.env.example
Normal file
24
ecs/jskult-webapp/.env.example
Normal file
@ -0,0 +1,24 @@
|
||||
#AWS
|
||||
##Cognito
|
||||
COGNITO_AUTH_DOMAIN=*******
|
||||
AUTHORIZE_ENDPOINT=*********
|
||||
TOKEN_ENDPOINT=***************
|
||||
COGNITO_IDENTITY_PROVIDER=*********
|
||||
COGNITO_REDIRECT_URI=*****************
|
||||
COGNITO_USER_POOL_ID=********************
|
||||
COGNITO_CLIENT_ID=**********************
|
||||
COGNITO_CLIENT_SECRET=******************************
|
||||
##DynamoDB
|
||||
AWS_REGION=**************
|
||||
SESSION_TABLE_NAME=***********************
|
||||
##S3
|
||||
BIO_ACCESS_LOG_BUCKET=*******************
|
||||
#MySQL
|
||||
DB_HOST=************
|
||||
DB_PORT=************
|
||||
DB_USERNAME=************
|
||||
DB_PASSWORD=************
|
||||
DB_SCHEMA=************
|
||||
#実装の設定
|
||||
BIO_SEARCH_RESULT_MAX_COUNT=35000
|
||||
SESSION_EXPIRE_MINUTE=20
|
||||
6
ecs/jskult-webapp/.gitignore
vendored
Normal file
6
ecs/jskult-webapp/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
__pycache__
|
||||
|
||||
.vscode/settings.json
|
||||
.env
|
||||
!src/data/BioData_template.xlsx
|
||||
src/data/*
|
||||
17
ecs/jskult-webapp/.vscode/launch.json
vendored
Normal file
17
ecs/jskult-webapp/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
// IntelliSense を使用して利用可能な属性を学べます。
|
||||
// 既存の属性の説明をホバーして表示します。
|
||||
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch AP Server(FastAPI)",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "uvicorn",
|
||||
"args": ["src.main:app","--reload", "--no-server-header"],
|
||||
"justMyCode": true,
|
||||
"envFile": "${workspaceFolder}/.env"
|
||||
}
|
||||
]
|
||||
}
|
||||
18
ecs/jskult-webapp/.vscode/recommended_settings.json
vendored
Normal file
18
ecs/jskult-webapp/.vscode/recommended_settings.json
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": null,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true
|
||||
}
|
||||
},
|
||||
// 自身の環境に合わせて変えてください
|
||||
"python.defaultInterpreterPath": "<pythonインタプリターのパス>",
|
||||
"python.linting.lintOnSave": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": false,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.flake8Args": ["--max-line-length=120"],
|
||||
"python.formatting.provider": "autopep8",
|
||||
"python.formatting.autopep8Args": ["--max-line-length", "120"]
|
||||
}
|
||||
19
ecs/jskult-webapp/Dockerfile
Normal file
19
ecs/jskult-webapp/Dockerfile
Normal file
@ -0,0 +1,19 @@
|
||||
FROM python:3.9
|
||||
|
||||
ENV TZ="Asia/Tokyo"
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY Pipfile Pipfile.lock ./
|
||||
RUN \
|
||||
apt update -y && \
|
||||
# パッケージのセキュリティアップデートのみを適用するコマンド
|
||||
apt install -y unattended-upgrades && \
|
||||
unattended-upgrades && \
|
||||
pip install --upgrade pip wheel setuptools && \
|
||||
pip install pipenv --no-cache-dir && \
|
||||
pipenv install --system --deploy && \
|
||||
pip uninstall -y pipenv virtualenv-clone virtualenv
|
||||
|
||||
COPY src ./src
|
||||
|
||||
CMD ["gunicorn", "src.main:app", "-w", "4", "-k" ,"uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:80"]
|
||||
34
ecs/jskult-webapp/Pipfile
Normal file
34
ecs/jskult-webapp/Pipfile
Normal file
@ -0,0 +1,34 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[scripts]
|
||||
app = "uvicorn src.main:app --reload --no-server-header"
|
||||
|
||||
[packages]
|
||||
fastapi = "*"
|
||||
uvicorn = "*"
|
||||
gunicorn = "*"
|
||||
boto3 = "*"
|
||||
jinja2 = "*"
|
||||
pyjwt = "*"
|
||||
"pyjwt[crypto]" = "*"
|
||||
requests = "*"
|
||||
python-multipart = "*"
|
||||
pynamodb = "*"
|
||||
PyMySQL = "*"
|
||||
pandas = "*"
|
||||
openpyxl = "*"
|
||||
xlrd = "*"
|
||||
sqlalchemy = "*"
|
||||
|
||||
[dev-packages]
|
||||
autopep8 = "*"
|
||||
flake8 = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.9"
|
||||
|
||||
[pipenv]
|
||||
allow_prereleases = true
|
||||
765
ecs/jskult-webapp/Pipfile.lock
generated
Normal file
765
ecs/jskult-webapp/Pipfile.lock
generated
Normal file
@ -0,0 +1,765 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "d78a6bf1a96aa14c45431185961cae6d54ca1da8ea0319e1976bad4c2bebd673"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.9"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"anyio": {
|
||||
"hashes": [
|
||||
"sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421",
|
||||
"sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.2'",
|
||||
"version": "==3.6.2"
|
||||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:278d896e9090a976f41ec68da5c572bc4e5b7cb1e515f1898fee8cb2fadfb50d",
|
||||
"sha256:3ce2225a61832d69831d669d912424ea3863268ca1cfa2a82203bb90952acefa"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.26.91"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:4ed6a488aee1b42367eace71f7d0993dda05b02eebd7dcdd78db5c9ce3d80da5",
|
||||
"sha256:a8a800a2a945da807758cace539fc5b5ec1d5082ce363799d3a3870c2c4ed6fc"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.29.91"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
||||
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.12.7"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5",
|
||||
"sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef",
|
||||
"sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104",
|
||||
"sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426",
|
||||
"sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405",
|
||||
"sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375",
|
||||
"sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a",
|
||||
"sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e",
|
||||
"sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc",
|
||||
"sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf",
|
||||
"sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185",
|
||||
"sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497",
|
||||
"sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3",
|
||||
"sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35",
|
||||
"sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c",
|
||||
"sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83",
|
||||
"sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21",
|
||||
"sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca",
|
||||
"sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984",
|
||||
"sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac",
|
||||
"sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd",
|
||||
"sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee",
|
||||
"sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a",
|
||||
"sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2",
|
||||
"sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192",
|
||||
"sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7",
|
||||
"sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585",
|
||||
"sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f",
|
||||
"sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e",
|
||||
"sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27",
|
||||
"sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b",
|
||||
"sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e",
|
||||
"sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e",
|
||||
"sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d",
|
||||
"sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c",
|
||||
"sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415",
|
||||
"sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82",
|
||||
"sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02",
|
||||
"sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314",
|
||||
"sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325",
|
||||
"sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c",
|
||||
"sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3",
|
||||
"sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914",
|
||||
"sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045",
|
||||
"sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d",
|
||||
"sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9",
|
||||
"sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5",
|
||||
"sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2",
|
||||
"sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c",
|
||||
"sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3",
|
||||
"sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2",
|
||||
"sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8",
|
||||
"sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d",
|
||||
"sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d",
|
||||
"sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9",
|
||||
"sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162",
|
||||
"sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76",
|
||||
"sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4",
|
||||
"sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e",
|
||||
"sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9",
|
||||
"sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6",
|
||||
"sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b",
|
||||
"sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01",
|
||||
"sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"
|
||||
],
|
||||
"version": "==1.15.1"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6",
|
||||
"sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1",
|
||||
"sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e",
|
||||
"sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373",
|
||||
"sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62",
|
||||
"sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230",
|
||||
"sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be",
|
||||
"sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c",
|
||||
"sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0",
|
||||
"sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448",
|
||||
"sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f",
|
||||
"sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649",
|
||||
"sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d",
|
||||
"sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0",
|
||||
"sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706",
|
||||
"sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a",
|
||||
"sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59",
|
||||
"sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23",
|
||||
"sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5",
|
||||
"sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb",
|
||||
"sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e",
|
||||
"sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e",
|
||||
"sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c",
|
||||
"sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28",
|
||||
"sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d",
|
||||
"sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41",
|
||||
"sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974",
|
||||
"sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce",
|
||||
"sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f",
|
||||
"sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1",
|
||||
"sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d",
|
||||
"sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8",
|
||||
"sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017",
|
||||
"sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31",
|
||||
"sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7",
|
||||
"sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8",
|
||||
"sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e",
|
||||
"sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14",
|
||||
"sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd",
|
||||
"sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d",
|
||||
"sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795",
|
||||
"sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b",
|
||||
"sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b",
|
||||
"sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b",
|
||||
"sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203",
|
||||
"sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f",
|
||||
"sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19",
|
||||
"sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1",
|
||||
"sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a",
|
||||
"sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac",
|
||||
"sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9",
|
||||
"sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0",
|
||||
"sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137",
|
||||
"sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f",
|
||||
"sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6",
|
||||
"sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5",
|
||||
"sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909",
|
||||
"sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f",
|
||||
"sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0",
|
||||
"sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324",
|
||||
"sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755",
|
||||
"sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb",
|
||||
"sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854",
|
||||
"sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c",
|
||||
"sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60",
|
||||
"sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84",
|
||||
"sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0",
|
||||
"sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b",
|
||||
"sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1",
|
||||
"sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531",
|
||||
"sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1",
|
||||
"sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11",
|
||||
"sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326",
|
||||
"sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df",
|
||||
"sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.1.0"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.3"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"version": "==39.0.2"
|
||||
},
|
||||
"et-xmlfile": {
|
||||
"hashes": [
|
||||
"sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c",
|
||||
"sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.1.0"
|
||||
},
|
||||
"fastapi": {
|
||||
"hashes": [
|
||||
"sha256:451387550c2d25a972193f22e408a82e75a8e7867c834a03076704fe20df3256",
|
||||
"sha256:4a75936dbf9eb74be5eb0d41a793adefe9f3fc6ba66dbdabd160120fd3c2d9cd"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.94.1"
|
||||
},
|
||||
"greenlet": {
|
||||
"hashes": [
|
||||
"sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a",
|
||||
"sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a",
|
||||
"sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43",
|
||||
"sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33",
|
||||
"sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8",
|
||||
"sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088",
|
||||
"sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca",
|
||||
"sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343",
|
||||
"sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645",
|
||||
"sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db",
|
||||
"sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df",
|
||||
"sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3",
|
||||
"sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86",
|
||||
"sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2",
|
||||
"sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a",
|
||||
"sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf",
|
||||
"sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7",
|
||||
"sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394",
|
||||
"sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40",
|
||||
"sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3",
|
||||
"sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6",
|
||||
"sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74",
|
||||
"sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0",
|
||||
"sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3",
|
||||
"sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91",
|
||||
"sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5",
|
||||
"sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9",
|
||||
"sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8",
|
||||
"sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b",
|
||||
"sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6",
|
||||
"sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb",
|
||||
"sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73",
|
||||
"sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b",
|
||||
"sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df",
|
||||
"sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9",
|
||||
"sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f",
|
||||
"sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0",
|
||||
"sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857",
|
||||
"sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a",
|
||||
"sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249",
|
||||
"sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30",
|
||||
"sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292",
|
||||
"sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b",
|
||||
"sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d",
|
||||
"sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b",
|
||||
"sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c",
|
||||
"sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca",
|
||||
"sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7",
|
||||
"sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75",
|
||||
"sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae",
|
||||
"sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b",
|
||||
"sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470",
|
||||
"sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564",
|
||||
"sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9",
|
||||
"sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099",
|
||||
"sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0",
|
||||
"sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5",
|
||||
"sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19",
|
||||
"sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1",
|
||||
"sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"
|
||||
],
|
||||
"markers": "platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))",
|
||||
"version": "==2.0.2"
|
||||
},
|
||||
"gunicorn": {
|
||||
"hashes": [
|
||||
"sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e",
|
||||
"sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==20.1.0"
|
||||
},
|
||||
"h11": {
|
||||
"hashes": [
|
||||
"sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
|
||||
"sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.14.0"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
|
||||
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.4"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
|
||||
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.1.2"
|
||||
},
|
||||
"jmespath": {
|
||||
"hashes": [
|
||||
"sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980",
|
||||
"sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed",
|
||||
"sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc",
|
||||
"sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2",
|
||||
"sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460",
|
||||
"sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7",
|
||||
"sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0",
|
||||
"sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1",
|
||||
"sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa",
|
||||
"sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03",
|
||||
"sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323",
|
||||
"sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65",
|
||||
"sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013",
|
||||
"sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036",
|
||||
"sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f",
|
||||
"sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4",
|
||||
"sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419",
|
||||
"sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2",
|
||||
"sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619",
|
||||
"sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a",
|
||||
"sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a",
|
||||
"sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd",
|
||||
"sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7",
|
||||
"sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666",
|
||||
"sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65",
|
||||
"sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859",
|
||||
"sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625",
|
||||
"sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff",
|
||||
"sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156",
|
||||
"sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd",
|
||||
"sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba",
|
||||
"sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f",
|
||||
"sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1",
|
||||
"sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094",
|
||||
"sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a",
|
||||
"sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513",
|
||||
"sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed",
|
||||
"sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d",
|
||||
"sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3",
|
||||
"sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147",
|
||||
"sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c",
|
||||
"sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603",
|
||||
"sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601",
|
||||
"sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a",
|
||||
"sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1",
|
||||
"sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d",
|
||||
"sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3",
|
||||
"sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54",
|
||||
"sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2",
|
||||
"sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6",
|
||||
"sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.2"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"markers": "python_version < '3.10'",
|
||||
"version": "==1.24.2"
|
||||
},
|
||||
"openpyxl": {
|
||||
"hashes": [
|
||||
"sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184",
|
||||
"sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.1.2"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.0rc0"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
|
||||
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
|
||||
],
|
||||
"version": "==2.21"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.10.6"
|
||||
},
|
||||
"pyjwt": {
|
||||
"extras": [
|
||||
"crypto"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd",
|
||||
"sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.6.0"
|
||||
},
|
||||
"pymysql": {
|
||||
"hashes": [
|
||||
"sha256:41fc3a0c5013d5f039639442321185532e3e2c8924687abe6537de157d403641",
|
||||
"sha256:816927a350f38d56072aeca5dfb10221fe1dc653745853d30a216637f5d7ad36"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"pynamodb": {
|
||||
"hashes": [
|
||||
"sha256:3c4d10867d59e6d7a2b54ee4ae213f1021d6f50ff93145e3909784bfc2b7560e",
|
||||
"sha256:e09c39880560e10251778185b3d0c7a97ee8f42ab363a940c674e9330b61bf9d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.4.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
|
||||
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.8.2"
|
||||
},
|
||||
"python-multipart": {
|
||||
"hashes": [
|
||||
"sha256:e9925a80bb668529f1b67c7fdb0a5dacdd7cbfc6fb0bff3ea443fe22bdd62132",
|
||||
"sha256:ee698bab5ef148b0a760751c261902cd096e57e10558e11aca17646b74ee1c18"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.0.6"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0",
|
||||
"sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"
|
||||
],
|
||||
"version": "==2022.7.1"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa",
|
||||
"sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.28.2"
|
||||
},
|
||||
"s3transfer": {
|
||||
"hashes": [
|
||||
"sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd",
|
||||
"sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077",
|
||||
"sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==67.6.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"sniffio": {
|
||||
"hashes": [
|
||||
"sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101",
|
||||
"sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.6"
|
||||
},
|
||||
"starlette": {
|
||||
"hashes": [
|
||||
"sha256:41da799057ea8620e4667a3e69a5b1923ebd32b1819c8fa75634bbe8d8bea9bd",
|
||||
"sha256:e87fce5d7cbdde34b76f0ac69013fd9d190d581d80681493016666e6f96c6d5e"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.26.1"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb",
|
||||
"sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.5.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305",
|
||||
"sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"
|
||||
],
|
||||
"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"
|
||||
},
|
||||
"uvicorn": {
|
||||
"hashes": [
|
||||
"sha256:8635a388062222082f4b06225b867b74a7e4ef942124453d4d1d1a5cb3750932",
|
||||
"sha256:e69e955cb621ae7b75f5590a814a4fcbfb14cb8f44a36dfe3c5c75ab8aee3ad5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.21.0"
|
||||
},
|
||||
"xlrd": {
|
||||
"hashes": [
|
||||
"sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd",
|
||||
"sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.1"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"autopep8": {
|
||||
"hashes": [
|
||||
"sha256:86e9303b5e5c8160872b2f5ef611161b2893e9bfe8ccc7e2f76385947d57a2f1",
|
||||
"sha256:f9849cdd62108cb739dbcdbfb7fdcc9a30d1b63c4cc3e1c1f893b5360941b61c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.0.2"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7",
|
||||
"sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.0.0"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
|
||||
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.7.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053",
|
||||
"sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.10.0"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf",
|
||||
"sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"tomli": {
|
||||
"hashes": [
|
||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
||||
],
|
||||
"markers": "python_version < '3.11'",
|
||||
"version": "==2.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
192
ecs/jskult-webapp/README.md
Normal file
192
ecs/jskult-webapp/README.md
Normal file
@ -0,0 +1,192 @@
|
||||
# FastAPI 試作プロジェクト
|
||||
|
||||
## 概要
|
||||
|
||||
PythonのWebフレームワーク「FastAPI」を利用して、MINE(旧称)のWebアプリケーションを開発してみるサンプルのリポジトリです。
|
||||
新名称はMeDaCA(Merck Database for Commercial Application)を表示する。
|
||||
|
||||
## 環境情報
|
||||
|
||||
- Python 3.9
|
||||
- MySQL 8.x
|
||||
- VSCode
|
||||
- その他ライブラリ Pipfileを参照
|
||||
|
||||
## 環境構築
|
||||
|
||||
- Pythonの構築
|
||||
- Merck_NewDWH開発2021のWiki、[Python環境構築](https://nds-tyo.backlog.com/alias/wiki/1874930)を参照
|
||||
- 「Pipenvの導入」までを行っておくこと
|
||||
- 構築完了後、プロジェクト配下で以下のコマンドを実行し、Pythonの仮想環境を作成する
|
||||
- `pipenv install`
|
||||
- この手順で出力される仮想環境のパスは、後述するVSCodeの設定手順で使用するため、控えておく
|
||||
|
||||
- MySQLの環境構築
|
||||
- Windowsの場合、以下のリンクからダウンロードする
|
||||
- <https://dev.mysql.com/downloads/installer/>
|
||||
- Dockerを利用する場合、「newsdwh-tools」リポジトリのMySQL設定を使用すると便利
|
||||
- 「crm-table-to-ddl」フォルダ内で以下のコマンドを実行すると
|
||||
- `docker-compose up -d`
|
||||
- Dockerの構築手順は、[Dockerのセットアップ手順](https://nds-tyo.backlog.com/alias/wiki/1754332)を参照のこと
|
||||
- データを投入する
|
||||
- 立ち上げたデータベースに「src05」スキーマを作成する
|
||||
- [ローカル開発用データ](https://ndstokyo.sharepoint.com/:f:/r/sites/merck-new-dwh-team/Shared%20Documents/03.NewDWH%E6%A7%8B%E7%AF%89%E3%83%95%E3%82%A7%E3%83%BC%E3%82%BA3/02.%E9%96%8B%E7%99%BA/90.%E9%96%8B%E7%99%BA%E5%85%B1%E6%9C%89/%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E9%96%8B%E7%99%BA%E7%94%A8%E3%83%87%E3%83%BC%E3%82%BF?csf=1&web=1&e=VVcRUs)をダウンロードし、mysqlコマンドを使用して復元する
|
||||
- `mysql -h <ホスト名> -P <ポート> -u <ユーザー名> -p src05 < src05_dump.sql`
|
||||
- 環境変数の設定
|
||||
- 「.env.example」ファイルをコピーし、「.env」ファイルを作成する
|
||||
- 環境変数を設定する。設定内容はPRJメンバーより共有を受けてください
|
||||
- VSCodeの設定
|
||||
- 「.vscode/recommended_settings.json」ファイルをコピーし、「settings.json」ファイルを作成する
|
||||
- 「python.defaultInterpreterPath」を、Pythonの構築手順で作成した仮想環境のパスに変更する
|
||||
|
||||
## 実行
|
||||
|
||||
- VSCode上で「F5」キーを押下すると、Webアプリケーションのサーバーが起動する
|
||||
- 「<http://localhost:8000/maintlogin>」にアクセスし、ログイン画面が表示されていれば成功
|
||||
|
||||
## フォルダ構成
|
||||
|
||||
```text
|
||||
.
|
||||
├── Pipfile -- Pythonモジュールの依存関係を管理するファイル
|
||||
├── Pipfile.lock -- Pythonモジュールの依存関係バージョン固定用ファイル
|
||||
├── README.md
|
||||
├── auth_flow.drawio -- 認証フローの説明図
|
||||
└── src -- ソースコードの保管場所
|
||||
├── aws -- AWSリソース操作用のコード
|
||||
│ ├── aws_api_client.py
|
||||
│ ├── cognito.py
|
||||
│ └── s3.py
|
||||
├── controller -- ルーティング層。基本的に1画面1つ
|
||||
│ ├── bio.py
|
||||
│ ├── bio_download.py
|
||||
│ ├── login.py
|
||||
│ ├── logout.py
|
||||
│ └── menu.py
|
||||
├── core -- APサーバーのコア設定。
|
||||
│ └── tasks.py -- 起動・終了時に実行するタスクを設定。
|
||||
├── data -- 生物由来照会のエクセルファイルテンプレート。これはS3に持っていくかも。
|
||||
│ └── BioData_template.xlsx
|
||||
├── db -- データベース関連処理。
|
||||
│ ├── database.py -- データベース接続、クエリ発行の共通モジュール。
|
||||
│ ├── sql_condition.py -- SQLの条件式を組み立てるためのモジュール
|
||||
│ └── tasks.py -- coreに渡すタスク。サーバー起動時にDBとの接続モジュールの初期化、終了時にインスタンス破棄を行っている。
|
||||
├── depends -- FastAPIの依存性注入(DI)使用するモジュールの置き場。Dependsで利用想定。
|
||||
│ ├── auth.py -- セッション等の認証関連
|
||||
│ ├── database.py -- リポジトリ層をコントローラーにDIするためのもの
|
||||
│ └── services.py -- サービス層をコントローラーにDIするためのもの
|
||||
├── error -- エラー処理関連のモジュール置き場
|
||||
│ ├── exception_handler.py -- FastAPI内部でエラー発生時のハンドリング
|
||||
│ └── exceptions.py -- カスタム例外クラス
|
||||
├── main.py -- APサーバーのエントリーポイント。ここでルーターやハンドラーの登録を行う
|
||||
├── model -- モデル層(MVCのM)
|
||||
│ ├── db -- リポジトリから返されるDBレコードのモデル
|
||||
│ │ ├── base_db_model.py
|
||||
│ │ ├── bio_sales_view.py
|
||||
│ │ ├── hdke_tbl.py
|
||||
│ │ ├── pharmacy_product_master.py
|
||||
│ │ ├── user_master.py
|
||||
│ │ └── wholesaler_master.py
|
||||
│ ├── jwt_token.py -- 認証用JWTトークンのモデル
|
||||
│ ├── request -- 画面からのリクエストを受け付けるモデル
|
||||
│ │ ├── bio.py
|
||||
│ │ ├── bio_download.py
|
||||
│ │ └── login.py
|
||||
│ ├── session.py -- セッションデータのモデル
|
||||
│ └── view -- ビューモデル。画面に対応したモデル。
|
||||
│ ├── bio_disp_model.py
|
||||
│ ├── bio_view_model.py
|
||||
│ ├── logout_view_model.py
|
||||
│ ├── mainte_login_view_model.py
|
||||
│ ├── menu_view_model.py
|
||||
│ └── user_view_model.py
|
||||
├── repositories -- リポジトリ層。DB操作モジュール置き場。
|
||||
│ ├── base_repository.py
|
||||
│ ├── bio_sales_view_repository.py
|
||||
│ ├── hdke_tbl_repository.py
|
||||
│ ├── pharmacy_product_master_repository.py
|
||||
│ ├── user_master_repository.py
|
||||
│ └── wholesaler_master_repository.py
|
||||
├── router -- コントローラー層の共通ルーティングの定義
|
||||
│ └── session_router.py
|
||||
├── services -- サービス層。ビジネスロジックはできる限りここに押し込む
|
||||
│ ├── base_service.py
|
||||
│ ├── batch_status_service.py
|
||||
│ ├── bio_view_service.py
|
||||
│ ├── login_service.py
|
||||
│ └── session_service.py
|
||||
├── static -- 静的ファイルの配信ルートディレクトリ
|
||||
│ ├── css
|
||||
│ │ ├── bioStyle.css
|
||||
│ │ ├── datepicker.css
|
||||
│ │ ├── menuStyle.css
|
||||
│ │ └── pagenation.css
|
||||
│ ├── function
|
||||
│ │ └── businessLogicScript.js
|
||||
│ ├── img
|
||||
│ │ ├── icon_modal_confirm.png
|
||||
│ │ └── icon_modal_error.png
|
||||
│ ├── lib
|
||||
│ │ └── fixed_midashi.js
|
||||
│ ├── sample.css
|
||||
│ └── sample.js
|
||||
├── system_var -- システム変数
|
||||
│ ├── constants.py -- 定数
|
||||
│ └── environment.py -- 環境変数
|
||||
├── templates -- ビューテンプレートエンジンの格納場所(Jinja2)
|
||||
│ ├── _header.html -- 共通ヘッダー
|
||||
│ ├── _modal.html -- モーダルの部品
|
||||
│ ├── bioSearchList.html
|
||||
│ ├── logout.html
|
||||
│ ├── maintlogin.html
|
||||
│ ├── menu.html
|
||||
└── util -- ユーティリティ関数置き場
|
||||
├── sanitize.py -- モデルクラスのサニタイズを行うデコレータ
|
||||
└── string_util.py -- 文字列操作関連のユーティリティ
|
||||
```
|
||||
|
||||
## (参考)ファイルの追いかけ方
|
||||
|
||||
- APサーバーそのものは「src/main」にある。
|
||||
- ルーター、例外処理ハンドラ、開始終了タスクの設定、静的ファイルディレクトリのマウントを行っている
|
||||
- URLパスに対する操作の実装は、「controller」フォルダを見る
|
||||
- `@router.xxx`の`xxx`の部分がHTTPメソッドに相当する。このデコレータが付与された関数が、HTTPメソッドを処理するパスオペレーション関数となる
|
||||
- パスオペレーション関数の引数には、リクエストで受け取るパラメータと、その関数内で使用できる依存関係を注入できる
|
||||
- Request型と、Response型の引数は、その名の通り。
|
||||
- str型, int型などの引数を指定した場合、その引数はクエリストリングを意味する
|
||||
- Dependsで初期値が設定される引数は、Depends関数に渡した関数が処理されてから代入される
|
||||
- たとえば、`get_service`関数にサービスクラスの型を渡すと、get_service関数でサービスクラスのインスタンスを作成して返してくれる
|
||||
- サービスクラスはリポジトリクラスに依存しているので、自分でインスタンスを組み立てる手間が省ける
|
||||
- formやリクエストボディのJSONを受け取る場合、リクエスト用のモデルクラスに「as_form」や「as_body」などの関数を実装し、リクエストを受け取れるようにする
|
||||
- ビジネスロジックは基本的にサービスクラスに押し込む。コントローラーではそのサービスクラスをDependsで依存して利用すること
|
||||
- ビジネスロジックに相当するサービスクラスは「services」フォルダに格納する。共通実装は以下。
|
||||
- REPOSITORIES定数に、依存するリポジトリクラスを辞書形式で指定する
|
||||
- CLIENTS定数に、依存するAWS APIクライアントクラスを辞書形式で指定する
|
||||
- `__init__`コンストラクタ内で、2つの定数に指定したキーに紐付いたインスタンスが渡ってくるため、インスタンス変数として登録する
|
||||
- あとは、ビジネスロジックにあたる関数を生やしていくだけ
|
||||
- DBへのアクセスを行うリポジトリクラスは「repositories」フォルダに格納する。
|
||||
- SQL文を用意し、`_db`インスタンス変数のメソッドを利用してクエリを実行する。
|
||||
- 必要に応じて条件設定をする。条件設定には`SQLCondition`クラスを使用する。
|
||||
- `BioSalesViewRepository`のやり方が参考になる
|
||||
- リポジトリクラスは、サービスクラスで利用するようにする(そのために、サービスクラス側に依存関係を書いている)
|
||||
- モデルクラスは、「models」フォルダに格納する
|
||||
- HTTPリクエスト用のモデルクラスは「request」フォルダへ
|
||||
- ビュー表示用のモデルクラス(View Model)は「view」フォルダへ
|
||||
- 画面への項目埋め込みや、非表示の制御を行うため、View Modelに値を詰めて、テンプレート側で操作するようにする
|
||||
- DBから取得したレコードのモデルクラスは「db」フォルダへ
|
||||
- 内部処理に利用するモデルクラスは「internal」フォルダへ
|
||||
- 「static」フォルダは、静的ファイルの置き場所
|
||||
- CSS, JS、画像ファイルを置く。
|
||||
- 画面の細かな制御は「BusinessLogicScript.js」で行っている。現行からちょこちょこ変える必要がある。
|
||||
- CSSも、現行は画面ごとに分かれているが、一つのベーススタイルにまとめたい
|
||||
- 「templates」フォルダは、テンプレートエンジンを格納する
|
||||
- 各画面1つのテンプレートエンジンを用意する
|
||||
- 内部で使う変数は、コントローラーで「templates.TemplateResponse」」に詰めて渡す
|
||||
- テンプレート内でincludeする用途のテンプレートは先頭に「_」をつける
|
||||
- `_header.html`は、`<head>`タグ内に記載する部品。共通的に読み込むCSS等のファイルを指定する。
|
||||
- PHPでは分岐などをベタ書きしているが、View Modelに宣言的な関数を用意して、可読性を向上させている。
|
||||
- コントローラーの共通処理は、「router」フォルダ内のモジュールで実装している
|
||||
- コントローラーのrouter変数が、`router.route_class = AfterSetCookieSessionRoute`となっている場合、レスポンス時、クッキーにセッションキーを登録する動きをする
|
||||
- コントローラーのrouter変数が、`router.route_class = Authenticate`となっている場合、以下の動きをする
|
||||
- リクエスト到達時にセッションの有無をチェックする
|
||||
- レスポンス時、クッキーにセッションキーを登録する
|
||||
0
ecs/jskult-webapp/src/__init__.py
Normal file
0
ecs/jskult-webapp/src/__init__.py
Normal file
0
ecs/jskult-webapp/src/aws/__init__.py
Normal file
0
ecs/jskult-webapp/src/aws/__init__.py
Normal file
5
ecs/jskult-webapp/src/aws/aws_api_client.py
Normal file
5
ecs/jskult-webapp/src/aws/aws_api_client.py
Normal file
@ -0,0 +1,5 @@
|
||||
from abc import ABCMeta
|
||||
|
||||
|
||||
class AWSAPIClient(metaclass=ABCMeta):
|
||||
pass
|
||||
25
ecs/jskult-webapp/src/aws/cognito.py
Normal file
25
ecs/jskult-webapp/src/aws/cognito.py
Normal file
@ -0,0 +1,25 @@
|
||||
import boto3
|
||||
|
||||
from src.aws.aws_api_client import AWSAPIClient
|
||||
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,
|
||||
AuthFlow='ADMIN_USER_PASSWORD_AUTH',
|
||||
AuthParameters={
|
||||
'USERNAME': username,
|
||||
'PASSWORD': password,
|
||||
'SECRET_HASH': secret_hash
|
||||
},
|
||||
)
|
||||
authentication_result = auth_response['AuthenticationResult']
|
||||
|
||||
return authentication_result['IdToken'], authentication_result['RefreshToken'],
|
||||
32
ecs/jskult-webapp/src/aws/s3.py
Normal file
32
ecs/jskult-webapp/src/aws/s3.py
Normal file
@ -0,0 +1,32 @@
|
||||
from urllib.parse import quote
|
||||
|
||||
import boto3
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
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={
|
||||
'Bucket': bucket_name,
|
||||
'Key': file_key,
|
||||
# 別ファイル名に変更するための仕掛け。Unicode文字はquoteでエスケープが必要
|
||||
'ResponseContentDisposition': f'attachment; filename="{quote(download_filename)}"'
|
||||
},
|
||||
# 有効期限20分
|
||||
ExpiresIn=1200
|
||||
)
|
||||
|
||||
return presigned_url
|
||||
0
ecs/jskult-webapp/src/controller/__init__.py
Normal file
0
ecs/jskult-webapp/src/controller/__init__.py
Normal file
87
ecs/jskult-webapp/src/controller/bio.py
Normal file
87
ecs/jskult-webapp/src/controller/bio.py
Normal file
@ -0,0 +1,87 @@
|
||||
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.model.internal.session import UserSession
|
||||
from src.model.request.bio import BioModel
|
||||
from src.model.view.bio_view_model import BioViewModel
|
||||
from src.router.session_router import AuthenticatedRoute
|
||||
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.templates import templates
|
||||
|
||||
router = APIRouter()
|
||||
router.route_class = AuthenticatedRoute
|
||||
|
||||
#########################
|
||||
# Views #
|
||||
#########################
|
||||
@router.get('/bio/BioSearchList')
|
||||
def bio_view(
|
||||
request: Request,
|
||||
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)
|
||||
# 検索項目の取得
|
||||
bio = bio_service.prepare_bio_view(session)
|
||||
# セッション書き換え
|
||||
session.update(
|
||||
actions=[
|
||||
UserSession.last_access_time.set(UserSession.new_last_access_time()),
|
||||
UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()),
|
||||
]
|
||||
)
|
||||
session_key = set_session(session)
|
||||
templates_response = templates.TemplateResponse(
|
||||
'bioSearchList.html', {
|
||||
'request': request,
|
||||
'bio': bio,
|
||||
},
|
||||
headers={'session_key': session_key}
|
||||
)
|
||||
return templates_response
|
||||
|
||||
@router.post('/bio/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))
|
||||
):
|
||||
# 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():
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=constants.LOGOUT_REASON_BATCH_PROCESSING)
|
||||
|
||||
# 生物由来データを検索
|
||||
bio_sales_view_data = bio_service.search_bio_data(bio_form)
|
||||
# 検索項目などのデータを取得
|
||||
bio: BioViewModel = bio_service.prepare_bio_view(session)
|
||||
bio.bio_data = bio_sales_view_data
|
||||
bio.form_data = bio_form
|
||||
# セッション書き換え
|
||||
session.update(
|
||||
actions=[
|
||||
UserSession.last_access_time.set(UserSession.new_last_access_time()),
|
||||
UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()),
|
||||
]
|
||||
)
|
||||
session_key = set_session(session)
|
||||
templates_response = templates.TemplateResponse(
|
||||
'bioSearchList.html', {
|
||||
'request': request,
|
||||
'bio': bio
|
||||
},
|
||||
headers={'session_key': session_key}
|
||||
)
|
||||
return templates_response
|
||||
121
ecs/jskult-webapp/src/controller/bio_download.py
Normal file
121
ecs/jskult-webapp/src/controller/bio_download.py
Normal file
@ -0,0 +1,121 @@
|
||||
"""生物由来ファイルダウンロード APIRoute"""
|
||||
from datetime import datetime
|
||||
from typing import Union
|
||||
|
||||
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.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
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
#########################
|
||||
# APIs #
|
||||
#########################
|
||||
@router.post('/api/bio/download')
|
||||
async def download_bio_data(
|
||||
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)
|
||||
):
|
||||
# 通常のビューとはルーティングの扱いを変えるために、個別のルーターで登録する
|
||||
# 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");
|
||||
# ファイル名に使用するタイムスタンプを初期化しておく
|
||||
now = datetime.now()
|
||||
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}
|
||||
)
|
||||
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]
|
||||
|
||||
# 値を変換
|
||||
# データ種別の正式名を設定
|
||||
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)
|
||||
|
||||
# ローカルファイルから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)
|
||||
except Exception as e:
|
||||
print('S3 access error', e.args)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail={'error': 'aws_error', 'message': e.args}
|
||||
)
|
||||
|
||||
# セッション書き換え
|
||||
session.update(
|
||||
actions=[
|
||||
UserSession.last_access_time.set(UserSession.new_last_access_time()),
|
||||
UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()),
|
||||
]
|
||||
)
|
||||
set_session(session)
|
||||
|
||||
# クッキーも書き換え
|
||||
json_response = JSONResponse(content={
|
||||
'status': 'ok',
|
||||
'download_url': download_file_url
|
||||
})
|
||||
json_response.set_cookie(
|
||||
key='session',
|
||||
value=session.session_key,
|
||||
max_age=20*60,
|
||||
secure=True,
|
||||
httponly=True
|
||||
)
|
||||
return json_response
|
||||
10
ecs/jskult-webapp/src/controller/healthcheck.py
Normal file
10
ecs/jskult-webapp/src/controller/healthcheck.py
Normal file
@ -0,0 +1,10 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
#########################
|
||||
# Views #
|
||||
#########################
|
||||
@router.get('/healthcheck')
|
||||
def healthcheck():
|
||||
return {'status': 'OK'}
|
||||
150
ecs/jskult-webapp/src/controller/login.py
Normal file
150
ecs/jskult-webapp/src/controller/login.py
Normal file
@ -0,0 +1,150 @@
|
||||
import os.path as path
|
||||
import secrets
|
||||
import urllib.parse as parse
|
||||
from typing import Union
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response
|
||||
from fastapi.responses import RedirectResponse
|
||||
from starlette import status
|
||||
|
||||
from src.depends.auth import code_security
|
||||
from src.depends.services import get_service
|
||||
from src.error.exceptions import JWTTokenVerifyException, NotAuthorizeException
|
||||
from src.model.internal.session import UserSession
|
||||
from src.model.request.login import LoginModel
|
||||
from src.model.view.mainte_login_view_model import MainteLoginViewModel
|
||||
from src.router.session_router import AfterSetCookieSessionRoute
|
||||
from src.services.login_service import LoginService
|
||||
from src.services.session_service import set_session
|
||||
from src.system_var import constants, environment
|
||||
from src.templates import templates
|
||||
|
||||
router = APIRouter()
|
||||
router.route_class = AfterSetCookieSessionRoute
|
||||
|
||||
#########################
|
||||
# Views #
|
||||
#########################
|
||||
@router.get('/userlogin')
|
||||
def login_user_redirect_view():
|
||||
auth_query_string = parse.urlencode(
|
||||
{
|
||||
'response_type': 'code',
|
||||
'identity_provider': environment.COGNITO_IDENTITY_PROVIDER,
|
||||
'client_id': environment.COGNITO_CLIENT_ID,
|
||||
'redirect_uri': environment.COGNITO_REDIRECT_URI
|
||||
}
|
||||
)
|
||||
authorize_endpoint_url = f'{environment.COGNITO_AUTH_DOMAIN}/{environment.AUTHORIZE_ENDPOINT}?{auth_query_string}'
|
||||
|
||||
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()
|
||||
return templates.TemplateResponse(
|
||||
'maintlogin.html',
|
||||
{
|
||||
'request': request,
|
||||
'mainte_login': mainte_login
|
||||
}
|
||||
)
|
||||
|
||||
#########################
|
||||
# APIs #
|
||||
#########################
|
||||
@router.post('/login')
|
||||
def sso_authorize(
|
||||
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:
|
||||
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
|
||||
user_record = login_service.logged_in_user(user_id)
|
||||
# ユーザーが有効ではない場合、ログアウトにリダイレクトする
|
||||
if not user_record.is_enable_user():
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_LOGIN_ERROR)
|
||||
# メンテユーザーではない場合、ログアウトにリダイレクトする
|
||||
if user_record is None or not user_record.is_maintenance_user():
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_LOGIN_ERROR)
|
||||
# CSRFトークンを生成
|
||||
csrf_token = secrets.token_urlsafe(32)
|
||||
# DynamoDBにトークンIDを設定する
|
||||
session_model: UserSession = UserSession.new(
|
||||
user_id=user_id,
|
||||
id_token=verified_token.id_token,
|
||||
refresh_token=verified_token.refresh_token,
|
||||
csrf_token=csrf_token,
|
||||
bio_flg=user_record.auth_flg1,
|
||||
doc_flg=user_record.auth_flg2,
|
||||
inst_flg=user_record.auth_flg3,
|
||||
master_mainte_flg=user_record.auth_flg4,
|
||||
user_flg=user_record.mntuser_flg
|
||||
)
|
||||
session_key = set_session(session_model)
|
||||
|
||||
response = RedirectResponse(
|
||||
url='/menu',
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
headers={'session_key': session_key}
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
@router.get('/authorize')
|
||||
def sso_authorize(
|
||||
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)
|
||||
|
||||
# トークン取得
|
||||
jwt_token = login_service.login_with_security_code(code)
|
||||
try:
|
||||
# トークン検証
|
||||
verified_token = jwt_token.verify_token()
|
||||
except JWTTokenVerifyException as e:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_SESSION_EXPIRED)
|
||||
|
||||
# トークンからユーザーIDを取得
|
||||
user_id = verified_token.user_id
|
||||
user_record = login_service.logged_in_user(user_id)
|
||||
# ユーザーが有効ではない場合、ログアウトにリダイレクトする
|
||||
if not user_record.is_enable_user():
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_LOGIN_ERROR)
|
||||
# Merckユーザーではない場合、ログアウトにリダイレクトする
|
||||
if user_record is None or not user_record.is_groupware_user():
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_LOGIN_ERROR)
|
||||
|
||||
# CSRFトークンを生成
|
||||
csrf_token = secrets.token_urlsafe(32)
|
||||
# DynamoDBにトークンIDを設定する
|
||||
session_model: UserSession = UserSession.new(
|
||||
user_id=user_id,
|
||||
id_token=verified_token.id_token,
|
||||
refresh_token=verified_token.refresh_token,
|
||||
csrf_token=csrf_token,
|
||||
bio_flg=user_record.auth_flg1,
|
||||
doc_flg=user_record.auth_flg2,
|
||||
inst_flg=user_record.auth_flg3,
|
||||
master_mainte_flg=user_record.auth_flg4,
|
||||
user_flg=user_record.mntuser_flg
|
||||
)
|
||||
session_key = set_session(session_model)
|
||||
response = RedirectResponse(
|
||||
url='/menu',
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
headers={'session_key': session_key}
|
||||
)
|
||||
return response
|
||||
41
ecs/jskult-webapp/src/controller/logout.py
Normal file
41
ecs/jskult-webapp/src/controller/logout.py
Normal file
@ -0,0 +1,41 @@
|
||||
from typing import Optional, Union
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
from src.depends.auth import verify_session
|
||||
from src.model.internal.session import UserSession
|
||||
from src.model.view.logout_view_model import LogoutViewModel
|
||||
from src.system_var import constants
|
||||
from src.templates import templates
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
#########################
|
||||
# Views #
|
||||
#########################
|
||||
@router.get('/logout', response_class=HTMLResponse)
|
||||
def logout_view(
|
||||
request: Request,
|
||||
reason: Optional[str] = None,
|
||||
session: Union[UserSession, None]=Depends(verify_session)
|
||||
):
|
||||
redirect_to = '/userlogin'
|
||||
link_text = 'MeDaCA機能メニューへ'
|
||||
if session is not None and session.user_flg == '1':
|
||||
redirect_to = '/maintlogin'
|
||||
link_text = 'Login画面に戻る'
|
||||
logout = LogoutViewModel()
|
||||
logout.redirect_to = redirect_to
|
||||
logout.reason = constants.LOGOUT_REASON_MESSAGE_MAP.get(reason, '')
|
||||
logout.link_text = link_text
|
||||
template_response = templates.TemplateResponse(
|
||||
'logout.html',
|
||||
{
|
||||
'request': request,
|
||||
'logout': logout,
|
||||
}
|
||||
)
|
||||
# クッキーを削除
|
||||
template_response.delete_cookie('session')
|
||||
return template_response
|
||||
55
ecs/jskult-webapp/src/controller/menu.py
Normal file
55
ecs/jskult-webapp/src/controller/menu.py
Normal file
@ -0,0 +1,55 @@
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
from src.depends.services import get_service
|
||||
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
|
||||
from src.router.session_router import AuthenticatedRoute
|
||||
from src.services.batch_status_service import BatchStatusService
|
||||
from src.services.session_service import set_session
|
||||
from src.templates import templates
|
||||
|
||||
router = APIRouter()
|
||||
router.route_class = AuthenticatedRoute
|
||||
|
||||
#########################
|
||||
# Views #
|
||||
#########################
|
||||
@router.get('/menu', response_class=HTMLResponse)
|
||||
def menu_view(
|
||||
request: Request,
|
||||
batch_status_service:BatchStatusService=Depends(get_service(BatchStatusService))
|
||||
):
|
||||
session: UserSession = request.session
|
||||
# 日付マスターからバッチ情報を取得する
|
||||
hdke_tbl_record = batch_status_service.hdke_table_record
|
||||
|
||||
batch_status = hdke_tbl_record.bch_actf
|
||||
user = UserViewModel(
|
||||
doc_flg=session.doc_flg,
|
||||
inst_flg=session.inst_flg,
|
||||
bio_flg=session.bio_flg,
|
||||
master_mainte_flg=session.master_mainte_flg
|
||||
)
|
||||
menu = MenuViewModel(
|
||||
batch_status=batch_status,
|
||||
user_model=user
|
||||
)
|
||||
# セッション書き換え
|
||||
session.update(
|
||||
actions=[
|
||||
UserSession.last_access_time.set(UserSession.new_last_access_time()),
|
||||
UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()),
|
||||
]
|
||||
)
|
||||
set_session(session)
|
||||
templates_response = templates.TemplateResponse(
|
||||
'menu.html',
|
||||
{
|
||||
'request': request,
|
||||
'menu': menu
|
||||
},
|
||||
headers={'session_key': session.session_key}
|
||||
)
|
||||
return templates_response
|
||||
0
ecs/jskult-webapp/src/core/__init__.py
Normal file
0
ecs/jskult-webapp/src/core/__init__.py
Normal file
21
ecs/jskult-webapp/src/core/tasks.py
Normal file
21
ecs/jskult-webapp/src/core/tasks.py
Normal file
@ -0,0 +1,21 @@
|
||||
"""FastAPIサーバーの起動・終了イベントのラッパー"""
|
||||
|
||||
from typing import Callable
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from src.db.tasks import close_db, init_db
|
||||
|
||||
|
||||
def create_start_app_handler(app: FastAPI) -> Callable:
|
||||
def start_app() -> None:
|
||||
init_db(app)
|
||||
|
||||
return start_app
|
||||
|
||||
|
||||
def create_stop_app_handler(app: FastAPI) -> Callable:
|
||||
def stop_app() -> None:
|
||||
close_db(app)
|
||||
|
||||
return stop_app
|
||||
0
ecs/jskult-webapp/src/db/__init__.py
Normal file
0
ecs/jskult-webapp/src/db/__init__.py
Normal file
71
ecs/jskult-webapp/src/db/database.py
Normal file
71
ecs/jskult-webapp/src/db/database.py
Normal file
@ -0,0 +1,71 @@
|
||||
from sqlalchemy import Engine, create_engine, text
|
||||
from sqlalchemy.engine.url import URL
|
||||
|
||||
from src.error.exceptions import DBException
|
||||
|
||||
|
||||
class Database:
|
||||
|
||||
__engine: Engine = None
|
||||
__host: str = None
|
||||
__port: str = None
|
||||
__user: str = None
|
||||
__password: str = None
|
||||
__schema: str = None
|
||||
__connection_string:str = None
|
||||
|
||||
def __init__(self, host: str, port: int, user: str, password: str, schema: str) -> None:
|
||||
self.__host = host
|
||||
self.__port = int(port)
|
||||
self.__user = user
|
||||
self.__password = password
|
||||
self.__schema = schema
|
||||
|
||||
self.__connection_string = URL.create(
|
||||
drivername='mysql+pymysql',
|
||||
username=self.__user,
|
||||
password=self.__password,
|
||||
host=self.__host,
|
||||
port=self.__port,
|
||||
database=self.__schema,
|
||||
query={"charset": "utf8mb4"}
|
||||
)
|
||||
|
||||
@property
|
||||
def connection(self):
|
||||
return self.__engine
|
||||
|
||||
def engine_init(self):
|
||||
engine = create_engine(
|
||||
self.__connection_string,
|
||||
pool_timeout=5
|
||||
)
|
||||
self.__engine = engine
|
||||
|
||||
def execute_query(self, select_query: str, parameters=None) -> list[dict]:
|
||||
if self.__engine is None:
|
||||
raise DBException('データベースが初期化されていません')
|
||||
|
||||
with self.__engine.begin() as trx:
|
||||
try:
|
||||
result = trx.execute(text(select_query), parameters=parameters)
|
||||
except Exception as e:
|
||||
trx.rollback()
|
||||
raise DBException(e)
|
||||
return result.mappings().all()
|
||||
|
||||
def execute(self, query: str, parameters=None) -> None:
|
||||
if self.__engine is None:
|
||||
raise DBException('データベースが初期化されていません')
|
||||
|
||||
with self.__engine.begin() as trx:
|
||||
try:
|
||||
trx.execute(text(query), parameters=parameters)
|
||||
except Exception as e:
|
||||
trx.rollback()
|
||||
raise DBException(e)
|
||||
|
||||
def close(self):
|
||||
if self.__engine is not None:
|
||||
self.__engine.dispose(close=True)
|
||||
self.__engine = None
|
||||
35
ecs/jskult-webapp/src/db/sql_condition.py
Normal file
35
ecs/jskult-webapp/src/db/sql_condition.py
Normal file
@ -0,0 +1,35 @@
|
||||
class SQLCondition:
|
||||
column: str
|
||||
operator: str
|
||||
param: str
|
||||
literal: bool
|
||||
|
||||
def __init__(self, column: str, operator: str, param: str, literal=False) -> None:
|
||||
"""
|
||||
Args:
|
||||
column (str): カラム名
|
||||
operator (str): 比較演算子
|
||||
param (str): パラメータ(プレースホルダーかリテラル値か)
|
||||
literal (bool, optional): リテラル値を埋め込むかどうか。
|
||||
画面から渡ってきた値を使うとSQLインジェクションの危険性があるため、固定値で使用すること。
|
||||
"""
|
||||
self.column = column
|
||||
self.operator = operator
|
||||
self.param = param
|
||||
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 = '<>'
|
||||
GT = '>'
|
||||
LT = '<'
|
||||
GE = '>='
|
||||
LE = '<='
|
||||
LIKE = 'LIKE'
|
||||
IS = 'IS'
|
||||
IS_NOT = 'IS NOT'
|
||||
22
ecs/jskult-webapp/src/db/tasks.py
Normal file
22
ecs/jskult-webapp/src/db/tasks.py
Normal file
@ -0,0 +1,22 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
from src.db.database import Database
|
||||
from src.system_var import environment
|
||||
|
||||
|
||||
def init_db(app: FastAPI) -> None:
|
||||
# DB接続モジュールを初期化
|
||||
database = Database(
|
||||
host=environment.DB_HOST,
|
||||
port=environment.DB_PORT,
|
||||
user=environment.DB_USERNAME,
|
||||
password=environment.DB_PASSWORD,
|
||||
schema=environment.DB_SCHEMA
|
||||
)
|
||||
database.engine_init()
|
||||
# FastAPI App内で使える変数として追加
|
||||
app.state._db = database
|
||||
|
||||
|
||||
def close_db(app: FastAPI) -> None:
|
||||
app.state._db.close()
|
||||
0
ecs/jskult-webapp/src/depends/__init__.py
Normal file
0
ecs/jskult-webapp/src/depends/__init__.py
Normal file
48
ecs/jskult-webapp/src/depends/auth.py
Normal file
48
ecs/jskult-webapp/src/depends/auth.py
Normal file
@ -0,0 +1,48 @@
|
||||
import datetime
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.security import APIKeyCookie, APIKeyQuery
|
||||
|
||||
from src.error.exceptions import JWTTokenVerifyException
|
||||
from src.model.internal.jwt_token import JWTToken
|
||||
from src.model.internal.session import UserSession
|
||||
from src.services.session_service import get_session
|
||||
from src.system_var import environment
|
||||
|
||||
cookie_security = APIKeyCookie(name='session', auto_error=False)
|
||||
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)):
|
||||
"""セッションの最後にアクセスした時間が、セッション有効期限切れであるかどうかをチェックする"""
|
||||
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)
|
||||
if session_expired_period < datetime.datetime.now():
|
||||
return None
|
||||
|
||||
return session
|
||||
|
||||
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)
|
||||
try:
|
||||
jwt_token.verify_token()
|
||||
except JWTTokenVerifyException as e:
|
||||
print(e)
|
||||
return None
|
||||
return session
|
||||
17
ecs/jskult-webapp/src/depends/database.py
Normal file
17
ecs/jskult-webapp/src/depends/database.py
Normal file
@ -0,0 +1,17 @@
|
||||
from typing import Callable, Type
|
||||
|
||||
from fastapi import Depends
|
||||
from starlette.requests import Request
|
||||
|
||||
from src.db.database import Database
|
||||
from src.repositories.base_repository import BaseRepository
|
||||
|
||||
|
||||
def get_database(request: Request) -> Database:
|
||||
return request.app.state._db
|
||||
|
||||
|
||||
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
|
||||
16
ecs/jskult-webapp/src/depends/services.py
Normal file
16
ecs/jskult-webapp/src/depends/services.py
Normal file
@ -0,0 +1,16 @@
|
||||
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
|
||||
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]:
|
||||
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
|
||||
0
ecs/jskult-webapp/src/error/__init__.py
Normal file
0
ecs/jskult-webapp/src/error/__init__.py
Normal file
15
ecs/jskult-webapp/src/error/exception_handler.py
Normal file
15
ecs/jskult-webapp/src/error/exception_handler.py
Normal file
@ -0,0 +1,15 @@
|
||||
from urllib import parse
|
||||
|
||||
from fastapi import Request
|
||||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.responses import RedirectResponse
|
||||
from starlette import status
|
||||
|
||||
|
||||
def http_exception_handler(request: Request, exc: HTTPException):
|
||||
# 非同期API呼び出しの場合、detailにdictが入ってくるため、そのまま流す
|
||||
if hasattr(exc, 'detail') is True and type(exc.detail) == dict:
|
||||
raise exc
|
||||
error_detail = exc.detail if hasattr(exc, 'detail') else ''
|
||||
reason = parse.quote(error_detail)
|
||||
return RedirectResponse(f'/logout?reason={reason}', status_code=status.HTTP_303_SEE_OTHER)
|
||||
33
ecs/jskult-webapp/src/error/exceptions.py
Normal file
33
ecs/jskult-webapp/src/error/exceptions.py
Normal file
@ -0,0 +1,33 @@
|
||||
from typing import Union
|
||||
|
||||
from starlette import status
|
||||
|
||||
|
||||
class MeDaCaException(Exception):
|
||||
"""Webアプリの共通例外"""
|
||||
pass
|
||||
|
||||
class NotAuthorizeException(MeDaCaException):
|
||||
"""認証失敗の例外"""
|
||||
pass
|
||||
|
||||
class JWTTokenVerifyException(MeDaCaException):
|
||||
"""トークン検証失敗の例外"""
|
||||
pass
|
||||
|
||||
class DBException(MeDaCaException):
|
||||
"""DB関連の例外"""
|
||||
pass
|
||||
|
||||
class UnexpectedException(MeDaCaException):
|
||||
"""予期しない例外"""
|
||||
|
||||
# デフォルトを500エラーとする
|
||||
default_status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
detail: Union[str, dict],
|
||||
) -> None:
|
||||
self.status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
self.detail = detail
|
||||
40
ecs/jskult-webapp/src/main.py
Normal file
40
ecs/jskult-webapp/src/main.py
Normal file
@ -0,0 +1,40 @@
|
||||
import os.path as path
|
||||
|
||||
from fastapi import FastAPI
|
||||
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.core import tasks
|
||||
from src.error.exception_handler import http_exception_handler
|
||||
from src.error.exceptions import UnexpectedException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# 静的ファイルをマウント
|
||||
app.mount('/static', StaticFiles(directory=path.dirname(static.__file__)), name='static')
|
||||
# ログイン関連のルーター
|
||||
app.include_router(login.router)
|
||||
# ログアウト関連のルーター
|
||||
app.include_router(logout.router)
|
||||
# メニュー画面関連のルーター
|
||||
app.include_router(menu.router)
|
||||
# 生物由来関連のルーター
|
||||
app.include_router(bio.router)
|
||||
# 生物由来のダウンロード用APIルーター。
|
||||
# クライアントから非同期呼出しされるため、共通ルーターとは異なる扱いとする。
|
||||
app.include_router(bio_download.router)
|
||||
# ヘルスチェック用のルーター
|
||||
app.include_router(healthcheck.router)
|
||||
|
||||
# エラー発生時にログアウト画面に遷移させるハンドラー
|
||||
app.add_exception_handler(status.HTTP_401_UNAUTHORIZED, http_exception_handler)
|
||||
app.add_exception_handler(status.HTTP_403_FORBIDDEN, http_exception_handler)
|
||||
|
||||
# サーバーエラーが発生した場合のハンドラー。HTTPExceptionではハンドリングできないため、個別に設定
|
||||
app.add_exception_handler(UnexpectedException, http_exception_handler)
|
||||
|
||||
# サーバー起動・終了イベントを登録
|
||||
app.add_event_handler('startup', tasks.create_start_app_handler(app))
|
||||
app.add_event_handler('shutdown', tasks.create_stop_app_handler(app))
|
||||
0
ecs/jskult-webapp/src/model/__init__.py
Normal file
0
ecs/jskult-webapp/src/model/__init__.py
Normal file
0
ecs/jskult-webapp/src/model/db/__init__.py
Normal file
0
ecs/jskult-webapp/src/model/db/__init__.py
Normal file
5
ecs/jskult-webapp/src/model/db/base_db_model.py
Normal file
5
ecs/jskult-webapp/src/model/db/base_db_model.py
Normal file
@ -0,0 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class BaseDBModel(BaseModel):
|
||||
pass
|
||||
74
ecs/jskult-webapp/src/model/db/bio_sales_view.py
Normal file
74
ecs/jskult-webapp/src/model/db/bio_sales_view.py
Normal file
@ -0,0 +1,74 @@
|
||||
from datetime import date, datetime
|
||||
from typing import Optional
|
||||
|
||||
from src.model.db.base_db_model import BaseDBModel
|
||||
|
||||
|
||||
class BioSalesViewModel(BaseDBModel):
|
||||
conv_cd: Optional[int]
|
||||
rec_data: Optional[str]
|
||||
rec_whs_cd: Optional[str]
|
||||
rec_whs_sub_cd: Optional[str]
|
||||
rec_whs_org_cd: Optional[str]
|
||||
rec_cust_cd: Optional[str]
|
||||
rec_comm_cd: Optional[str]
|
||||
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_lot_num: Optional[str]
|
||||
rec_amt: Optional[str]
|
||||
rec_ymd: Optional[str]
|
||||
sale_data_cat: Optional[str]
|
||||
slip_file_nm: Optional[str]
|
||||
slip_mgt_no: Optional[str]
|
||||
row_num: Optional[int]
|
||||
hsdn_ymd: Optional[str]
|
||||
exec_dt: Optional[str]
|
||||
v_tran_cd: Optional[int]
|
||||
tran_kbn_nm: Optional[str]
|
||||
whs_org_cd: Optional[str]
|
||||
v_whsorg_cd: Optional[str]
|
||||
whs_org_nm: Optional[str]
|
||||
whs_org_kn: Optional[str]
|
||||
v_whs_cd: Optional[int]
|
||||
whs_nm: Optional[str]
|
||||
nnsk_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]
|
||||
whs_rep_comm_nm: Optional[str]
|
||||
whs_rep_nnskfcl_nm: Optional[str]
|
||||
whs_rep_nnsk_fcl_addr: Optional[str]
|
||||
mkr_inf_1: Optional[str]
|
||||
mkr_cd: Optional[str]
|
||||
htdnymd_err_kbn: Optional[str]
|
||||
prd_exis_kbn: Optional[str]
|
||||
fcl_exis_kbn: Optional[str]
|
||||
amt: Optional[int]
|
||||
slip_org_kbn: Optional[str]
|
||||
bef_slip_mgt_no: Optional[str]
|
||||
lot_no_err_flg: Optional[str]
|
||||
iko_flg: Optional[str]
|
||||
kjyo_ym: Optional[str]
|
||||
tksnbk_kbn: Optional[str]
|
||||
fcl_exec_kbn: Optional[str]
|
||||
rec_sts_kbn: Optional[str]
|
||||
ins_dt: Optional[datetime]
|
||||
ins_usr: Optional[str]
|
||||
dcf_inst_cd: Optional[str]
|
||||
inst_cd: Optional[str]
|
||||
inst_name_form: Optional[str]
|
||||
address: Optional[str]
|
||||
tel_no: Optional[str]
|
||||
data_kbn: Optional[str]
|
||||
ser_no: Optional[str]
|
||||
lot_num: Optional[str]
|
||||
expr_dt: Optional[date]
|
||||
amt_fugo: Optional[str]
|
||||
8
ecs/jskult-webapp/src/model/db/hdke_tbl.py
Normal file
8
ecs/jskult-webapp/src/model/db/hdke_tbl.py
Normal file
@ -0,0 +1,8 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from src.model.db.base_db_model import BaseDBModel
|
||||
|
||||
|
||||
class HdkeTblModel(BaseDBModel):
|
||||
bch_actf: Optional[str]
|
||||
@ -0,0 +1,5 @@
|
||||
from src.model.db.base_db_model import BaseDBModel
|
||||
|
||||
|
||||
class PharmacyProductMasterModel(BaseDBModel):
|
||||
mkr_cd_nm: str
|
||||
36
ecs/jskult-webapp/src/model/db/user_master.py
Normal file
36
ecs/jskult-webapp/src/model/db/user_master.py
Normal file
@ -0,0 +1,36 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from src.model.db.base_db_model import BaseDBModel
|
||||
|
||||
|
||||
class UserMasterModel(BaseDBModel):
|
||||
user_id: Optional[str]
|
||||
mail_adr: Optional[str]
|
||||
user_nm: Optional[str]
|
||||
auth_flg1: Optional[str]
|
||||
auth_flg2: Optional[str]
|
||||
auth_flg3: Optional[str]
|
||||
auth_flg4: Optional[str]
|
||||
auth_flg5: Optional[str]
|
||||
auth_flg6: Optional[str]
|
||||
auth_flg7: Optional[str]
|
||||
auth_flg8: Optional[str]
|
||||
auth_flg9: Optional[str]
|
||||
auth_flg10: Optional[str]
|
||||
pwd: Optional[str]
|
||||
enabled_flg: Optional[str]
|
||||
creater: Optional[str]
|
||||
create_date: Optional[datetime]
|
||||
updater: Optional[str]
|
||||
update_date: Optional[datetime]
|
||||
mntuser_flg: Optional[str]
|
||||
|
||||
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'
|
||||
8
ecs/jskult-webapp/src/model/db/wholesaler_master.py
Normal file
8
ecs/jskult-webapp/src/model/db/wholesaler_master.py
Normal file
@ -0,0 +1,8 @@
|
||||
from src.model.db.base_db_model import BaseDBModel
|
||||
|
||||
|
||||
class WholesalerMasterModel(BaseDBModel):
|
||||
rec_whs_cd: str
|
||||
rec_whs_sub_cd: str
|
||||
nm: str
|
||||
whs_nm: str
|
||||
152
ecs/jskult-webapp/src/model/internal/jwt_token.py
Normal file
152
ecs/jskult-webapp/src/model/internal/jwt_token.py
Normal file
@ -0,0 +1,152 @@
|
||||
import base64
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
import jwt
|
||||
import requests
|
||||
from starlette import status
|
||||
|
||||
from src.error.exceptions import JWTTokenVerifyException
|
||||
from src.system_var import environment
|
||||
|
||||
|
||||
class JWTToken:
|
||||
id_token: str
|
||||
refresh_token: str
|
||||
verified_jwt: Optional[dict]
|
||||
|
||||
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:
|
||||
raise JWTTokenVerifyException('検証されていないトークン')
|
||||
return self.verified_jwt
|
||||
|
||||
@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:
|
||||
# 一般ユーザーログインによる(SSO経由)
|
||||
user_id = identities[0]['userId']
|
||||
# <社員番号>@<ドメイン名>となっているため、@で分割
|
||||
user_id = user_id.split('@')[0]
|
||||
else:
|
||||
# メンテユーザーによるログイン
|
||||
user_id = verified_token.get('cognito:username')
|
||||
|
||||
return user_id
|
||||
|
||||
@classmethod
|
||||
def request(cls, code: str):
|
||||
"""JWTをリクエストし、新たなインスタンスを返す
|
||||
|
||||
Args:
|
||||
code (str): セキュリティコード
|
||||
|
||||
Raises:
|
||||
JWTTokenVerifyException: 認証失敗
|
||||
|
||||
Returns:
|
||||
JWTToken: JWTTokenのモデルクラス
|
||||
"""
|
||||
token_url = f'{environment.COGNITO_AUTH_DOMAIN}/{environment.TOKEN_ENDPOINT}'
|
||||
request_params = {
|
||||
'grant_type': 'authorization_code',
|
||||
'client_id': environment.COGNITO_CLIENT_ID,
|
||||
'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 = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': f'Basic {auth_header_value}'
|
||||
}
|
||||
|
||||
res = requests.post(token_url, params=request_params, headers=request_headers)
|
||||
if res.status_code != status.HTTP_200_OK:
|
||||
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
|
||||
def refresh(cls, refresh_token: str):
|
||||
"""JWTをリフレッシュし、新たなインスタンスを返す
|
||||
|
||||
Args:
|
||||
refresh_token (str): リフレッシュトークン
|
||||
|
||||
Raises:
|
||||
JWTTokenVerifyException: 認証失敗
|
||||
|
||||
Returns:
|
||||
JWTToken: JWTTokenのモデルクラス
|
||||
"""
|
||||
token_url = f'{environment.COGNITO_AUTH_DOMAIN}/{environment.TOKEN_ENDPOINT}'
|
||||
request_params = {
|
||||
'grant_type': 'refresh_token',
|
||||
'client_id': environment.COGNITO_CLIENT_ID,
|
||||
'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 = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': f'Basic {auth_header_value}'
|
||||
}
|
||||
|
||||
res = requests.post(token_url, params=request_params, headers=request_headers)
|
||||
if res.status_code != status.HTTP_200_OK:
|
||||
raise JWTTokenVerifyException(res.text)
|
||||
|
||||
token_response = json.loads(res.text)
|
||||
return cls(id_token=token_response['id_token'], refresh_token=refresh_token)
|
||||
|
||||
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,
|
||||
signing_key.key,
|
||||
algorithms=['RS256'],
|
||||
audience=environment.COGNITO_CLIENT_ID,
|
||||
issuer=issuer,
|
||||
# Cognitoのサーバー時間とのズレにより、Issued atクレームの検証に失敗するパターンに対処する
|
||||
options={'verify_iat': False}
|
||||
)
|
||||
# 有効期限(exp)が切れた場合、トークンをリフレッシュする
|
||||
except jwt.ExpiredSignatureError:
|
||||
refreshed_jwt_token = JWTToken.refresh(self.refresh_token)
|
||||
return refreshed_jwt_token.verified_token()
|
||||
# 有効期限以外の検証に失敗した場合は例外とする
|
||||
except jwt.InvalidTokenError as e:
|
||||
raise JWTTokenVerifyException('Invalid token', e)
|
||||
# IDトークンを使用していることを検証する
|
||||
if verified_jwt['token_use'] != 'id':
|
||||
raise JWTTokenVerifyException('Invalid `token_use` claim, should be `id`.')
|
||||
|
||||
return JWTToken(
|
||||
id_token=self.id_token,
|
||||
refresh_token=self.refresh_token,
|
||||
verified_jwt=verified_jwt
|
||||
)
|
||||
53
ecs/jskult-webapp/src/model/internal/session.py
Normal file
53
ecs/jskult-webapp/src/model/internal/session.py
Normal file
@ -0,0 +1,53 @@
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
from pynamodb.attributes import NumberAttribute, UnicodeAttribute
|
||||
from pynamodb.models import Model as DynamoDBTableModel
|
||||
|
||||
from src.system_var import environment
|
||||
|
||||
|
||||
class UserSession(DynamoDBTableModel):
|
||||
class Meta:
|
||||
table_name = environment.SESSION_TABLE_NAME
|
||||
region = environment.AWS_REGION
|
||||
session_key = UnicodeAttribute(hash_key=True)
|
||||
user_id = UnicodeAttribute()
|
||||
id_token = UnicodeAttribute()
|
||||
doc_flg = UnicodeAttribute()
|
||||
inst_flg = UnicodeAttribute()
|
||||
bio_flg = UnicodeAttribute()
|
||||
master_mainte_flg = UnicodeAttribute()
|
||||
user_flg = UnicodeAttribute()
|
||||
refresh_token = UnicodeAttribute()
|
||||
csrf_token = UnicodeAttribute()
|
||||
last_access_time = NumberAttribute()
|
||||
record_expiration_time = NumberAttribute()
|
||||
|
||||
@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())
|
||||
return (last_access_time + datetime.timedelta(minutes=expire)).timestamp()
|
||||
|
||||
@classmethod
|
||||
def new(
|
||||
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,
|
||||
id_token=id_token,
|
||||
refresh_token=refresh_token,
|
||||
csrf_token=csrf_token,
|
||||
doc_flg=doc_flg,
|
||||
inst_flg=inst_flg,
|
||||
bio_flg=bio_flg,
|
||||
master_mainte_flg=master_mainte_flg,
|
||||
user_flg=user_flg,
|
||||
last_access_time=cls.new_last_access_time(),
|
||||
record_expiration_time=cls.new_record_expiration_time()
|
||||
)
|
||||
0
ecs/jskult-webapp/src/model/request/__init__.py
Normal file
0
ecs/jskult-webapp/src/model/request/__init__.py
Normal file
137
ecs/jskult-webapp/src/model/request/bio.py
Normal file
137
ecs/jskult-webapp/src/model/request/bio.py
Normal file
@ -0,0 +1,137 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Body, Form
|
||||
from pydantic import BaseModel
|
||||
|
||||
from src.util.sanitize import sanitize
|
||||
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_ymd_from: Optional[str]
|
||||
rec_ymd_to: Optional[str]
|
||||
rec_lot_num: Optional[str]
|
||||
data_kbn: Optional[str]
|
||||
maker_cd: Optional[str]
|
||||
rev_hsdnymd_srk_from: Optional[str]
|
||||
rev_hsdnymd_srk_to: Optional[str]
|
||||
ikoFlg: Optional[str]
|
||||
|
||||
@classmethod
|
||||
def as_form(
|
||||
cls,
|
||||
ctrl_wholesaler: str = Form(None),
|
||||
ctrl_org_kbn: str = Form(None),
|
||||
ctrl_rec_ymd_from: str = Form(None),
|
||||
ctrl_rec_ymd_to: str = Form(None),
|
||||
ctrl_rec_lot_num: str = Form(None),
|
||||
ctrl_data_kbn: str = Form(None),
|
||||
ctrl_maker_cd: str = Form(None),
|
||||
ctrl_rev_hsdnymd_srk_from: str = Form(None),
|
||||
ctrl_rev_hsdnymd_srk_to: str = Form(None),
|
||||
ikoFlg: str = Form(None)
|
||||
):
|
||||
|
||||
return cls.__convert_request_param(
|
||||
cls,
|
||||
ctrl_wholesaler,
|
||||
ctrl_org_kbn,
|
||||
ctrl_rec_ymd_from,
|
||||
ctrl_rec_ymd_to,
|
||||
ctrl_rec_lot_num,
|
||||
ctrl_data_kbn,
|
||||
ctrl_maker_cd,
|
||||
ctrl_rev_hsdnymd_srk_from,
|
||||
ctrl_rev_hsdnymd_srk_to,
|
||||
ikoFlg
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def as_body(
|
||||
cls,
|
||||
ctrl_wholesaler: str = Body(None),
|
||||
ctrl_org_kbn: str = Body(None),
|
||||
ctrl_rec_ymd_from: str = Body(None),
|
||||
ctrl_rec_ymd_to: str = Body(None),
|
||||
ctrl_rec_lot_num: str = Body(None),
|
||||
ctrl_data_kbn: str = Body(None),
|
||||
ctrl_maker_cd: str = Body(None),
|
||||
ctrl_rev_hsdnymd_srk_from: str = Body(None),
|
||||
ctrl_rev_hsdnymd_srk_to: str = Body(None),
|
||||
ikoFlg: str = Body(None)
|
||||
):
|
||||
|
||||
return cls.__convert_request_param(
|
||||
cls,
|
||||
ctrl_wholesaler,
|
||||
ctrl_org_kbn,
|
||||
ctrl_rec_ymd_from,
|
||||
ctrl_rec_ymd_to,
|
||||
ctrl_rec_lot_num,
|
||||
ctrl_data_kbn,
|
||||
ctrl_maker_cd,
|
||||
ctrl_rev_hsdnymd_srk_from,
|
||||
ctrl_rev_hsdnymd_srk_to,
|
||||
ikoFlg
|
||||
)
|
||||
|
||||
|
||||
def __convert_request_param(
|
||||
cls,
|
||||
ctrl_wholesaler: str,
|
||||
ctrl_org_kbn: str,
|
||||
ctrl_rec_ymd_from: str,
|
||||
ctrl_rec_ymd_to: str,
|
||||
ctrl_rec_lot_num: str,
|
||||
ctrl_data_kbn: str,
|
||||
ctrl_maker_cd: str,
|
||||
ctrl_rev_hsdnymd_srk_from: str,
|
||||
ctrl_rev_hsdnymd_srk_to: str,
|
||||
ikoFlg: str
|
||||
):
|
||||
wholesaler_code = None
|
||||
wholesaler_sub_code = None
|
||||
wholesaler_name = None
|
||||
# 卸コード・卸サブコード
|
||||
if is_not_empty(ctrl_wholesaler):
|
||||
# 卸コードは`020-01:卸名`という感じのデータで来るので、分割
|
||||
wholesaler_without_name = ctrl_wholesaler.split(':')[0]
|
||||
wholesaler_name = ctrl_wholesaler.split(':')[1]
|
||||
wholesaler_code = wholesaler_without_name.split('-')[0]
|
||||
wholesaler_sub_code = wholesaler_without_name.split('-')[1]
|
||||
|
||||
# 処理日
|
||||
rec_ymd_from = None
|
||||
rec_ymd_to = None
|
||||
if is_not_empty(ctrl_rec_ymd_from):
|
||||
rec_ymd_from = ctrl_rec_ymd_from.replace('/', '')
|
||||
if is_not_empty(ctrl_rec_ymd_to):
|
||||
rec_ymd_to = ctrl_rec_ymd_to.replace('/', '')
|
||||
|
||||
# 発伝年月日
|
||||
rev_hsdnymd_srk_from = None
|
||||
rev_hsdnymd_srk_to = None
|
||||
if is_not_empty(ctrl_rev_hsdnymd_srk_from):
|
||||
rev_hsdnymd_srk_from = ctrl_rev_hsdnymd_srk_from.replace('/', '')
|
||||
if is_not_empty(ctrl_rev_hsdnymd_srk_to):
|
||||
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_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,
|
||||
rev_hsdnymd_srk_from=rev_hsdnymd_srk_from,
|
||||
rev_hsdnymd_srk_to=rev_hsdnymd_srk_to,
|
||||
ikoFlg=ikoFlg
|
||||
)
|
||||
20
ecs/jskult-webapp/src/model/request/bio_download.py
Normal file
20
ecs/jskult-webapp/src/model/request/bio_download.py
Normal file
@ -0,0 +1,20 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Body
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class BioDownloadModel(BaseModel):
|
||||
user_id: str
|
||||
kind: str
|
||||
|
||||
@classmethod
|
||||
def as_body(
|
||||
cls,
|
||||
user_id: str = Body(),
|
||||
kind: str = Body()
|
||||
):
|
||||
return cls(
|
||||
user_id=user_id,
|
||||
kind=kind
|
||||
)
|
||||
15
ecs/jskult-webapp/src/model/request/login.py
Normal file
15
ecs/jskult-webapp/src/model/request/login.py
Normal file
@ -0,0 +1,15 @@
|
||||
from fastapi import Form
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class LoginModel(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
@classmethod
|
||||
def as_form(
|
||||
cls,
|
||||
ctrl_username: str = Form(),
|
||||
ctrl_password: str = Form()
|
||||
):
|
||||
return cls(username=ctrl_username, password=ctrl_password)
|
||||
0
ecs/jskult-webapp/src/model/view/__init__.py
Normal file
0
ecs/jskult-webapp/src/model/view/__init__.py
Normal file
19
ecs/jskult-webapp/src/model/view/bio_disp_model.py
Normal file
19
ecs/jskult-webapp/src/model/view/bio_disp_model.py
Normal file
@ -0,0 +1,19 @@
|
||||
from src.model.db.bio_sales_view import BioSalesViewModel
|
||||
from src.system_var import constants
|
||||
from src.util.sanitize import sanitize
|
||||
|
||||
|
||||
@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):
|
||||
self.ins_dt = ""
|
||||
self.ins_usr = ""
|
||||
137
ecs/jskult-webapp/src/model/view/bio_view_model.py
Normal file
137
ecs/jskult-webapp/src/model/view/bio_view_model.py
Normal file
@ -0,0 +1,137 @@
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from src.model.db.pharmacy_product_master import PharmacyProductMasterModel
|
||||
from src.model.db.wholesaler_master import WholesalerMasterModel
|
||||
from src.model.request.bio import BioModel
|
||||
from src.model.view.bio_disp_model import BisDisplayModel
|
||||
from src.system_var import environment
|
||||
|
||||
|
||||
class BioViewModel(BaseModel):
|
||||
subtitle: str = '生物由来検索一覧'
|
||||
user_id: Optional[str]
|
||||
batch_status: Optional[str]
|
||||
whs_models: list[WholesalerMasterModel]
|
||||
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}'
|
||||
for whs_model in self.whs_models
|
||||
]
|
||||
return display_names
|
||||
|
||||
def display_org_kbn(self):
|
||||
return OrderedDict(
|
||||
{
|
||||
'': '',
|
||||
'J': 'JD-NET',
|
||||
'N': 'NHI',
|
||||
'H': '手入力'
|
||||
}
|
||||
)
|
||||
|
||||
def display_data_kbn(self):
|
||||
return OrderedDict(
|
||||
{
|
||||
'' : '',
|
||||
'0': '正常',
|
||||
'1': 'ロットエラー',
|
||||
'3': 'ロット不明',
|
||||
'9': 'エラー(解消済)',
|
||||
'2': '除外'
|
||||
}
|
||||
)
|
||||
|
||||
def bio_data_json_str(self):
|
||||
def date_handler(obj):
|
||||
return obj.isoformat() if hasattr(obj, 'isoformat') else obj
|
||||
return json.dumps([model.dict() for model in self.bio_data], ensure_ascii=False, default=date_handler)
|
||||
|
||||
def is_selected_whs_name(self, selected_wholesaler):
|
||||
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}'
|
||||
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)
|
||||
|
||||
def is_input_rec_ymd_from(self):
|
||||
if not self.is_form_submitted():
|
||||
return ''
|
||||
|
||||
return self._format_date_string(self.form_data.rec_ymd_from)
|
||||
|
||||
def is_input_rec_ymd_to(self):
|
||||
if not self.is_form_submitted():
|
||||
return ''
|
||||
|
||||
return self._format_date_string(self.form_data.rec_ymd_to)
|
||||
|
||||
def is_input_lot_num(self):
|
||||
if not self.is_form_submitted():
|
||||
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)
|
||||
|
||||
def is_input_rev_hsdnymd_srk_from(self):
|
||||
if not self.is_form_submitted():
|
||||
return ''
|
||||
|
||||
return self._format_date_string(self.form_data.rev_hsdnymd_srk_from)
|
||||
|
||||
def is_input_rev_hsdnymd_srk_to(self):
|
||||
if not self.is_form_submitted():
|
||||
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 ''
|
||||
|
||||
def disabled_button(self):
|
||||
return 'disabled' if self.is_data_empty() or self.is_data_overflow_max_length() else ''
|
||||
|
||||
def is_form_submitted(self):
|
||||
return self.form_data is not None
|
||||
|
||||
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
|
||||
|
||||
def _format_date_string(self, date_string):
|
||||
if date_string is None:
|
||||
return ''
|
||||
date = datetime.strptime(date_string, '%Y%m%d')
|
||||
return date.strftime('%Y/%m/%d')
|
||||
|
||||
def _selected_value(self, form_value: str, current_value: str):
|
||||
return 'selected' if form_value == current_value else ''
|
||||
10
ecs/jskult-webapp/src/model/view/logout_view_model.py
Normal file
10
ecs/jskult-webapp/src/model/view/logout_view_model.py
Normal file
@ -0,0 +1,10 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class LogoutViewModel(BaseModel):
|
||||
subtitle: str = 'MeDaCA Logout'
|
||||
redirect_to: Optional[str]
|
||||
reason: Optional[str]
|
||||
link_text:Optional[str]
|
||||
@ -0,0 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class MainteLoginViewModel(BaseModel):
|
||||
subtitle: str = 'MeDaCA Mainte Login'
|
||||
26
ecs/jskult-webapp/src/model/view/menu_view_model.py
Normal file
26
ecs/jskult-webapp/src/model/view/menu_view_model.py
Normal file
@ -0,0 +1,26 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from src.model.view.user_view_model import UserViewModel
|
||||
|
||||
|
||||
class MenuViewModel(BaseModel):
|
||||
subtitle: str = 'MeDaCA 機能メニュー'
|
||||
batch_status: Optional[str]
|
||||
user_model: UserViewModel
|
||||
|
||||
def is_batch_processing(self):
|
||||
return self.batch_status == '1'
|
||||
|
||||
def is_available_ult_doctor_menu(self):
|
||||
return self.user_model.has_ult_doctor_permission()
|
||||
|
||||
def is_available_ult_inst_menu(self):
|
||||
return self.user_model.has_ult_inst_permission()
|
||||
|
||||
def is_available_bio_menu(self):
|
||||
return self.user_model.has_bio_permission()
|
||||
|
||||
def is_available_master_maintenance_menu(self):
|
||||
return self.user_model.has_master_maintenance_permission()
|
||||
26
ecs/jskult-webapp/src/model/view/user_view_model.py
Normal file
26
ecs/jskult-webapp/src/model/view/user_view_model.py
Normal file
@ -0,0 +1,26 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserViewModel(BaseModel):
|
||||
bio_flg: str # AUTH_FLG1
|
||||
doc_flg: str # AUTH_FLG2
|
||||
inst_flg: str # AUTH_FLG3
|
||||
master_mainte_flg: str # AUTH_FLG4
|
||||
user_flg: Optional[str] # MNTUSER_FLG
|
||||
|
||||
def has_ult_doctor_permission(self):
|
||||
return self.doc_flg == '1'
|
||||
|
||||
def has_ult_inst_permission(self):
|
||||
return self.inst_flg == '1'
|
||||
|
||||
def has_bio_permission(self):
|
||||
return self.bio_flg == '1'
|
||||
|
||||
def has_master_maintenance_permission(self):
|
||||
return self.master_mainte_flg == '1'
|
||||
|
||||
def is_maintenance_user(self):
|
||||
return self.user_flg == '1'
|
||||
0
ecs/jskult-webapp/src/repositories/__init__.py
Normal file
0
ecs/jskult-webapp/src/repositories/__init__.py
Normal file
43
ecs/jskult-webapp/src/repositories/base_repository.py
Normal file
43
ecs/jskult-webapp/src/repositories/base_repository.py
Normal file
@ -0,0 +1,43 @@
|
||||
from abc import ABCMeta
|
||||
|
||||
import pandas as pd
|
||||
from sqlalchemy import text
|
||||
|
||||
from src.db.database import Database
|
||||
from src.model.db.base_db_model import BaseDBModel
|
||||
|
||||
|
||||
class BaseRepository(metaclass=ABCMeta):
|
||||
|
||||
_database: Database
|
||||
def __init__(self, db: Database) -> None:
|
||||
self._database = db
|
||||
|
||||
def fetch_all(self) -> list[BaseDBModel]:
|
||||
"""データ全件取得メソッド"""
|
||||
pass
|
||||
|
||||
def fetch_one(self, parameter: dict) -> BaseDBModel:
|
||||
"""データ1件取得メソッド"""
|
||||
pass
|
||||
|
||||
def fetch_many(self, parameter: dict) -> list[BaseDBModel]:
|
||||
"""条件付きデータ取得メソッド"""
|
||||
pass
|
||||
|
||||
def fetch_as_data_frame(self, parameter: dict) -> pd.DataFrame:
|
||||
pass
|
||||
|
||||
|
||||
def _to_data_frame(self, query, parameter: BaseDBModel):
|
||||
params = params=parameter.dict()
|
||||
|
||||
sql_query = pd.read_sql(
|
||||
text(query),
|
||||
con=self._database.connection,
|
||||
params=params)
|
||||
df = pd.DataFrame(
|
||||
sql_query,
|
||||
index=None
|
||||
)
|
||||
return df
|
||||
116
ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py
Normal file
116
ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py
Normal file
@ -0,0 +1,116 @@
|
||||
from src.db import sql_condition as condition
|
||||
from src.db.sql_condition import SQLCondition
|
||||
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
|
||||
|
||||
|
||||
class BioSalesViewRepository(BaseRepository):
|
||||
FETCH_SQL = """\
|
||||
SELECT
|
||||
(
|
||||
CASE
|
||||
WHEN LEFT(bs.v_tran_cd, 1) = 2
|
||||
AND bs.amt >= 1 THEN CONCAT('-', bs.amt)
|
||||
ELSE bs.amt
|
||||
END
|
||||
) AS amt_fugo,
|
||||
bs.*,
|
||||
ln.ser_no,
|
||||
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
|
||||
AND bs.rec_lot_num = ln.lot_num
|
||||
WHERE
|
||||
{where_clause}
|
||||
ORDER BY
|
||||
bs.rec_whs_cd,
|
||||
bs.rec_whs_sub_cd,
|
||||
bs.rev_hsdnymd_srk,
|
||||
bs.slip_mgt_no
|
||||
ASC\
|
||||
"""
|
||||
|
||||
def fetch_many(self, parameter: BioModel) -> list[BioSalesViewModel]:
|
||||
try:
|
||||
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");
|
||||
result = self._database.execute_query(query, parameter.dict())
|
||||
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}")
|
||||
raise e
|
||||
|
||||
def fetch_as_data_frame(self, parameter: BioModel):
|
||||
try:
|
||||
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");
|
||||
# result = self._database.execute_query(query, parameter.dict())
|
||||
# models = [BioSalesViewModel(**r) for r in result]
|
||||
# error_log(date("Y/m/d H:i:s") . " [INFO] count=" . $count . "\r\n", 3, "$execLog");
|
||||
df = self._to_data_frame(query, parameter)
|
||||
return df
|
||||
except Exception as e:
|
||||
# TODO: ファイルへの書き出しはloggerでやる
|
||||
print(f"[ERROR] DB Error : Exception={e.args}")
|
||||
raise e
|
||||
|
||||
def __build_condition(self, parameter: BioModel):
|
||||
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.org_kbn):
|
||||
where_clauses.append(SQLCondition('slip_org_kbn', condition.EQ, 'org_kbn'))
|
||||
# 処理日 開始日
|
||||
if is_not_empty(parameter.rec_ymd_from):
|
||||
where_clauses.append(SQLCondition('rec_ymd', condition.GE, 'rec_ymd_from'))
|
||||
# 処理日 終了日
|
||||
if is_not_empty(parameter.rec_ymd_to):
|
||||
where_clauses.append(SQLCondition('rec_ymd', condition.LE, 'rec_ymd_to'))
|
||||
# ロット番号
|
||||
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
|
||||
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.rev_hsdnymd_srk_from):
|
||||
where_clauses.append(SQLCondition('rev_hsdnymd_srk', condition.GE, 'rev_hsdnymd_srk_from'))
|
||||
# 発伝年月日 終了日
|
||||
if is_not_empty(parameter.rev_hsdnymd_srk_to):
|
||||
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))
|
||||
# 固定条件
|
||||
# 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");
|
||||
return where_clauses_str
|
||||
18
ecs/jskult-webapp/src/repositories/hdke_tbl_repository.py
Normal file
18
ecs/jskult-webapp/src/repositories/hdke_tbl_repository.py
Normal file
@ -0,0 +1,18 @@
|
||||
from src.model.db.hdke_tbl import HdkeTblModel
|
||||
from src.model.request.bio import BioModel
|
||||
from src.repositories.base_repository import BaseRepository
|
||||
|
||||
|
||||
class HdkeTblRepository(BaseRepository):
|
||||
FETCH_SQL = "SELECT bch_actf FROM src05.hdke_tbl"
|
||||
|
||||
def fetch_all(self) -> list[HdkeTblModel]:
|
||||
try:
|
||||
query = self.FETCH_SQL
|
||||
result = self._database.execute_query(query)
|
||||
models = [HdkeTblModel(**r) for r in result]
|
||||
return models
|
||||
except Exception as e:
|
||||
# TODO: ファイルへの書き出しはloggerでやる
|
||||
print(f"[ERROR] DB Error : Exception={e.args}")
|
||||
raise e
|
||||
@ -0,0 +1,36 @@
|
||||
from src.model.db.pharmacy_product_master import PharmacyProductMasterModel
|
||||
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
|
||||
FROM
|
||||
src05.phm_prd_mst_v t1
|
||||
INNER JOIN
|
||||
(
|
||||
SELECT
|
||||
prd_cd,MAX(sub_no) 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
|
||||
WHERE
|
||||
mkr_cd IS NOT NULL
|
||||
ORDER BY mkr_cd
|
||||
"""
|
||||
|
||||
def fetch_all(self) -> list[PharmacyProductMasterModel]:
|
||||
try:
|
||||
result = self._database.execute_query(self.FETCH_SQL)
|
||||
models = [PharmacyProductMasterModel(**r) for r in result]
|
||||
return models
|
||||
except Exception as e:
|
||||
# TODO: ファイルへの書き出しはloggerでやる
|
||||
print(f"[ERROR] getOroshiData DB Error. ")
|
||||
print(f"[ERROR] ErrorMessage: {e.args}")
|
||||
raise e
|
||||
27
ecs/jskult-webapp/src/repositories/user_master_repository.py
Normal file
27
ecs/jskult-webapp/src/repositories/user_master_repository.py
Normal file
@ -0,0 +1,27 @@
|
||||
from src.model.db.user_master import UserMasterModel
|
||||
from src.model.request.bio import BioModel
|
||||
from src.repositories.base_repository import BaseRepository
|
||||
|
||||
|
||||
class UserMasterRepository(BaseRepository):
|
||||
FETCH_SQL = """\
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
src05.user_mst
|
||||
WHERE
|
||||
LOWER(user_id) = LOWER(:user_id)\
|
||||
"""
|
||||
|
||||
def fetch_one(self, parameter: dict) -> UserMasterModel:
|
||||
try:
|
||||
query = self.FETCH_SQL
|
||||
result = self._database.execute_query(query, parameter)
|
||||
models = [UserMasterModel(**r) for r in result]
|
||||
if len(models) == 0:
|
||||
return None
|
||||
return models[0]
|
||||
except Exception as e:
|
||||
# TODO: ファイルへの書き出しはloggerでやる
|
||||
print(f"[ERROR] DB Error : Exception={e.args}")
|
||||
raise e
|
||||
@ -0,0 +1,35 @@
|
||||
from src.model.db.wholesaler_master import WholesalerMasterModel
|
||||
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
|
||||
FROM src05.bio_sales b
|
||||
LEFT OUTER JOIN
|
||||
(
|
||||
SELECT sub_no, nm, 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
|
||||
AND v2.rec_sts_kbn <> '9'
|
||||
ORDER BY b.rec_whs_cd, b.rec_whs_sub_cd , b.whs_nm DESC
|
||||
"""
|
||||
|
||||
def fetch_all(self) -> list[WholesalerMasterModel]:
|
||||
try:
|
||||
result = self._database.execute_query(self.FETCH_SQL)
|
||||
result_data = [res for res in result]
|
||||
models = [WholesalerMasterModel(**r) for r in result_data]
|
||||
return models
|
||||
except Exception as e:
|
||||
# TODO: ファイルへの書き出しはloggerでやる
|
||||
print(f"[ERROR] getOroshiData DB Error. ")
|
||||
print(f"[ERROR] ErrorMessage: {e.args}")
|
||||
raise e
|
||||
0
ecs/jskult-webapp/src/router/__init__.py
Normal file
0
ecs/jskult-webapp/src/router/__init__.py
Normal file
125
ecs/jskult-webapp/src/router/session_router.py
Normal file
125
ecs/jskult-webapp/src/router/session_router.py
Normal file
@ -0,0 +1,125 @@
|
||||
import logging
|
||||
from typing import Callable
|
||||
|
||||
from fastapi import Request, Response
|
||||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.routing import APIRoute
|
||||
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.system_var import constants, environment
|
||||
|
||||
logger = logging.getLogger('uvicorn')
|
||||
|
||||
class MeDaCaRoute(APIRoute):
|
||||
"""アプリケーションのカスタムルーター
|
||||
|
||||
Args:
|
||||
APIRoute (APIRoute): FastAPIの標準APIRoute
|
||||
"""
|
||||
def get_route_handler(self) -> Callable:
|
||||
"""前後処理を付加するルートハンドラーを返す
|
||||
|
||||
Raises:
|
||||
e: HTTPException
|
||||
UnexpectedException: HTTPException以外の例外をカスタム例外にする
|
||||
|
||||
Returns:
|
||||
Callable: カスタムルートハンドラー
|
||||
"""
|
||||
original_route_handler = super().get_route_handler()
|
||||
|
||||
# 返却するルートハンドラーを定義。必ず非同期関数にする必要がある。
|
||||
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
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
raise UnexpectedException(detail=constants.LOGOUT_REASON_UNEXPECTED)
|
||||
return custom_route_handler
|
||||
|
||||
async def pre_process_route(self, request: Request) -> Request:
|
||||
"""ルートハンドラーの事前処理
|
||||
|
||||
Args:
|
||||
request (Request): FastAPIのリクエストクラス
|
||||
|
||||
Returns:
|
||||
Request: 加工後のRequestインスタンス
|
||||
"""
|
||||
return request
|
||||
async def post_process_route(self, request: Request, response: Response) -> Response:
|
||||
"""ルートハンドラーの事後処理
|
||||
|
||||
Args:
|
||||
request (Request): FastAPIのリクエストインスタンス
|
||||
response (Response): FastAPIのレスポンスインスタンス(original_route_handlerのレスポンス)
|
||||
Returns:
|
||||
Response: 加工後のResponseインスタンス
|
||||
"""
|
||||
return response
|
||||
|
||||
class BeforeCheckSessionRoute(MeDaCaRoute):
|
||||
"""事前処理として、セッションチェックを行うルートハンドラー
|
||||
|
||||
Args:
|
||||
MeDaCaRoute (MeDaCaRoute): 共通ルートハンドラー
|
||||
"""
|
||||
async def pre_process_route(self, request: Request):
|
||||
request = await super().pre_process_route(request)
|
||||
# セッションを取得
|
||||
session_key = request.cookies.get('session')
|
||||
current_session = get_current_session(session_key)
|
||||
checked_session = check_session_expired(current_session)
|
||||
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)
|
||||
scope = request.scope
|
||||
scope['session'] = verified_session
|
||||
session_request = Request(receive=request.receive, scope=scope)
|
||||
return session_request
|
||||
|
||||
class AfterSetCookieSessionRoute(MeDaCaRoute):
|
||||
"""事後処理として、セッションキーをcookieに設定するカスタムルートハンドラー
|
||||
|
||||
Args:
|
||||
MeDaCaRoute (MeDaCaRoute): 共通ルートハンドラー
|
||||
"""
|
||||
async def post_process_route(self, request: Request, response: Response):
|
||||
response = await super().post_process_route(request, response)
|
||||
session_key = response.headers.get('session_key', None)
|
||||
# セッションキーがない場合はセットせずに返す
|
||||
if session_key is None:
|
||||
return response
|
||||
|
||||
del response.headers['session_key']
|
||||
# クッキーにセッションを設定
|
||||
response.set_cookie(
|
||||
key='session',
|
||||
value=session_key,
|
||||
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
|
||||
0
ecs/jskult-webapp/src/services/__init__.py
Normal file
0
ecs/jskult-webapp/src/services/__init__.py
Normal file
13
ecs/jskult-webapp/src/services/base_service.py
Normal file
13
ecs/jskult-webapp/src/services/base_service.py
Normal file
@ -0,0 +1,13 @@
|
||||
from abc import ABCMeta
|
||||
|
||||
from src.aws.aws_api_client import AWSAPIClient
|
||||
from src.repositories.base_repository import BaseRepository
|
||||
|
||||
|
||||
class BaseService(metaclass=ABCMeta):
|
||||
# 各サービスが依存するrepositoryクラスのマップ
|
||||
REPOSITORIES: dict[str, BaseRepository] = {}
|
||||
# 各サービスが依存するAWS APIクライアントクラスのマップ
|
||||
CLIENTS: dict[str, AWSAPIClient] = {}
|
||||
def __init__(self, repositories: dict[str, BaseRepository], clients: dict[str, AWSAPIClient]) -> None:
|
||||
pass
|
||||
41
ecs/jskult-webapp/src/services/batch_status_service.py
Normal file
41
ecs/jskult-webapp/src/services/batch_status_service.py
Normal file
@ -0,0 +1,41 @@
|
||||
from src.aws.aws_api_client import AWSAPIClient
|
||||
from src.error.exceptions import DBException
|
||||
from src.model.db.hdke_tbl import HdkeTblModel
|
||||
from src.repositories.base_repository import BaseRepository
|
||||
from src.repositories.hdke_tbl_repository import HdkeTblRepository
|
||||
from src.services.base_service import BaseService
|
||||
|
||||
|
||||
class BatchStatusService(BaseService):
|
||||
|
||||
REPOSITORIES = {
|
||||
'hdke_table_repository': HdkeTblRepository
|
||||
}
|
||||
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']
|
||||
# サービスインスタンス生成時に日付テーブルを取得する。取得できない場合は例外とする
|
||||
try:
|
||||
self.__hdke_table_record = self.hdke_table_repository.fetch_all()
|
||||
except Exception as e:
|
||||
raise DBException(e)
|
||||
|
||||
@property
|
||||
def hdke_table_record(self) -> HdkeTblModel:
|
||||
# 日付マスタのレコードがあることを確認
|
||||
self.__assert_record_exists()
|
||||
# 日付テーブルのレコードは必ず1件
|
||||
return self.__hdke_table_record[0]
|
||||
|
||||
def is_batch_processing(self):
|
||||
# 日付マスタのレコードがあることを確認
|
||||
self.__assert_record_exists()
|
||||
return self.hdke_table_record.bch_actf == '1' # TODO: 定数化する
|
||||
|
||||
def __assert_record_exists(self):
|
||||
# 日付マスタのレコードがない場合は例外とする
|
||||
if len(self.__hdke_table_record) == 0:
|
||||
raise DBException('日付テーブルのレコードが存在しません')
|
||||
|
||||
119
ecs/jskult-webapp/src/services/bio_view_service.py
Normal file
119
ecs/jskult-webapp/src/services/bio_view_service.py
Normal file
@ -0,0 +1,119 @@
|
||||
import os.path as path
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from src.aws.aws_api_client import AWSAPIClient
|
||||
from src.aws.s3 import S3Client
|
||||
from src.model.internal.session import UserSession
|
||||
from src.model.request.bio import BioModel
|
||||
from src.model.view.bio_disp_model import BisDisplayModel
|
||||
from src.model.view.bio_view_model import BioViewModel
|
||||
from src.repositories.base_repository import BaseRepository
|
||||
from src.repositories.bio_sales_view_repository import BioSalesViewRepository
|
||||
from src.repositories.pharmacy_product_master_repository import \
|
||||
PharmacyProductMasterRepository
|
||||
from src.repositories.wholesaler_master_repository import \
|
||||
WholesalerMasterRepository
|
||||
from src.services.base_service import BaseService
|
||||
from src.system_var import constants, environment
|
||||
|
||||
|
||||
class BioViewService(BaseService):
|
||||
REPOSITORIES = {
|
||||
'whs_repository': WholesalerMasterRepository,
|
||||
'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']
|
||||
self.phm_repository = repositories['phm_repository']
|
||||
self.bio_sales_repository = repositories['bio_sales_repository']
|
||||
self.s3_client = clients['s3_client']
|
||||
|
||||
def prepare_bio_view(
|
||||
self,
|
||||
session: UserSession
|
||||
) ->BioViewModel:
|
||||
# 卸リストを取得
|
||||
wholesalers = self.whs_repository.fetch_all()
|
||||
# 製品リストを取得
|
||||
products = self.phm_repository.fetch_all()
|
||||
bio = BioViewModel(
|
||||
whs_models=wholesalers,
|
||||
phm_models=products,
|
||||
user_id=session.user_id
|
||||
)
|
||||
return bio
|
||||
|
||||
def search_bio_data(self, search_params: BioModel):
|
||||
# 生物由来データを検索
|
||||
bio_sales_view_data = self.bio_sales_repository.fetch_many(parameter=search_params)
|
||||
# 画面表示用に加工
|
||||
display_bio_data: list[BisDisplayModel] = [BisDisplayModel(data) for data in bio_sales_view_data]
|
||||
|
||||
return display_bio_data
|
||||
|
||||
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
|
||||
|
||||
def write_excel_file(self, data_frame: pd.DataFrame, user_id: str, timestamp: datetime):
|
||||
# Excelに書き込み
|
||||
output_file_path = path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, f'Result_{user_id.upper()}_{timestamp:%Y%m%d%H%M%S%f}.xlsx')
|
||||
|
||||
# テンプレートファイルをコピーして出力ファイルの枠だけを作る
|
||||
shutil.copyfile(
|
||||
src=constants.BIO_EXCEL_TEMPLATE_FILE_PATH,
|
||||
dst=output_file_path
|
||||
)
|
||||
# ExcelWriterの追記モード(`mode`='a')でファイルを開く
|
||||
# `engine``='openpyxlは、追記モードでExcelを開くためのおまじない(xlsxしか動作しないが、こちらが出すものなので問題ナシ)
|
||||
# 既存シートへの書き込みは、`if_sheet_exists='overlay'を指定する
|
||||
with pd.ExcelWriter(output_file_path, engine='openpyxl', mode='a', if_sheet_exists='overlay') as writer:
|
||||
# `sheet_name`引数を省略した場合は、「Sheet1」に書き込む。
|
||||
# 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):
|
||||
# csvに書き込み
|
||||
output_file_path = path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, f'Result_{user_id.upper()}_{timestamp:%Y%m%d%H%M%S%f}.csv')
|
||||
# 横長のDataFrameとするため、ヘッダーの加工処理
|
||||
header_data = {}
|
||||
for df_column, header_column in zip(data_frame.columns, header):
|
||||
header_data[df_column] = header_column
|
||||
|
||||
header_df = pd.DataFrame([header_data], index=None)
|
||||
output_df = pd.concat([header_df, data_frame])
|
||||
# ヘッダー行としてではなく、1レコードとして出力する
|
||||
output_df.to_csv(output_file_path, index=False, header=False)
|
||||
|
||||
return output_file_path
|
||||
|
||||
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)}'
|
||||
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:
|
||||
bucket_name = environment.BIO_ACCESS_LOG_BUCKET
|
||||
# TODO: フォルダを変える
|
||||
file_key = f'bio/{path.basename(local_file_path)}'
|
||||
download_filename = f'{user_id.upper()}_生物由来卸販売データ.{kind}'
|
||||
return self.s3_client.generate_presigned_url(bucket_name, file_key, download_filename)
|
||||
57
ecs/jskult-webapp/src/services/login_service.py
Normal file
57
ecs/jskult-webapp/src/services/login_service.py
Normal file
@ -0,0 +1,57 @@
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
|
||||
from src.aws.aws_api_client import AWSAPIClient
|
||||
from src.aws.cognito import CognitoClient
|
||||
from src.error.exceptions import NotAuthorizeException
|
||||
from src.model.db.user_master import UserMasterModel
|
||||
from src.model.internal.jwt_token import JWTToken
|
||||
from src.repositories.base_repository import BaseRepository
|
||||
from src.repositories.user_master_repository import UserMasterRepository
|
||||
from src.services.base_service import BaseService
|
||||
from src.system_var import environment
|
||||
|
||||
|
||||
class LoginService(BaseService):
|
||||
REPOSITORIES = {
|
||||
'user_repository': UserMasterRepository
|
||||
}
|
||||
|
||||
CLIENTS = {
|
||||
'cognito_client': CognitoClient
|
||||
}
|
||||
|
||||
def __init__(self, repositories: dict[str, BaseRepository], clients: dict[str, AWSAPIClient]) -> None:
|
||||
super().__init__(repositories, clients)
|
||||
self.user_repository = repositories['user_repository']
|
||||
self.cognito_client = clients['cognito_client']
|
||||
|
||||
def login(self, username: str, password: str) -> JWTToken:
|
||||
try:
|
||||
id_token, refresh_token = self.cognito_client.login_by_user_password_flow(
|
||||
username,
|
||||
password,
|
||||
self.__secret_hash(username)
|
||||
)
|
||||
except Exception as e:
|
||||
if e.response['Error']['Code'] == 'NotAuthorizedException':
|
||||
raise NotAuthorizeException(e)
|
||||
else:
|
||||
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
|
||||
|
||||
def __secret_hash(self, username: str):
|
||||
# see - https://aws.amazon.com/jp/premiumsupport/knowledge-center/cognito-unable-to-verify-secret-hash/ # noqa
|
||||
message = bytes(username + environment.COGNITO_CLIENT_ID, 'utf-8')
|
||||
key = bytes(environment.COGNITO_CLIENT_SECRET, 'utf-8')
|
||||
digest = hmac.new(key, message, digestmod=hashlib.sha256).digest()
|
||||
return base64.b64encode(digest).decode()
|
||||
15
ecs/jskult-webapp/src/services/session_service.py
Normal file
15
ecs/jskult-webapp/src/services/session_service.py
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
from src.model.internal.session import UserSession
|
||||
|
||||
|
||||
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)
|
||||
return session
|
||||
except UserSession.DoesNotExist as e:
|
||||
print(e)
|
||||
return None
|
||||
0
ecs/jskult-webapp/src/static/__init__.py
Normal file
0
ecs/jskult-webapp/src/static/__init__.py
Normal file
291
ecs/jskult-webapp/src/static/css/bioStyle.css
Normal file
291
ecs/jskult-webapp/src/static/css/bioStyle.css
Normal file
@ -0,0 +1,291 @@
|
||||
body {
|
||||
white-space: nowrap;
|
||||
background-color: LightCyan;
|
||||
font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", "メイリオ", Meiryo, Osaka, "MS Pゴシック", "MS PGothic", sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 155%;
|
||||
margin-left: 2%;
|
||||
margin-top: 0%;
|
||||
margin-bottom: 0%;
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
table{
|
||||
border-collapse : collapse;
|
||||
}
|
||||
|
||||
.search_table {
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: solid 1px gray;
|
||||
width: 1132px;
|
||||
}
|
||||
|
||||
._form {
|
||||
width: 1132px;
|
||||
margin-left: 10px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.back_bt {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
._form input[type=text] {
|
||||
width: 193px;
|
||||
height: 25px;
|
||||
}
|
||||
._form input[type=checkbox] {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
._form select {
|
||||
width: 193px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.result_info {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
.search_tb {
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.search_bt {
|
||||
/* width: 60px; */
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.clear_bt{
|
||||
margin-left: 120px;
|
||||
/* width: 60px */
|
||||
}
|
||||
|
||||
.search_dropdown {
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
.bioScroll_div {
|
||||
overflow: auto;
|
||||
padding-top: 10px;
|
||||
height: 250px;
|
||||
width: 1132px;
|
||||
}
|
||||
|
||||
.noLine{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.resultAreaMsg {
|
||||
margin-top: 5%;
|
||||
text-align: center;
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
.search_btTd {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.selection {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#page-1 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search_middleTd {
|
||||
padding-right: 25px;
|
||||
width : 450px;
|
||||
}
|
||||
|
||||
.docSearchScroll_div {
|
||||
overflow: auto;
|
||||
height: 200px;
|
||||
width: 1132px;
|
||||
}
|
||||
|
||||
.transition{
|
||||
text-align: right;
|
||||
margin-right: 60px;
|
||||
}
|
||||
|
||||
.transition_bt{
|
||||
width: 110px;
|
||||
height: 40px;
|
||||
margin-left: 15px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.instutionInfo_table{
|
||||
width: 1132px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.institution_column {
|
||||
width : 160px;
|
||||
background : rgb(225, 233, 250);
|
||||
border : solid 1px;
|
||||
}
|
||||
|
||||
.institution_data {
|
||||
background : rgb(244, 244, 244);
|
||||
border : solid 1px;
|
||||
padding-left : 0.5em;
|
||||
padding-right : 0.5em;
|
||||
}
|
||||
|
||||
.data_width_long {
|
||||
width : 500px;
|
||||
}
|
||||
|
||||
.data_width_middle {
|
||||
width : 300px;
|
||||
}
|
||||
|
||||
.data_width_short {
|
||||
width : 100px;
|
||||
}
|
||||
|
||||
.checkbox_margin {
|
||||
margin-left : 20px;
|
||||
}
|
||||
|
||||
.border_top_none {
|
||||
border-top-style:none;
|
||||
}
|
||||
|
||||
.border_bottom_none {
|
||||
border-bottom-style:none;
|
||||
}
|
||||
|
||||
.textbox_margin {
|
||||
margin-left : 20px;
|
||||
}
|
||||
|
||||
.textbox_margin_short {
|
||||
margin-left : 5px;
|
||||
}
|
||||
|
||||
.label_margin {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.trt_course{
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.small_tb{
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.docBelongScroll_div {
|
||||
overflow: auto;
|
||||
height: 100px;
|
||||
width: 500px;
|
||||
margin: 0px 30px 0px 30px;
|
||||
}
|
||||
|
||||
.rightPadding_table{
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.verticalBar_td{
|
||||
width: 1px;
|
||||
height: 150px;
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
.docPlaceScroll_div {
|
||||
overflow: auto;
|
||||
height: 150px;
|
||||
width: 700px;
|
||||
margin: 0px 30px 0px 30px;
|
||||
}
|
||||
|
||||
.result_tr{
|
||||
overflow-y: scroll;
|
||||
overflow-x: scroll;
|
||||
|
||||
}
|
||||
|
||||
.result_data{
|
||||
overflow-y: scroll;
|
||||
overflow-x: scroll;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
/* tablesoter */
|
||||
table.tablesorter {
|
||||
font-family:arial;
|
||||
background-color: #CDCDCD;
|
||||
font-size: 12pt;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.tablesorter thead tr th, table.tablesorter tfoot tr th {
|
||||
background-color: #e6EEEE;
|
||||
border: 0.1px solid silver;
|
||||
font-size: 12pt;
|
||||
padding: 4px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
table.tablesorter thead tr .header {
|
||||
background-image: url(bg.gif);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center right;
|
||||
cursor: pointer;
|
||||
}
|
||||
table.tablesorter tbody td {
|
||||
color: #3D3D3D;
|
||||
padding: 4px;
|
||||
background-color: #FFF;
|
||||
border: 0.1px solid silver;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.tablesorter tbody td div{
|
||||
float: right;
|
||||
}
|
||||
table.tablesorter tbody tr.odd td {
|
||||
background-color:#F0F0F6;
|
||||
}
|
||||
table.tablesorter thead tr .headerSortUp {
|
||||
background-image: url(asc.gif);
|
||||
}
|
||||
table.tablesorter thead tr .headerSortDown {
|
||||
background-image: url(desc.gif);
|
||||
}
|
||||
table.tablesorter thead tr .headerSortDown, table.tablesorter thead tr .headerSortUp {
|
||||
background-color: #8dbdd8;
|
||||
}
|
||||
|
||||
#loading {
|
||||
z-index: 10000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #FFF;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
outline: 0;
|
||||
text-align: center;
|
||||
display: none;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
#loading_content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
11
ecs/jskult-webapp/src/static/css/datepicker.css
Normal file
11
ecs/jskult-webapp/src/static/css/datepicker.css
Normal file
@ -0,0 +1,11 @@
|
||||
/* 日曜日:赤 */
|
||||
.flatpickr-calendar .flatpickr-innerContainer .flatpickr-weekdays .flatpickr-weekday:nth-child(7n + 1),
|
||||
.flatpickr-calendar .flatpickr-innerContainer .flatpickr-days .flatpickr-day:not(.flatpickr-disabled):not(.prevMonthDay):not(.nextMonthDay):nth-child(7n + 1) {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* 土曜日:青 */
|
||||
.flatpickr-calendar .flatpickr-innerContainer .flatpickr-weekdays .flatpickr-weekday:nth-child(7),
|
||||
.flatpickr-calendar .flatpickr-innerContainer .flatpickr-days .flatpickr-day:not(.flatpickr-disabled):not(.prevMonthDay):not(.nextMonthDay):nth-child(7n) {
|
||||
color: blue;
|
||||
}
|
||||
49
ecs/jskult-webapp/src/static/css/menuStyle.css
Normal file
49
ecs/jskult-webapp/src/static/css/menuStyle.css
Normal file
@ -0,0 +1,49 @@
|
||||
body{
|
||||
background-color: LightCyan;
|
||||
background-size: 220%,220%;
|
||||
font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", "メイリオ", Meiryo, Osaka, "MS Pゴシック", "MS PGothic", sans-serif;
|
||||
}
|
||||
|
||||
.background{
|
||||
margin-top: 5%;
|
||||
padding: 2%;
|
||||
background-color: white;
|
||||
width: 40%;
|
||||
border-radius: 25px;
|
||||
box-shadow:5px 5px rgba(0,0,0,0.4);;
|
||||
}
|
||||
|
||||
.btn_width {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.form_login{
|
||||
width: 80%;
|
||||
font-size: 180%;
|
||||
margin: 1%;
|
||||
}
|
||||
|
||||
.form_login::-webkit-input-placeholder{
|
||||
color: gray;
|
||||
}
|
||||
.form_login:-ms-input-placeholder{
|
||||
color: gray;
|
||||
}
|
||||
.form_login::-moz-placeholder{
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.logout_p{
|
||||
font-size: 160%;
|
||||
}
|
||||
|
||||
.notUseBioMsg{
|
||||
font-size: 143%;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.batchMsg{
|
||||
color: red;
|
||||
font-size: 120%;
|
||||
text-align: center;
|
||||
}
|
||||
65
ecs/jskult-webapp/src/static/css/pagenation.css
Normal file
65
ecs/jskult-webapp/src/static/css/pagenation.css
Normal file
@ -0,0 +1,65 @@
|
||||
.pagination {
|
||||
position: relative;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
/* .paginationjs {
|
||||
width: 100%;
|
||||
} */
|
||||
|
||||
.paginationjs > .paginationjs-nav.J-paginationjs-nav{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
div.paginationjs-pages ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.paginationjs-pages > ul > li > a {
|
||||
padding: 6px 18px;
|
||||
color: white;
|
||||
background-color: gainsboro;
|
||||
border: 1px solid;
|
||||
}
|
||||
.paginationjs-pages > ul > li > a:hover {
|
||||
color: black;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
.paginationjs-pages > ul > li.active > a {
|
||||
color: white;
|
||||
background-color: gray;
|
||||
}
|
||||
.paginationjs-pages > ul > li.active > a:hover {
|
||||
color: white;
|
||||
background-color: gray;
|
||||
cursor: text;
|
||||
}
|
||||
.paginationjs-pages > ul > li.disabled > a {
|
||||
color: white;
|
||||
background-color: gray;
|
||||
cursor: text;
|
||||
}
|
||||
.paginationjs-pages > ul > li.disabled > a:hover {
|
||||
color: white;
|
||||
background-color: gray;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.paginationjs-page {
|
||||
margin: 0 4px;
|
||||
}
|
||||
.paginationjs-pages > ul {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
.paginationjs-pages > ul > li.paginationjs-ellipsis.disabled > a {
|
||||
border: none;
|
||||
background-color: LightCyan;
|
||||
color: black;
|
||||
margin: 0 4px;
|
||||
padding: 0;
|
||||
}
|
||||
213
ecs/jskult-webapp/src/static/function/businessLogicScript.js
Normal file
213
ecs/jskult-webapp/src/static/function/businessLogicScript.js
Normal file
@ -0,0 +1,213 @@
|
||||
// 検索フォーム
|
||||
|
||||
// 戻るボタンの関数
|
||||
// 機能概要:メニュー画面に遷移する
|
||||
function backToMenu(){
|
||||
location.href = "/menu";
|
||||
}
|
||||
|
||||
// クリアボタンの関数
|
||||
// 利用条件1:form名がsearch
|
||||
// 利用条件2:form入力値名がctrl_で始まる
|
||||
function clr() {
|
||||
const formElement = document.search
|
||||
const formInputElements = Array.from(formElement.elements)
|
||||
for (const formInput of formInputElements) {
|
||||
if (formInput.name.startsWith('ctrl_')) {
|
||||
formInput.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
// 検索ボタンを再度非活性にする
|
||||
formBtDisabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* ボタンの活性非活性関数
|
||||
* @param {*} buttonId ボタンのID デフォルトは`search_bt`
|
||||
* @param {*} formId フォームのname デフォルトは`search`
|
||||
* @param {*} all 全インプットが入力されている場合に活性化するかどうか
|
||||
*/
|
||||
function formBtDisabled(buttonId='search_bt', formId='search', all=false) {
|
||||
const formElement = document[formId]
|
||||
const formInputElements = Array.from(formElement.elements)
|
||||
// 検査対象を
|
||||
const checkTargetValueLength = formInputElements
|
||||
.filter((elem) => elem.name.startsWith('ctrl_'))
|
||||
.map((elem) => elem.value.length)
|
||||
|
||||
// 活性、非活性の判断
|
||||
let validFlg = false;
|
||||
if (all) {
|
||||
validFlg = checkTargetValueLength.every((num) => num !== 0)
|
||||
} else {
|
||||
validFlg = checkTargetValueLength.some((num) => num !== 0)
|
||||
}
|
||||
|
||||
if (validFlg == true) {
|
||||
$(`#${buttonId}`).removeAttr('disabled');
|
||||
// クリアボタンはあれば活性化する
|
||||
if ($('#clear')) {
|
||||
$('#clear').removeAttr('disabled');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$(`#${buttonId}`).attr('disabled', 'disabled');
|
||||
// クリアボタンはあれば非活性化する
|
||||
if ($('#clear')) {
|
||||
$('#clear').attr('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ドロップダウンリスト再選択の関数
|
||||
// 引数:ドロップダウンリストのID
|
||||
// 引数:選択したいリスト選択表示文字列
|
||||
function selectDropDowList(id, selectedName){
|
||||
// 以前の検索条件再読み込み
|
||||
var select = document.getElementById(id);
|
||||
select = select.children[0];
|
||||
var options = select.options;
|
||||
if (options != null)
|
||||
{
|
||||
for(var i = 0; i < options.length; i++){
|
||||
if(options[i].text === selectedName){
|
||||
options[i].selected = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DatePickerを設定
|
||||
*/
|
||||
function enableDatePicker() {
|
||||
// カレンダーの表示を日曜日始まりに変更
|
||||
flatpickr.l10ns.ja.firstDayOfWeek = 0;
|
||||
$('.date_picker').flatpickr(
|
||||
{
|
||||
locale: 'ja', // 日本語カレンダー
|
||||
allowInput: true, // 入力可能にする
|
||||
dateFormat: 'Y/m/d' // 日付のフォーマットを修正
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 日付入力チェック
|
||||
// 引数:チェックするテキストボックスNo
|
||||
function autoModifyDate($this){
|
||||
// 日付フォーマットチェック
|
||||
|
||||
if($this.value === "" ||
|
||||
(!$this.value.match(/^\d{4}\/\d{2}\/\d{2}$/) && !$this.value.match(/^\d{4}\d{2}\d{2}$/)))
|
||||
{
|
||||
$this.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type { string }*/
|
||||
let strFormat = $this.value;;
|
||||
// yyyyMMddの場合→yyyy/MM/dd
|
||||
const datePatternMatches = strFormat.match(/^(\d{4})(\d{2})(\d{2})$/);
|
||||
if (datePatternMatches){
|
||||
strFormat = `${datePatternMatches[1]}/${datePatternMatches[2]}/${datePatternMatches[3]}`;
|
||||
}
|
||||
// yyyy/00/00~yyyy/00/00の場合→yyyy/01/01~yyyy/12/31
|
||||
// yyyy/MM/00~yyyy/MM/01の場合→yyyy/MM/01~yyyy/MM/末日
|
||||
// 開始日の場合
|
||||
if ($this.name.includes('from')){
|
||||
strFormat = strFormat.replace("00/00", "01/01");
|
||||
strFormat = strFormat.replace("00", "01");
|
||||
}
|
||||
// 終了日の場合
|
||||
else if ($this.name.includes('to')){
|
||||
strFormat = strFormat.replace("00/00", "12/31");
|
||||
const date = new Date(strFormat.slice(0, 4), strFormat.slice(5, 7), 0).getDate();
|
||||
strFormat = strFormat.replace("00", date.toString());
|
||||
}
|
||||
$this.value = strFormat;
|
||||
}
|
||||
|
||||
// 他のページで共通化しよう
|
||||
// ページが読み込まれたときにsendクラスのボタンを押せないようにする
|
||||
// 初期値をdisabledにしときゃいい
|
||||
$(function(){
|
||||
$(".send").prop('disabled',true);
|
||||
});
|
||||
|
||||
// チェックボックス全選択関数
|
||||
// 条件:チェックボックスのクラス名に"selectedページ数"というのがついていること
|
||||
// 条件:ボタンにクラス名 send がついていること
|
||||
function allOn(){
|
||||
var selected = ".selected" + tableCurrentPage + " input.checkbox";
|
||||
$(selected).prop("checked", true);
|
||||
$(".send").prop('disabled',false);
|
||||
}
|
||||
|
||||
// チェックボックス全解除関数
|
||||
// 条件:チェックボックスのクラス名に"selectedページ数"というのがついていること
|
||||
// 条件:ボタンにクラス名 send がついていること
|
||||
function allOff(pageCount){
|
||||
for (var i = 1; i <= pageCount; i++) {
|
||||
var selected = ".selected" + i + " input.checkbox";
|
||||
$(selected).prop("checked", false);
|
||||
}
|
||||
$(".send").prop('disabled',true);
|
||||
}
|
||||
|
||||
// 検索結果のところのボタンをチェックが1個でも付いたら押せるようにして、チェックがなければ押せないようにする関数
|
||||
// 条件:チェックボックスのクラス名に"selectedページ数"というのがついていること
|
||||
// 条件:ボタンにクラス名 send がついていること
|
||||
function resultBtDisablead(){
|
||||
var selected = ".selected" + tableCurrentPage;
|
||||
var cnt1 = $(selected + ' :checked').length;
|
||||
selected += " input.checkbox";
|
||||
|
||||
if(cnt1 == 0) {
|
||||
$(".send").prop('disabled',true);
|
||||
}
|
||||
else {
|
||||
$(".send").prop('disabled',false);
|
||||
}
|
||||
}
|
||||
|
||||
// 前のスペースを許さない入力チェック
|
||||
function checkSpaceForm($this)
|
||||
{
|
||||
var str=$this.value;
|
||||
|
||||
while(str.match(/(^\s+)/g))
|
||||
{
|
||||
str=str.replace(/(^\s+)/g, "");
|
||||
}
|
||||
|
||||
$this.value=str;
|
||||
}
|
||||
|
||||
// あいまい検索用に半角の英数字と%以外を許さない入力チェック
|
||||
function checkAimaiSearhForm($this)
|
||||
{
|
||||
var str=$this.value;
|
||||
while(str.match(/[^A-Z^a-z^0-9\%]/))
|
||||
{
|
||||
str=str.replace(/[^A-Z^a-z^0-9\%]/,"");
|
||||
}
|
||||
$this.value=str;
|
||||
}
|
||||
|
||||
// 大文字小文字-以外を許さない入力チェック
|
||||
function checkPassForm($this)
|
||||
{
|
||||
var str=$this.value;
|
||||
while(str.match(/[^ -\~]/))
|
||||
{
|
||||
str=str.replace(/[^ -\~]/,"");
|
||||
}
|
||||
$this.value=str;
|
||||
}
|
||||
|
||||
// 廃止予定
|
||||
function DisplayErrorDialog(strMesssage) {
|
||||
$("#errorTxt").html(strMesssage);
|
||||
$("#error").dialog("open");
|
||||
}
|
||||
BIN
ecs/jskult-webapp/src/static/img/icon_modal_confirm.png
Normal file
BIN
ecs/jskult-webapp/src/static/img/icon_modal_confirm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
ecs/jskult-webapp/src/static/img/icon_modal_error.png
Normal file
BIN
ecs/jskult-webapp/src/static/img/icon_modal_error.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
165
ecs/jskult-webapp/src/static/lib/fixed_midashi.js
Normal file
165
ecs/jskult-webapp/src/static/lib/fixed_midashi.js
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* FixedMidashi JavaScript Library, version 1.11 (2018/12/03)
|
||||
* http://hp.vector.co.jp/authors/VA056612/fixed_midashi/
|
||||
* Copyright (C) 2012-2018 K.Koiso
|
||||
*/
|
||||
|
||||
/* Exsample */
|
||||
/*******************************************************************
|
||||
<script type="text/javascript" src="hoge/fixed_midashi.js"></script>
|
||||
|
||||
<!-- body mode -->
|
||||
<body onLoad="FixedMidashi.create();">
|
||||
<table _fixedhead="rows:1; cols:1">
|
||||
...
|
||||
|
||||
<!-- div mode -->
|
||||
<style type="text/css" media="screen">
|
||||
div.scroll_div { overflow: auto; }
|
||||
</style>
|
||||
<body onLoad="FixedMidashi.create();">
|
||||
<div class="scroll_div">
|
||||
<table _fixedhead="rows:1; cols:1">
|
||||
...
|
||||
*******************************************************************/
|
||||
|
||||
var FixedMidashi = new function()
|
||||
{
|
||||
|
||||
var DISABLED = false;
|
||||
|
||||
var ROWS = 1;
|
||||
var COLS = 0;
|
||||
var DIV_FULL_MODE = false;
|
||||
var DIV_AUTO_SIZE = "both"; // both | width | height | none
|
||||
var COPY_ID = true;
|
||||
var BORDER_COLOR = null;
|
||||
var BORDER_STYLE = null;
|
||||
var BORDER_WIDTH = null;
|
||||
var BOX_SHADOW = null;
|
||||
|
||||
var DIV_MIN_WIDTH = 150;
|
||||
var DIV_MIN_HEIGHT = 150;
|
||||
|
||||
var DIV_BODY_SCROLL = 1;
|
||||
|
||||
var RADIO_PREFIX = "_FIXED_HEADER_";
|
||||
|
||||
var POS_FIXED = 1;
|
||||
var POS_ABSOLUTE = 2;
|
||||
var POS_MIX = 3;
|
||||
var _positionMode = -1;
|
||||
|
||||
var h="checkbox",f="select-multiple",e="select-one",g="change",m="radio",l="INPUT",cc="fixed",bc="transparent",ac="0px",Zb="absolute",Yb="none",Xb="hidden",Wb="auto",Vb="",I="class",Ub="THEAD",Tb="scroll",n="resize",Sb=-1,Rb=null,Qb=true,Pb="both",Ob=false,Nb=this;
|
||||
|
||||
var TIMER_WATCH_TABLESIZE=3e3,TID_HEADER="H",TID_NUMBER="N",TID_CORNER="C",PX="px",HEIGHT_MARGIN=10,MIN_SIZE=1,_isIE=Ob,_IEver=0,_isIE11=Ob,_isFirefox=Ob,_isOpera=Ob,_isSafari=Ob,_isChrome=Ob,_isMobile=Ob,_isBackCompat=Ob,_fixedHeaders=Rb,_fixedList=Rb,_body=Rb,_resizeTimerId=Rb,_execFlag=Ob,_IE_retryCount=0;
|
||||
|
||||
Nb.create=function(){var f="_fixedhead";if(DISABLED)return Sb;if(!document.body.getBoundingClientRect)return -2;if(!window.addEventListener&&!window.attachEvent)return -3;var l=(new Date).getTime(),h=_fixedHeaders==Rb;if(!h)for(var b=0;b<_fixedHeaders.length;b++)_fixedHeaders[b].removeAllTables(Ob);for(var i=document.body.getElementsByTagName("TABLE"),e=[],b=0;b<i.length;b++){var c=i[b],d=c.getAttribute(f);if(d==Rb)d=c._fixedhead;if(d==undefined)continue;if(c.rows.length==0)continue;e.push(c)}if(e.length==0)return -4;var g=Rb;if(h){var a=navigator.userAgent.toLowerCase();_isIE=a.indexOf("msie")>=0;if(_isIE){var j=a.indexOf("msie"),m=a.indexOf(";",j);_IEver=Number(a.substring(j+5,m))}if(!_isIE)_isIE11=a.indexOf("trident")>=0;_isFirefox=a.indexOf("firefox")>=0;_isOpera=a.indexOf("opera")>=0;_isSafari=a.indexOf("safari")>=0&&a.indexOf("chrome")<0;_isChrome=a.indexOf("chrome")>=0;_isMobile=a.indexOf("mobile")>=0;if(_isIE&&_rect(i[0]).right>=1e4&&_IE_retryCount<10){setTimeout(FixedMidashi.create,10);_IE_retryCount++;return -5}_isBackCompat=document.compatMode=="BackCompat";_body=_isBackCompat?document.body:document.documentElement;if(_isIE&&(_IEver<=7||_IEver<=9&&_isBackCompat))_positionMode=POS_ABSOLUTE;else if(_positionMode==Sb){_positionMode=POS_MIX;if(_isMobile)_positionMode=POS_FIXED}if(_isIE&&_IEver==8&&!_isBackCompat)_fixedList=new _FixedElementList;if(_isFirefox)g=_createObjectForFirefox();_isChrome&&_createObjectForChrome();_addEventListener(window,n,_onBodyResize);_isMobile&&_addEventListener(window,"orientationchange",_onBodyResize);(_isFirefox||_isSafari)&&!_isMobile&&TIMER_WATCH_TABLESIZE>=0&&setInterval(_checkZoom,TIMER_WATCH_TABLESIZE);_addEventListener(window,Tb,_onBodyScroll)}_fixedHeaders=[];for(var b=0;b<e.length;b++){var c=e[b],d=c.getAttribute(f);if(d==Rb)d=c._fixedhead;var k=_createFixedHeader(c,d,e.length);_fixedHeaders.push(k)}_execute("init");h&&_createCSS("print",".fixed_header_display_none_at_print { display: none; visibility: hidden; }");g!=Rb&&g.parentNode.removeChild(g);return (new Date).getTime()-l};
|
||||
function _checkZoom(){if(_fixedHeaders==Rb)return;for(var a=0;a<_fixedHeaders.length;a++)if(_fixedHeaders[a].checkZoom()){_execute(n);break}}
|
||||
Nb.remove=function(){if(_fixedHeaders==Rb)return;for(var a=0;a<_fixedHeaders.length;a++)_fixedHeaders[a].removeAllTables(Qb);_fixedHeaders=[]};
|
||||
Nb.syncValue=function(a){if(_fixedHeaders==Rb)return;if(!a)return;_copyValues(a)};
|
||||
Nb.syncStyle=function(a,d){if(_fixedHeaders==Rb)return;if(!a)return;if(_fixedList!=Rb){var e=_fixedList.getAll(a);if(e==Rb)return;for(var b=0;b<e.length;b++)_copyStyle(a,e[b],d);return}var c=a.$FXH_FIXED_ELEMENT;if(c==undefined)return;if(!c.$IS_ARRAY)_copyStyle(a,c,d);else for(var b=0;b<c.length;b++)_copyStyle(a,c[b],d)};
|
||||
Nb.getFixedElement=function(b){if(_fixedHeaders==Rb)return Rb;if(!b)return Rb;if(_fixedList!=Rb)return _fixedList.get(b);var a=b.$FXH_FIXED_ELEMENT;if(!a)return Rb;if(!a.$IS_ARRAY)return a;if(a.length==0)return Rb;return a[a.length-1]};
|
||||
Nb.getFixedElements=function(b){if(_fixedHeaders==Rb)return Rb;if(!b)return Rb;if(_fixedList!=Rb)return _fixedList.getAll(b);var a=b.$FXH_FIXED_ELEMENT;if(!a)return Rb;if(a.$IS_ARRAY&&a.length==0)return Rb;var c=[];if(!a.$IS_ARRAY)c.push(a);else for(var d=0;d<a.length;d++)c.push(a[d]);return c};
|
||||
Nb.getSourceElement=function(a){if(_fixedHeaders==Rb)return Rb;if(!a)return Rb;var b=a.$SOURCE_ELEMENT;return !b?Rb:b};
|
||||
Nb.isFixedElement=function(a){if(_fixedHeaders==Rb)return Ob;if(!a)return Ob;return a.$SOURCE_ELEMENT!=undefined};
|
||||
function _createFixedHeader(k,H,F){for(var B=k.rows,A=_cells(B[0]),r=0,e=0;e<A.length;e++)r+=A[e].colSpan;var d=ROWS,j=COLS,c=Sb,b=Sb,z=COPY_ID,C=Rb,f=BORDER_COLOR,g=BORDER_STYLE,h=BORDER_WIDTH,i=BOX_SHADOW,l=Rb,m=1,s=DIV_FULL_MODE,v=DIV_AUTO_SIZE,p=Rb,q=Rb,D=_getElementByTagName(k,Ub);if(D!=Rb)d=D.rows.length;for(var E=H.split(";"),e=0;e<E.length;e++){var t=E[e].split(":");if(t.length!=2)continue;var G=_trim(t[0]).toLowerCase(),a=_trim(t[1]);switch(G){case "rows":d=Number(a);break;case "cols":j=Number(a);break;case "div-max-width":c=_percent(a);break;case "div-max-height":b=_percent(a);break;case "div-full-mode":s=a.toLowerCase()=="yes";break;case "div-auto-size":v=a.toLowerCase();break;case "copyid":z=a.toLowerCase()=="yes";break;case I:C=a;break;case "border-color":f=a;break;case "border-style":g=a;break;case "border-width":h=a;break;case "box-shadow":i=a.replace(/ +/g," ");break;case "bgcolor":l=a;break;case "opacity":m=Number(a);break;case "body-header-id":p=a;break;case "body-left-header-id":q=a}}if(d<0||d>=Math.min(B.length,11))d=0;if(j<0||j>=Math.min(r,11))j=0;if(!c||c<1||c>100)c=Sb;if(!b||b<1||b>100)b=Sb;if(F>1)s=Ob;if(m<0||m>1)m=1;var n=document.createElement("DIV");if(f!=Rb&&!_setStyle(n,"borderColor",f))f=Rb;if(g!=Rb&&!_setStyle(n,"borderStyle",g))g=Rb;if(h!=Rb&&!_setStyle(n,"borderWidth",h))h=Rb;if(i!=Rb&&!_setStyle(n,"boxShadow",i))i=Rb;if(l!=Rb&&!_setStyle(n,"backgroundColor",l))l=Rb;var w=Rb,x=Rb;if(p!=Rb&&!(_isIE&&(_IEver<=7||_IEver<=9&&_isBackCompat)))w=document.getElementById(p);if(q!=Rb&&!(_isIE&&(_IEver<=7||_IEver<=9&&_isBackCompat)))x=document.getElementById(q);var y=Rb;if(k.parentNode.tagName=="DIV"){var u=k.parentNode,o=u.currentStyle||document.defaultView.getComputedStyle(u,Vb);if(o.overflowX==Wb||o.overflowX==Tb||(o.overflowY==Wb||o.overflowY==Tb))y=u}return new _FixedHeader(y,k,r,d,j,c,b,s,v,z,C,f,g,h,i,l,m,w,x)}function
|
||||
_onBodyScroll(){for(var a=0;a<_fixedHeaders.length;a++)_fixedHeaders[a].onBodyScroll()}function
|
||||
_onBodyResize(){if(_execFlag)return;_resizeTimerId!=Rb&&clearTimeout(_resizeTimerId);var a=function(){_execute(n)};
|
||||
_resizeTimerId=setTimeout(a,500)}function
|
||||
_execute(b){_execFlag=Qb;_resizeTimerId=Rb;for(var a=0;a<_fixedHeaders.length;a++)if(b==n)_fixedHeaders[a].initOnResize();else _fixedHeaders[a].init();_resizeSourceDiv();for(var a=0;a<_fixedHeaders.length;a++)_fixedHeaders[a].main();_execFlag=Ob}function
|
||||
_resizeSourceDiv(){for(var b=Ob,a=0;a<_fixedHeaders.length;a++)if(_fixedHeaders[a].hideAllDivs(Qb))b=Qb;if(!b)return;for(var a=0;a<_fixedHeaders.length;a++)_fixedHeaders[a].resizeSourceDiv1();var d=document.body.style.overflowX,c=document.body.currentStyle||document.defaultView.getComputedStyle(document.body,Vb);if(_body.scrollWidth<=_body.clientWidth&&c.overflowX!=Tb)document.body.style.overflowX=Xb;for(var a=0;a<_fixedHeaders.length;a++)_fixedHeaders[a].resizeDivHeight();for(var a=0;a<_fixedHeaders.length;a++)_fixedHeaders[a].resizeDivWidth();document.body.style.overflowX=d;for(var a=0;a<_fixedHeaders.length;a++)_fixedHeaders[a].resizeSourceDiv2();for(var a=0;a<_fixedHeaders.length;a++)_fixedHeaders[a].hideAllDivs(Ob)}function
|
||||
_FixedHeader(a,d,E,p,y,v,B,xb,Z,F,G,J,K,L,zb,Q,bb,M,N){var o="CLIENT",j="TBODY",g="1px",q=this,f=a!=Rb,b=Rb,c=Rb,e=Rb,rb=0,qb=0,hb=Vb,gb=Vb,U=Rb,W=Rb,u=[],yb=[],Y=0,fb=0,Cb=d.style.zIndex?d.style.zIndex:0,r=Ob,x=Ob,k=0,n=0,R=0,S=0,ib=0,mb=0,lb=0,ob=0,eb=0,db=0,X=0,Ib=d.style.display,jb=Sb,nb=Sb,m=Rb,z=Sb,A=Sb,i=0,l=0,V=Rb,T=Rb;
|
||||
q.init=function(){if(Q==Rb){var e=f?a:d,b=_getBackgroundColor(e);if(b==Rb){var c=e.parentNode;while(c){b=_getBackgroundColor(c);if(b!=Rb)break;if(c.tagName=="HTML")break;c=c.parentNode}if(b==Rb)b="white"}Q=b}if(f){_addEventListener(a,Tb,ab);Jb();X=_offsetWidth(d)}if(M)i=Math.max(_rect(M).bottom,0);if(N)l=Math.max(_rect(N).right,0);wb()};
|
||||
q.initOnResize=function(){A=Sb;z=Sb;nb=Sb;jb=Sb;if(M)i=Math.max(_rect(M).bottom,0);if(N)l=Math.max(_rect(N).right,0);if(f){if(r||v>0)a.style.width=Vb;if(x||B>0)a.style.height=Vb;X=_offsetWidth(d)}wb()};
|
||||
q.removeAllTables=function(d){b&&w(b);c&&w(c);e&&w(e);b=Rb;c=Rb;e=Rb;if(f){_removeEventListener(a,Tb,ab);if(d){if(r||v>0)a.style.width=Vb;if(x||B>0)a.style.height=Vb}}};
|
||||
q.hideAllDivs=function(h){if(!f)return Ob;var d=b==Rb?Rb:b.parentNode,g=c==Rb?Rb:c.parentNode,j=e==Rb?Rb:e.parentNode;if(h){d&&I(d,DIV_MIN_WIDTH);g&&H(g,DIV_MIN_HEIGHT)}var i=h?Yb:Vb;if(d)d.style.display=i;if(g)g.style.display=i;if(j)j.style.display=i;(r||x||v>0||B>0)&&Fb(h);if(h){r&&t(a,DIV_MIN_WIDTH);x&&D(a,DIV_MIN_HEIGHT)}return Qb};
|
||||
q.resizeSourceDiv1=function(){if(!f)return;var e=m!=Rb?m:d;if(v>0){k=_body.clientWidth/100*v;k=Math.max(k,DIV_MIN_WIDTH);var c=_offsetWidth(e)+eb;c=Math.min(c,k);t(a,c)}if(B>0){n=_body.clientHeight/100*B;n=Math.max(n,DIV_MIN_HEIGHT);var b=_offsetHeight(e)+db;b=Math.min(b,n);D(a,b)}};
|
||||
q.resizeDivHeight=function(){if(!f)return;if(!x)return;if(_body.scrollHeight>_body.clientHeight){var e=Math.max(_body.scrollHeight-_body.clientHeight,0);a.style.height=Math.max(_body.clientHeight-30,MIN_SIZE)+PX;var g=Math.max(_body.scrollHeight-_body.clientHeight,0);n=_offsetHeight(a)-(g-e)}else{a.style.height=_body.clientHeight+PX;var h=Math.max(_body.scrollHeight-_body.clientHeight,0);n=_offsetHeight(a)-h}n--;n=Math.max(n,DIV_MIN_HEIGHT);var c=m!=Rb?m:d,b=_offsetHeight(c)+db;b=Math.min(b,n);if(xb)b=n;D(a,b)};
|
||||
q.resizeDivWidth=function(){if(!f)return;if(!r)return;if(_body.scrollWidth>_body.clientWidth){var e=Math.max(_body.scrollWidth-_body.clientWidth,0);a.style.width=Math.max(_body.clientWidth-16,MIN_SIZE)+PX;var g=Math.max(_body.scrollWidth-_body.clientWidth,0);k=_offsetWidth(a)-(g-e)}else{a.style.width=_body.clientWidth+PX;var h=Math.max(_body.scrollWidth-_body.clientWidth,0);k=_offsetWidth(a)-h}k--;k=Math.max(k,DIV_MIN_WIDTH);var c=m!=Rb?m:d,b=_offsetWidth(c)+eb;b=Math.min(b,k);if(xb)b=k;if(_isIE)b--;t(a,b)};
|
||||
q.resizeSourceDiv2=function(){if(!f)return;var b=m!=Rb?m:d;if((r||v>0)&&a.scrollWidth>a.clientWidth&&_offsetWidth(a)<k){var e=a.scrollWidth-a.clientWidth,c=Math.min(_offsetWidth(a)+e,k),i=a.style.overflowY;if(_isIE)a.style.overflowY=Xb;t(a,c);if(_isIE)a.style.overflowY=i}if((x||B>0)&&a.scrollHeight>a.clientHeight&&_offsetHeight(a)<n){var e=a.scrollHeight-a.clientHeight,g=Math.min(_offsetHeight(a)+e,n);D(a,g)}if((r||v>0)&&X>_offsetWidth(b)&&_offsetWidth(a)<k){var h=X-_offsetWidth(b),c=Math.min(_offsetWidth(a)+h,k);t(a,c)}};
|
||||
q.checkZoom=function(){if(Math.abs(_offsetWidth(d)-rb)>=1||Math.abs(_offsetHeight(d)-qb)>=1||_colsWidthList(_cells(d.rows[0]))!=hb||_rowsHeightList(d.rows)!=gb)return Qb};
|
||||
q.main=function(){var h=_offsetWidth(d),g=_offsetHeight(d),b=rb!=h,a=qb!=g;rb=h;qb=g;var c=_colsWidthList(_cells(d.rows[0]));if(hb!=c){hb=c;b=Qb}var e=_rowsHeightList(d.rows);if(gb!=e){gb=e;a=Qb}var i=Gb(b||a);(i||b||a)&&Hb();f&&Lb();if(f){ub();ab()}else kb(Qb)};
|
||||
function Gb(l){if(l){b!=Rb&&w(b);c!=Rb&&w(c);e!=Rb&&w(e);b=Rb;c=Rb;e=Rb}var i=Ob,j=d.rows,k=f?d.parentNode:_body,h=Ob,g=Ob;if(f){h=a.clientHeight<a.scrollHeight&&a.clientHeight!=0;g=a.clientWidth<a.scrollWidth&&a.clientWidth!=0}if(!f||DIV_BODY_SCROLL!=0){if(!h)h=_body.clientHeight<_body.scrollHeight;if(!g)g=_body.clientWidth<_body.scrollWidth;if(!h&&!g&&_body==document.documentElement){h=document.body.clientHeight<document.body.scrollHeight;g=document.body.clientWidth<document.body.scrollWidth}}if(p>0&&h)if(_rowsHeight(j,p)+30>=k.clientHeight)h=Ob;if(y>0&&g){var m=_cells(j[0]);if(_colsWidth(m,y)+30>=k.clientWidth)g=Ob}if(y>0&&g){if(c==Rb){c=pb(TID_NUMBER,j.length,y);if(c!=Rb)i=Qb}}else if(c!=Rb){w(c);c=Rb}if(p>0&&h){if(b==Rb){b=pb(TID_HEADER,p,E);if(b!=Rb)i=Qb}}else if(b!=Rb){w(b);b=Rb}if(b!=Rb&&c!=Rb){if(e==Rb){e=pb(TID_CORNER,p,y);i=Qb}}else if(e!=Rb){w(e);e=Rb}return i}function
|
||||
Hb(){if(b!=Rb){t(b,_offsetWidth(d));cb(b)}if(c!=Rb){var g=_colsWidth(_cells(d.rows[0]),y),f=_colsWidth(_cells(c.rows[0]),y),a=f-g;a!=0&&t(c,_offsetWidth(c)-a);cb(c);D(c,_offsetHeight(d))}if(e!=Rb){t(e,_offsetWidth(c));cb(e);D(e,_offsetHeight(b))}}function
|
||||
Lb(){var j=1,d=b==Rb?Rb:b.parentNode,f=c==Rb?Rb:c.parentNode,g=e==Rb?Rb:e.parentNode;d!=Rb&&s(d);f!=Rb&&s(f);g!=Rb&&s(g);if(d!=Rb){I(d,a.clientWidth);H(d,_offsetHeight(b));var i=_rect(b).bottom-_rect(d).bottom;i>0&&H(d,_offsetHeight(b)+i);d.clientHeight>=a.clientHeight&&H(d,a.clientHeight-j)}if(f!=Rb){H(f,a.clientHeight);I(f,_offsetWidth(c));var h=_rect(c).right-_rect(f).right;h>0&&I(f,_offsetWidth(c)+h);f.clientWidth>=a.clientWidth&&I(f,a.clientWidth-j)}if(g!=Rb){H(g,d.clientHeight);I(g,f.clientWidth)}}function
|
||||
Jb(){var b=a.cloneNode(Ob),c=d.cloneNode(Ob);b.style.position=Zb;b.style.left=ac;b.style.top=ac;b.style.minWidth=g;b.style.minHeight=g;a.parentNode.appendChild(b);var e=document.createElement("DIV");e.style.position=Zb;e.style.left=ac;e.style.top=ac;a.parentNode.appendChild(e);var i=_rect(a),f=_rect(b);S=i.top-f.top;R=i.left-f.left;c.style.width="50px";c.style.height="50px";var k=document.createElement(j),m=document.createElement("TR"),l=document.createElement("TD");l.appendChild(document.createTextNode("x"));m.appendChild(l);k.appendChild(m);c.appendChild(k);var o=b.offsetWidth,n=b.offsetHeight;b.appendChild(c);r=b.offsetWidth!=o;x=b.offsetHeight!=n;if(r){if(v>0||Z!=Pb&&Z!="width")r=Ob}else v=Sb;if(x){if(B>0||Z!=Pb&&Z!="height")x=Ob}else B=Sb;eb=_offsetWidth(b)-_offsetWidth(c);db=_offsetHeight(b)-_offsetHeight(c);var h=_rect(e);mb=f.top-h.top;ib=f.left-h.left;ob=_rect(c).top-f.top;lb=_rect(c).left-f.left;b.parentNode.removeChild(b);e.parentNode.removeChild(e)}function
|
||||
wb(){if(p==0&&y==0)return;var a=d.cloneNode(Ob);a.style.position=Zb;a.style.left=ac;a.style.top=ac;a.style.width=Wb;a.style.height=Wb;a.width=Vb;a.height=Vb;var i,m=_getElementByTagName(d,Ub);if(m==Rb)m=_getElementByTagName(d,j);if(m!=Rb)i=m.cloneNode(Ob);else i=document.createElement(j);a.appendChild(i);d.parentNode.appendChild(a);for(var x=Kb(),v=p>0?p:1,t=d.rows,q=0,c=0;c<v;c++){var o=t[c],n=o.cloneNode(Ob);n.style.height=_trHeight(o)+PX;for(var l=_cells(o),f=0;f<l.length;f++){var e=l[f],b=e.cloneNode(Ob);b.width=Vb;if(e.colSpan==1)b.style.width=q+PX;else{for(var s=Qb,r=1;r<e.colSpan;r++)if(!x[e.$FXH_COLINDEX+r]){s=Ob;break}if(s)b.style.width=Wb;else b.style.width=q+PX}b.appendChild(document.createTextNode(" "));n.appendChild(b)}i.appendChild(n);if(_isIE&&_IEver<=7&&!_isBackCompat){Y=_trHeight(n)-_trHeight(o);Y+=2}}for(var c=0;c<v;c++)for(var l=_cells(t[c]),w=_cells(i.rows[c]),f=0;f<l.length;f++){var e=l[f],b=w[f];if(b.style.width==Wb)continue;var h=c+"."+e.cellIndex;yb[h]=_offsetWidth(b)-b.clientWidth;u[h]=b.clientWidth-q;var g=e.currentStyle||document.defaultView.getComputedStyle(e,Vb),k=Sb;if(g.paddingLeft.match(/px$/)!=Rb&&g.paddingRight.match(/px$/)!=Rb)k=_pixel(g.paddingLeft)+_pixel(g.paddingRight);else if(g.padding.match(/px$/)!=Rb)k=_pixel(g.padding)*2;if(k>0)u[h]=Math.min(u[h],k);fb=Math.max(u[h],fb)}a.parentNode.removeChild(a)}function
|
||||
Kb(){for(var g=p>0?p:1,l=d.rows,f=new Array(g),b=0;b<g;b++){f[b]=new Array(E);for(var a=0;a<E;a++)f[b][a]=Qb}for(var b=0;b<g;b++)for(var m=l[b],i=_cells(m),h=0,a=0;a<E;){if(!f[b][a]){a++;continue}if(h>=i.length)break;var e=i[h];if(e.rowSpan>=2&&e.colSpan>=2)for(var c=0;c<e.rowSpan;c++){if(b+c>=g)break;for(var j=0;j<e.colSpan;j++){if(c==0&&j==0)continue;f[b+c][a+j]=Ob}}else{if(e.rowSpan>=2)for(var c=1;c<e.rowSpan;c++){if(b+c>=g)break;f[b+c][a]=Ob}if(e.colSpan>=2)for(var c=1;c<e.colSpan;c++)f[b][a+c]=Ob}h++;a+=e.colSpan}for(var k=new Array(E),a=0;a<E;a++)k[a]=Ob;for(var b=0;b<g;b++)for(var m=l[b],i=_cells(m),h=0,a=0;a<E;a++){if(!f[b][a])continue;if(h>=i.length)break;var e=i[h];e.$FXH_COLINDEX=a;if(e.colSpan==1)k[a]=Qb;h++}return k}function
|
||||
pb(e,v,x){var n=" fixed_header_display_none_at_print",c=d.cloneNode(Ob),r=_getElementByTagName(d,"CAPTION"),s=_getElementByTagName(d,Ub),u=_getElementByTagName(d,j),k=Rb,p=Rb,o=Rb;if(r!=Rb){k=r.cloneNode(Qb);k.style.backgroundColor=Q;k.style.overflow=Xb;if(e!=TID_HEADER){k.innerHTML=" ";k.style.height=_offsetHeight(r)+PX;k.style.backgroundColor=bc}c.appendChild(k)}var t=0;if(s!=Rb){p=s.cloneNode(Ob);c.appendChild(p);t=s.rows.length}if(u!=Rb&&t<v){o=u.cloneNode(Ob);c.appendChild(o)}if(Mb(c,o,p,e,v,x,t)==Ob)return Rb;_linkElement(c,d,e,F,G,Ob);k!=Rb&&_linkElement(k,r,e,F,G,Qb);p!=Rb&&_linkElement(p,s,e,F,G,Ob);o!=Rb&&_linkElement(o,u,e,F,G,Ob);if(e!=TID_HEADER){c.style.marginRight=ac;c.style.borderRightWidth=ac;c.style.paddingRight=ac}if(e!=TID_NUMBER){c.style.marginBottom=ac;c.style.borderBottomWidth=ac;c.style.paddingBottom=ac}c.style.minWidth=g;c.style.minHeight=g;var m=Rb;if(zb!=Rb){var w=zb.split(" ");m=Vb;for(var q=0;q<w.length;q++)if(q==0&&e==TID_HEADER)m+="0 ";else if(q==1&&e==TID_NUMBER)m+="0 ";else m+=w[q]+" "}if(f){var b=a.cloneNode(Ob);b.$FXH_PADDING_WIDTH=undefined;b.$FXH_PADDING_HEIGHT=undefined;_linkElement(b,a,e,F,G,Ob);b.className+=n;b.style.overflowX=Xb;b.style.overflowY=Xb;_removeEventListener(b,Tb,ab);e!=TID_CORNER&&_addEventListener(b,Tb,function(){Eb(b,e)});if(e==TID_HEADER)b.style.borderRightWidth=ac;else{b.style.marginRight=ac;b.style.borderRightWidth=ac;b.style.paddingRight=ac}if(e==TID_NUMBER)b.style.borderBottomWidth=ac;else{b.style.marginBottom=ac;b.style.borderBottomWidth=ac;b.style.paddingBottom=ac}b.style.width="30px";b.style.height="30px";b.style.minWidth=g;b.style.minHeight=g;if(m!=Rb)b.style.boxShadow=m;b.style.position=Zb;b.style.top=S+PX;b.style.left=R+PX;b.style.backgroundColor=Q;b.appendChild(c);a.parentNode.appendChild(b)}else{c.className+=n;if(m!=Rb)c.style.boxShadow=m;switch(_positionMode){case POS_FIXED:c.style.position=cc;break;case POS_ABSOLUTE:c.style.position=Zb;break;default:c.style.position=cc}c.style.marginTop=ac;c.style.marginLeft=ac;c.style.top=i+PX;c.style.left=l+PX;c.style.backgroundColor=Q;d.parentNode.appendChild(c)}_isOpera&&h(f?b:c,Ob);return c}function
|
||||
Mb(v,w,C,g,x,q,z){for(var f=new Array(q),h=0;h<f.length;h++)f[h]=0;for(var s=d.rows,c=0;c<x;c++){var k=s[c],b=k.cloneNode(Ob);_linkElement(b,k,g,F,G,Ob);b.style.height=_trHeight(k)-Y+PX;if(c==p-1&&g!=TID_NUMBER){if(J!=Rb)b.style.borderBottomColor=J;if(K!=Rb)b.style.borderBottomStyle=K;if(L!=Rb)b.style.borderBottomWidth=L}if(g!=TID_HEADER)b.style.borderRightWidth=ac;if(c<z)C.appendChild(b);else if(w!=Rb)w.appendChild(b);else v.appendChild(b);for(var t=_cells(k),l=0,e=0;e<q;){if(c<f[e]){e++;continue}if(l>=t.length)break;var a=t[l];l++;if(c+a.rowSpan>x)return Ob;f[e]=c+a.rowSpan;if(a.colSpan>=2){for(var h=1;h<a.colSpan;h++)f[e+h]=f[e];if(e+a.colSpan>q)return Ob}_radioCtl(a,"backup");var j=a.cloneNode(Qb);_radioCtl(a,"restore");_linkElement(j,a,g,F,G,Qb);b.appendChild(j);try{var o=c+"."+a.cellIndex;if(u[o]!=undefined){var D=u[o]+yb[o];j.style.width=_offsetWidth(a)-D+PX}else if(_isIE&&_IEver<=8&&a.colSpan>=2)j.style.width=a.clientWidth-fb+PX}catch(E){}var i=j.style;if(c+a.rowSpan==p&&g!=TID_NUMBER){if(J!=Rb)i.borderBottomColor=J;if(K!=Rb)i.borderBottomStyle=K;if(L!=Rb)i.borderBottomWidth=L}if(e+a.colSpan==y&&g!=TID_HEADER){if(J!=Rb)i.borderRightColor=J;if(K!=Rb)i.borderRightStyle=K;if(L!=Rb)i.borderRightWidth=L}e+=a.colSpan}if(_isIE&&_IEver<=9&&l==0){b.style.height=ac;var A=_rect(k).bottom,m=b.parentNode;if(m.tagName!="TABLE")m=m.parentNode;var r=m.rows,n=c-1;while(r[n].style.height==ac)n--;var B=A-_rect(s[n]).top;r[n].style.height=B-Y+PX}}_radioCtl(v,"sync");return Qb}function
|
||||
s(b){if(b.style.position==cc)return;var f=_rect(a),e=_rect(b),d=e.top-f.top,c=e.left-f.left;if(_isIE){if(d==Sb&&b.$TOP_DIFF==1)d=0;else b.$TOP_DIFF=d;if(c==Sb&&b.$LEFT_DIFF==1)c=0;else b.$LEFT_DIFF=c}if(Math.abs(d)>=1)b.style.top=_pixel(b.style.top)-d+PX;if(Math.abs(c)>=1)b.style.left=_pixel(b.style.left)-c+PX}function
|
||||
w(a){if(f)a=a.parentNode;_unlinkElement(a);a.parentNode&&a.parentNode.removeChild(a)}
|
||||
q.onBodyScroll=function(){if(M)i=Math.max(_rect(M).bottom,0);if(N)l=Math.max(_rect(N).right,0);if(f){ub();return}if(_positionMode==POS_ABSOLUTE){if(!_isMobile){if(_getBodyScrollTop()!=A){h(b,Ob);h(e,Ob)}if(_getBodyScrollLeft()!=z){h(c,Ob);h(e,Ob)}}V!=Rb&&clearTimeout(V);V=setTimeout(kb,200)}else kb()};
|
||||
function kb(k){V=Rb;var j=_getBodyScrollTop()!=A,g=_getBodyScrollLeft()!=z;A=_getBodyScrollTop();z=_getBodyScrollLeft();if(j&&g)k=Qb;var f=_rect(d),o=b!=Rb&&f.top<i&&f.bottom>=b.offsetHeight+i,p=c!=Rb&&f.left<l&&f.right>=c.offsetWidth+l;b!=Rb&&h(b,o);c!=Rb&&h(c,p);e!=Rb&&h(e,o&&p);if(_positionMode==POS_MIX&&b!=Rb){var a=b.style;if(k||j&&a.position==Zb){a.position=cc;a.left=f.left+PX;a.top=i+PX;if(e!=Rb)e.style.top=a.top;O(Ob)}else if(!j&&g&&a.position==cc){a.position=Zb;a.left=z+f.left+PX;a.top=A+i+PX;vb()}}if(_positionMode==POS_MIX&&c!=Rb){var a=c.style;if(k||g&&a.position==Zb){a.position=cc;a.left=l+PX;a.top=f.top+PX;P(Ob)}else if(!g&&j&&a.position==cc){a.position=Zb;a.left=z+l+PX;a.top=A+f.top+PX;tb()}}if(_positionMode==POS_FIXED){if(b!=Rb&&g)b.style.left=f.left+PX;if(c!=Rb&&j)c.style.top=f.top+PX;if(b!=Rb&&b.style.top!=i+PX){b.style.top=i+PX;if(e!=Rb)e.style.top=b.style.top}}if(_positionMode==POS_ABSOLUTE&&k){if(b!=Rb)b.style.left=z+f.left+PX;if(c!=Rb)c.style.top=A+f.top+PX}if(_positionMode==POS_ABSOLUTE&&(j||g)){var n,m,q=_isMobile?1:4;if(b!=Rb&&j){b.style.top=i+A-_offsetHeight(b)+PX;if(e!=Rb)e.style.top=b.style.top;n=_offsetHeight(b)/q}if(c!=Rb&&g){c.style.left=l+z-_offsetWidth(c)+PX;if(e!=Rb)e.style.left=c.style.left;m=_offsetWidth(c)/q}T!=Rb&&clearTimeout(T);sb(j,g,n,m)}b!=Rb&&g&&O(Ob);c!=Rb&&j&&P(Ob)}function
|
||||
sb(m,j,k,h){T=Rb;var g=_getBodyScrollTop()+i,f=_getBodyScrollLeft()+l,d=g,a=f;if(b!=Rb&&m){d=_pixel(b.style.top)+k;if(k>0)d=Math.min(d,g);else d=Math.max(d,g);b.style.top=d+PX;if(e!=Rb)e.style.top=b.style.top}if(c!=Rb&&j){a=_pixel(c.style.left)+h;if(h>0)a=Math.min(a,f);else a=Math.max(a,f);c.style.left=a+PX;if(e!=Rb)e.style.left=c.style.left}if(g==d&&f==a){if(b!=Rb&&m){vb();if(e!=Rb)e.style.top=b.style.top}if(c!=Rb&&j){tb();if(e!=Rb)e.style.left=c.style.left}return}var n=function(){sb(m,j,k,h)};
|
||||
T=setTimeout(n,20)}function
|
||||
ub(){if(DIV_BODY_SCROLL==0)return;if(_positionMode==POS_ABSOLUTE)return;var f=b==Rb?Rb:b.parentNode,g=c==Rb?Rb:c.parentNode,j=e==Rb?Rb:e.parentNode,k=_rect(a),o=_rect(d),p=0;if(_isIE11)p=1;var m=Ob,n=Ob;if(f&&(DIV_BODY_SCROLL==2||a.scrollHeight-p<=a.clientHeight)){var q=Math.min(k.bottom,o.bottom);if(k.top<i-ob&&q>=_offsetHeight(b)){if(f.style.position!=cc){f.style.position=cc;f.style.top=i-mb-ob+PX}f.style.left=k.left-ib+PX;h(f,Qb);m=Qb}else{if(f.style.position!=Zb){f.style.position=Zb;f.style.top=S+PX;f.style.left=R+PX}h(f,a.scrollTop>0);s(f)}C(f)&&O(Ob)}if(g&&(DIV_BODY_SCROLL==2||a.scrollWidth<=a.clientWidth)){var r=Math.min(k.right,o.right);if(k.left<l-lb&&r>=_offsetWidth(c)){if(g.style.position!=cc){g.style.position=cc;g.style.left=l-ib-lb+PX}g.style.top=k.top-mb+PX;h(g,Qb);n=Qb}else{if(g.style.position!=Zb){g.style.position=Zb;g.style.top=S+PX;g.style.left=R+PX}h(g,a.scrollLeft>0);s(g)}C(g)&&P(Ob)}if(j){if(m||n){if(j.style.position!=cc)j.style.position=cc;if(m)j.style.top=f.style.top;else j.style.top=g.style.top;if(n)j.style.left=g.style.left;else j.style.left=f.style.left}else if(j.style.position!=Zb){j.style.position=Zb;j.style.top=S+PX;j.style.left=R+PX;s(j)}h(j,C(f)&&C(g))}}function
|
||||
ab(){var d=b==Rb?Rb:b.parentNode,f=c==Rb?Rb:c.parentNode,g=e==Rb?Rb:e.parentNode,i=d&&d.style.position==cc,k=f&&f.style.position==cc;h(d,a.scrollTop>0||i);h(f,a.scrollLeft>0||k);h(g,C(d)&&C(f));d!=Rb&&s(d);f!=Rb&&s(f);g!=Rb&&s(g);var j=a.scrollLeft!=jb,l=a.scrollTop!=nb;jb=a.scrollLeft;nb=a.scrollTop;if(d!=Rb&&j){d.$FXH_SCROLL_LEFT=a.scrollLeft;d.scrollLeft=a.scrollLeft;d.scrollLeft>0&&O(Ob)}if(f!=Rb&&l){f.$FXH_SCROLL_TOP=a.scrollTop;f.scrollTop=a.scrollTop;f.scrollTop>0&&P(Ob)}}function
|
||||
Eb(b,c){if(c==TID_HEADER){if(Math.abs(b.scrollLeft-b.$FXH_SCROLL_LEFT)<5)return}else if(Math.abs(b.scrollTop-b.$FXH_SCROLL_TOP)<5)return;if(c==TID_HEADER)a.scrollLeft=b.scrollLeft;else a.scrollTop=b.scrollTop}function
|
||||
vb(){if(b==Rb)return;var a=_rect(b);if(a.top<=i)return;b.style.top=i+_pixel(b.style.top)-a.top+PX}function
|
||||
tb(){if(c==Rb)return;var a=_rect(c);if(a.left<=l)return;c.style.left=l+_pixel(c.style.left)-a.left+PX}function
|
||||
O(q){if(b==Rb)return;if(!q){U!=Rb&&clearTimeout(U);U=setTimeout(function(){O(Qb)},200);return}U=Rb;var h,j;if(f){h=_rect(a).left;j=h+a.clientWidth}else{h=0;j=_body.clientWidth}if(C(c))h+=_offsetWidth(c);for(var m=_cells(d.rows[0]),o=_cells(b.rows[0]),e=0,l=0,i=0;i<m.length;i++){var k=_rect(m[i]).left;if(k<h)continue;if(k>j)break;var p=_rect(o[i]).left;e+=p-k;l++}if(l==0)return;if(e==0)return;e=e/l;e=Math.round(e);if(e==0)return;if(f){var g=b.parentNode;if(g.style.position==cc){var n=_pixel(g.style.left)-e;g.style.left=n+PX}else{g.$FXH_SCROLL_LEFT=g.scrollLeft+e;g.scrollLeft+=e}}else{var n=_pixel(b.style.left)-e;b.style.left=n+PX}}function
|
||||
P(p){if(c==Rb)return;if(_isOpera)return;if(!p){W!=Rb&&clearTimeout(W);W=setTimeout(function(){P(Qb)},200);return}W=Rb;var h,j;if(f){h=_rect(a).top;j=h+a.clientHeight}else{h=0;j=_body.clientHeight}if(C(b))h+=_offsetHeight(b);for(var n=d.rows,m=c.rows,e=0,l=0,i=0;i<n.length;i++){var k=_rect(n[i]).top;if(k<h)continue;if(k>j)break;if(_cells(m[i]).length==0)continue;var q=_rect(m[i]).top;e+=q-k;l++}if(l==0)return;if(e==0)return;e=e/l;e=Math.round(e);if(e==0)return;if(f){var g=c.parentNode;if(g.style.position==cc){var o=_pixel(g.style.top)-e;g.style.top=o+PX}else{g.$FXH_SCROLL_TOP=g.scrollTop+e;g.scrollTop+=e}}else{var o=_pixel(c.style.top)-e;c.style.top=o+PX}}function
|
||||
C(a){return a!=Rb&&a.style.visibility=="visible"}function
|
||||
h(a,b){if(a==Rb)return;var c=b?"visible":Xb;if(a.style.visibility==c)return;a.style.visibility=c;if(_isIE)a.style.zIndex=b?Cb:Cb-1;if(_isOpera)a.style.opacity=b?bb:0;if(b&&bb<1){a.style.opacity=bb;a.style.filter="alpha(opacity="+bb*100+")"}}function
|
||||
Fb(b){if(!_isIE&&!_isIE11&&!_isFirefox&&!_isOpera)return;if(b){m=Db();a.appendChild(m);t(m,_offsetWidth(d));D(m,_offsetHeight(d));d.style.display=Yb}else{d.style.display=Ib;a.removeChild(m);m=Rb}}function
|
||||
Db(){var a=d.cloneNode(Ob),b=document.createElement(j),e=document.createElement("TR"),c=document.createElement("TD");c.appendChild(document.createTextNode("dummy"));e.appendChild(c);b.appendChild(e);a.appendChild(b);return a}function
|
||||
I(a,b){Bb(a,o,b)}function
|
||||
t(a,b){Bb(a,"OFFSET",b)}function
|
||||
H(a,b){Ab(a,o,b)}function
|
||||
D(a,b){Ab(a,"OFFSET",b)}function
|
||||
Bb(a,g,f){var b=f;if(a.$FXH_PADDING_WIDTH!=undefined)b-=a.$FXH_PADDING_WIDTH;for(var d,c,e=0;e<2;e++){if(b<MIN_SIZE)b=MIN_SIZE;a.style.width=b+PX;d=g==o?a.clientWidth:_offsetWidth(a);c=d-f;if(a.$FXH_PADDING_WIDTH==undefined)a.$FXH_PADDING_WIDTH=c;if(c==0||b==MIN_SIZE)break;b-=c}}function
|
||||
Ab(a,g,e){var b=e;if(a.$FXH_PADDING_HEIGHT!=undefined)b-=a.$FXH_PADDING_HEIGHT;for(var d,c,f=0;f<2;f++){if(b<MIN_SIZE)b=MIN_SIZE;a.style.height=b+PX;d=g==o?a.clientHeight:_offsetHeight(a);c=d-e;if(a.$FXH_PADDING_HEIGHT==undefined)a.$FXH_PADDING_HEIGHT=c;if(c==0||b==MIN_SIZE)break;b-=c}}function
|
||||
cb(p){var g=d.rows,c=p.rows,e=c.length,k=_rowsHeight(g,e)+HEIGHT_MARGIN;if(_rowsHeight(c,e)<k)return;for(var a=0;a<e;a++){var q=_trHeight(c[a])-_trHeight(g[a]);if(q<HEIGHT_MARGIN)continue;for(var o=_cells(g[a]),j=_cells(c[a]),f=0;f<j.length;f++){var l=o[f],i=j[f],b=0,h=a+"."+l.cellIndex;if(u[h]!=undefined){if(u[h]<=0)continue;b=u[h]-2;if(b<0)b=0}var n=Math.ceil(b/2),m=Math.floor(b/2);i.style.paddingLeft=n+PX;i.style.paddingRight=m+PX;i.style.width=l.clientWidth-b+PX;if(_rowsHeight(c,e)<k)return}}}}function
|
||||
_FixedElementList(){var b=this,a=[];b.add=function(b){a.push(b)};
|
||||
b.remove=function(c){for(var b=0;b<a.length;b++)if(a[b]==c){a.splice(b,1);return}};
|
||||
b.get=function(d){for(var b=0;b<a.length;b++){var c=a[b];if(c.$SOURCE_ELEMENT==d)return c}return Rb};
|
||||
b.getAll=function(e){for(var b=Rb,c=0;c<a.length;c++){var d=a[c];if(d.$SOURCE_ELEMENT==e){if(b==Rb)b=[];b.push(d)}}return b}}function
|
||||
_linkElement(a,b,k,i,c,g){b.id&&!i&&a.removeAttribute("id");if(c){var h=a.getAttribute(I);if(h)a.setAttribute(I,h+" "+c);else a.setAttribute(I,c)}if(b.name)if(b.tagName==l&&b.type==m)a.name=RADIO_PREFIX+k+"_"+b.name;else a.removeAttribute("name");a.$SOURCE_ELEMENT=b;if(_fixedList!=Rb)_fixedList.add(a);else if(!b.$FXH_FIXED_ELEMENT)b.$FXH_FIXED_ELEMENT=a;else{var j=b.$FXH_FIXED_ELEMENT;if(!j.$IS_ARRAY){var e=[];e.$IS_ARRAY=Qb;b.$FXH_FIXED_ELEMENT=e;e.push(j)}b.$FXH_FIXED_ELEMENT.push(a)}_setEventHandler(a,b);if(g)for(var d=0;d<a.childNodes.length;d++){var f=a.childNodes[d];if(!f)continue;if(!f.tagName)continue;_linkElement(f,b.childNodes[d],k,i,c,g)}}function
|
||||
_unlinkElement(c){if(_fixedList!=Rb){_fixedList.remove(c);var a=c.$SOURCE_ELEMENT;if(a&&a.$FXH_ON_CHANGE_FUNC&&_fixedList.get(a)==Rb){_removeEventListener(a,g,a.$FXH_ON_CHANGE_FUNC);a.$FXH_ON_CHANGE_FUNC=undefined}}else{var a=c.$SOURCE_ELEMENT;if(a&&a.$FXH_FIXED_ELEMENT){var d=a.$FXH_FIXED_ELEMENT;if(!d.$IS_ARRAY||d.length==1){a.$FXH_FIXED_ELEMENT=undefined;if(a.$FXH_ON_CHANGE_FUNC){_removeEventListener(a,g,a.$FXH_ON_CHANGE_FUNC);a.$FXH_ON_CHANGE_FUNC=undefined}}else{for(var f=[],b=0;b<d.length;b++)d[b]!=c&&f.push(d[b]);a.$FXH_FIXED_ELEMENT=f}}}for(var b=0;b<c.childNodes.length;b++){var e=c.childNodes[b];if(!e)continue;if(!e.tagName)continue;_unlinkElement(e)}}function
|
||||
_setEventHandler(b,a){if(a.onclick)b.onclick=function(){return a.onclick()};
|
||||
if(a.ondblclick)b.ondblclick=function(){return a.ondblclick()};
|
||||
if(a.onkeydown)b.onkeydown=function(){return a.onkeydown()};
|
||||
if(a.onkeypress)b.onkeypress=function(){return a.onkeypress()};
|
||||
if(a.onkeyup)b.onkeyup=function(){return a.onkeyup()};
|
||||
if(a.onmousedown)b.onmousedown=function(){return a.onmousedown()};
|
||||
if(a.onmouseup)b.onmouseup=function(){return a.onmouseup()};
|
||||
if(a.onmouseover)b.onmouseover=function(){return a.onmouseover()};
|
||||
if(a.onmouseout)b.onmouseout=function(){return a.onmouseout()};
|
||||
if(a.onmousemove)b.onmousemove=function(){return a.onmousemove()};
|
||||
if(a.tagName==l||a.tagName=="SELECT"||a.tagName=="TEXTAREA"){switch(a.type){case e:case f:case h:_copyValue(a,b)}switch(a.type){case h:case m:case e:case f:case "text":case "password":case "textarea":default:b.onclick=function(){_copyValue(b,a);_copyValues(a,b);if(a.onclick)return a.onclick();return Qb};
|
||||
b.onchange=function(){_copyValue(b,a);_copyValues(a,b);if(a.onchange)return a.onchange();return Qb};
|
||||
b.onkeyup=function(){_copyValue(b,a);_copyValues(a,b);if(a.onkeyup)return a.onkeyup();return Qb};
|
||||
if(a.$FXH_ON_CHANGE_FUNC==undefined){a.$FXH_ON_CHANGE_FUNC=function(){_copyValues(a)};
|
||||
_addEventListener(a,g,a.$FXH_ON_CHANGE_FUNC);_addEventListener(a,"keyup",a.$FXH_ON_CHANGE_FUNC)}break;case "button":case "submit":case "image":case "reset":b.onclick=function(){a.click()};
|
||||
break;case Xb:case "file":}if(a.form&&a.$FXH_ON_RESET_FUNC==undefined){a.$FXH_ON_RESET_FUNC=function(){for(var b=0;b<a.form.elements.length;b++)_copyValues(a.form.elements[b])};
|
||||
_addEventListener(a.form,"reset",function(){setTimeout(a.$FXH_ON_RESET_FUNC,30)})}}else if(a.tagName=="FORM")b.onsubmit=function(){return Ob}}function
|
||||
_copyValues(d,c){if(c==undefined)c=Rb;var a;if(_fixedList!=Rb){a=_fixedList.getAll(d);if(!a)return;for(var b=0;b<a.length;b++){if(a[b]==c)continue;_copyValue(d,a[b])}return}a=d.$FXH_FIXED_ELEMENT;if(!a)return;if(!a.$IS_ARRAY)a!=c&&_copyValue(d,a);else for(var b=0;b<a.length;b++){if(a[b]==c)continue;_copyValue(d,a[b])}}function
|
||||
_copyValue(a,b){switch(a.type){case h:case m:b.checked=a.checked;break;case e:case f:for(var c=0;c<a.length;c++)b.options[c].selected=a.options[c].selected;b.selectedIndex=a.selectedIndex;break;default:try{b.value=a.value}catch(d){}}}function
|
||||
_copyStyle(src,dst,styleName){for(var buf=styleName.split(","),i=0;i<buf.length;i++){var name=_trim(buf[i]);try{eval("dst.style."+name+" = src.style."+name)}catch(e){}}}function
|
||||
_setStyle(element,styleName,value){try{eval("element.style."+styleName+" = value");return Qb}catch(e){return Ob}}function
|
||||
_radioCtl(d,e){for(var c=d.getElementsByTagName(l),b=0;b<c.length;b++){var a=c[b];if(a.type!=m)continue;switch(e){case "backup":a.$FXH_CHECKED=a.checked;break;case "restore":a.checked=a.$FXH_CHECKED;break;case "sync":a.checked=a.$SOURCE_ELEMENT.checked}}}function
|
||||
_createCSS(c,b){var a=document.createElement("STYLE");a.setAttribute("type","text/css");a.setAttribute("media",c);if(a.styleSheet)a.styleSheet.cssText=b;else a.appendChild(document.createTextNode(b));document.body.appendChild(a)}function
|
||||
_getBackgroundColor(d){var e=d.currentStyle||document.defaultView.getComputedStyle(d,Vb),a=e.backgroundColor;if(a==bc)return Rb;if(a.match(/^rgba\(/)==Rb)return a;var g=a.replace(/^rgba\(/,Vb).replace(/\)/,Vb),c=g.split(","),b=Number(c[3]);if(b==1)return a;var f="rgb("+_color(Number(c[0]),b)+", "+_color(Number(c[1]),b)+", "+_color(Number(c[2]),b)+")";return f}function
|
||||
_color(a,b){var c=Math.round(a+(255-a)*(1-b));return Math.min(c,255)}function
|
||||
_addEventListener(a,b,c){if(a.addEventListener)a.addEventListener(b,c,Ob);else a.attachEvent&&a.attachEvent("on"+b,c)}function
|
||||
_removeEventListener(a,b,c){if(a.removeEventListener)a.removeEventListener(b,c,Ob);else a.detachEvent&&a.detachEvent("on"+b,c)}function
|
||||
_getBodyScrollTop(){if(document.body.scrollTop)return document.body.scrollTop;return document.documentElement.scrollTop}function
|
||||
_getBodyScrollLeft(){if(document.body.scrollLeft)return document.body.scrollLeft;return document.documentElement.scrollLeft}function
|
||||
_offsetWidth(b){var a=_rect(b);return a.right-a.left}function
|
||||
_offsetHeight(b){var a=_rect(b);return a.bottom-a.top}function
|
||||
_rowsHeight(a,b){return _rect(a[b-1]).bottom-_rect(a[0]).top}function
|
||||
_colsWidth(a,c){for(var d=0,b=0;b<a.length;b++){d+=a[b].colSpan;if(d==c){c=b+1;break}}return _rect(a[c-1]).right-_rect(a[0]).left}function
|
||||
_colsWidthList(c){for(var b=Vb,a=0;a<c.length;a++){if(a>0)b+=",";b+=c[a].offsetWidth}return b}function
|
||||
_rowsHeightList(c){for(var b=Vb,a=0;a<c.length;a++){if(a>0)b+=",";b+=c[a].offsetHeight}return b}function
|
||||
_trHeight(a){if(_isIE&&_IEver==8&&!_isBackCompat)return a.clientHeight;else return _offsetHeight(a)}function
|
||||
_cells(e){var a=e.childNodes;if(a.length==0)return a;for(var b=[],d=0;d<a.length;d++){var c=a[d];(c.tagName=="TD"||c.tagName=="TH")&&b.push(c)}if(b.length==0&&a[0].tagName=="FORM")return _cells(a[0]);return b}function
|
||||
_pixel(a){if(a.match(/px$/)!=Rb)a=a.substring(0,a.length-2);return Number(a)}function
|
||||
_percent(a){if(a.match(/%$/)!=Rb)a=a.substring(0,a.length-1);return Number(a)}function
|
||||
_trim(a){return a.replace(/^[ \u7e32\u0080]+/,Vb).replace(/[ \u7e32\u0080]+$/,Vb)}function
|
||||
_rect(a){return a.getBoundingClientRect()}function
|
||||
_getElementByTagName(b,c){var a=b.getElementsByTagName(c);if(a.length==0)return Rb;return a[0]}function
|
||||
_createObjectForFirefox(){var a=document.createElement("SPAN");a.style.display=Yb;a.style.position=Zb;a.style.top=ac;a.style.left=ac;document.body.appendChild(a);return a}function
|
||||
_createObjectForChrome(){var a=document.createElement("SPAN");a.style.position=cc;a.style.top="0";a.style.left="0";a.style.height="0";a.style.width="0";document.body.appendChild(a)}
|
||||
}
|
||||
0
ecs/jskult-webapp/src/system_var/__init__.py
Normal file
0
ecs/jskult-webapp/src/system_var/__init__.py
Normal file
129
ecs/jskult-webapp/src/system_var/constants.py
Normal file
129
ecs/jskult-webapp/src/system_var/constants.py
Normal file
@ -0,0 +1,129 @@
|
||||
import os.path as path
|
||||
|
||||
BIO_TEMPORARY_FILE_DIR_PATH = path.join(path.curdir, 'src', 'data')
|
||||
BIO_EXCEL_TEMPLATE_FILE_PATH = path.join(BIO_TEMPORARY_FILE_DIR_PATH, 'BioData_template.xlsx')
|
||||
|
||||
BIO_EXTRACT_COLUMNS = [
|
||||
'slip_org_kbn',
|
||||
'slip_mgt_no',
|
||||
'rec_ymd',
|
||||
'rec_whs_cd',
|
||||
'rec_whs_sub_cd',
|
||||
'whs_nm',
|
||||
'rec_whs_org_cd',
|
||||
'rec_urag_no',
|
||||
'rev_hsdnymd_srk',
|
||||
'rec_tran_kbn',
|
||||
'tran_kbn_nm',
|
||||
'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',
|
||||
'rec_lot_num',
|
||||
'amt_fugo',
|
||||
'expr_dt',
|
||||
'data_kbn',
|
||||
'lot_no_err_flg',
|
||||
'bef_slip_mgt_no',
|
||||
'ins_usr',
|
||||
'ins_dt',
|
||||
'inst_cd',
|
||||
'inst_name_form',
|
||||
'address',
|
||||
'tel_no',
|
||||
'v_whs_cd',
|
||||
'v_whsorg_cd',
|
||||
'whs_org_nm',
|
||||
'v_tran_cd',
|
||||
'iko_flg'
|
||||
]
|
||||
|
||||
BIO_CSV_HEADER = [
|
||||
'データ種別',
|
||||
'伝票管理NO',
|
||||
'処理日',
|
||||
'卸コード',
|
||||
'卸サブコード',
|
||||
'卸名',
|
||||
'卸組織コード',
|
||||
'伝票番号',
|
||||
'発伝年月日',
|
||||
'卸取引区分',
|
||||
'取引区分名',
|
||||
'製品コード',
|
||||
'統一商品コード',
|
||||
'商品名',
|
||||
'卸報告商品名',
|
||||
'納入先コード',
|
||||
'納入先名',
|
||||
'卸報告納入先名',
|
||||
'納入先住所',
|
||||
'卸報告納入先住所',
|
||||
'ロット番号',
|
||||
'数量',
|
||||
'有効期限',
|
||||
'データ区分',
|
||||
'エラー詳細種別',
|
||||
'訂正前伝票管理NO',
|
||||
'修正者',
|
||||
'修正日時',
|
||||
'施設コード',
|
||||
'施設名',
|
||||
'施設住所',
|
||||
'施設電話番号',
|
||||
'Veeva卸コード',
|
||||
'Veeva卸組織コード',
|
||||
'卸組織名',
|
||||
'Veeva取引区分コード',
|
||||
'移行'
|
||||
]
|
||||
|
||||
SLIP_ORG_KBN_FULL_NAME = {
|
||||
'J': 'JD-NET',
|
||||
'N': 'NHI',
|
||||
'H': '手入力'
|
||||
}
|
||||
DATA_KBN_JP_NAME = {
|
||||
'0': '正常',
|
||||
'1': 'ロットエラー',
|
||||
'2': '除外',
|
||||
'3': 'ロット不明',
|
||||
'9': 'エラー(解消済)',
|
||||
}
|
||||
LOT_NO_ERR_FLG_JP_NAME = {
|
||||
'0': '正常',
|
||||
'1': 'ロットエラー',
|
||||
'2': '日付エラー',
|
||||
'3': 'ロットエラー(解消済)',
|
||||
'4': 'ロットエラー(調査不能)',
|
||||
'5': '日付エラー(解消済)',
|
||||
'6': '日付エラー(調査不能)',
|
||||
'7': '除外(卸都合)',
|
||||
'8': '除外(再送信)',
|
||||
'Z': '過去データ'
|
||||
}
|
||||
|
||||
LOGOUT_REASON_DO_LOGOUT = 'do_logout'
|
||||
LOGOUT_REASON_LOGIN_ERROR = 'login_error'
|
||||
LOGOUT_REASON_BATCH_PROCESSING = 'batch_processing'
|
||||
LOGOUT_REASON_BATCH_PROCESSING_FOR_MAINTE = 'batch_processing_ult'
|
||||
LOGOUT_REASON_NOT_LOGIN = 'not_login'
|
||||
LOGOUT_REASON_SESSION_EXPIRED = 'session_expired'
|
||||
LOGOUT_REASON_UNEXPECTED = 'unexpected'
|
||||
|
||||
LOGOUT_REASON_MESSAGE_MAP = {
|
||||
LOGOUT_REASON_DO_LOGOUT: 'Logoutしました。',
|
||||
LOGOUT_REASON_LOGIN_ERROR: '存在しないユーザー、<br>またはパスワードが違います。',
|
||||
# 機能を利用できないとかでいいんじゃ
|
||||
LOGOUT_REASON_BATCH_PROCESSING: '日次バッチ処理中なので、<br>生物由来データ参照は使用出来ません。',
|
||||
LOGOUT_REASON_BATCH_PROCESSING_FOR_MAINTE: '日次バッチ処理中のため、<br>マスタ-メンテは使用出来ません。',
|
||||
# 使ってなさそう
|
||||
LOGOUT_REASON_NOT_LOGIN: 'Loginしてからページにアクセスしてください。',
|
||||
LOGOUT_REASON_SESSION_EXPIRED: 'セッションが切れています。<br>再度Loginしてください。',
|
||||
LOGOUT_REASON_UNEXPECTED: '予期しないエラーが発生しました。<br>再度Loginするか、<br>管理者に問い合わせてください。'
|
||||
}
|
||||
22
ecs/jskult-webapp/src/system_var/environment.py
Normal file
22
ecs/jskult-webapp/src/system_var/environment.py
Normal file
@ -0,0 +1,22 @@
|
||||
import os
|
||||
|
||||
COGNITO_AUTH_DOMAIN = os.environ['COGNITO_AUTH_DOMAIN']
|
||||
AUTHORIZE_ENDPOINT = os.environ['AUTHORIZE_ENDPOINT']
|
||||
TOKEN_ENDPOINT = os.environ['TOKEN_ENDPOINT']
|
||||
COGNITO_IDENTITY_PROVIDER = os.environ['COGNITO_IDENTITY_PROVIDER']
|
||||
COGNITO_REDIRECT_URI = os.environ['COGNITO_REDIRECT_URI']
|
||||
COGNITO_USER_POOL_ID = os.environ['COGNITO_USER_POOL_ID']
|
||||
COGNITO_CLIENT_ID = os.environ['COGNITO_CLIENT_ID']
|
||||
COGNITO_CLIENT_SECRET = os.environ['COGNITO_CLIENT_SECRET']
|
||||
AWS_REGION = os.environ['AWS_REGION']
|
||||
SESSION_TABLE_NAME = os.environ['SESSION_TABLE_NAME']
|
||||
BIO_ACCESS_LOG_BUCKET = os.environ['BIO_ACCESS_LOG_BUCKET']
|
||||
|
||||
DB_HOST = os.environ['DB_HOST']
|
||||
DB_PORT = int(os.environ['DB_PORT'])
|
||||
DB_USERNAME = os.environ['DB_USERNAME']
|
||||
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'])
|
||||
6
ecs/jskult-webapp/src/templates/__init__.py
Normal file
6
ecs/jskult-webapp/src/templates/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
import os
|
||||
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
templates_path = os.path.dirname(os.path.relpath(__file__))
|
||||
templates = Jinja2Templates(templates_path)
|
||||
14
ecs/jskult-webapp/src/templates/_header.html
Normal file
14
ecs/jskult-webapp/src/templates/_header.html
Normal file
@ -0,0 +1,14 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="format-detection" content="telephone=no, address=no" http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>{{subtitle}}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css" integrity="sha384-b6lVK+yci+bfDmaY1u0zE8YYJt0TZxLEAFyYSLHId4xoVvsrQu3INevFKo+Xir8e" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
|
||||
<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
||||
<script src="https://pagination.js.org/dist/2.5.0/pagination.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/ja.min.js"></script>
|
||||
<script src="/static/function/businessLogicScript.js"></script>
|
||||
40
ecs/jskult-webapp/src/templates/_modal.html
Normal file
40
ecs/jskult-webapp/src/templates/_modal.html
Normal file
@ -0,0 +1,40 @@
|
||||
{% with
|
||||
icon_data = {
|
||||
'info': {
|
||||
'alert': 'alert-primary',
|
||||
'icon_src': '/static/img/icon_modal_confirm.png'
|
||||
},
|
||||
'warning': {
|
||||
'alert': 'alert-warning',
|
||||
'icon_src': '/static/img/icon_modal_error.png'
|
||||
}
|
||||
}
|
||||
%}
|
||||
<div class="modal fade" id="{{modal_id}}" tabindex="-1" aria-labelledby="{{modal_id}}ModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header alert {{icon_data[icon_key]['alert']}} d-flex align-items-center">
|
||||
<h5 class="modal-title" id="exampleModalLabel">{{modal_title}}</h5>
|
||||
<button type="button" id="{{modal_id}}_close" onclick="{{modal_close_event}}" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<img src="{{icon_data[icon_key]['icon_src']}}" loading="lazy" style="width: 50px;">
|
||||
{% autoescape False %}
|
||||
{{message}}
|
||||
{% endautoescape %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{% for button in buttons%}
|
||||
<button type="button"
|
||||
id="{{button['id']}}"
|
||||
class="{{button['class']}}"
|
||||
data-bs-dismiss="{{button.dismiss}}"
|
||||
{% if button.get('onclick_event') %} onclick="{{button['onclick_event']}}" {% endif %}>
|
||||
{{button.text}}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
462
ecs/jskult-webapp/src/templates/bioSearchList.html
Normal file
462
ecs/jskult-webapp/src/templates/bioSearchList.html
Normal file
@ -0,0 +1,462 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
{% with subtitle = bio.subtitle %}
|
||||
{% include '_header.html' %}
|
||||
{% endwith %}
|
||||
<link rel="stylesheet" href="/static/css/bioStyle.css">
|
||||
<link rel="stylesheet" href="/static/css/pagenation.css">
|
||||
<link rel="stylesheet" href="/static/css/datepicker.css">
|
||||
<script src="/static/lib/fixed_midashi.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
// 見出し固定初期化
|
||||
FixedMidashi.create();
|
||||
// ボタン、テキストボックス初期化
|
||||
formBtDisabled();
|
||||
// DatePickerを有効化
|
||||
enableDatePicker();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>生物由来検索一覧</h1>
|
||||
<form class="_form" id="bio_search" name="search" action="/bio/BioSearchList" method="POST">
|
||||
<table class="search_table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="back_bt" colspan="7" align="right">
|
||||
<input type="button" name="back" value="メニューへ" onclick="location.href='/menu'">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>卸:</td>
|
||||
<td class="search_tb" id="oroshi_dd">
|
||||
<select class="text search_dropdown" name="ctrl_wholesaler" value="" onChange="formBtDisabled();">
|
||||
<option value=""></option>
|
||||
{% for whs_name in bio.display_wholesaler_names() %}
|
||||
<option
|
||||
value="{{whs_name}}"
|
||||
{{bio.is_selected_whs_name(whs_name)}}>
|
||||
{{whs_name}}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td>データ種別:</td>
|
||||
<td class="search_tb">
|
||||
<select class="text search_dropdown" name="ctrl_org_kbn" onChange="formBtDisabled();" value="">
|
||||
{% for org_kbn_code, org_kbn_value in bio.display_org_kbn().items() %}
|
||||
<option value="{{org_kbn_code}}" {{bio.is_selected_org_kbn(org_kbn_code)}} >{{org_kbn_value}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td>処理日:</td>
|
||||
<td colspan="2">
|
||||
<input type="text" id="shoribi_start" class="date_picker" name="ctrl_rec_ymd_from"
|
||||
value="{{bio.is_input_rec_ymd_from()}}"
|
||||
onchange="formBtDisabled()"
|
||||
onblur="autoModifyDate(this)"
|
||||
>
|
||||
~
|
||||
<input type="text" id="shoribi_end" class="date_picker" name="ctrl_rec_ymd_to"
|
||||
value="{{bio.is_input_rec_ymd_to()}}"
|
||||
onchange="formBtDisabled()"
|
||||
onblur="autoModifyDate(this)"
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ロット番号:</td>
|
||||
<td class="search_tb">
|
||||
<input class="text" type="text" id="lot_tb" name="ctrl_rec_lot_num" style="ime-mode:disabled" maxlength="10"
|
||||
value="{{bio.is_input_lot_num()}}"
|
||||
oninput="checkSpaceForm(this); checkAimaiSearhForm(this); formBtDisabled()">
|
||||
</td>
|
||||
<td>データ区分:</td>
|
||||
<td class="search_tb">
|
||||
<select class="text search_dropdown" name="ctrl_data_kbn" onchange="formBtDisabled()">
|
||||
{% for data_kbn_code, data_kbn_value in bio.display_data_kbn().items() %}
|
||||
<option value="{{data_kbn_code}}" {{bio.is_selected_data_kbn(data_kbn_code)}} >{{data_kbn_value}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td>製品:</td>
|
||||
<td class="search_tb" id="seihin_dd">
|
||||
<select class="text search_dropdown" name="ctrl_maker_cd" value="" onChange="formBtDisabled();">
|
||||
<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']}}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>発伝年月日:</td>
|
||||
<td colspan="3">
|
||||
<input type="text" id="shoribi_start" class="date_picker" name="ctrl_rev_hsdnymd_srk_from"
|
||||
value="{{bio.is_input_rev_hsdnymd_srk_from()}}"
|
||||
onchange="formBtDisabled()"
|
||||
onblur="autoModifyDate(this)"
|
||||
>
|
||||
~
|
||||
<input type="text" id="shoribi_start" class="date_picker" name="ctrl_rev_hsdnymd_srk_to"
|
||||
value="{{bio.is_input_rev_hsdnymd_srk_to()}}"
|
||||
onchange="formBtDisabled()"
|
||||
onblur="autoModifyDate(this)"
|
||||
>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<input type="checkbox" id="ikoFlg" name="ikoFlg" value="true" {{bio.is_checked_iko_flg()}}>
|
||||
<label for="ikoFlg">2017年11月以前のデータを含める</label>
|
||||
</td>
|
||||
<td>
|
||||
<input class="text clear_bt" id="clear" type="button" name="clear_bt" value="クリア" onclick="clr()">
|
||||
<input class="search_bt" id="search_bt" value="検索" type="submit">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<!--検索結果-->
|
||||
<form class="_form" id="searchResult" name="searchResult">
|
||||
<input type="button" id="outExcel" name="outExcel" value="Excel出力" {{bio.disabled_button()}}
|
||||
data-bs-toggle="modal" data-bs-target="#modal_xlsx" data-bs-message="生物由来卸販売データ一覧をExcel出力しますか?"/>
|
||||
<input type="button" id="outCSV" name="outCSV" value="CSV出力" {{bio.disabled_button()}}
|
||||
data-bs-toggle="modal" data-bs-target="#modal_csv" data-bs-message="生物由来卸販売データ一覧をCSV出力しますか?" />
|
||||
<!--ページネーション-->
|
||||
<div id="light-pagination" class="pagination"></div>
|
||||
<!--検索結果表示テーブル-->
|
||||
<div class="bioScroll_div">
|
||||
<table class="tablesorter" _fixedhead='rows:1; cols:0; border-color:black; border-width:2px;'>
|
||||
<thead>
|
||||
<tr class="result_tr">
|
||||
<th>データ種別</th>
|
||||
<th>伝票管理NO</th>
|
||||
<th>処理日</th>
|
||||
<th>卸コード</th>
|
||||
<th>卸サブコード</th>
|
||||
<th>卸名</th>
|
||||
<th>卸組織コード</th>
|
||||
<th>伝票番号</th>
|
||||
<th>発伝年月日</th>
|
||||
<th>卸取引区分</th>
|
||||
<th>取引区分名</th>
|
||||
<th>製品コード</th>
|
||||
<th>統一商品コード</th>
|
||||
<th>商品名</th>
|
||||
<th>卸報告商品名</th>
|
||||
<th>納入先コード</th>
|
||||
<th>納入先名</th>
|
||||
<th>卸報告納入先名</th>
|
||||
<th>納入先住所</th>
|
||||
<th>卸報告納入先住所</th>
|
||||
<th>ロット番号</th>
|
||||
<th>数量</th>
|
||||
<th>有効期限</th>
|
||||
<th>データ区分</th>
|
||||
<th>エラー詳細種別</th>
|
||||
<th>訂正前伝票管理NO</th>
|
||||
<th>修正者</th>
|
||||
<th>修正日時</th>
|
||||
<th>施設コード</th>
|
||||
<th>施設名</th>
|
||||
<th>施設住所</th>
|
||||
<th>施設電話番号</th>
|
||||
<th>Veeva卸コード</th>
|
||||
<th>Veeva卸組織コード</th>
|
||||
<th>卸組織名</th>
|
||||
<th>Veeva取引区分コード</th>
|
||||
<th>移行</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="result_data" class="result_data"></tbody>
|
||||
</table>
|
||||
{% if bio.is_form_submitted() and bio.is_data_overflow_max_length() %}
|
||||
<div class="resultAreaMsg">
|
||||
検索結果が最大件数を超えました。検索条件を見直しして下さい。
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if bio.is_form_submitted() and bio.is_data_empty() %}
|
||||
<div class="resultAreaMsg">
|
||||
対象のデータが存在しません。
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- CSV/Excelダウンロードボタン。ここはajaxでやってる -->
|
||||
<script type="text/javascript">
|
||||
function download(filename, kind) {
|
||||
$(`#loading`).toggle()
|
||||
|
||||
// 検索パラメータを取得
|
||||
const formData = $('#bio_search').serializeArray()
|
||||
// リクエスト用に加工
|
||||
const searchParams = {}
|
||||
for (let i = 0; i < formData.length; i++) {
|
||||
searchParams[formData[i].name] = formData[i].value
|
||||
}
|
||||
// ダウンロード固有のパラメータを設定
|
||||
const downloadRequestParams = {
|
||||
user_id: '{{bio.user_id}}',
|
||||
kind: kind,
|
||||
}
|
||||
$.extend(downloadRequestParams, searchParams)
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/bio/download",
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(downloadRequestParams),
|
||||
scriptCharset: 'utf-8',
|
||||
async: true,
|
||||
success: function(data) {
|
||||
try {
|
||||
if (data.status === 'batch_processing') {
|
||||
location.href('/logout?reason=batchProcessing')
|
||||
return
|
||||
}
|
||||
|
||||
if (data.status === 'session_expired') {
|
||||
location.href('/logout?reason=session_expired')
|
||||
return
|
||||
}
|
||||
// データが存在しない場合の考慮が必要
|
||||
/**if (data.download_url === '') {
|
||||
// 予期せぬエラーが発生した場合
|
||||
$(`#loading`).toggle();
|
||||
$(`#modal_${kind}`).modal('toggle');
|
||||
$(`#ErrorModal_Unexpected`).modal('toggle');
|
||||
}
|
||||
*/
|
||||
// S3の期限付き署名URLがレスポンスされる
|
||||
window.location.href = data.download_url;
|
||||
$(`#loading`).toggle();
|
||||
$(`#modal_${kind}`).modal('toggle');
|
||||
} catch (e) {
|
||||
alert("エラーが発生しました。:" + e.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
const responseJson = jqXHR.responseJSON
|
||||
if (responseJson?.detail?.error === 'db_error') {
|
||||
$(`#loading`).toggle();
|
||||
$(`#modal_${kind}`).modal('toggle');
|
||||
$(`#ErrorModal_DB`).modal('toggle');
|
||||
return
|
||||
}
|
||||
if (responseJson?.detail?.error === 'aws_error') {
|
||||
$(`#loading`).toggle();
|
||||
$(`#modal_${kind}`).modal('toggle');
|
||||
$(`#ErrorModal_AWS`).modal('toggle');
|
||||
return
|
||||
}
|
||||
|
||||
// 予期せぬエラーが発生した場合
|
||||
$(`#loading`).toggle();
|
||||
$(`#modal_${kind}`).modal('toggle');
|
||||
$(`#ErrorModal_Unexpected`).modal('toggle');
|
||||
return
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// <! --ページネーションの作成-- >
|
||||
$(function() {
|
||||
// スピナー出さない場合は以下、エスケープせず埋め込む
|
||||
// {% autoescape False%}
|
||||
let searchResultString = '{{bio.bio_data_json_str()}}'
|
||||
// {% endautoescape%}
|
||||
const searchResultData = JSON.parse(searchResultString)
|
||||
if (searchResultData.length == 0) {
|
||||
return
|
||||
}
|
||||
$(".pagination").pagination({
|
||||
// 以下はテスト用コード
|
||||
dataSource: function(done) {
|
||||
done(searchResultData)
|
||||
},
|
||||
pageNumber: 1, // 初期ページ番号
|
||||
pageSize: 50, //表示するコンテンツ数
|
||||
pageRange: 2, //選択されているページネーション番号の両隣に表示する個数
|
||||
ellipsisText: '...', //省略文字
|
||||
prevText: 'Prev', //「前へ」の文字。エスケープ文字
|
||||
nextText: 'Next', //「次へ」の文字。エスケープ文字
|
||||
showNavigator: true,
|
||||
formatNavigator: '件数: <%= totalNumber %>件 ページ数: <%= totalPage %>',
|
||||
callback: function(data, pagination) {
|
||||
$('#result_data').html(pagination_content(data))
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function pagination_content(datas) {
|
||||
const display_keys = [
|
||||
'slip_org_kbn',
|
||||
'slip_mgt_no',
|
||||
'rec_ymd',
|
||||
'rec_whs_cd',
|
||||
'rec_whs_sub_cd',
|
||||
'whs_nm',
|
||||
'rec_whs_org_cd',
|
||||
'rec_urag_no',
|
||||
'rev_hsdnymd_srk',
|
||||
'rec_tran_kbn',
|
||||
'tran_kbn_nm',
|
||||
'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',
|
||||
'rec_lot_num',
|
||||
'amt_fugo',
|
||||
'expr_dt',
|
||||
'data_kbn',
|
||||
'lot_no_err_flg',
|
||||
'bef_slip_mgt_no',
|
||||
'ins_usr',
|
||||
'ins_dt',
|
||||
'inst_cd',
|
||||
'inst_name_form',
|
||||
'address',
|
||||
'tel_no',
|
||||
'v_whs_cd',
|
||||
'v_whsorg_cd',
|
||||
'whs_org_nm',
|
||||
'v_tran_cd',
|
||||
'iko_flg',
|
||||
];
|
||||
const tableRow = document.createElement('tr')
|
||||
return datas.map(function (data) {
|
||||
return `
|
||||
<tr class="result_data">
|
||||
${display_keys.map((key) =>`<td>${data[key] || ''}</td>`)}
|
||||
</tr>
|
||||
`
|
||||
})
|
||||
return tableRow
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Excel出力モーダル -->
|
||||
{% with
|
||||
modal_id='modal_xlsx',
|
||||
modal_title='確認',
|
||||
message='生物由来卸販売データ一覧をExcel出力しますか?',
|
||||
icon_key='info',
|
||||
modal_close_event='',
|
||||
buttons = [
|
||||
{
|
||||
'id': 'excel_confirm_ok',
|
||||
'class': 'btn btn-primary',
|
||||
'text': 'OK',
|
||||
'onclick_event': 'download("filename", "xlsx")'
|
||||
},
|
||||
{
|
||||
'id': 'excel_confirm_cancel',
|
||||
'class': 'btn btn-secondary',
|
||||
'dismiss': 'modal',
|
||||
'text': 'Cancel',
|
||||
}
|
||||
]
|
||||
%}
|
||||
{% include '_modal.html' %}
|
||||
{% endwith %}
|
||||
<!-- CSV出力モーダル -->
|
||||
{% with
|
||||
modal_id='modal_csv',
|
||||
modal_title='確認',
|
||||
message='生物由来卸販売データ一覧をCSV出力しますか?',
|
||||
icon_key='info',
|
||||
modal_close_event='',
|
||||
buttons = [
|
||||
{
|
||||
'id': 'csv_confirm_ok',
|
||||
'class': 'btn btn-primary',
|
||||
'text': 'OK',
|
||||
'onclick_event': 'download("filename", "csv")'
|
||||
},
|
||||
{
|
||||
'id': 'csv_confirm_cancel',
|
||||
'class': 'btn btn-secondary',
|
||||
'dismiss': 'modal',
|
||||
'text': 'Cancel',
|
||||
}
|
||||
]
|
||||
%}
|
||||
{% include '_modal.html' %}
|
||||
{% endwith %}
|
||||
<!-- AWS環境異常エラーモーダル -->
|
||||
{% with
|
||||
modal_id='ErrorModal_AWS',
|
||||
modal_title='エラー',
|
||||
message='AWS環境に異常が発生しました。管理者にお問い合わせください。',
|
||||
icon_key='warning',
|
||||
modal_close_event='location.href="/logout?reason="',
|
||||
buttons = [
|
||||
{
|
||||
'id': 'error_modal_aws',
|
||||
'class': 'btn btn-primary',
|
||||
'text': 'OK',
|
||||
'onclick_event': 'location.href="/logout?reason=''"'
|
||||
}
|
||||
]
|
||||
%}
|
||||
{% include '_modal.html' %}
|
||||
{% endwith %}
|
||||
<!-- DB接続失敗エラーモーダル -->
|
||||
{% with
|
||||
modal_id='ErrorModal_DB',
|
||||
modal_title='エラー',
|
||||
message='DB接続に失敗しました。管理者にお問い合わせください。',
|
||||
icon_key='warning',
|
||||
modal_close_event='location.href="/logout?reason="',
|
||||
buttons = [
|
||||
{
|
||||
'id': 'error_modal_db',
|
||||
'class': 'btn btn-primary',
|
||||
'text': 'OK',
|
||||
'onclick_event': 'location.href="/logout?reason=''"'
|
||||
}
|
||||
]
|
||||
%}
|
||||
{% include '_modal.html' %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- エラーモーダル -->
|
||||
{% with
|
||||
modal_id='ErrorModal_Unexpected',
|
||||
modal_title='エラー',
|
||||
message='サーバーエラーが発生しました。管理者にお問い合わせください。',
|
||||
icon_key='warning',
|
||||
modal_close_event='location.href="/logout?reason="',
|
||||
buttons = [
|
||||
{
|
||||
'id': 'error_modal_unexpected',
|
||||
'class': 'btn btn-primary',
|
||||
'text': 'OK',
|
||||
'onclick_event': 'location.href="/logout?reason=''"'
|
||||
}
|
||||
]
|
||||
%}
|
||||
{% include '_modal.html' %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- ローディング -->
|
||||
<div id="loading">
|
||||
<div id="loading_content">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>出力中...
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
75
ecs/jskult-webapp/src/templates/logout.html
Normal file
75
ecs/jskult-webapp/src/templates/logout.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
{% with subtitle = logout.subtitle %}
|
||||
{% include '_header.html' %}
|
||||
{% endwith %}
|
||||
<link href="/static/css/menuStyle.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid text-center background">
|
||||
<h1>MeDaCA</h1>
|
||||
<br><br>
|
||||
<p class="logout_p">
|
||||
{% autoescape False %}
|
||||
{{logout.reason}}
|
||||
{% endautoescape %}
|
||||
</p>
|
||||
<!-- <?php
|
||||
// getが来ておらず理由がわからない場合
|
||||
if(!(isset($_GET['reason']))){
|
||||
$userflg = null;
|
||||
|
||||
// ログアウトボタンを押されたとき
|
||||
} else if($_GET['reason'] == 'logoutBtn'){
|
||||
?>
|
||||
<p class="logout_p"><?php echo $logoutMsg ?></p>
|
||||
<?php
|
||||
// ログイン失敗時に表示
|
||||
} else if($_GET['reason'] == 'loginErr'){
|
||||
?>
|
||||
<p class="logout_p"><?php echo $loginErrMsg ?></p>
|
||||
<?php
|
||||
// 日時バッチ処理中エラー時
|
||||
} else if($_GET['reason'] == 'batchProcess'){
|
||||
?>
|
||||
<p class="logout_p"><?php echo $batchProcessMsg ?></p>
|
||||
<?php
|
||||
// マスターメンテ日時バッチ処理中エラー時
|
||||
} else if($_GET['reason'] == 'batchProcessNewInstEmpRegist'){
|
||||
?>
|
||||
<p class="logout_p"><?php echo $batchProcessNewInstEmpRegistMsg ?></p>
|
||||
<?php
|
||||
// どっちのユーザーでログインしたかわからないとき
|
||||
} else if (!(isset($userflg))) {
|
||||
|
||||
} else {
|
||||
$userflg = null;
|
||||
?>
|
||||
<p class="logout_p"><?php echo $unexpectedErrMsg ?></p>
|
||||
<?php
|
||||
}
|
||||
?> -->
|
||||
<br><br><br>
|
||||
<p class="logout_p"><a href="{{ logout.redirect_to }}">{{logout.link_text}}</a></p>
|
||||
<!-- MeDaCA機能メニューへ -->
|
||||
<!-- <p class="logout_p"><a href="redirect_to">Login画面に戻る</a></p> -->
|
||||
<!-- <?php
|
||||
if (!(isset($userflg))) {
|
||||
?>
|
||||
<p class="logout_p"><a href="<?php echo $groupwarePath ?>"><?php echo $groupwareBackMsg ?></a></p>
|
||||
<?php
|
||||
} else if($userflg == 1){
|
||||
?>
|
||||
<p class="logout_p"><a href="<?php echo $maintLoginPath ?>"><?php echo $loginBackMsg ?></a></p>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<p class="logout_p"><a href="<?php echo $groupwarePath ?>"><?php echo $groupwareBackMsg ?></a></p>
|
||||
<?php
|
||||
}
|
||||
?> -->
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
27
ecs/jskult-webapp/src/templates/maintlogin.html
Normal file
27
ecs/jskult-webapp/src/templates/maintlogin.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
{% with subtitle = mainte_login.subtitle %}
|
||||
{% include '_header.html' %}
|
||||
{% endwith %}
|
||||
<link href="/static/css/menuStyle.css" rel="stylesheet">
|
||||
</head>
|
||||
<body onload="formBtDisabled('login_button', 'login', true)">
|
||||
<div class="container-fluid text-center background">
|
||||
<h1>
|
||||
MeDaCA<br>
|
||||
Mainte Login
|
||||
</h1>
|
||||
<br><br>
|
||||
<form name="login" class="text-center" method="post" action="/login">
|
||||
<div class="form-group">
|
||||
<input type="text" name="ctrl_username" maxlength='10' style="ime-mode:disabled;" placeholder="UserID" class="form_login" onchange="formBtDisabled('login_button', 'login', true)">
|
||||
<input type="password" name="ctrl_password" style="ime-mode:disabled;" placeholder="Password" class="form_login" onchange="formBtDisabled('login_button', 'login', true)" onkeyup="formBtDisabled('login_button', 'login', true)" oninput="checkPassForm(this)">
|
||||
</div>
|
||||
<!-- ログイン判断のみなので、いらない -->
|
||||
<input type="hidden" name="scrId" value="maint">
|
||||
<input type="submit" id="login_button" name="login" class="btn btn-info btn-lg btn_width" id="submit" value="Login">
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
36
ecs/jskult-webapp/src/templates/menu.html
Normal file
36
ecs/jskult-webapp/src/templates/menu.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
{% with subtitle = menu.subtitle %}
|
||||
{% include '_header.html' %}
|
||||
{% endwith %}
|
||||
<link href="/static/css/menuStyle.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid text-center background">
|
||||
<h1>MeDaCA<br/>機能メニュー</h1>
|
||||
<br><br>
|
||||
{% if menu.is_available_ult_doctor_menu() %}
|
||||
<a href="{{docSearchPath}}" class="btn btn-primary btn-lg btn_width">Ultmarc照会(医師)</a><br><br>
|
||||
{% endif %}
|
||||
{% if menu.is_available_ult_inst_menu() %}
|
||||
<a href="{{instSearchPath}}" class="btn btn-primary btn-lg btn_width">Ultmarc照会(施設)</a><br><br>
|
||||
{% endif %}
|
||||
{% if menu.is_available_bio_menu() %}
|
||||
{% if not menu.is_batch_processing() %}
|
||||
<a href="/bio/BioSearchList" class="btn btn-primary btn-lg btn_width">生物由来データ参照</a><br><br>
|
||||
{% else %}
|
||||
<div class="notUseBioMsg">生物由来データ参照は <br> 日次バッチ処理中のため利用出来ません</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if menu.is_available_master_maintenance_menu() %}
|
||||
{% if not menu.is_batch_processing() %}
|
||||
<a href="{{masterMaintePath}}" class="btn btn-primary btn-lg btn_width">マスターメンテメニュー</a><br><br>
|
||||
{% else %}
|
||||
<div class="notUseBioMsg"> マスターメンテメニューは <br> 日次バッチ処理中のため利用出来ません </div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<br><br><a href="/logout?reason=do_logout" class="btn btn-info btn-lg btn_width">Logout</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
14
ecs/jskult-webapp/src/util/sanitize.py
Normal file
14
ecs/jskult-webapp/src/util/sanitize.py
Normal file
@ -0,0 +1,14 @@
|
||||
"""サニタイズ処理用デコレーター"""
|
||||
|
||||
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
|
||||
10
ecs/jskult-webapp/src/util/string_util.py
Normal file
10
ecs/jskult-webapp/src/util/string_util.py
Normal file
@ -0,0 +1,10 @@
|
||||
def is_not_empty(check_string: str) -> bool:
|
||||
"""文字列が空ではない場合True
|
||||
|
||||
Args:
|
||||
check_string (str): 検査する文字列
|
||||
|
||||
Returns:
|
||||
bool: 文字列が殻ではない場合、True
|
||||
"""
|
||||
return check_string is not None and len(check_string) > 0
|
||||
Loading…
x
Reference in New Issue
Block a user