From d9497744e3373c68e9c1755cb716d7c02a1f8df8 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Fri, 12 May 2023 13:30:54 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20DB=E3=81=AE=E5=AE=9F=E8=A3=85?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/jskult-webapp/src/db/database.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ecs/jskult-webapp/src/db/database.py b/ecs/jskult-webapp/src/db/database.py index c639a82a..85759f96 100644 --- a/ecs/jskult-webapp/src/db/database.py +++ b/ecs/jskult-webapp/src/db/database.py @@ -77,7 +77,10 @@ class Database: Raises: DBException: 接続失敗 """ - self.__connection = self.__engine.connect() + try: + self.__connection = self.__engine.connect() + except Exception as e: + raise DBException(e) def execute_select(self, select_query: str, parameters=None) -> list[dict]: """SELECTクエリを実行します。 From 311e76346cc523a6ae700657da7568fddcf841ca Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Fri, 12 May 2023 13:31:10 +0900 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20=E9=9B=91=E3=81=AB=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E9=96=A2=E6=95=B0=E3=82=92=E7=A7=BB=E6=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/jskult-webapp/src/logging/get_logger.py | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 ecs/jskult-webapp/src/logging/get_logger.py diff --git a/ecs/jskult-webapp/src/logging/get_logger.py b/ecs/jskult-webapp/src/logging/get_logger.py new file mode 100644 index 00000000..f36f1199 --- /dev/null +++ b/ecs/jskult-webapp/src/logging/get_logger.py @@ -0,0 +1,37 @@ +import logging + +from src.system_var.environment import LOG_LEVEL + +# boto3関連モジュールのログレベルを事前に個別指定し、モジュール内のDEBUGログの表示を抑止する +for name in ["boto3", "botocore", "s3transfer", "urllib3"]: + logging.getLogger(name).setLevel(logging.WARNING) + + +def get_logger(log_name: str) -> logging.Logger: + """一意のログ出力モジュールを取得します。 + + Args: + log_name (str): ロガー名 + + Returns: + _type_: _description_ + """ + logger = logging.getLogger(log_name) + level = logging.getLevelName(LOG_LEVEL) + if not isinstance(level, int): + level = logging.INFO + logger.setLevel(level) + + if not logger.hasHandlers(): + handler = logging.StreamHandler() + logger.addHandler(handler) + + formatter = logging.Formatter( + '%(name)s\t[%(levelname)s]\t%(asctime)s\t%(message)s', + '%Y-%m-%d %H:%M:%S' + ) + + for handler in logger.handlers: + handler.setFormatter(formatter) + + return logger From 65acc3ce093fef1cc8c0cb9011a52d0867b38c4f Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Fri, 12 May 2023 13:39:14 +0900 Subject: [PATCH 03/15] =?UTF-8?q?style:=20format=E9=81=A9=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/jskult-webapp/src/aws/cognito.py | 6 ++-- ecs/jskult-webapp/src/aws/s3.py | 16 +++++----- ecs/jskult-webapp/src/controller/bio.py | 12 ++++--- .../src/controller/bio_download.py | 31 ++++++++++++------- .../src/controller/healthcheck.py | 2 ++ ecs/jskult-webapp/src/controller/login.py | 28 ++++++++++------- ecs/jskult-webapp/src/controller/logout.py | 10 +++--- ecs/jskult-webapp/src/controller/menu.py | 8 +++-- ecs/jskult-webapp/src/core/tasks.py | 2 +- ecs/jskult-webapp/src/db/sql_condition.py | 7 +++-- ecs/jskult-webapp/src/depends/auth.py | 12 ++++--- ecs/jskult-webapp/src/depends/database.py | 2 +- ecs/jskult-webapp/src/depends/services.py | 5 ++- ecs/jskult-webapp/src/error/exceptions.py | 4 +++ ecs/jskult-webapp/src/model/db/hdke_tbl.py | 1 - ecs/jskult-webapp/src/model/db/user_master.py | 4 +-- .../src/model/internal/jwt_token.py | 18 +++++------ .../src/model/internal/session.py | 8 ++--- ecs/jskult-webapp/src/model/request/bio.py | 5 ++- .../src/model/request/bio_download.py | 4 +-- ecs/jskult-webapp/src/model/request/login.py | 2 +- .../src/model/view/bio_disp_model.py | 4 +-- .../src/model/view/bio_view_model.py | 22 +++++++------ .../src/model/view/logout_view_model.py | 2 +- .../src/model/view/user_view_model.py | 6 ++-- .../src/repositories/base_repository.py | 6 ++-- .../repositories/bio_sales_view_repository.py | 5 ++- .../src/repositories/hdke_tbl_repository.py | 1 - .../pharmacy_product_master_repository.py | 8 ++--- .../repositories/user_master_repository.py | 3 +- .../wholesaler_master_repository.py | 4 +-- .../src/router/session_router.py | 12 +++++-- .../src/services/base_service.py | 1 + .../src/services/batch_status_service.py | 2 +- .../src/services/bio_view_service.py | 21 +++++++------ .../src/services/login_service.py | 6 ++-- .../src/services/session_service.py | 1 + .../src/system_var/environment.py | 2 +- 38 files changed, 163 insertions(+), 130 deletions(-) diff --git a/ecs/jskult-webapp/src/aws/cognito.py b/ecs/jskult-webapp/src/aws/cognito.py index c80f5bac..ee93f7ae 100644 --- a/ecs/jskult-webapp/src/aws/cognito.py +++ b/ecs/jskult-webapp/src/aws/cognito.py @@ -7,9 +7,9 @@ from src.system_var import environment class CognitoClient(AWSAPIClient): def __init__(self) -> None: self.__client = boto3.client('cognito-idp') - + def login_by_user_password_flow(self, username: str, password: str, secret_hash: str): - + auth_response = self.__client.admin_initiate_auth( UserPoolId=environment.COGNITO_USER_POOL_ID, ClientId=environment.COGNITO_CLIENT_ID, @@ -21,5 +21,5 @@ class CognitoClient(AWSAPIClient): }, ) authentication_result = auth_response['AuthenticationResult'] - + return authentication_result['IdToken'], authentication_result['RefreshToken'], diff --git a/ecs/jskult-webapp/src/aws/s3.py b/ecs/jskult-webapp/src/aws/s3.py index 852c7ceb..de11949c 100644 --- a/ecs/jskult-webapp/src/aws/s3.py +++ b/ecs/jskult-webapp/src/aws/s3.py @@ -7,16 +7,16 @@ from src.aws.aws_api_client import AWSAPIClient class S3Client(AWSAPIClient): __s3_client = boto3.client('s3') - + def upload_file(self, local_file_path: str, bucket_name: str, file_key: str): self.__s3_client.upload_file( - local_file_path, - Bucket=bucket_name, - Key=file_key - ) + local_file_path, + Bucket=bucket_name, + Key=file_key + ) - def generate_presigned_url(self, bucket_name: str, file_key: str, download_filename: str=''): - # presigned_urlを生成 + def generate_presigned_url(self, bucket_name: str, file_key: str, download_filename: str = ''): + # presigned_urlを生成 presigned_url = self.__s3_client.generate_presigned_url( 'get_object', Params={ @@ -28,5 +28,5 @@ class S3Client(AWSAPIClient): # 有効期限20分 ExpiresIn=1200 ) - + return presigned_url diff --git a/ecs/jskult-webapp/src/controller/bio.py b/ecs/jskult-webapp/src/controller/bio.py index a54909bd..188560fb 100644 --- a/ecs/jskult-webapp/src/controller/bio.py +++ b/ecs/jskult-webapp/src/controller/bio.py @@ -1,7 +1,6 @@ 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 @@ -21,11 +20,13 @@ router.route_class = AuthenticatedRoute ######################### # Views # ######################### + + @router.get('/BioSearchList') def bio_view( request: Request, - batch_status_service:BatchStatusService=Depends(get_service(BatchStatusService)), - bio_service: BioViewService=Depends(get_service(BioViewService)) + batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService)), + bio_service: BioViewService = Depends(get_service(BioViewService)) ): session: UserSession = request.session # バッチ処理中の場合、機能を利用させない @@ -50,12 +51,13 @@ def bio_view( ) return templates_response + @router.post('/BioSearchList') def search_bio( request: Request, bio_form: Optional[BioModel] = Depends(BioModel.as_form), - bio_service: BioViewService=Depends(get_service(BioViewService)), - batch_status_service:BatchStatusService=Depends(get_service(BatchStatusService)) + bio_service: BioViewService = Depends(get_service(BioViewService)), + batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService)) ): # error_log(date("Y/m/d H:i:s") . " [INFO] UserId:" . $UserId . "\r\n", 3, "$execLog"); session: UserSession = request.session diff --git a/ecs/jskult-webapp/src/controller/bio_download.py b/ecs/jskult-webapp/src/controller/bio_download.py index 67593fb7..0d172e8a 100644 --- a/ecs/jskult-webapp/src/controller/bio_download.py +++ b/ecs/jskult-webapp/src/controller/bio_download.py @@ -3,7 +3,6 @@ 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 @@ -23,14 +22,16 @@ router = APIRouter() ######################### # APIs # ######################### + + @router.post('/download') async def download_bio_data( - search_param: BioModel=Depends(BioModel.as_body), - download_param: BioDownloadModel=Depends(BioDownloadModel.as_body), + search_param: BioModel = Depends(BioModel.as_body), + download_param: BioDownloadModel = Depends(BioDownloadModel.as_body), bio_service: BioViewService = Depends(get_service(BioViewService)), batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService)), - session: Union[UserSession, None]=Depends(verify_session) - ): + session: Union[UserSession, None] = Depends(verify_session) +): # 通常のビューとはルーティングの扱いを変えるために、個別のルーターで登録する # error_log(date("Y/m/d H:i:s") . " [INFO] getBioData start" . "\r\n", 3, "$execLog"); # 改修後のパラメータを打ち出すようにする @@ -68,14 +69,18 @@ async def download_bio_data( # 値を変換 # データ種別の正式名を設定 - 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[:, '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 '') + 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 = '' @@ -84,12 +89,14 @@ async def download_bio_data( 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) + 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) + 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( diff --git a/ecs/jskult-webapp/src/controller/healthcheck.py b/ecs/jskult-webapp/src/controller/healthcheck.py index bd571edd..cdb8f00e 100644 --- a/ecs/jskult-webapp/src/controller/healthcheck.py +++ b/ecs/jskult-webapp/src/controller/healthcheck.py @@ -5,6 +5,8 @@ router = APIRouter() ######################### # Views # ######################### + + @router.get('/') def healthcheck(): return {'status': 'OK'} diff --git a/ecs/jskult-webapp/src/controller/login.py b/ecs/jskult-webapp/src/controller/login.py index a7bc6808..e8246322 100644 --- a/ecs/jskult-webapp/src/controller/login.py +++ b/ecs/jskult-webapp/src/controller/login.py @@ -1,4 +1,3 @@ -import os.path as path import secrets import urllib.parse as parse from typing import Union @@ -25,6 +24,8 @@ router.route_class = AfterSetCookieSessionRoute ######################### # Views # ######################### + + @router.get('/userlogin') def login_user_redirect_view(): auth_query_string = parse.urlencode( @@ -39,6 +40,7 @@ def login_user_redirect_view(): return RedirectResponse(url=authorize_endpoint_url, status_code=status.HTTP_303_SEE_OTHER) + @router.get('/maintlogin') def login_maintenance_view(request: Request): mainte_login = MainteLoginViewModel() @@ -53,20 +55,22 @@ def login_maintenance_view(request: Request): ######################### # APIs # ######################### + + @router.post('/maintlogin') def login( - response: Response, - request: LoginModel = Depends(LoginModel.as_form), - login_service: LoginService = Depends(get_service(LoginService)) - ): + response: Response, + request: LoginModel = Depends(LoginModel.as_form), + login_service: LoginService = Depends(get_service(LoginService)) +): try: jwt_token = login_service.login(request.username, request.password) except NotAuthorizeException as e: print(e) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_LOGIN_ERROR) - except JWTTokenVerifyException as e: + except JWTTokenVerifyException: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_SESSION_EXPIRED) - + verified_token = jwt_token.verify_token() # 普通の認証だと、`cognito:username`に入る。 user_id = verified_token.user_id @@ -92,7 +96,7 @@ def login( user_flg=user_record.mntuser_flg ) session_key = set_session(session_model) - + response = RedirectResponse( url='/menu', status_code=status.HTTP_303_SEE_OTHER, @@ -103,9 +107,9 @@ def login( @router.get('/authorize') def sso_authorize( - code:Union[str, None]=Depends(code_security), - login_service: LoginService=Depends(get_service(LoginService)) - ) -> Response: + code: Union[str, None] = Depends(code_security), + login_service: LoginService = Depends(get_service(LoginService)) +) -> Response: if not code: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=constants.LOGOUT_REASON_NOT_LOGIN) @@ -114,7 +118,7 @@ def sso_authorize( try: # トークン検証 verified_token = jwt_token.verify_token() - except JWTTokenVerifyException as e: + except JWTTokenVerifyException: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_SESSION_EXPIRED) # トークンからユーザーIDを取得 diff --git a/ecs/jskult-webapp/src/controller/logout.py b/ecs/jskult-webapp/src/controller/logout.py index 8cad1b5f..79de281c 100644 --- a/ecs/jskult-webapp/src/controller/logout.py +++ b/ecs/jskult-webapp/src/controller/logout.py @@ -14,12 +14,14 @@ router = APIRouter() ######################### # Views # ######################### + + @router.get('/', response_class=HTMLResponse) def logout_view( - request: Request, - reason: Optional[str] = None, - session: Union[UserSession, None]=Depends(verify_session) - ): + request: Request, + reason: Optional[str] = None, + session: Union[UserSession, None] = Depends(verify_session) +): redirect_to = '/login/userlogin' link_text = 'MeDaCA機能メニューへ' if session is not None and session.user_flg == '1': diff --git a/ecs/jskult-webapp/src/controller/menu.py b/ecs/jskult-webapp/src/controller/menu.py index a9e450bb..27065dc7 100644 --- a/ecs/jskult-webapp/src/controller/menu.py +++ b/ecs/jskult-webapp/src/controller/menu.py @@ -16,11 +16,13 @@ router.route_class = AuthenticatedRoute ######################### # Views # ######################### + + @router.get('/', response_class=HTMLResponse) def menu_view( - request: Request, - batch_status_service:BatchStatusService=Depends(get_service(BatchStatusService)) - ): + request: Request, + batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService)) +): session: UserSession = request.session # 日付マスターからバッチ情報を取得する hdke_tbl_record = batch_status_service.hdke_table_record diff --git a/ecs/jskult-webapp/src/core/tasks.py b/ecs/jskult-webapp/src/core/tasks.py index 42352465..93f4c8f5 100644 --- a/ecs/jskult-webapp/src/core/tasks.py +++ b/ecs/jskult-webapp/src/core/tasks.py @@ -18,4 +18,4 @@ def create_stop_app_handler(app: FastAPI) -> Callable: def stop_app() -> None: close_db(app) - return stop_app \ No newline at end of file + return stop_app diff --git a/ecs/jskult-webapp/src/db/sql_condition.py b/ecs/jskult-webapp/src/db/sql_condition.py index dfd73858..d0d645c7 100644 --- a/ecs/jskult-webapp/src/db/sql_condition.py +++ b/ecs/jskult-webapp/src/db/sql_condition.py @@ -16,13 +16,14 @@ class SQLCondition: self.column = column self.operator = operator self.param = param - self.literal=literal - + self.literal = literal + def apply(self): # literalがFalseならプレースホルダー。Trueだったならは固定値。 param = f':{self.param}' if self.literal is False else self.param return f' {self.column} {self.operator} {param}' + # 定数 EQ = '=' NE = '<>' @@ -32,4 +33,4 @@ GE = '>=' LE = '<=' LIKE = 'LIKE' IS = 'IS' -IS_NOT = 'IS NOT' \ No newline at end of file +IS_NOT = 'IS NOT' diff --git a/ecs/jskult-webapp/src/depends/auth.py b/ecs/jskult-webapp/src/depends/auth.py index 3310df6f..893c95e4 100644 --- a/ecs/jskult-webapp/src/depends/auth.py +++ b/ecs/jskult-webapp/src/depends/auth.py @@ -17,26 +17,28 @@ code_security = APIKeyQuery(name='code', auto_error=False) def get_current_session(session_key=Depends(cookie_security)): if session_key is None: return None - + session = get_session(session_key) # sessionが存在しない場合はNoneが返る return session -def check_session_expired(session:Union[UserSession, None]=Depends(get_current_session)): +def check_session_expired(session: Union[UserSession, None] = Depends(get_current_session)): """セッションの最後にアクセスした時間が、セッション有効期限切れであるかどうかをチェックする""" if session is None: return None last_access_time = session.last_access_time - session_expired_period = datetime.datetime.fromtimestamp(last_access_time) + datetime.timedelta(minutes=environment.SESSION_EXPIRE_MINUTE) + session_expired_period = datetime.datetime.fromtimestamp( + last_access_time) + datetime.timedelta(minutes=environment.SESSION_EXPIRE_MINUTE) if session_expired_period < datetime.datetime.now(): return None - + return session -def verify_session(session:Union[UserSession, None]=Depends(check_session_expired)): + +def verify_session(session: Union[UserSession, None] = Depends(check_session_expired)): if session is None: return None jwt_token = JWTToken(session.id_token, session.refresh_token) diff --git a/ecs/jskult-webapp/src/depends/database.py b/ecs/jskult-webapp/src/depends/database.py index f764d5c5..c5b28042 100644 --- a/ecs/jskult-webapp/src/depends/database.py +++ b/ecs/jskult-webapp/src/depends/database.py @@ -14,4 +14,4 @@ def get_database(request: Request) -> Database: def get_repository(Repo_type: Type[BaseRepository]) -> Callable: def get_repo(db: Database = Depends(get_database)) -> Type[BaseRepository]: return Repo_type(db) - return get_repo \ No newline at end of file + return get_repo diff --git a/ecs/jskult-webapp/src/depends/services.py b/ecs/jskult-webapp/src/depends/services.py index d93a0c97..40bf31cf 100644 --- a/ecs/jskult-webapp/src/depends/services.py +++ b/ecs/jskult-webapp/src/depends/services.py @@ -1,7 +1,6 @@ from typing import Callable, Type from fastapi import Depends -from starlette.requests import Request from src.db.database import Database from src.depends.database import get_database @@ -9,8 +8,8 @@ from src.services.base_service import BaseService def get_service(Service_type: Type[BaseService]) -> Callable: - def get_service(db: Database=Depends(get_database)) -> Type[BaseService]: + def get_service(db: Database = Depends(get_database)) -> Type[BaseService]: repositories = {key: repository(db) for key, repository in Service_type.REPOSITORIES.items()} clients = {key: client() for key, client in Service_type.CLIENTS.items()} return Service_type(repositories=repositories, clients=clients) - return get_service \ No newline at end of file + return get_service diff --git a/ecs/jskult-webapp/src/error/exceptions.py b/ecs/jskult-webapp/src/error/exceptions.py index 53b299db..4e73515c 100644 --- a/ecs/jskult-webapp/src/error/exceptions.py +++ b/ecs/jskult-webapp/src/error/exceptions.py @@ -7,18 +7,22 @@ class MeDaCaException(Exception): """Webアプリの共通例外""" pass + class NotAuthorizeException(MeDaCaException): """認証失敗の例外""" pass + class JWTTokenVerifyException(MeDaCaException): """トークン検証失敗の例外""" pass + class DBException(MeDaCaException): """DB関連の例外""" pass + class UnexpectedException(MeDaCaException): """予期しない例外""" diff --git a/ecs/jskult-webapp/src/model/db/hdke_tbl.py b/ecs/jskult-webapp/src/model/db/hdke_tbl.py index 920cb2e3..944581d5 100644 --- a/ecs/jskult-webapp/src/model/db/hdke_tbl.py +++ b/ecs/jskult-webapp/src/model/db/hdke_tbl.py @@ -1,4 +1,3 @@ -from datetime import datetime from typing import Optional from src.model.db.base_db_model import BaseDBModel diff --git a/ecs/jskult-webapp/src/model/db/user_master.py b/ecs/jskult-webapp/src/model/db/user_master.py index 5b55c014..82fe50f9 100644 --- a/ecs/jskult-webapp/src/model/db/user_master.py +++ b/ecs/jskult-webapp/src/model/db/user_master.py @@ -28,9 +28,9 @@ class UserMasterModel(BaseDBModel): def is_enable_user(self): return self.enabled_flg == 'Y' - + def is_maintenance_user(self): return self.mntuser_flg == '1' - + def is_groupware_user(self): return self.mntuser_flg == '0' diff --git a/ecs/jskult-webapp/src/model/internal/jwt_token.py b/ecs/jskult-webapp/src/model/internal/jwt_token.py index a10635ad..d7544125 100644 --- a/ecs/jskult-webapp/src/model/internal/jwt_token.py +++ b/ecs/jskult-webapp/src/model/internal/jwt_token.py @@ -15,11 +15,11 @@ class JWTToken: refresh_token: str verified_jwt: Optional[dict] - def __init__(self, id_token: str, refresh_token: str, verified_jwt: dict=None) -> None: + def __init__(self, id_token: str, refresh_token: str, verified_jwt: dict = None) -> None: self.id_token = id_token self.refresh_token = refresh_token self.verified_jwt = verified_jwt - + @property def verified_token(self): if self.verified_jwt is None: @@ -29,7 +29,7 @@ class JWTToken: @property def user_id(self): verified_token = self.verified_token - + user_id: str = None identities: dict = verified_token.get('identities') if identities is not None: @@ -63,7 +63,7 @@ class JWTToken: 'code': code, 'redirect_uri': environment.COGNITO_REDIRECT_URI } - + message = bytes(f'{environment.COGNITO_CLIENT_ID}:{environment.COGNITO_CLIENT_SECRET}', 'utf8') auth_header_value = base64.b64encode(message).decode() request_headers = { @@ -76,7 +76,7 @@ class JWTToken: raise JWTTokenVerifyException(res.text) token_response = json.loads(res.text) - + return cls(id_token=token_response['id_token'], refresh_token=token_response['refresh_token']) @classmethod @@ -99,7 +99,7 @@ class JWTToken: 'refresh_token': refresh_token, 'redirect_uri': environment.COGNITO_REDIRECT_URI } - + message = bytes(f'{environment.COGNITO_CLIENT_ID}:{environment.COGNITO_CLIENT_SECRET}', 'utf8') auth_header_value = base64.b64encode(message).decode() request_headers = { @@ -117,13 +117,13 @@ class JWTToken: def verify_token(self): if self.id_token is None: raise Exception('アクセストークンがない') - + issuer = f'https://cognito-idp.{environment.AWS_REGION}.amazonaws.com/{environment.COGNITO_USER_POOL_ID}' jwks_url = f'{issuer}/.well-known/jwks.json' - + jwks_client = jwt.PyJWKClient(jwks_url) signing_key = jwks_client.get_signing_key_from_jwt(self.id_token) - + try: verified_jwt = jwt.decode( self.id_token, diff --git a/ecs/jskult-webapp/src/model/internal/session.py b/ecs/jskult-webapp/src/model/internal/session.py index 5235d0f3..15739c28 100644 --- a/ecs/jskult-webapp/src/model/internal/session.py +++ b/ecs/jskult-webapp/src/model/internal/session.py @@ -27,7 +27,7 @@ class UserSession(DynamoDBTableModel): @classmethod def new_last_access_time(cls): return datetime.datetime.now().timestamp() - + @classmethod def new_record_expiration_time(cls, expire=environment.SESSION_EXPIRE_MINUTE): last_access_time = datetime.datetime.fromtimestamp(cls.new_last_access_time()) @@ -35,8 +35,8 @@ class UserSession(DynamoDBTableModel): @classmethod def new( - cls, user_id, id_token, refresh_token, csrf_token, doc_flg, inst_flg, bio_flg, master_mainte_flg, user_flg - ): + cls, user_id, id_token, refresh_token, csrf_token, doc_flg, inst_flg, bio_flg, master_mainte_flg, user_flg + ): return cls( session_key=str(uuid.uuid4()), user_id=user_id, @@ -47,7 +47,7 @@ class UserSession(DynamoDBTableModel): inst_flg=inst_flg, bio_flg=bio_flg, master_mainte_flg=master_mainte_flg, - user_flg=user_flg, + user_flg=user_flg, last_access_time=cls.new_last_access_time(), record_expiration_time=cls.new_record_expiration_time() ) diff --git a/ecs/jskult-webapp/src/model/request/bio.py b/ecs/jskult-webapp/src/model/request/bio.py index a32bb2f2..63994e07 100644 --- a/ecs/jskult-webapp/src/model/request/bio.py +++ b/ecs/jskult-webapp/src/model/request/bio.py @@ -50,7 +50,7 @@ class BioModel(BaseModel): ctrl_rev_hsdnymd_srk_to, ikoFlg ) - + @classmethod def as_body( cls, @@ -79,7 +79,6 @@ class BioModel(BaseModel): ctrl_rev_hsdnymd_srk_to, ikoFlg ) - def __convert_request_param( cls, @@ -134,4 +133,4 @@ class BioModel(BaseModel): rev_hsdnymd_srk_from=rev_hsdnymd_srk_from, rev_hsdnymd_srk_to=rev_hsdnymd_srk_to, ikoFlg=ikoFlg - ) \ No newline at end of file + ) diff --git a/ecs/jskult-webapp/src/model/request/bio_download.py b/ecs/jskult-webapp/src/model/request/bio_download.py index 8b4b5ae9..51bea2e3 100644 --- a/ecs/jskult-webapp/src/model/request/bio_download.py +++ b/ecs/jskult-webapp/src/model/request/bio_download.py @@ -1,5 +1,3 @@ -from typing import Optional - from fastapi import Body from pydantic import BaseModel @@ -7,7 +5,7 @@ from pydantic import BaseModel class BioDownloadModel(BaseModel): user_id: str kind: str - + @classmethod def as_body( cls, diff --git a/ecs/jskult-webapp/src/model/request/login.py b/ecs/jskult-webapp/src/model/request/login.py index f0c431ab..0e7710cb 100644 --- a/ecs/jskult-webapp/src/model/request/login.py +++ b/ecs/jskult-webapp/src/model/request/login.py @@ -5,7 +5,7 @@ from pydantic import BaseModel class LoginModel(BaseModel): username: str password: str - + @classmethod def as_form( cls, diff --git a/ecs/jskult-webapp/src/model/view/bio_disp_model.py b/ecs/jskult-webapp/src/model/view/bio_disp_model.py index 0f1a11fd..42bd0f42 100644 --- a/ecs/jskult-webapp/src/model/view/bio_disp_model.py +++ b/ecs/jskult-webapp/src/model/view/bio_disp_model.py @@ -7,12 +7,12 @@ from src.util.sanitize import sanitize class BisDisplayModel(BioSalesViewModel): def __init__(self, param: BioSalesViewModel) -> None: super().__init__(**param.dict()) - + # 区分・フラグの正式名称を設定 self.slip_org_kbn = constants.SLIP_ORG_KBN_FULL_NAME.get(self.slip_org_kbn) self.data_kbn = constants.DATA_KBN_JP_NAME.get(self.data_kbn) self.lot_no_err_flg = constants.LOT_NO_ERR_FLG_JP_NAME.get(self.lot_no_err_flg) - + # 訂正前伝票管理番号がセットされているときのみ修正日時、修正者、エラー詳細種別をセット if (self.bef_slip_mgt_no is None): self.ins_dt = "" diff --git a/ecs/jskult-webapp/src/model/view/bio_view_model.py b/ecs/jskult-webapp/src/model/view/bio_view_model.py index 0657bd3c..1c6c98e3 100644 --- a/ecs/jskult-webapp/src/model/view/bio_view_model.py +++ b/ecs/jskult-webapp/src/model/view/bio_view_model.py @@ -20,7 +20,7 @@ class BioViewModel(BaseModel): phm_models: list[PharmacyProductMasterModel] bio_data: Optional[list[BisDisplayModel]] = [] form_data: Optional[BioModel] - + def display_wholesaler_names(self): display_names = [ f'{whs_model.rec_whs_cd}-{whs_model.rec_whs_sub_cd}:{whs_model.nm}' @@ -41,7 +41,7 @@ class BioViewModel(BaseModel): def display_data_kbn(self): return OrderedDict( { - '' : '', + '': '', '0': '正常', '1': 'ロットエラー', '3': 'ロット不明', @@ -59,14 +59,16 @@ class BioViewModel(BaseModel): if not self.is_form_submitted(): return '' - form_wholesaler_full_name = f'{self.form_data.wholesaler_code}-{self.form_data.wholesaler_sub_code}:{self.form_data.wholesaler_name}' + form_wholesaler_full_name = \ + f'{self.form_data.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 '' @@ -84,17 +86,17 @@ class BioViewModel(BaseModel): return '' return self.form_data.rec_lot_num or '' - + def is_selected_data_kbn(self, selected_data_kbn): if not self.is_form_submitted(): return '' - + return self._selected_value(self.form_data.data_kbn, selected_data_kbn) def is_selected_maker_cd(self, selected_maker_cd): if not self.is_form_submitted(): return '' - + return self._selected_value(self.form_data.maker_cd, selected_maker_cd) def is_input_rev_hsdnymd_srk_from(self): @@ -108,13 +110,13 @@ class BioViewModel(BaseModel): return '' return self._format_date_string(self.form_data.rev_hsdnymd_srk_to) - + def is_checked_iko_flg(self): if not self.is_form_submitted(): return '' return 'checked' if self.form_data.ikoFlg else '' - + def disabled_button(self): return 'disabled' if self.is_data_empty() or self.is_data_overflow_max_length() else '' @@ -123,7 +125,7 @@ class BioViewModel(BaseModel): def is_data_empty(self): return len(self.bio_data) == 0 - + def is_data_overflow_max_length(self): return len(self.bio_data) >= environment.BIO_SEARCH_RESULT_MAX_COUNT diff --git a/ecs/jskult-webapp/src/model/view/logout_view_model.py b/ecs/jskult-webapp/src/model/view/logout_view_model.py index b219918a..6935a309 100644 --- a/ecs/jskult-webapp/src/model/view/logout_view_model.py +++ b/ecs/jskult-webapp/src/model/view/logout_view_model.py @@ -7,4 +7,4 @@ class LogoutViewModel(BaseModel): subtitle: str = 'MeDaCA Logout' redirect_to: Optional[str] reason: Optional[str] - link_text:Optional[str] + link_text: Optional[str] diff --git a/ecs/jskult-webapp/src/model/view/user_view_model.py b/ecs/jskult-webapp/src/model/view/user_view_model.py index 7d36fba2..3ef9ca12 100644 --- a/ecs/jskult-webapp/src/model/view/user_view_model.py +++ b/ecs/jskult-webapp/src/model/view/user_view_model.py @@ -7,9 +7,9 @@ class UserViewModel(BaseModel): bio_flg: str # AUTH_FLG1 doc_flg: str # AUTH_FLG2 inst_flg: str # AUTH_FLG3 - master_mainte_flg: str # AUTH_FLG4 + master_mainte_flg: str # AUTH_FLG4 user_flg: Optional[str] # MNTUSER_FLG - + def has_ult_doctor_permission(self): return self.doc_flg == '1' @@ -21,6 +21,6 @@ class UserViewModel(BaseModel): def has_master_maintenance_permission(self): return self.master_mainte_flg == '1' - + def is_maintenance_user(self): return self.user_flg == '1' diff --git a/ecs/jskult-webapp/src/repositories/base_repository.py b/ecs/jskult-webapp/src/repositories/base_repository.py index 60f9ba33..20aac09f 100644 --- a/ecs/jskult-webapp/src/repositories/base_repository.py +++ b/ecs/jskult-webapp/src/repositories/base_repository.py @@ -8,8 +8,9 @@ from src.model.db.base_db_model import BaseDBModel class BaseRepository(metaclass=ABCMeta): - + _database: Database + def __init__(self, db: Database) -> None: self._database = db @@ -29,10 +30,9 @@ class BaseRepository(metaclass=ABCMeta): """DBの取得結果をデータフレームにして返す""" pass - def _to_data_frame(self, query, parameter: BaseDBModel): """DBの取得結果をデータフレームに変換する""" - params = params=parameter.dict() + params = params = parameter.dict() sql_query = pd.read_sql( text(query), diff --git a/ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py b/ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py index 69e50783..c9a60c30 100644 --- a/ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py +++ b/ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py @@ -55,7 +55,6 @@ class BioSalesViewRepository(BaseRepository): finally: self._database.disconnect() - def fetch_as_data_frame(self, parameter: BioModel): try: self._database.connect() @@ -95,7 +94,7 @@ class BioSalesViewRepository(BaseRepository): if is_not_empty(parameter.rec_lot_num): rec_lot_num = parameter.rec_lot_num # あいまい検索文字列('%')が含まれる場合は'LIKE'、でなければ'='で検索 - rec_lot_num_comparator = condition.LIKE if rec_lot_num in '%' else condition.EQ + rec_lot_num_comparator = condition.LIKE if rec_lot_num in '%' else condition.EQ where_clauses.append(SQLCondition('rec_lot_num', rec_lot_num_comparator, 'rec_lot_num')) # データ区分 if is_not_empty(parameter.data_kbn): @@ -112,7 +111,7 @@ class BioSalesViewRepository(BaseRepository): # 移行フラグ # チェックが入っていない場合、移行対象(IKO_FLG = '*')を省く if parameter.ikoFlg is None: - where_clauses.append(SQLCondition('iko_flg', condition.IS, 'NULL', literal=True)) + 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)) diff --git a/ecs/jskult-webapp/src/repositories/hdke_tbl_repository.py b/ecs/jskult-webapp/src/repositories/hdke_tbl_repository.py index e3a72f23..6927533b 100644 --- a/ecs/jskult-webapp/src/repositories/hdke_tbl_repository.py +++ b/ecs/jskult-webapp/src/repositories/hdke_tbl_repository.py @@ -1,5 +1,4 @@ from src.model.db.hdke_tbl import HdkeTblModel -from src.model.request.bio import BioModel from src.repositories.base_repository import BaseRepository diff --git a/ecs/jskult-webapp/src/repositories/pharmacy_product_master_repository.py b/ecs/jskult-webapp/src/repositories/pharmacy_product_master_repository.py index f6523612..2720955f 100644 --- a/ecs/jskult-webapp/src/repositories/pharmacy_product_master_repository.py +++ b/ecs/jskult-webapp/src/repositories/pharmacy_product_master_repository.py @@ -3,15 +3,15 @@ from src.repositories.base_repository import BaseRepository class PharmacyProductMasterRepository(BaseRepository): - + FETCH_SQL = """\ - SELECT + SELECT CONCAT(IFNULL(mkr_cd, ''), ' ', IFNULL(mkr_inf_1, '')) AS mkr_cd_nm FROM src05.phm_prd_mst_v t1 - INNER JOIN + INNER JOIN ( - SELECT + SELECT prd_cd,MAX(sub_no) AS sno FROM src05.phm_prd_mst_v diff --git a/ecs/jskult-webapp/src/repositories/user_master_repository.py b/ecs/jskult-webapp/src/repositories/user_master_repository.py index e5e1ba41..60228074 100644 --- a/ecs/jskult-webapp/src/repositories/user_master_repository.py +++ b/ecs/jskult-webapp/src/repositories/user_master_repository.py @@ -1,5 +1,4 @@ from src.model.db.user_master import UserMasterModel -from src.model.request.bio import BioModel from src.repositories.base_repository import BaseRepository @@ -27,4 +26,4 @@ class UserMasterRepository(BaseRepository): print(f"[ERROR] DB Error : Exception={e.args}") raise e finally: - self._database.disconnect() \ No newline at end of file + self._database.disconnect() diff --git a/ecs/jskult-webapp/src/repositories/wholesaler_master_repository.py b/ecs/jskult-webapp/src/repositories/wholesaler_master_repository.py index 0f625bc7..246eea5e 100644 --- a/ecs/jskult-webapp/src/repositories/wholesaler_master_repository.py +++ b/ecs/jskult-webapp/src/repositories/wholesaler_master_repository.py @@ -3,7 +3,7 @@ from src.repositories.base_repository import BaseRepository class WholesalerMasterRepository(BaseRepository): - + FETCH_SQL = """\ SELECT DISTINCT b.rec_whs_cd, @@ -17,7 +17,7 @@ class WholesalerMasterRepository(BaseRepository): FROM src05.whs_mst_v WHERE (SELECT STR_TO_DATE(syor_date, '%Y%m%d') FROM src05.hdke_tbl) BETWEEN start_date AND end_date ) v2 - ON b.v_whs_cd = v2.v_whs_cd + ON b.v_whs_cd = v2.v_whs_cd AND v2.rec_sts_kbn <> '9' ORDER BY b.rec_whs_cd, b.rec_whs_sub_cd , b.whs_nm DESC """ diff --git a/ecs/jskult-webapp/src/router/session_router.py b/ecs/jskult-webapp/src/router/session_router.py index 7a4ada09..e2699784 100644 --- a/ecs/jskult-webapp/src/router/session_router.py +++ b/ecs/jskult-webapp/src/router/session_router.py @@ -13,12 +13,14 @@ 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: """前後処理を付加するルートハンドラーを返す @@ -60,6 +62,7 @@ class MeDaCaRoute(APIRoute): Request: 加工後のRequestインスタンス """ return request + async def post_process_route(self, request: Request, response: Response) -> Response: """ルートハンドラーの事後処理 @@ -71,6 +74,7 @@ class MeDaCaRoute(APIRoute): """ return response + class BeforeCheckSessionRoute(MeDaCaRoute): """事前処理として、セッションチェックを行うルートハンドラー @@ -86,12 +90,14 @@ class BeforeCheckSessionRoute(MeDaCaRoute): verified_session = verify_session(checked_session) # セッションが有効でない場合、エラーにする if verified_session is None: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=constants.LOGOUT_REASON_SESSION_EXPIRED) + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, + detail=constants.LOGOUT_REASON_SESSION_EXPIRED) scope = request.scope scope['session'] = verified_session session_request = Request(receive=request.receive, scope=scope) return session_request + class AfterSetCookieSessionRoute(MeDaCaRoute): """事後処理として、セッションキーをcookieに設定するカスタムルートハンドラー @@ -110,16 +116,18 @@ class AfterSetCookieSessionRoute(MeDaCaRoute): response.set_cookie( key='session', value=session_key, - max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける + max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける secure=True, httponly=True ) return response + class AuthenticatedRoute(BeforeCheckSessionRoute, AfterSetCookieSessionRoute): async def pre_process_route(self, request: Request): request = await super().pre_process_route(request) return request + async def post_process_route(self, request: Request, response: Response): response = await super().post_process_route(request, response) return response diff --git a/ecs/jskult-webapp/src/services/base_service.py b/ecs/jskult-webapp/src/services/base_service.py index a2669225..7fd71b3b 100644 --- a/ecs/jskult-webapp/src/services/base_service.py +++ b/ecs/jskult-webapp/src/services/base_service.py @@ -9,5 +9,6 @@ class BaseService(metaclass=ABCMeta): REPOSITORIES: dict[str, BaseRepository] = {} # 各サービスが依存するAWS APIクライアントクラスのマップ CLIENTS: dict[str, AWSAPIClient] = {} + def __init__(self, repositories: dict[str, BaseRepository], clients: dict[str, AWSAPIClient]) -> None: pass diff --git a/ecs/jskult-webapp/src/services/batch_status_service.py b/ecs/jskult-webapp/src/services/batch_status_service.py index e30f0bed..c9f6c6a7 100644 --- a/ecs/jskult-webapp/src/services/batch_status_service.py +++ b/ecs/jskult-webapp/src/services/batch_status_service.py @@ -13,6 +13,7 @@ class BatchStatusService(BaseService): } hdke_table_repository: HdkeTblRepository __hdke_table_record: list[HdkeTblModel] = [] + def __init__(self, repositories: dict[str, BaseRepository], clients: dict[str, AWSAPIClient]) -> None: super().__init__(repositories, clients) self.hdke_table_repository = repositories['hdke_table_repository'] @@ -38,4 +39,3 @@ class BatchStatusService(BaseService): # 日付マスタのレコードがない場合は例外とする if len(self.__hdke_table_record) == 0: raise DBException('日付テーブルのレコードが存在しません') - diff --git a/ecs/jskult-webapp/src/services/bio_view_service.py b/ecs/jskult-webapp/src/services/bio_view_service.py index 99726d23..8fa05778 100644 --- a/ecs/jskult-webapp/src/services/bio_view_service.py +++ b/ecs/jskult-webapp/src/services/bio_view_service.py @@ -26,15 +26,16 @@ class BioViewService(BaseService): 'phm_repository': PharmacyProductMasterRepository, 'bio_sales_repository': BioSalesViewRepository } - + CLIENTS = { 's3_client': S3Client } - + whs_repository: WholesalerMasterRepository phm_repository: PharmacyProductMasterRepository bio_sales_repository: BioSalesViewRepository s3_client: S3Client + def __init__(self, repositories: dict[str, BaseRepository], clients: dict[str, AWSAPIClient]) -> None: super().__init__(repositories, clients) self.whs_repository = repositories['whs_repository'] @@ -45,7 +46,7 @@ class BioViewService(BaseService): def prepare_bio_view( self, session: UserSession - ) ->BioViewModel: + ) -> BioViewModel: # 卸リストを取得 wholesalers = self.whs_repository.fetch_all() # 製品リストを取得 @@ -72,7 +73,8 @@ class BioViewService(BaseService): 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}_{timestamp:%Y%m%d%H%M%S%f}.xlsx') + output_file_path = path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, + f'Result_{user_id}_{timestamp:%Y%m%d%H%M%S%f}.xlsx') # テンプレートファイルをコピーして出力ファイルの枠だけを作る shutil.copyfile( @@ -87,12 +89,13 @@ class BioViewService(BaseService): # DF内のヘッダと連番を書き込みたくない場合、`header`と`index`をFalseに指定する。 # `startrow`と`startcol`で、Excelの書き込み位置を決定する。省略した場合はA1セルから書く。 data_frame.to_excel(writer, header=False, index=False, startrow=1, startcol=0) - + return output_file_path def write_csv_file(self, data_frame: pd.DataFrame, user_id: str, header: list[str], timestamp: datetime): # csvに書き込み - output_file_path = path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, f'Result_{user_id}_{timestamp:%Y%m%d%H%M%S%f}.csv') + output_file_path = path.join(constants.BIO_TEMPORARY_FILE_DIR_PATH, + f'Result_{user_id}_{timestamp:%Y%m%d%H%M%S%f}.csv') # 横長のDataFrameとするため、ヘッダーの加工処理 header_data = {} for df_column, header_column in zip(data_frame.columns, header): @@ -108,10 +111,10 @@ class BioViewService(BaseService): def upload_bio_data_file(self, local_file_path: str) -> None: bucket_name = environment.BIO_ACCESS_LOG_BUCKET # TODO: フォルダを変える - file_key =f'bio/{path.basename(local_file_path)}' + 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: + + 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)}' diff --git a/ecs/jskult-webapp/src/services/login_service.py b/ecs/jskult-webapp/src/services/login_service.py index 6442c82a..aa1f37fd 100644 --- a/ecs/jskult-webapp/src/services/login_service.py +++ b/ecs/jskult-webapp/src/services/login_service.py @@ -17,7 +17,7 @@ class LoginService(BaseService): REPOSITORIES = { 'user_repository': UserMasterRepository } - + CLIENTS = { 'cognito_client': CognitoClient } @@ -41,10 +41,10 @@ class LoginService(BaseService): raise e return JWTToken(id_token, refresh_token) - + def login_with_security_code(self, code: str) -> JWTToken: return JWTToken.request(code) - + def logged_in_user(self, user_id): user_record: UserMasterModel = self.user_repository.fetch_one({'user_id': user_id}) return user_record diff --git a/ecs/jskult-webapp/src/services/session_service.py b/ecs/jskult-webapp/src/services/session_service.py index 92a7a193..e93d9dde 100644 --- a/ecs/jskult-webapp/src/services/session_service.py +++ b/ecs/jskult-webapp/src/services/session_service.py @@ -6,6 +6,7 @@ def set_session(session: UserSession) -> str: session.save() return session.session_key + def get_session(key: str) -> UserSession: try: session = UserSession.get(hash_key=key, consistent_read=True) diff --git a/ecs/jskult-webapp/src/system_var/environment.py b/ecs/jskult-webapp/src/system_var/environment.py index aed9916c..7d82d560 100644 --- a/ecs/jskult-webapp/src/system_var/environment.py +++ b/ecs/jskult-webapp/src/system_var/environment.py @@ -19,4 +19,4 @@ 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']) \ No newline at end of file +SESSION_EXPIRE_MINUTE = int(os.environ['SESSION_EXPIRE_MINUTE']) From 6f715e96e5b7aff1b60ddefc4b06b837d460bf6a Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Fri, 12 May 2023 13:46:07 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20=E3=83=AB=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=83=91=E3=82=B9=E3=81=B8=E3=81=AE=E3=82=A2=E3=82=AF=E3=82=BB?= =?UTF-8?q?=E3=82=B9=E3=81=AF=E9=A1=A7=E5=AE=A2=E3=83=AD=E3=82=B0=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E7=94=BB=E9=9D=A2=E3=81=AB=E3=83=AA=E3=83=80=E3=82=A4?= =?UTF-8?q?=E3=83=AC=E3=82=AF=E3=83=88=E3=81=95=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/jskult-webapp/src/controller/root.py | 11 +++++++++++ ecs/jskult-webapp/src/main.py | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 ecs/jskult-webapp/src/controller/root.py diff --git a/ecs/jskult-webapp/src/controller/root.py b/ecs/jskult-webapp/src/controller/root.py new file mode 100644 index 00000000..c02471ca --- /dev/null +++ b/ecs/jskult-webapp/src/controller/root.py @@ -0,0 +1,11 @@ +from fastapi import APIRouter +from fastapi.responses import RedirectResponse +from starlette import status + +router = APIRouter() + + +@router.get('/') +def redirect_to_user_login(): + # ルートパスへのアクセスは、顧客ユーザーログイン画面にリダイレクトさせる + return RedirectResponse(url='/login/userlogin', status_code=status.HTTP_303_SEE_OTHER) diff --git a/ecs/jskult-webapp/src/main.py b/ecs/jskult-webapp/src/main.py index f00f5ac3..bd95af18 100644 --- a/ecs/jskult-webapp/src/main.py +++ b/ecs/jskult-webapp/src/main.py @@ -5,7 +5,8 @@ from fastapi.staticfiles import StaticFiles from starlette import status import src.static as static -from src.controller import bio, bio_download, healthcheck, login, logout, menu +from src.controller import (bio, bio_download, healthcheck, login, logout, + menu, root) from src.core import tasks from src.error.exception_handler import http_exception_handler from src.error.exceptions import UnexpectedException @@ -14,6 +15,8 @@ app = FastAPI() # 静的ファイルをマウント app.mount('/static', StaticFiles(directory=path.dirname(static.__file__)), name='static') +# ルートパス。顧客ログイン画面にリダイレクトさせる +app.include_router(root.router) # ログイン関連のルーター app.include_router(login.router, prefix='/login') # ログアウト関連のルーター From 04396eb1d53b8f571bd92b6c429784595d27e0f1 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 30 May 2023 15:20:40 +0900 Subject: [PATCH 05/15] =?UTF-8?q?feat:=20=E3=82=AB=E3=83=A9=E3=83=A0?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=81=AB=E5=AF=BE=E5=BF=9C=EF=BC=88=E7=94=BB?= =?UTF-8?q?=E9=9D=A2=E3=81=8C=E3=81=BE=E3=81=A0=E8=A1=A8=E7=A4=BA=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/jskult-webapp/.env.example | 2 + ecs/jskult-webapp/README.md | 2 +- ecs/jskult-webapp/src/controller/bio.py | 5 +- .../src/controller/bio_download.py | 8 +-- .../src/model/db/bio_sales_view.py | 24 +++---- .../src/model/db/pharmacy_product_master.py | 2 +- .../src/model/db/wholesaler_master.py | 4 +- .../src/model/view/bio_disp_model.py | 2 +- .../src/model/view/bio_view_model.py | 2 +- .../repositories/bio_sales_view_repository.py | 34 +++++----- .../pharmacy_product_master_repository.py | 8 +-- .../wholesaler_master_repository.py | 12 ++-- .../src/services/logout_service.py | 65 +++++++++++++++++++ ecs/jskult-webapp/src/system_var/constants.py | 18 ++--- .../src/system_var/environment.py | 3 + .../src/templates/bioSearchList.html | 50 +++++++------- 16 files changed, 158 insertions(+), 83 deletions(-) create mode 100644 ecs/jskult-webapp/src/services/logout_service.py diff --git a/ecs/jskult-webapp/.env.example b/ecs/jskult-webapp/.env.example index f330f3d5..dddde44d 100644 --- a/ecs/jskult-webapp/.env.example +++ b/ecs/jskult-webapp/.env.example @@ -21,5 +21,7 @@ DB_SCHEMA=src05 AWS_REGION=ap-northeast-1 AUTHORIZE_ENDPOINT=oauth2/authorize TOKEN_ENDPOINT=oauth2/token +LOGOUT_ENDPOINT=logout BIO_SEARCH_RESULT_MAX_COUNT=35000 SESSION_EXPIRE_MINUTE=20 +LOG_LEVEL=DEBUG \ No newline at end of file diff --git a/ecs/jskult-webapp/README.md b/ecs/jskult-webapp/README.md index 783f82a2..97632b8f 100644 --- a/ecs/jskult-webapp/README.md +++ b/ecs/jskult-webapp/README.md @@ -23,7 +23,7 @@ - Merck_NewDWH開発2021のWiki、[Python環境構築](https://nds-tyo.backlog.com/alias/wiki/1874930)を参照 - 「Pipenvの導入」までを行っておくこと - 構築完了後、プロジェクト配下で以下のコマンドを実行し、Pythonの仮想環境を作成する - - `pipenv install --python ` + - `pipenv install --python --dev` - この手順で出力される仮想環境のパスは、後述するVSCodeの設定手順で使用するため、控えておく - MySQLの環境構築 diff --git a/ecs/jskult-webapp/src/controller/bio.py b/ecs/jskult-webapp/src/controller/bio.py index 188560fb..108fc747 100644 --- a/ecs/jskult-webapp/src/controller/bio.py +++ b/ecs/jskult-webapp/src/controller/bio.py @@ -4,6 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request from starlette import status from src.depends.services import get_service +from src.logging.get_logger import get_logger from src.model.internal.session import UserSession from src.model.request.bio import BioModel from src.model.view.bio_view_model import BioViewModel @@ -16,6 +17,7 @@ from src.templates import templates router = APIRouter() router.route_class = AuthenticatedRoute +logger = get_logger('生物由来参照') ######################### # Views # @@ -32,6 +34,8 @@ def bio_view( # バッチ処理中の場合、機能を利用させない if batch_status_service.is_batch_processing(): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=constants.LOGOUT_REASON_BATCH_PROCESSING) + + logger.debug(f'UserId: {session.user_id}') # 検索項目の取得 bio = bio_service.prepare_bio_view(session) # セッション書き換え @@ -59,7 +63,6 @@ def search_bio( 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(): diff --git a/ecs/jskult-webapp/src/controller/bio_download.py b/ecs/jskult-webapp/src/controller/bio_download.py index 0d172e8a..863c41e4 100644 --- a/ecs/jskult-webapp/src/controller/bio_download.py +++ b/ecs/jskult-webapp/src/controller/bio_download.py @@ -77,10 +77,10 @@ async def download_bio_data( 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 '') + extract_df.loc[:, 'ins_dt'] = extract_df['bef_slip_mgt_num'].apply( + lambda bef_slip_mgt_num: extract_df['ins_dt'] if bef_slip_mgt_num is not None else '') + extract_df.loc[:, 'ins_usr'] = extract_df['bef_slip_mgt_num'].apply( + lambda bef_slip_mgt_num: extract_df['ins_usr'] if bef_slip_mgt_num is not None else '') # 種別によって出力を変える local_file_path = '' diff --git a/ecs/jskult-webapp/src/model/db/bio_sales_view.py b/ecs/jskult-webapp/src/model/db/bio_sales_view.py index 92288443..9fd90ed7 100644 --- a/ecs/jskult-webapp/src/model/db/bio_sales_view.py +++ b/ecs/jskult-webapp/src/model/db/bio_sales_view.py @@ -15,28 +15,28 @@ class BioSalesViewModel(BaseDBModel): rec_tran_kbn: Optional[str] rev_hsdnymd_wrk: Optional[str] rev_hsdnymd_srk: Optional[str] - rec_urag_no: Optional[str] - rec_comm_nm: Optional[str] - rec_nnskfcl_nm: Optional[str] - rec_nnsk_fcl_addr: Optional[str] + rec_urag_num: Optional[str] + rec_comm_name: Optional[str] + rec_nonyu_fcl_name: Optional[str] + rec_nonyu_fcl_addr: Optional[str] rec_lot_num: Optional[str] - rec_amt: Optional[str] + rec_qty: Optional[str] rec_ymd: Optional[str] sale_data_cat: Optional[str] - slip_file_nm: Optional[str] - slip_mgt_no: Optional[str] + slip_file_name: Optional[str] + slip_mgt_num: Optional[str] row_num: Optional[int] hsdn_ymd: Optional[str] exec_dt: Optional[str] v_tran_cd: Optional[int] - tran_kbn_nm: Optional[str] + tran_kbn_name: Optional[str] whs_org_cd: Optional[str] v_whsorg_cd: Optional[str] - whs_org_nm: Optional[str] + whs_org_name: Optional[str] whs_org_kn: Optional[str] v_whs_cd: Optional[int] - whs_nm: Optional[str] - nnsk_cd: Optional[str] + whs_name: Optional[str] + nonyu_fcl_cd: Optional[str] v_inst_cd: Optional[str] v_inst_kn: Optional[str] v_inst_nm: Optional[str] @@ -53,7 +53,7 @@ class BioSalesViewModel(BaseDBModel): fcl_exis_kbn: Optional[str] amt: Optional[int] slip_org_kbn: Optional[str] - bef_slip_mgt_no: Optional[str] + bef_slip_mgt_num: Optional[str] lot_no_err_flg: Optional[str] iko_flg: Optional[str] kjyo_ym: Optional[str] diff --git a/ecs/jskult-webapp/src/model/db/pharmacy_product_master.py b/ecs/jskult-webapp/src/model/db/pharmacy_product_master.py index 863fd87d..2f498246 100644 --- a/ecs/jskult-webapp/src/model/db/pharmacy_product_master.py +++ b/ecs/jskult-webapp/src/model/db/pharmacy_product_master.py @@ -4,4 +4,4 @@ from src.model.db.base_db_model import BaseDBModel class PharmacyProductMasterModel(BaseDBModel): - mkr_cd_nm: Optional[str] + mkr_cd_name: Optional[str] diff --git a/ecs/jskult-webapp/src/model/db/wholesaler_master.py b/ecs/jskult-webapp/src/model/db/wholesaler_master.py index 762711a5..b24f58b7 100644 --- a/ecs/jskult-webapp/src/model/db/wholesaler_master.py +++ b/ecs/jskult-webapp/src/model/db/wholesaler_master.py @@ -6,5 +6,5 @@ from src.model.db.base_db_model import BaseDBModel class WholesalerMasterModel(BaseDBModel): rec_whs_cd: Optional[str] rec_whs_sub_cd: Optional[str] - nm: Optional[str] - whs_nm: Optional[str] + name: Optional[str] + whs_name: Optional[str] diff --git a/ecs/jskult-webapp/src/model/view/bio_disp_model.py b/ecs/jskult-webapp/src/model/view/bio_disp_model.py index 42bd0f42..e78d4f29 100644 --- a/ecs/jskult-webapp/src/model/view/bio_disp_model.py +++ b/ecs/jskult-webapp/src/model/view/bio_disp_model.py @@ -14,6 +14,6 @@ class BisDisplayModel(BioSalesViewModel): self.lot_no_err_flg = constants.LOT_NO_ERR_FLG_JP_NAME.get(self.lot_no_err_flg) # 訂正前伝票管理番号がセットされているときのみ修正日時、修正者、エラー詳細種別をセット - if (self.bef_slip_mgt_no is None): + if (self.bef_slip_mgt_num is None): self.ins_dt = "" self.ins_usr = "" diff --git a/ecs/jskult-webapp/src/model/view/bio_view_model.py b/ecs/jskult-webapp/src/model/view/bio_view_model.py index 1c6c98e3..933f76a4 100644 --- a/ecs/jskult-webapp/src/model/view/bio_view_model.py +++ b/ecs/jskult-webapp/src/model/view/bio_view_model.py @@ -23,7 +23,7 @@ class BioViewModel(BaseModel): def display_wholesaler_names(self): display_names = [ - f'{whs_model.rec_whs_cd}-{whs_model.rec_whs_sub_cd}:{whs_model.nm}' + f'{whs_model.rec_whs_cd}-{whs_model.rec_whs_sub_cd}:{whs_model.name}' for whs_model in self.whs_models ] return display_names diff --git a/ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py b/ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py index c9a60c30..5e590314 100644 --- a/ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py +++ b/ecs/jskult-webapp/src/repositories/bio_sales_view_repository.py @@ -1,10 +1,13 @@ from src.db import sql_condition as condition from src.db.sql_condition import SQLCondition +from src.logging.get_logger import get_logger from src.model.db.bio_sales_view import BioSalesViewModel from src.model.request.bio import BioModel from src.repositories.base_repository import BaseRepository from src.util.string_util import is_not_empty +logger = get_logger('生物由来参照') + class BioSalesViewRepository(BaseRepository): FETCH_SQL = """\ @@ -12,19 +15,19 @@ class BioSalesViewRepository(BaseRepository): ( CASE WHEN LEFT(bs.v_tran_cd, 1) = 2 - AND bs.amt >= 1 THEN CONCAT('-', bs.amt) - ELSE bs.amt + AND bs.qty >= 1 THEN CONCAT('-', bs.qty) + ELSE bs.qty END ) AS amt_fugo, bs.*, - ln.ser_no, + ln.ser_num, ln.lot_num, ln.expr_dt FROM src05.bio_sales_view bs LEFT OUTER JOIN src05.lot_num_mst ln - ON bs.mkr_cd = ln.ser_no + ON bs.mkr_cd = ln.ser_num AND bs.rec_lot_num = ln.lot_num WHERE {where_clause} @@ -32,25 +35,23 @@ class BioSalesViewRepository(BaseRepository): bs.rec_whs_cd, bs.rec_whs_sub_cd, bs.rev_hsdnymd_srk, - bs.slip_mgt_no + bs.slip_mgt_num ASC\ """ def fetch_many(self, parameter: BioModel) -> list[BioSalesViewModel]: try: self._database.connect() + logger.debug('DB参照実行') where_clause = self.__build_condition(parameter) - # error_log(date("Y/m/d H:i:s") . " [INFO] DB Return=" . $result . "\r\n", 3, "$execLog"); - # error_log(date("Y/m/d H:i:s") . " [INFO] DB参照実行" . "\r\n", 3, "$execLog"); query = self.FETCH_SQL.format(where_clause=where_clause) - # error_log(date("Y/m/d H:i:s") . " [INFO] SQL: " . $query . "\r\n", 3, "$execLog"); + logger.debug(f'SQL: {query}') result = self._database.execute_select(query, parameter.dict()) + logger.debug(f'count= {len(result)}') models = [BioSalesViewModel(**r) for r in result] - # error_log(date("Y/m/d H:i:s") . " [INFO] count=" . $count . "\r\n", 3, "$execLog"); return models except Exception as e: - # TODO: ファイルへの書き出しはloggerでやる - print(f"[ERROR] DB Error : Exception={e.args}") + logger.exception(f"DB Error : Exception={e.args}") raise e finally: self._database.disconnect() @@ -58,18 +59,15 @@ class BioSalesViewRepository(BaseRepository): def fetch_as_data_frame(self, parameter: BioModel): try: self._database.connect() + logger.debug('DB参照実行') where_clause = self.__build_condition(parameter) - # error_log(date("Y/m/d H:i:s") . " [INFO] DB Return=" . $result . "\r\n", 3, "$execLog"); - # error_log(date("Y/m/d H:i:s") . " [INFO] DB参照実行" . "\r\n", 3, "$execLog"); query = self.FETCH_SQL.format(where_clause=where_clause) - # error_log(date("Y/m/d H:i:s") . " [INFO] SQL: " . $query . "\r\n", 3, "$execLog"); - # models = [BioSalesViewModel(**r) for r in result] - # error_log(date("Y/m/d H:i:s") . " [INFO] count=" . $count . "\r\n", 3, "$execLog"); + logger.debug(f'SQL: {query}') df = self._to_data_frame(query, parameter) + logger.debug(f'count= {len(df.index)}') return df except Exception as e: - # TODO: ファイルへの書き出しはloggerでやる - print(f"[ERROR] DB Error : Exception={e.args}") + logger.exception(f"DB Error : Exception={e.args}") raise e finally: self._database.disconnect() diff --git a/ecs/jskult-webapp/src/repositories/pharmacy_product_master_repository.py b/ecs/jskult-webapp/src/repositories/pharmacy_product_master_repository.py index 2720955f..4f10e090 100644 --- a/ecs/jskult-webapp/src/repositories/pharmacy_product_master_repository.py +++ b/ecs/jskult-webapp/src/repositories/pharmacy_product_master_repository.py @@ -6,21 +6,21 @@ class PharmacyProductMasterRepository(BaseRepository): FETCH_SQL = """\ SELECT - CONCAT(IFNULL(mkr_cd, ''), ' ', IFNULL(mkr_inf_1, '')) AS mkr_cd_nm + CONCAT(IFNULL(t1.mkr_cd, ''), ' ', IFNULL(t1.mkr_inf_1, '')) AS mkr_cd_name FROM src05.phm_prd_mst_v t1 INNER JOIN ( SELECT - prd_cd,MAX(sub_no) AS sno + prd_cd, MAX(sub_num) AS sno FROM src05.phm_prd_mst_v WHERE rec_sts_kbn <> '9' GROUP BY prd_cd ) fmv2 - ON t1.prd_cd = fmv2.prd_cd AND t1.sub_no = fmv2.sno + ON t1.prd_cd = fmv2.prd_cd AND t1.sub_num = fmv2.sno WHERE - mkr_cd IS NOT NULL + t1.mkr_cd IS NOT NULL ORDER BY mkr_cd """ diff --git a/ecs/jskult-webapp/src/repositories/wholesaler_master_repository.py b/ecs/jskult-webapp/src/repositories/wholesaler_master_repository.py index 246eea5e..5e58e1b5 100644 --- a/ecs/jskult-webapp/src/repositories/wholesaler_master_repository.py +++ b/ecs/jskult-webapp/src/repositories/wholesaler_master_repository.py @@ -8,18 +8,22 @@ class WholesalerMasterRepository(BaseRepository): SELECT DISTINCT b.rec_whs_cd, b.rec_whs_sub_cd, - v2.nm, - b.whs_nm + v2.name, + b.whs_name FROM src05.bio_sales b LEFT OUTER JOIN ( - SELECT sub_no, nm, v_whs_cd, rec_sts_kbn + SELECT + sub_num, + name, + v_whs_cd, + rec_sts_kbn FROM src05.whs_mst_v WHERE (SELECT STR_TO_DATE(syor_date, '%Y%m%d') FROM src05.hdke_tbl) BETWEEN start_date AND end_date ) v2 ON b.v_whs_cd = v2.v_whs_cd AND v2.rec_sts_kbn <> '9' - ORDER BY b.rec_whs_cd, b.rec_whs_sub_cd , b.whs_nm DESC + ORDER BY b.rec_whs_cd, b.rec_whs_sub_cd , b.whs_name DESC """ def fetch_all(self) -> list[WholesalerMasterModel]: diff --git a/ecs/jskult-webapp/src/services/logout_service.py b/ecs/jskult-webapp/src/services/logout_service.py new file mode 100644 index 00000000..8944ccde --- /dev/null +++ b/ecs/jskult-webapp/src/services/logout_service.py @@ -0,0 +1,65 @@ +# import base64 +# import hashlib +# import hmac + +# import requests + +# 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 LogoutService(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 logout(self) -> None: +# logout_endpoint = f'{environment.COGNITO_AUTH_DOMAIN}/{environment.LOGOUT_ENDPOINT}' +# request_params = { +# 'client_id': environment.COGNITO_CLIENT_ID, +# 'code': code, +# 'redirect_uri': environment.COGNITO_REDIRECT_URI +# } +# 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() diff --git a/ecs/jskult-webapp/src/system_var/constants.py b/ecs/jskult-webapp/src/system_var/constants.py index 1ebf4eac..9b022b3d 100644 --- a/ecs/jskult-webapp/src/system_var/constants.py +++ b/ecs/jskult-webapp/src/system_var/constants.py @@ -5,31 +5,31 @@ BIO_EXCEL_TEMPLATE_FILE_PATH = path.join(BIO_TEMPORARY_FILE_DIR_PATH, 'BioData_t BIO_EXTRACT_COLUMNS = [ 'slip_org_kbn', - 'slip_mgt_no', + 'slip_mgt_num', 'rec_ymd', 'rec_whs_cd', 'rec_whs_sub_cd', - 'whs_nm', + 'whs_name', 'rec_whs_org_cd', - 'rec_urag_no', + 'rec_urag_num', 'rev_hsdnymd_srk', 'rec_tran_kbn', - 'tran_kbn_nm', + 'tran_kbn_name', 'mkr_cd', 'rec_comm_cd', 'comm_nm', 'whs_rep_comm_nm', - 'nnsk_cd', - 'rec_nnskfcl_nm', + 'nonyu_fcl_cd', + 'rec_nonyu_fcl_name', 'whs_rep_nnskfcl_nm', - 'rec_nnsk_fcl_addr', + 'rec_nonyu_fcl_addr', 'whs_rep_nnsk_fcl_addr', 'rec_lot_num', 'amt_fugo', 'expr_dt', 'data_kbn', 'lot_no_err_flg', - 'bef_slip_mgt_no', + 'bef_slip_mgt_num', 'ins_usr', 'ins_dt', 'inst_cd', @@ -38,7 +38,7 @@ BIO_EXTRACT_COLUMNS = [ 'tel_no', 'v_whs_cd', 'v_whsorg_cd', - 'whs_org_nm', + 'whs_org_name', 'v_tran_cd', 'iko_flg' ] diff --git a/ecs/jskult-webapp/src/system_var/environment.py b/ecs/jskult-webapp/src/system_var/environment.py index 7d82d560..dea354bf 100644 --- a/ecs/jskult-webapp/src/system_var/environment.py +++ b/ecs/jskult-webapp/src/system_var/environment.py @@ -3,6 +3,7 @@ import os COGNITO_AUTH_DOMAIN = os.environ['COGNITO_AUTH_DOMAIN'] AUTHORIZE_ENDPOINT = os.environ['AUTHORIZE_ENDPOINT'] TOKEN_ENDPOINT = os.environ['TOKEN_ENDPOINT'] +LOGOUT_ENDPOINT = os.environ['LOGOUT_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'] @@ -20,3 +21,5 @@ 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']) + +LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO') diff --git a/ecs/jskult-webapp/src/templates/bioSearchList.html b/ecs/jskult-webapp/src/templates/bioSearchList.html index cc208efd..2c813f12 100644 --- a/ecs/jskult-webapp/src/templates/bioSearchList.html +++ b/ecs/jskult-webapp/src/templates/bioSearchList.html @@ -37,7 +37,7 @@ ロット番号: - データ区分: @@ -88,9 +88,9 @@ @@ -99,13 +99,13 @@ 発伝年月日: - ~ - - {% with + {% with modal_id='modal_xlsx', modal_title='確認', message='生物由来卸販売データ一覧をExcel出力しますか?', @@ -373,7 +373,7 @@ {% include '_modal.html' %} {% endwith %} - {% with + {% with modal_id='modal_csv', modal_title='確認', message='生物由来卸販売データ一覧をCSV出力しますか?', @@ -397,7 +397,7 @@ {% include '_modal.html' %} {% endwith %} - {% with + {% with modal_id='ErrorModal_AWS', modal_title='エラー', message='AWS環境に異常が発生しました。管理者にお問い合わせください。', @@ -415,7 +415,7 @@ {% include '_modal.html' %} {% endwith %} - {% with + {% with modal_id='ErrorModal_DB', modal_title='エラー', message='DB接続に失敗しました。管理者にお問い合わせください。', @@ -434,7 +434,7 @@ {% endwith %} - {% with + {% with modal_id='ErrorModal_Unexpected', modal_title='エラー', message='サーバーエラーが発生しました。管理者にお問い合わせください。', From 79f561eb15d181c4923b05e679c13ad6ae805f38 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Wed, 31 May 2023 18:34:18 +0900 Subject: [PATCH 06/15] =?UTF-8?q?=E3=83=95=E3=82=A3=E3=83=BC=E3=83=AB?= =?UTF-8?q?=E3=83=89=E5=90=8D=E5=A4=89=E6=9B=B4=E3=81=A8download=E5=87=A6?= =?UTF-8?q?=E7=90=86=E3=82=92=E3=81=A1=E3=82=87=E3=81=A3=E3=81=A8=E6=94=B9?= =?UTF-8?q?=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/controller/bio_download.py | 123 +++++++++++------- .../src/model/db/bio_sales_view.py | 2 +- .../src/model/request/bio_download.py | 6 +- .../src/router/session_router.py | 7 +- ecs/jskult-webapp/src/system_var/constants.py | 12 +- .../src/templates/bioSearchList.html | 17 ++- 6 files changed, 95 insertions(+), 72 deletions(-) diff --git a/ecs/jskult-webapp/src/controller/bio_download.py b/ecs/jskult-webapp/src/controller/bio_download.py index 863c41e4..620ca20c 100644 --- a/ecs/jskult-webapp/src/controller/bio_download.py +++ b/ecs/jskult-webapp/src/controller/bio_download.py @@ -2,6 +2,7 @@ from datetime import datetime from typing import Union +import pandas as pd from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import JSONResponse from starlette import status @@ -9,13 +10,16 @@ from starlette import status from src.depends.auth import verify_session from src.depends.services import get_service from src.error.exceptions import DBException +from src.logging.get_logger import get_logger from src.model.internal.session import UserSession from src.model.request.bio import BioModel from src.model.request.bio_download import BioDownloadModel from src.services.batch_status_service import BatchStatusService from src.services.bio_view_service import BioViewService from src.services.session_service import set_session -from src.system_var import constants +from src.system_var import constants, environment + +logger = get_logger('生物由来参照') router = APIRouter() @@ -33,70 +37,35 @@ async def download_bio_data( session: Union[UserSession, None] = Depends(verify_session) ): # 通常のビューとはルーティングの扱いを変えるために、個別のルーターで登録する - # error_log(date("Y/m/d H:i:s") . " [INFO] getBioData start" . "\r\n", 3, "$execLog"); - # 改修後のパラメータを打ち出すようにする - # いらない error_log(date("Y/m/d H:i:s") . " [INFO] param:szConditions=" . htmlspecialchars($_POST["szConditions"], ENT_QUOTES) . "\r\n", 3, "$execLog"); - # いらない error_log(date("Y/m/d H:i:s") . " [INFO] param:pageNum=" . htmlspecialchars($_POST["pageNum"], ENT_QUOTES) . "\r\n", 3, "$execLog"); - # いらない error_log(date("Y/m/d H:i:s") . " [INFO] szUser=" . htmlspecialchars($_POST["szUser"], ENT_QUOTES) . "\r\n", 3, "$execLog"); - # いらない error_log(date("Y/m/d H:i:s") . " [INFO] szfilename=" . htmlspecialchars($_POST["szfilename"], ENT_QUOTES) . "\r\n", 3, "$execLog"); - # いらない error_log(date("Y/m/d H:i:s") . " [INFO] extension=" . htmlspecialchars($_POST["extension"], ENT_QUOTES) . "\r\n", 3, "$execLog"); - # いらない error_log(date("Y/m/d H:i:s") . " [INFO] sql=" . htmlspecialchars($_POST["sql"], ENT_QUOTES) . "\r\n", 3, "$execLog"); - # いらない error_log(date("Y/m/d H:i:s") . " [INFO] arrayPrepare=" . $_POST["arrayPrepare"] . "\r\n", 3, "$execLog"); + logger.info('生物由来データダウンロード開始') + logger.info(f'ユーザーID: {download_param.user_id}') + logger.info(f'拡張子: {download_param.ext}') # ファイル名に使用するタイムスタンプを初期化しておく - now = datetime.now() + current_timestamp = datetime.now() if session is None: return {'status': 'session_expired'} # バッチ処理中の場合、機能を利用させない if batch_status_service.is_batch_processing(): return {'status': 'batch_processing'} - try: - # 生物由来データを検索 - search_result_df = bio_service.search_download_bio_data(search_param) - except DBException as e: - # error_log(date("Y/m/d H:i:s") . " [ERROR] " . "\r\n", 3, "$execLog"); - print('DB Error', e.args) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail={'error': 'db_error', 'message': e.args} - ) + # 生物由来データを検索 + search_result_df = _search_bio_data(bio_service, search_param, download_param.user_id) if search_result_df.size < 1: # 検索結果が0件の場合、download_urlを返さない print('Bio data not found') return {'status': 'ok', 'download_url': None} # ファイルに打ち出すカラムを抽出 - extract_df = search_result_df[constants.BIO_EXTRACT_COLUMNS] + # TODO: SQLクエリを修正するため、この処理は不要になる + extract_df = _extract_output_df(search_result_df) - # 値を変換 - # データ種別の正式名を設定 - extract_df.loc[:, 'slip_org_kbn'] = extract_df['slip_org_kbn'].apply( - lambda key: constants.SLIP_ORG_KBN_FULL_NAME.get(key)) - # データ区分の区分の日本語名を設定 - extract_df.loc[:, 'data_kbn'] = extract_df['data_kbn'].apply(lambda key: constants.DATA_KBN_JP_NAME.get(key)) - # ロット番号エラーフラグの日本語名を設定 - extract_df.loc[:, 'lot_no_err_flg'] = extract_df['lot_no_err_flg'].apply( - lambda key: constants.LOT_NO_ERR_FLG_JP_NAME.get(key)) - # 訂正前伝票管理番号がセットされているときのみ修正日時、修正者、エラー詳細種別をセット - extract_df.loc[:, 'ins_dt'] = extract_df['bef_slip_mgt_num'].apply( - lambda bef_slip_mgt_num: extract_df['ins_dt'] if bef_slip_mgt_num is not None else '') - extract_df.loc[:, 'ins_usr'] = extract_df['bef_slip_mgt_num'].apply( - lambda bef_slip_mgt_num: extract_df['ins_usr'] if bef_slip_mgt_num is not None else '') - - # 種別によって出力を変える - local_file_path = '' - if download_param.kind == 'xlsx': - # error_log(date("Y/m/d H:i:s") . " [INFO] 今回はExcelファイルに出力する" . "\r\n", 3, "$execLog"); - local_file_path = bio_service.write_excel_file(extract_df, download_param.user_id, timestamp=now) - elif download_param.kind == 'csv': - # error_log(date("Y/m/d H:i:s") . " [INFO] 今回はCSVファイルに出力する" . "\r\n", 3, "$execLog"); - local_file_path = bio_service.write_csv_file( - extract_df, download_param.user_id, header=constants.BIO_CSV_HEADER, timestamp=now) + # ファイルを書き出し(Excel or CSV) + local_file_path = __write_bio_data_to_file(bio_service, download_param, extract_df, current_timestamp) # ローカルファイルから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) + local_file_path, download_param.user_id, download_param.ext) except Exception as e: print('S3 access error', e.args) raise HTTPException( @@ -121,8 +90,66 @@ async def download_bio_data( json_response.set_cookie( key='session', value=session.session_key, - max_age=20*60, + max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける secure=True, httponly=True ) return json_response + + +def _search_bio_data(bio_service: BioViewService, search_param: BioModel, user_id: str) -> pd.DataFrame: + try: + # 生物由来データを検索 + search_result_df = bio_service.search_download_bio_data(search_param) + logger.info(f'ユーザーID: {user_id} Value: {search_param}') + # TODO: ファイルにも出力する + except DBException as e: + logger.exception(f'DB Error: {e}') + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail={'error': 'db_error', 'message': e.args} + ) + + return search_result_df + + +def _extract_output_df(search_result_df: pd.DataFrame) -> pd.DataFrame: + extract_df = search_result_df[constants.BIO_EXTRACT_COLUMNS] + + # 値を変換 + # データ種別の正式名を設定 + extract_df.loc[:, 'slip_org_kbn'] = extract_df['slip_org_kbn'].apply( + lambda key: constants.SLIP_ORG_KBN_FULL_NAME.get(key)) + # データ区分の区分の日本語名を設定 + extract_df.loc[:, 'data_kbn'] = extract_df['data_kbn'].apply(lambda key: constants.DATA_KBN_JP_NAME.get(key)) + # ロット番号エラーフラグの日本語名を設定 + extract_df.loc[:, 'lot_num_err_flg'] = extract_df['lot_num_err_flg'].apply( + lambda key: constants.LOT_NO_ERR_FLG_JP_NAME.get(key)) + # 訂正前伝票管理番号がセットされているときのみ修正日時、修正者、エラー詳細種別をセット + extract_df.loc[:, 'ins_dt'] = extract_df['bef_slip_mgt_num'].apply( + lambda bef_slip_mgt_num: extract_df['ins_dt'] if bef_slip_mgt_num is not None else '') + extract_df.loc[:, 'ins_usr'] = extract_df['bef_slip_mgt_num'].apply( + lambda bef_slip_mgt_num: extract_df['ins_usr'] if bef_slip_mgt_num is not None else '') + + return extract_df + + +def __write_bio_data_to_file( + bio_service: BioViewService, + download_param: BioDownloadModel, + df: pd.DataFrame, + current_timestamp: datetime +) -> str: + # 種別によって出力を変える + local_file_path = '' + if download_param.ext == 'xlsx': + # TODO: ファイルにも出力する + logger.info('今回はExcelファイルに出力する') + local_file_path = bio_service.write_excel_file(df, download_param.user_id, timestamp=current_timestamp) + elif download_param.ext == 'csv': + # TODO: ファイルにも出力する + logger.info('今回はCSVファイルに出力する') + local_file_path = bio_service.write_csv_file( + df, download_param.user_id, header=constants.BIO_CSV_HEADER, timestamp=current_timestamp) + + return local_file_path diff --git a/ecs/jskult-webapp/src/model/db/bio_sales_view.py b/ecs/jskult-webapp/src/model/db/bio_sales_view.py index 9fd90ed7..325ccbbc 100644 --- a/ecs/jskult-webapp/src/model/db/bio_sales_view.py +++ b/ecs/jskult-webapp/src/model/db/bio_sales_view.py @@ -42,7 +42,7 @@ class BioSalesViewModel(BaseDBModel): v_inst_nm: Optional[str] v_inst_addr: Optional[str] comm_cd: Optional[str] - comm_nm: Optional[str] + product_name: Optional[str] whs_rep_comm_nm: Optional[str] whs_rep_nnskfcl_nm: Optional[str] whs_rep_nnsk_fcl_addr: Optional[str] diff --git a/ecs/jskult-webapp/src/model/request/bio_download.py b/ecs/jskult-webapp/src/model/request/bio_download.py index 51bea2e3..5f9e049b 100644 --- a/ecs/jskult-webapp/src/model/request/bio_download.py +++ b/ecs/jskult-webapp/src/model/request/bio_download.py @@ -4,15 +4,15 @@ from pydantic import BaseModel class BioDownloadModel(BaseModel): user_id: str - kind: str + ext: str @classmethod def as_body( cls, user_id: str = Body(), - kind: str = Body() + ext: str = Body() ): return cls( user_id=user_id, - kind=kind + ext=ext ) diff --git a/ecs/jskult-webapp/src/router/session_router.py b/ecs/jskult-webapp/src/router/session_router.py index e2699784..8188c10f 100644 --- a/ecs/jskult-webapp/src/router/session_router.py +++ b/ecs/jskult-webapp/src/router/session_router.py @@ -1,4 +1,3 @@ -import logging from typing import Callable from fastapi import Request, Response @@ -9,9 +8,10 @@ from starlette import status from src.depends.auth import (check_session_expired, get_current_session, verify_session) from src.error.exceptions import UnexpectedException +from src.logging.get_logger import get_logger from src.system_var import constants, environment -logger = logging.getLogger('uvicorn') +logger = get_logger('medaca_router') class MeDaCaRoute(APIRoute): @@ -36,14 +36,11 @@ class MeDaCaRoute(APIRoute): # 返却するルートハンドラーを定義。必ず非同期関数にする必要がある。 async def custom_route_handler(request: Request) -> Response: try: - logger.info('pre routing process') # 事前処理 request = await self.pre_process_route(request) # 本来のルーティング処理 - logger.info('routing process') response = await original_route_handler(request) # 事後処理 - logger.info('post routing process') return await self.post_process_route(request, response) except HTTPException as e: raise e diff --git a/ecs/jskult-webapp/src/system_var/constants.py b/ecs/jskult-webapp/src/system_var/constants.py index 9b022b3d..99bdd872 100644 --- a/ecs/jskult-webapp/src/system_var/constants.py +++ b/ecs/jskult-webapp/src/system_var/constants.py @@ -17,25 +17,25 @@ BIO_EXTRACT_COLUMNS = [ 'tran_kbn_name', 'mkr_cd', 'rec_comm_cd', - 'comm_nm', - 'whs_rep_comm_nm', + 'product_name', + 'whs_rep_comm_name', 'nonyu_fcl_cd', 'rec_nonyu_fcl_name', - 'whs_rep_nnskfcl_nm', + 'whs_rep_nonyu_fcl_name', 'rec_nonyu_fcl_addr', - 'whs_rep_nnsk_fcl_addr', + 'whs_rep_nonyu_fcl_addr', 'rec_lot_num', 'amt_fugo', 'expr_dt', 'data_kbn', - 'lot_no_err_flg', + 'lot_num_err_flg', 'bef_slip_mgt_num', 'ins_usr', 'ins_dt', 'inst_cd', 'inst_name_form', 'address', - 'tel_no', + 'tel_num', 'v_whs_cd', 'v_whsorg_cd', 'whs_org_name', diff --git a/ecs/jskult-webapp/src/templates/bioSearchList.html b/ecs/jskult-webapp/src/templates/bioSearchList.html index 2c813f12..8dae43e4 100644 --- a/ecs/jskult-webapp/src/templates/bioSearchList.html +++ b/ecs/jskult-webapp/src/templates/bioSearchList.html @@ -192,7 +192,7 @@