From cd00e24966dda5818fd44e59ce1cfbb96b841941 Mon Sep 17 00:00:00 2001 From: Asuka Asakura Date: Fri, 2 Feb 2024 11:57:41 +0900 Subject: [PATCH 01/31] =?UTF-8?q?MSJ=5FPatient=5F=5Fc=20=E3=81=AE=E5=B1=A5?= =?UTF-8?q?=E6=AD=B4=E7=AE=A1=E7=90=86=E3=81=A8=E9=A0=85=E7=9B=AE=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crm/object_info/crm_object_list_diff.json | 18 +++++++++++++++++- s3/data/crm/settings/CRM_MSJ_Patient__c.txt | 8 ++++---- s3/data/crm/settings/CRM_MSJ_Patient__c_ex.sql | 1 + 3 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 s3/data/crm/settings/CRM_MSJ_Patient__c_ex.sql diff --git a/s3/config/crm/object_info/crm_object_list_diff.json b/s3/config/crm/object_info/crm_object_list_diff.json index 243b07db..e328fe4c 100644 --- a/s3/config/crm/object_info/crm_object_list_diff.json +++ b/s3/config/crm/object_info/crm_object_list_diff.json @@ -2659,7 +2659,23 @@ "MSJ_Child_Account_Name__c", "MSJ_Child_Account__c", "MSJ_Parent_Account_Name__c", - "MSJ_Parent_Child_Name__c" + "MSJ_Parent_Child_Name__c", + "MSJ_Age__c", + "MSJ_Comments__c", + "MSJ_Effectiveness_evaluation_date__c", + "MSJ_Gender__c", + "MSJ_Informed_consent__c", + "MSJ_Metastases_location__c", + "MSJ_PS__c", + "MSJ_Scheduled_date_of_informed_consent__c", + "MSJ_Scheduled_start_date_administration__c", + "MSJ_CDX_type__c", + "MSJ_First_line_chemotherapy_efficacy__c", + "MSJ_Num_cycles_of_1st_line_chemotherapy__c", + "MSJ_Primary_site_location__c", + "MSJ_Regimen_of_first_line_chemotherapy__c", + "MSJ_Starting_dose__c" + ], "is_skip": false, "is_update_last_fetch_datetime": true diff --git a/s3/data/crm/settings/CRM_MSJ_Patient__c.txt b/s3/data/crm/settings/CRM_MSJ_Patient__c.txt index 8f470747..4f47c679 100644 --- a/s3/data/crm/settings/CRM_MSJ_Patient__c.txt +++ b/s3/data/crm/settings/CRM_MSJ_Patient__c.txt @@ -4,10 +4,10 @@ utf-8 " CRLF 1 -37 -Id,OwnerId,IsDeleted,Name,CreatedDate,CreatedById,LastModifiedDate,LastModifiedById,SystemModstamp,MayEdit,IsLocked,MSJ_Account_Name__c,MSJ_CRC_Group__c,MSJ_Casus_or_Transfer_Point__c,MSJ_Entry_Date__c,MSJ_IST_Name__c,MSJ_Indication__c,MSJ_Line__c,MSJ_MR_Comments__c,MSJ_MUID__c,MSJ_Medical_Regimen__c,MSJ_Month__c,MSJ_Report_Comments__c,MSJ_Start_Date_Of_Administration__c,MSJ_Year__c,Mobile_ID_vod__c,MSJ_CRC_RAS_KRAS__c,MSJ_End_Date_Of_Administration__c,MSJ_End_Date_of_Stop_Administration__c,MSJ_HN_Hospitalized_Type__c,MSJ_Start_Date_of_Stop_Administration__c,MSJ_Patient_Status__c,MSJ_Patient_TA__c,MSJ_Child_Account_Name__c,MSJ_Child_Account__c,MSJ_Parent_Account_Name__c,MSJ_Parent_Child_Name__c -Id,OwnerId,IsDeleted,Name,CreatedDate,CreatedById,LastModifiedDate,LastModifiedById,SystemModstamp,MayEdit,IsLocked,MSJ_Account_Name__c,MSJ_CRC_Group__c,MSJ_Casus_or_Transfer_Point__c,MSJ_Entry_Date__c,MSJ_IST_Name__c,MSJ_Indication__c,MSJ_Line__c,MSJ_MR_Comments__c,MSJ_MUID__c,MSJ_Medical_Regimen__c,MSJ_Month__c,MSJ_Report_Comments__c,MSJ_Start_Date_Of_Administration__c,MSJ_Year__c,Mobile_ID_vod__c,MSJ_CRC_RAS_KRAS__c,MSJ_End_Date_Of_Administration__c,MSJ_End_Date_of_Stop_Administration__c,MSJ_HN_Hospitalized_Type__c,MSJ_Start_Date_of_Stop_Administration__c,MSJ_Patient_Status__c,MSJ_Patient_TA__c,MSJ_Child_Account_Name__c,MSJ_Child_Account__c,MSJ_Parent_Account_Name__c,MSJ_Parent_Child_Name__c +52 +Id,OwnerId,IsDeleted,Name,CreatedDate,CreatedById,LastModifiedDate,LastModifiedById,SystemModstamp,MayEdit,IsLocked,MSJ_Account_Name__c,MSJ_CRC_Group__c,MSJ_Casus_or_Transfer_Point__c,MSJ_Entry_Date__c,MSJ_IST_Name__c,MSJ_Indication__c,MSJ_Line__c,MSJ_MR_Comments__c,MSJ_MUID__c,MSJ_Medical_Regimen__c,MSJ_Month__c,MSJ_Report_Comments__c,MSJ_Start_Date_Of_Administration__c,MSJ_Year__c,Mobile_ID_vod__c,MSJ_CRC_RAS_KRAS__c,MSJ_End_Date_Of_Administration__c,MSJ_End_Date_of_Stop_Administration__c,MSJ_HN_Hospitalized_Type__c,MSJ_Start_Date_of_Stop_Administration__c,MSJ_Patient_Status__c,MSJ_Patient_TA__c,MSJ_Child_Account_Name__c,MSJ_Child_Account__c,MSJ_Parent_Account_Name__c,MSJ_Parent_Child_Name__c,MSJ_Age__c, MSJ_Comments__c,MSJ_Effectiveness_evaluation_date__c,MSJ_Gender__c,MSJ_Informed_consent__c,MSJ_Metastases_location__c,MSJ_PS__c,MSJ_Scheduled_date_of_informed_consent__c,MSJ_Scheduled_start_date_administration__c,MSJ_CDX_type__c,MSJ_First_line_chemotherapy_efficacy__c,MSJ_Num_cycles_of_1st_line_chemotherapy__c,MSJ_Primary_site_location__c,MSJ_Regimen_of_first_line_chemotherapy__c,MSJ_Starting_dose__c +Id,OwnerId,IsDeleted,Name,CreatedDate,CreatedById,LastModifiedDate,LastModifiedById,SystemModstamp,MayEdit,IsLocked,MSJ_Account_Name__c,MSJ_CRC_Group__c,MSJ_Casus_or_Transfer_Point__c,MSJ_Entry_Date__c,MSJ_IST_Name__c,MSJ_Indication__c,MSJ_Line__c,MSJ_MR_Comments__c,MSJ_MUID__c,MSJ_Medical_Regimen__c,MSJ_Month__c,MSJ_Report_Comments__c,MSJ_Start_Date_Of_Administration__c,MSJ_Year__c,Mobile_ID_vod__c,MSJ_CRC_RAS_KRAS__c,MSJ_End_Date_Of_Administration__c,MSJ_End_Date_of_Stop_Administration__c,MSJ_HN_Hospitalized_Type__c,MSJ_Start_Date_of_Stop_Administration__c,MSJ_Patient_Status__c,MSJ_Patient_TA__c,MSJ_Child_Account_Name__c,MSJ_Child_Account__c,MSJ_Parent_Account_Name__c,MSJ_Parent_Child_Name__c,MSJ_Age__c, MSJ_Comments__c,MSJ_Effectiveness_evaluation_date__c,MSJ_Gender__c,MSJ_Informed_consent__c,MSJ_Metastases_location__c,MSJ_PS__c,MSJ_Scheduled_date_of_informed_consent__c,MSJ_Scheduled_start_date_administration__c,MSJ_CDX_type__c,MSJ_First_line_chemotherapy_efficacy__c,MSJ_Num_cycles_of_1st_line_chemotherapy__c,MSJ_Primary_site_location__c,MSJ_Regimen_of_first_line_chemotherapy__c,MSJ_Starting_dose__c src02c.crm_MSJ_Patient__c org02.crm_MSJ_Patient__c - +CRM_MSJ_Patient__c_ex.sql diff --git a/s3/data/crm/settings/CRM_MSJ_Patient__c_ex.sql b/s3/data/crm/settings/CRM_MSJ_Patient__c_ex.sql new file mode 100644 index 00000000..161d38ea --- /dev/null +++ b/s3/data/crm/settings/CRM_MSJ_Patient__c_ex.sql @@ -0,0 +1 @@ +CALL internal02.crm_history('src02c.crm_MSJ_Patient__c', 'SystemModstamp'); From 930b3931964968188e710e32230fda3753bd6eac Mon Sep 17 00:00:00 2001 From: Asuka Asakura Date: Fri, 2 Feb 2024 12:01:15 +0900 Subject: [PATCH 02/31] =?UTF-8?q?MSJ=5FPatient=5F=5Fc=20=E3=81=AE=E5=B1=A5?= =?UTF-8?q?=E6=AD=B4=E7=AE=A1=E7=90=86=E3=81=A8=E9=A0=85=E7=9B=AE=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- s3/config/crm/object_info/crm_object_list_diff.json | 1 - 1 file changed, 1 deletion(-) diff --git a/s3/config/crm/object_info/crm_object_list_diff.json b/s3/config/crm/object_info/crm_object_list_diff.json index e328fe4c..5fc83f7d 100644 --- a/s3/config/crm/object_info/crm_object_list_diff.json +++ b/s3/config/crm/object_info/crm_object_list_diff.json @@ -2675,7 +2675,6 @@ "MSJ_Primary_site_location__c", "MSJ_Regimen_of_first_line_chemotherapy__c", "MSJ_Starting_dose__c" - ], "is_skip": false, "is_update_last_fetch_datetime": true From 27fd3536d98d20caeafe980ec2ead27d12f2aa0a Mon Sep 17 00:00:00 2001 From: Asuka Asakura Date: Tue, 6 Feb 2024 09:32:24 +0900 Subject: [PATCH 03/31] =?UTF-8?q?=E8=AA=A4=E3=81=A3=E3=81=9F=E3=82=BF?= =?UTF-8?q?=E3=83=96=E3=81=AE=E9=99=A4=E5=8E=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- s3/data/crm/settings/CRM_MSJ_Patient__c.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/s3/data/crm/settings/CRM_MSJ_Patient__c.txt b/s3/data/crm/settings/CRM_MSJ_Patient__c.txt index 4f47c679..9bf209e6 100644 --- a/s3/data/crm/settings/CRM_MSJ_Patient__c.txt +++ b/s3/data/crm/settings/CRM_MSJ_Patient__c.txt @@ -5,8 +5,8 @@ utf-8 CRLF 1 52 -Id,OwnerId,IsDeleted,Name,CreatedDate,CreatedById,LastModifiedDate,LastModifiedById,SystemModstamp,MayEdit,IsLocked,MSJ_Account_Name__c,MSJ_CRC_Group__c,MSJ_Casus_or_Transfer_Point__c,MSJ_Entry_Date__c,MSJ_IST_Name__c,MSJ_Indication__c,MSJ_Line__c,MSJ_MR_Comments__c,MSJ_MUID__c,MSJ_Medical_Regimen__c,MSJ_Month__c,MSJ_Report_Comments__c,MSJ_Start_Date_Of_Administration__c,MSJ_Year__c,Mobile_ID_vod__c,MSJ_CRC_RAS_KRAS__c,MSJ_End_Date_Of_Administration__c,MSJ_End_Date_of_Stop_Administration__c,MSJ_HN_Hospitalized_Type__c,MSJ_Start_Date_of_Stop_Administration__c,MSJ_Patient_Status__c,MSJ_Patient_TA__c,MSJ_Child_Account_Name__c,MSJ_Child_Account__c,MSJ_Parent_Account_Name__c,MSJ_Parent_Child_Name__c,MSJ_Age__c, MSJ_Comments__c,MSJ_Effectiveness_evaluation_date__c,MSJ_Gender__c,MSJ_Informed_consent__c,MSJ_Metastases_location__c,MSJ_PS__c,MSJ_Scheduled_date_of_informed_consent__c,MSJ_Scheduled_start_date_administration__c,MSJ_CDX_type__c,MSJ_First_line_chemotherapy_efficacy__c,MSJ_Num_cycles_of_1st_line_chemotherapy__c,MSJ_Primary_site_location__c,MSJ_Regimen_of_first_line_chemotherapy__c,MSJ_Starting_dose__c -Id,OwnerId,IsDeleted,Name,CreatedDate,CreatedById,LastModifiedDate,LastModifiedById,SystemModstamp,MayEdit,IsLocked,MSJ_Account_Name__c,MSJ_CRC_Group__c,MSJ_Casus_or_Transfer_Point__c,MSJ_Entry_Date__c,MSJ_IST_Name__c,MSJ_Indication__c,MSJ_Line__c,MSJ_MR_Comments__c,MSJ_MUID__c,MSJ_Medical_Regimen__c,MSJ_Month__c,MSJ_Report_Comments__c,MSJ_Start_Date_Of_Administration__c,MSJ_Year__c,Mobile_ID_vod__c,MSJ_CRC_RAS_KRAS__c,MSJ_End_Date_Of_Administration__c,MSJ_End_Date_of_Stop_Administration__c,MSJ_HN_Hospitalized_Type__c,MSJ_Start_Date_of_Stop_Administration__c,MSJ_Patient_Status__c,MSJ_Patient_TA__c,MSJ_Child_Account_Name__c,MSJ_Child_Account__c,MSJ_Parent_Account_Name__c,MSJ_Parent_Child_Name__c,MSJ_Age__c, MSJ_Comments__c,MSJ_Effectiveness_evaluation_date__c,MSJ_Gender__c,MSJ_Informed_consent__c,MSJ_Metastases_location__c,MSJ_PS__c,MSJ_Scheduled_date_of_informed_consent__c,MSJ_Scheduled_start_date_administration__c,MSJ_CDX_type__c,MSJ_First_line_chemotherapy_efficacy__c,MSJ_Num_cycles_of_1st_line_chemotherapy__c,MSJ_Primary_site_location__c,MSJ_Regimen_of_first_line_chemotherapy__c,MSJ_Starting_dose__c +Id,OwnerId,IsDeleted,Name,CreatedDate,CreatedById,LastModifiedDate,LastModifiedById,SystemModstamp,MayEdit,IsLocked,MSJ_Account_Name__c,MSJ_CRC_Group__c,MSJ_Casus_or_Transfer_Point__c,MSJ_Entry_Date__c,MSJ_IST_Name__c,MSJ_Indication__c,MSJ_Line__c,MSJ_MR_Comments__c,MSJ_MUID__c,MSJ_Medical_Regimen__c,MSJ_Month__c,MSJ_Report_Comments__c,MSJ_Start_Date_Of_Administration__c,MSJ_Year__c,Mobile_ID_vod__c,MSJ_CRC_RAS_KRAS__c,MSJ_End_Date_Of_Administration__c,MSJ_End_Date_of_Stop_Administration__c,MSJ_HN_Hospitalized_Type__c,MSJ_Start_Date_of_Stop_Administration__c,MSJ_Patient_Status__c,MSJ_Patient_TA__c,MSJ_Child_Account_Name__c,MSJ_Child_Account__c,MSJ_Parent_Account_Name__c,MSJ_Parent_Child_Name__c,MSJ_Age__c,MSJ_Comments__c,MSJ_Effectiveness_evaluation_date__c,MSJ_Gender__c,MSJ_Informed_consent__c,MSJ_Metastases_location__c,MSJ_PS__c,MSJ_Scheduled_date_of_informed_consent__c,MSJ_Scheduled_start_date_administration__c,MSJ_CDX_type__c,MSJ_First_line_chemotherapy_efficacy__c,MSJ_Num_cycles_of_1st_line_chemotherapy__c,MSJ_Primary_site_location__c,MSJ_Regimen_of_first_line_chemotherapy__c,MSJ_Starting_dose__c +Id,OwnerId,IsDeleted,Name,CreatedDate,CreatedById,LastModifiedDate,LastModifiedById,SystemModstamp,MayEdit,IsLocked,MSJ_Account_Name__c,MSJ_CRC_Group__c,MSJ_Casus_or_Transfer_Point__c,MSJ_Entry_Date__c,MSJ_IST_Name__c,MSJ_Indication__c,MSJ_Line__c,MSJ_MR_Comments__c,MSJ_MUID__c,MSJ_Medical_Regimen__c,MSJ_Month__c,MSJ_Report_Comments__c,MSJ_Start_Date_Of_Administration__c,MSJ_Year__c,Mobile_ID_vod__c,MSJ_CRC_RAS_KRAS__c,MSJ_End_Date_Of_Administration__c,MSJ_End_Date_of_Stop_Administration__c,MSJ_HN_Hospitalized_Type__c,MSJ_Start_Date_of_Stop_Administration__c,MSJ_Patient_Status__c,MSJ_Patient_TA__c,MSJ_Child_Account_Name__c,MSJ_Child_Account__c,MSJ_Parent_Account_Name__c,MSJ_Parent_Child_Name__c,MSJ_Age__c,MSJ_Comments__c,MSJ_Effectiveness_evaluation_date__c,MSJ_Gender__c,MSJ_Informed_consent__c,MSJ_Metastases_location__c,MSJ_PS__c,MSJ_Scheduled_date_of_informed_consent__c,MSJ_Scheduled_start_date_administration__c,MSJ_CDX_type__c,MSJ_First_line_chemotherapy_efficacy__c,MSJ_Num_cycles_of_1st_line_chemotherapy__c,MSJ_Primary_site_location__c,MSJ_Regimen_of_first_line_chemotherapy__c,MSJ_Starting_dose__c src02c.crm_MSJ_Patient__c org02.crm_MSJ_Patient__c CRM_MSJ_Patient__c_ex.sql From 866db319ea333e86e2b542b7dfd6f83a32f4473c Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Fri, 9 Feb 2024 10:35:52 +0900 Subject: [PATCH 04/31] =?UTF-8?q?feat:=20Encise=E3=83=87=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E8=BB=A2=E9=80=81=E5=87=A6=E7=90=86=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transfer-encise-data.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 lambda/transfer-encise-data/transfer-encise-data.py diff --git a/lambda/transfer-encise-data/transfer-encise-data.py b/lambda/transfer-encise-data/transfer-encise-data.py new file mode 100644 index 00000000..f109d008 --- /dev/null +++ b/lambda/transfer-encise-data/transfer-encise-data.py @@ -0,0 +1,101 @@ +import datetime +import logging +import os +from zoneinfo import ZoneInfo + +import boto3 + +# 環境変数 +DATA_IMPORT_BUCKET = os.environ["DATA_IMPORT_BUCKET"] +ENCISE_BACKUP_BUCKET = os.environ["ENCISE_BACKUP_BUCKET"] +ENCISE_TARGET_FOLDER = os.environ["ENCISE_TARGET_FOLDER"] +LOG_LEVEL = os.environ["LOG_LEVEL"] +TZ = os.environ["TZ"] + +# 定数 +EXCLUSIVE_CONTROL_FILE_EXT = '.doing' + +# S3クライアント +s3_client = boto3.client('s3') + +# logger設定 +logger = logging.getLogger() + + +def log_datetime_convert_tz(*arg): + """ログに出力するタイムスタンプのロケールを変更する(JST指定)""" + return datetime.datetime.now(ZoneInfo(TZ)).timetuple() + + +formatter = logging.Formatter( + '[%(levelname)s]\t%(asctime)s\t%(message)s\n', + '%Y-%m-%d %H:%M:%S' +) +formatter.converter = log_datetime_convert_tz +for handler in logger.handlers: + handler.setFormatter(formatter) + +level = logging.getLevelName(LOG_LEVEL) +if not isinstance(level, int): + level = logging.INFO +logger.setLevel(level) + + +def lambda_handler(event, context): + try: + # ① 処理開始ログを出力する + logger.info('I-01-01 処理開始 Encise受信データ転送処理') + + # ② 処理開始時に受け取ったイベント情報をログに出力する + # バケット名・フォルダ名・受信データファイル名をメモリに保持 + s3_event = event["Records"][0]["s3"] + event_bucket_name = s3_event["bucket"]["name"] + event_object_key = s3_event["object"]["key"] + event_file_name = os.path.basename(event_object_key) + event_folder_name = os.path.dirname(event_object_key).split('/')[0] + logger.info(f'I-02-01 受信バケット:{event_bucket_name}') + logger.info(f'I-02-01 フォルダ名:{event_folder_name}') + logger.info(f'I-02-01 ファイル名:{event_file_name}') + + # ③ S3イベントによるLambdaの重複発火防止の為、メモリに保持したバケット名/フォルダ名内に、「受信データファイル名.doing」ファイルが存在するかチェックする + try: + s3_client.head_object(Bucket=event_bucket_name, Key=f'{event_object_key}{EXCLUSIVE_CONTROL_FILE_EXT}') + logger.error(f'E-01-01 {event_bucket_name}/{event_object_key}は現在処理中です。処理を終了します。') + return + except Exception: + # .doingファイルが見つからなかった場合は、処理を続行する + # メモリに保持したバケット名/フォルダ名内に、「受信データファイル名.doing」ファイルを作成する + logger.info('I-03-01 Encise受信データを転送します') + s3_client.put_object( + Bucket=event_bucket_name, Key=f'{event_object_key}{EXCLUSIVE_CONTROL_FILE_EXT}', Body=b'') + + # ⑤ 受信データファイルを、Enciseデータバックアップバケットにコピーする + copy_source = {'Bucket': event_bucket_name, 'Key': event_object_key} + execute_date_yyyymm = datetime.date.today().strftime('%Y/%m') + s3_client.copy_object( + Bucket=ENCISE_BACKUP_BUCKET, + Key=f'{event_folder_name}/{execute_date_yyyymm}/{event_file_name}', + CopySource=copy_source + ) + logger.info(f'I-04-01 Encise受信データのバックアップ完了:{ENCISE_BACKUP_BUCKET}/{event_folder_name}/{execute_date_yyyymm}/{event_file_name}') + + # ⑥ 受信データファイルを、データ登録バケットのEnciseデータ取込フォルダに移動する + s3_client.copy_object( + Bucket=DATA_IMPORT_BUCKET, + Key=f'{ENCISE_TARGET_FOLDER}/{event_file_name}', + CopySource=copy_source + ) + # コピー後、元のバケットからは削除する + s3_client.delete_object(Bucket=event_bucket_name, Key=event_object_key) + logger.info(f'I-05-01 Encise受信データの転送完了:{DATA_IMPORT_BUCKET}/{ENCISE_TARGET_FOLDER}/{event_file_name}') + + # ⑦ メモリに保持したバケット名/フォルダ名内の「受信データファイル名.doing」ファイルを削除する + s3_client.delete_object(Bucket=event_bucket_name, Key=f'{event_object_key}{EXCLUSIVE_CONTROL_FILE_EXT}') + + logger.info('I-01-06 処理終了 Encise受信データ転送処理') + + except Exception as e: + logger.exception(f'想定外のエラーが発生しました。処理を終了します。 例外内容:{e}') + raise e + + return From ee2557928c5674578c8ed7591e59f109eaf85eca Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Fri, 9 Feb 2024 10:42:01 +0900 Subject: [PATCH 05/31] =?UTF-8?q?style:=20=E3=82=BF=E3=83=96=E2=86=92?= =?UTF-8?q?=E3=82=B9=E3=83=9A=E3=83=BC=E3=82=B9=E3=81=AB=E5=A4=89=E6=8F=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lambda/transfer-encise-data/transfer-encise-data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lambda/transfer-encise-data/transfer-encise-data.py b/lambda/transfer-encise-data/transfer-encise-data.py index f109d008..bc58963c 100644 --- a/lambda/transfer-encise-data/transfer-encise-data.py +++ b/lambda/transfer-encise-data/transfer-encise-data.py @@ -89,7 +89,7 @@ def lambda_handler(event, context): s3_client.delete_object(Bucket=event_bucket_name, Key=event_object_key) logger.info(f'I-05-01 Encise受信データの転送完了:{DATA_IMPORT_BUCKET}/{ENCISE_TARGET_FOLDER}/{event_file_name}') - # ⑦ メモリに保持したバケット名/フォルダ名内の「受信データファイル名.doing」ファイルを削除する + # ⑦ メモリに保持したバケット名/フォルダ名内の「受信データファイル名.doing」ファイルを削除する s3_client.delete_object(Bucket=event_bucket_name, Key=f'{event_object_key}{EXCLUSIVE_CONTROL_FILE_EXT}') logger.info('I-01-06 処理終了 Encise受信データ転送処理') From add64b3bc8b9bfa46a1f6ff6adb92383f4d13082 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 12:49:11 +0900 Subject: [PATCH 06/31] =?UTF-8?q?feat:=20Encise=E8=87=AA=E5=8B=95=E9=80=A3?= =?UTF-8?q?=E6=90=BA=20=E3=83=87=E3=83=BC=E3=82=BF=E6=9C=AA=E5=8F=97?= =?UTF-8?q?=E4=BF=A1=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E5=87=A6=E7=90=86?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=80=82=E7=B4=B0=E3=81=8B=E3=81=84=E3=83=AD?= =?UTF-8?q?=E3=82=B8=E3=83=83=E3=82=AF=E3=81=AE=E5=AE=9F=E8=A3=85=E3=81=8C?= =?UTF-8?q?=E3=81=BE=E3=81=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check.py | 293 ++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 lambda/encise-data-unreceive-check/encise-data-unreceive-check.py diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py new file mode 100644 index 00000000..6dc2e89c --- /dev/null +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -0,0 +1,293 @@ +import csv +import datetime +import io +import json +import logging +import os +import re +from zoneinfo import ZoneInfo + +import boto3 + +# 環境変数 +CHECK_BUCKET_NAME = os.environ["CHECK_BUCKET_NAME"] +CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH = os.environ["CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH"] +CONFIG_BUCKET_NAME = os.environ["CONFIG_BUCKET_NAME"] +LOG_LEVEL = os.environ["LOG_LEVEL"] +MAIL_TEMPLATE_FOLDER_PATH = os.environ["MAIL_TEMPLATE_FOLDER_PATH"] +MBJ_NOTICE_TOPIC = os.environ["MBJ_NOTICE_TOPIC"] +NDS_NOTICE_TITLE = os.environ["NDS_NOTICE_TITLE"] +NDS_NOTICE_TOPIC = os.environ["NDS_NOTICE_TOPIC"] +PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME = os.environ["PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME"] +PROCESSED_MESSAGE_EXPIRES_PERIOD = os.environ["PROCESSED_MESSAGE_EXPIRES_PERIOD"] +TZ = os.environ["TZ"] + +# 定数 +ROW_COMMENT_SYMBOL = '#' +INDEX_REGEX = 0 +INDEX_DATA_NAME = 1 +INDEX_ROW_COMMENT_SYMBOL = 0 +INDEX_SPLIT_NUM = 1 +INDEX_LAST = -1 + +# メール本文に出力する不足ファイル名一覧のインデント +MAIL_INDENT = '  ' + +# AWS操作クライアント +s3_client = boto3.client('s3') +sns_client = boto3.client('sns') +dynamodb_client = boto3.client('dynamodb') + + +# logger設定 + + +def log_datetime_convert_tz(*arg): + """ログに出力するタイムスタンプのロケールを変更する(JST指定)""" + return datetime.datetime.now(ZoneInfo(TZ)).timetuple() + + +logger = logging.getLogger() +formatter = logging.Formatter( + '[%(levelname)s]\t%(asctime)s\t%(message)s\n', + '%Y-%m-%d %H:%M:%S' +) +formatter.converter = log_datetime_convert_tz +for handler in logger.handlers: + handler.setFormatter(formatter) + +level = logging.getLevelName(LOG_LEVEL) +if not isinstance(level, int): + level = logging.INFO +logger.setLevel(level) + + +def is_duplicate_message(message_id: str) -> bool: + """DynamoDBテーブルに処理済みのSQSメッセージIdが存在するかどうかを調査する + + Args: + message_id (str): SQSメッセージId + + Returns: + bool: 存在する場合はTrue + """ + return dynamodb_client.query( + TableName=PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME, + Select='COUNT', + KeyConditionExpression='message_id = :message_id', + ExpressionAttributeValues={ + ':message_id': {'S': message_id} + } + )["Count"] != 0 + + +# Put the message to the DynamoDB Table. +# @input string batch_item_success +# +# @param batch_item_success of the message to put. +# @return Boolean +def push_success_messages_to_dynamo_db(batch_item_success: list[str]) -> bool: + """処理済みのSQSメッセージIdをDynamoDBにPushする + + Args: + batch_item_success (list[str]): SQSメッセージIdのリスト + + Returns: + bool: 登録成功の場合、True + """ + for message_id in batch_item_success: + dynamodb_client.put_item( + TableName=PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME, + Item={'message_id': {'S': message_id}} + ) + return True + + +def substitute_mail_template(mail_template: str, receive_timing: str, mail_msg: str) -> str: + """メールテンプレートのプレースホルダーを置き換えます + + Args: + mail_template (str): 置き換え前のメールテンプレート + receive_timing (str): メールテンプレートのプレースホルダーを置き換える文言(受信タイミング) + mail_msg (str): メールテンプレートのプレースホルダーを置き換える文言(ファイル一覧) + + Returns: + str: 置き換え後のメール本文 + """ + substitute_dict = { + "receive_timing": receive_timing, + "notice_file_names": mail_msg + } + mail_str = mail_template.format_map(substitute_dict) + return mail_str + + +def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, str]], list[str]]: + + batch_item_failures = [] + batch_item_success = [] + + for record in records: + # メール挿入用文言を格納するためのメモリを保持する + mail_message = '' + + try: + # 1.SQSメッセージIDを取得する + message_id = record["messageId"] + # 2.DynamoDBテーブルからレコードを取得し、処理済みメッセージかどうかを判別する + if is_duplicate_message(message_id): + logger.info(f'受信したメッセージは既に処理済みのため、処理をスキップします。メッセージID: {message_id}') + continue + except Exception as e: + # TODO: エラー処理実装 + raise e + + # SQSパラメータをJSONシリアライズし、Pythonの辞書オブジェクト(イベントパラメータ)を取得する。 + + event_parameter = json.loads(record['body']) + + # ③ 設定ファイル[受領チェック対象ファイルリスト]を読み込む + try: + logger.info( + 'I-03-01 ' + + f'受領チェック対象ファイルリスト読込 読込元:{CONFIG_BUCKET_NAME}/{CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH}/{event_parameter["check_target_file_list"]}' + ) + check_target_file_list_response = s3_client.get_object( + Bucket=CONFIG_BUCKET_NAME, + Key=f'{CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH}/{event_parameter["check_target_file_list"]}' + ) + logger.info('I-03-02 受領チェック対象ファイルリストを読み込みました') + except Exception as e: + # TODO: エラー処理実装 + raise e + + # ④ 受領チェック処理を行う + receive_timing = event_parameter['receive_timing'] + logger.info(f'I-04-01 Enciseデータ受領チェック ({receive_timing}) 処理開始') + object_prefix = f'{event_parameter["check_folder_prefix"]}{execute_month}/' + # 1.Enciseデータバックアップ保管バケットの処理稼働月に該当するサブフォルダにあるファイル一覧を取得する + logger.info( + f'I-04-02 オブジェクトリストの取得 取得先:{CHECK_BUCKET_NAME}/{object_prefix}' + ) + check_target_file_list_response = s3_client.list_objects_v2(CHECK_BUCKET_NAME, Prefix=object_prefix) + check_target_file_list = [] + for content in check_target_file_list_response[['Contents']]: + # オブジェクトのキーからファイル名を切り出してリストに追加 + obj_key = content['Key'].rsplit('/', INDEX_SPLIT_NUM) + check_target_file_list.append(obj_key[INDEX_LAST]) + + # 2.I/Fファイルチェック処理 + logger.info(f'I-04-03 Enciseデータ({receive_timing}) ファイルチェック処理開始') + receive_monthly_file_name_body = io.TextIOWrapper(io.BytesIO(check_target_file_list_response["Body"].read()), encoding='utf-8') + match_count = 0 + row_count = 0 + for tsv_row in csv.reader(receive_monthly_file_name_body, delimiter='\t'): + # 「④1.」で取得したリストが「③」で読み込んだファイル内に存在するか確認する + is_file_not_exists = True + for file_name in check_target_file_list: + match_result = re.fullmatch(tsv_row[INDEX_REGEX], file_name) + # 「③」で読み込んだファイルに記載されている全てが「④1.」で取得したリストに存在した場合 + if match_result is not None: + # 存在したファイルの年月部分を抜き出し、チェック対象年月(処理稼働月-1)である場合 + if True: # TODO + logger.info(f'I-04-04 I/Fファイルの受領を確認しました ファイル名:{file_name}') + is_file_not_exists = False + match_count += 1 + else: + logger.info(f'I-04-07 I/Fファイルの年月がチェック対象年月と一致しません ファイル名:{file_name}') + mail_message += f'{MAIL_INDENT}{tsv_row[INDEX_DATA_NAME]}(受領年月が不正:{file_name})\n' + break + if is_file_not_exists: + logger.info(f'E-04-06 月次I/Fファイルに不足があります ファイル名:{tsv_row[INDEX_DATA_NAME]}') + mail_message += f'{MAIL_INDENT}{tsv_row[INDEX_DATA_NAME]}\n' + + row_count += 1 + + if row_count == match_count: + logger.info('I-04-05 I/Fファイルは全て受領していることを確認しました') + + # ⑤ 「①」でメモリ保持しているメール挿入用文言に出力内容が存在するか確認する + logger.info('I-05-01 メール送信処理開始') + + if len(mail_message) == 0: + logger.info(f'I-05-09 {execute_month} {receive_timing}データI/Fファイルに不足が無いため、メール送信処理をスキップします') + batch_item_success.append(message_id) + continue + + # 1.存在した場合 + logger.info(f'I-05-02 {execute_month} {receive_timing}データI/Fファイルに不足があるため、メール送信処理を開始します') + + try: + logger.info( + 'I-05-03 ' + + f'通知メール(タイトル)テンプレートファイル読込 読込元:{CONFIG_BUCKET_NAME}/{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_title_template"]}' + ) + mail_title_response = s3_client.get_object( + Bucket=CONFIG_BUCKET_NAME, + Key=f'{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_title_template"]}' + ) + mail_title_template = mail_title_response['Body'].read().decode('utf-8') + # 改行を取り除く + mail_title = substitute_mail_template(mail_title_template, receive_timing, mail_message) + mail_title_without_line_break = mail_title.splitlines()[0] + logger.info('I-05-04 通知メール(タイトル)テンプレートファイルを読み込みました') + except Exception as e: + logger.error(f'E-05-01 通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') + raise e + + try: + logger.info( + 'I-05-05 ' + + f'通知メール(本文)テンプレートファイル読込 読込元:{CONFIG_BUCKET_NAME}/{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_body_template"]}' + ) + mail_body_template_response = s3_client.get_object( + Bucket=CONFIG_BUCKET_NAME, + Key=f'{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_body_template"]}' + ) + mail_body_template = mail_body_template_response['Body'].read().decode('utf-8') + # メール本文内のプレースホルダーを置き換える + mail_body = substitute_mail_template(mail_body_template, receive_timing, mail_message) + logger.info('I-05-06 通知メール(本文)テンプレートファイルを読み込みました') + except Exception as e: + logger.error(f'E-05-02 通知メール(本文)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') + raise e + + logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_NOTICE_TOPIC}') + params = { + 'TopicArn': MBJ_NOTICE_TOPIC, + 'Subject': mail_title_without_line_break, + 'Message': mail_body + } + sns_client.publish(**params) + logger.info('I-05-08 メール送信指示をしました') + + batch_item_success.append(message_id) + + return batch_item_failures, batch_item_success + + +def lambda_handler(event, context): + try: + # ① 処理開始ログを出力する + logger.info('I-01-01 処理開始 Enciseデータ受領チェック処理') + # 処理稼働年月を取得しメモリに保持する + execute_date_today = datetime.date.today() + execute_month = execute_date_today.strftime('%Y/%m') + logger.info(f'I-01-02 処理稼働月:{execute_month}') + # 処理失敗メッセージIDリストをメモリに保持する(初期値=空のリスト) + batch_item_failures = [] + # 処理成功メッセージIDリストをメモリに保持する(初期値=空のリスト) + batch_item_success = [] + # ② SQSメッセージ重複排除処理を行う + logger.info('I-02-01 メッセージ処理開始') + batch_item_failures, batch_item_success = unreceive_check(event["Records"], execute_month) + logger.info('I-06-01 すべてのメッセージの処理完了') + + # ⑦ メッセージを処理済として、以下のDynamoDBテーブルに記録する + push_success_messages_to_dynamo_db(batch_item_success) + logger.info('I-07-01 処理済みメッセージIDの記録完了') + logger.info('I-07-02 処理終了 Enciseデータ受領チェック処理') + except Exception as e: + logger.exception(f'E-99 想定外のエラーが発生しました エラー内容:{e}') + + return batch_item_failures From 43d959f6698b980770a0208a80afab74640e8b07 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 13:04:36 +0900 Subject: [PATCH 07/31] =?UTF-8?q?feat:=20=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E5=87=A6=E7=90=86=E3=82=92=E5=AE=9F=E8=A3=85=EF=BC=88=E3=81=BE?= =?UTF-8?q?=E3=81=A0=E9=80=94=E4=B8=AD=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check.py | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index 6dc2e89c..6448adcd 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -122,6 +122,18 @@ def substitute_mail_template(mail_template: str, receive_timing: str, mail_msg: return mail_str +def make_failure_item_on_error(message_id: str) -> dict[str, str]: + """Report batch item failuresによる処理に失敗したメッセージの判別のためのレスポンスを作成する + @see + Args: + message_id (str): SQSメッセージId + + Returns: + dict[str, str]: Report batch item failuresで失敗したSQSメッセージを判別するための辞書オブジェクト + """ + return {"itemIdentifier": message_id} + + def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, str]], list[str]]: batch_item_failures = [] @@ -139,11 +151,11 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s logger.info(f'受信したメッセージは既に処理済みのため、処理をスキップします。メッセージID: {message_id}') continue except Exception as e: - # TODO: エラー処理実装 - raise e + logger.error(f"E-02-01 メッセージ重複チェック処理に失敗しました エラー内容:{e}") + batch_item_failures.append(make_failure_item_on_error(message_id)) + continue # SQSパラメータをJSONシリアライズし、Pythonの辞書オブジェクト(イベントパラメータ)を取得する。 - event_parameter = json.loads(record['body']) # ③ 設定ファイル[受領チェック対象ファイルリスト]を読み込む @@ -158,8 +170,9 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s ) logger.info('I-03-02 受領チェック対象ファイルリストを読み込みました') except Exception as e: - # TODO: エラー処理実装 - raise e + logger.error(f"E-03-01 受領チェック対象ファイルリストの読み込みに失敗しました エラー内容:{e}") + batch_item_failures.append(make_failure_item_on_error(message_id)) + continue # ④ 受領チェック処理を行う receive_timing = event_parameter['receive_timing'] @@ -232,8 +245,10 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s mail_title_without_line_break = mail_title.splitlines()[0] logger.info('I-05-04 通知メール(タイトル)テンプレートファイルを読み込みました') except Exception as e: + # TODO: エラー処理実装 logger.error(f'E-05-01 通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') - raise e + batch_item_failures.append(make_failure_item_on_error(message_id)) + continue try: logger.info( @@ -250,7 +265,8 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s logger.info('I-05-06 通知メール(本文)テンプレートファイルを読み込みました') except Exception as e: logger.error(f'E-05-02 通知メール(本文)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') - raise e + batch_item_failures.append(make_failure_item_on_error(message_id)) + continue logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_NOTICE_TOPIC}') params = { @@ -267,27 +283,24 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s def lambda_handler(event, context): - try: - # ① 処理開始ログを出力する - logger.info('I-01-01 処理開始 Enciseデータ受領チェック処理') - # 処理稼働年月を取得しメモリに保持する - execute_date_today = datetime.date.today() - execute_month = execute_date_today.strftime('%Y/%m') - logger.info(f'I-01-02 処理稼働月:{execute_month}') - # 処理失敗メッセージIDリストをメモリに保持する(初期値=空のリスト) - batch_item_failures = [] - # 処理成功メッセージIDリストをメモリに保持する(初期値=空のリスト) - batch_item_success = [] - # ② SQSメッセージ重複排除処理を行う - logger.info('I-02-01 メッセージ処理開始') - batch_item_failures, batch_item_success = unreceive_check(event["Records"], execute_month) - logger.info('I-06-01 すべてのメッセージの処理完了') + # ① 処理開始ログを出力する + logger.info('I-01-01 処理開始 Enciseデータ受領チェック処理') + # 処理稼働年月を取得しメモリに保持する + execute_date_today = datetime.date.today() + execute_month = execute_date_today.strftime('%Y/%m') + logger.info(f'I-01-02 処理稼働月:{execute_month}') + # 処理失敗メッセージIDリストをメモリに保持する(初期値=空のリスト) + batch_item_failures = [] + # 処理成功メッセージIDリストをメモリに保持する(初期値=空のリスト) + batch_item_success = [] + # ② SQSメッセージ重複排除処理を行う + logger.info('I-02-01 メッセージ処理開始') + batch_item_failures, batch_item_success = unreceive_check(event["Records"], execute_month) + logger.info('I-06-01 すべてのメッセージの処理完了') - # ⑦ メッセージを処理済として、以下のDynamoDBテーブルに記録する - push_success_messages_to_dynamo_db(batch_item_success) - logger.info('I-07-01 処理済みメッセージIDの記録完了') - logger.info('I-07-02 処理終了 Enciseデータ受領チェック処理') - except Exception as e: - logger.exception(f'E-99 想定外のエラーが発生しました エラー内容:{e}') + # ⑦ メッセージを処理済として、以下のDynamoDBテーブルに記録する + push_success_messages_to_dynamo_db(batch_item_success) + logger.info('I-07-01 処理済みメッセージIDの記録完了') + logger.info('I-07-02 処理終了 Enciseデータ受領チェック処理') return batch_item_failures From 29dccb84fff52ef7d9ef2445aad14a9d8e5739ee Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 13:23:03 +0900 Subject: [PATCH 08/31] =?UTF-8?q?style:=20=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E5=87=A6=E7=90=86=E5=AE=9F=E8=A3=85TODO=E3=82=B3=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check/encise-data-unreceive-check.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index 6448adcd..e1e9761b 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -245,7 +245,6 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s mail_title_without_line_break = mail_title.splitlines()[0] logger.info('I-05-04 通知メール(タイトル)テンプレートファイルを読み込みました') except Exception as e: - # TODO: エラー処理実装 logger.error(f'E-05-01 通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') batch_item_failures.append(make_failure_item_on_error(message_id)) continue From 58e3a182bc62edd32a69c043796dcd1c47ae5e4e Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 13:33:45 +0900 Subject: [PATCH 09/31] =?UTF-8?q?feat:=20=E6=83=B3=E5=AE=9A=E5=A4=96?= =?UTF-8?q?=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC=E7=99=BA=E7=94=9F=E6=99=82?= =?UTF-8?q?=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC=E5=87=A6=E7=90=86=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check.py | 243 +++++++++--------- 1 file changed, 124 insertions(+), 119 deletions(-) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index e1e9761b..ca77d714 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -142,142 +142,147 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s for record in records: # メール挿入用文言を格納するためのメモリを保持する mail_message = '' - try: - # 1.SQSメッセージIDを取得する - message_id = record["messageId"] - # 2.DynamoDBテーブルからレコードを取得し、処理済みメッセージかどうかを判別する - if is_duplicate_message(message_id): - logger.info(f'受信したメッセージは既に処理済みのため、処理をスキップします。メッセージID: {message_id}') + try: + # 1.SQSメッセージIDを取得する + message_id = record["messageId"] + # 2.DynamoDBテーブルからレコードを取得し、処理済みメッセージかどうかを判別する + if is_duplicate_message(message_id): + logger.info(f'受信したメッセージは既に処理済みのため、処理をスキップします。メッセージID: {message_id}') + continue + except Exception as e: + logger.error(f"E-02-01 メッセージ重複チェック処理に失敗しました エラー内容:{e}") + batch_item_failures.append(make_failure_item_on_error(message_id)) continue - except Exception as e: - logger.error(f"E-02-01 メッセージ重複チェック処理に失敗しました エラー内容:{e}") - batch_item_failures.append(make_failure_item_on_error(message_id)) - continue - # SQSパラメータをJSONシリアライズし、Pythonの辞書オブジェクト(イベントパラメータ)を取得する。 - event_parameter = json.loads(record['body']) + # SQSパラメータをJSONシリアライズし、Pythonの辞書オブジェクト(イベントパラメータ)を取得する。 + event_parameter = json.loads(record['body']) - # ③ 設定ファイル[受領チェック対象ファイルリスト]を読み込む - try: + # ③ 設定ファイル[受領チェック対象ファイルリスト]を読み込む + try: + logger.info( + 'I-03-01 ' + + '受領チェック対象ファイルリスト読込 読込元:' + + f'{CONFIG_BUCKET_NAME}/{CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH}/{event_parameter["check_target_file_list"]}' + ) + check_target_file_list_response = s3_client.get_object( + Bucket=CONFIG_BUCKET_NAME, + Key=f'{CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH}/{event_parameter["check_target_file_list"]}' + ) + logger.info('I-03-02 受領チェック対象ファイルリストを読み込みました') + except Exception as e: + logger.error(f"E-03-01 受領チェック対象ファイルリストの読み込みに失敗しました エラー内容:{e}") + batch_item_failures.append(make_failure_item_on_error(message_id)) + continue + + # ④ 受領チェック処理を行う + receive_timing = event_parameter['receive_timing'] + logger.info(f'I-04-01 Enciseデータ受領チェック ({receive_timing}) 処理開始') + object_prefix = f'{event_parameter["check_folder_prefix"]}{execute_month}/' + # 1.Enciseデータバックアップ保管バケットの処理稼働月に該当するサブフォルダにあるファイル一覧を取得する logger.info( - 'I-03-01 ' + - f'受領チェック対象ファイルリスト読込 読込元:{CONFIG_BUCKET_NAME}/{CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH}/{event_parameter["check_target_file_list"]}' + f'I-04-02 オブジェクトリストの取得 取得先:{CHECK_BUCKET_NAME}/{object_prefix}' ) - check_target_file_list_response = s3_client.get_object( - Bucket=CONFIG_BUCKET_NAME, - Key=f'{CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH}/{event_parameter["check_target_file_list"]}' - ) - logger.info('I-03-02 受領チェック対象ファイルリストを読み込みました') - except Exception as e: - logger.error(f"E-03-01 受領チェック対象ファイルリストの読み込みに失敗しました エラー内容:{e}") - batch_item_failures.append(make_failure_item_on_error(message_id)) - continue + check_target_file_list_response = s3_client.list_objects_v2(CHECK_BUCKET_NAME, Prefix=object_prefix) + check_target_file_list = [] + for content in check_target_file_list_response[['Contents']]: + # オブジェクトのキーからファイル名を切り出してリストに追加 + obj_key = content['Key'].rsplit('/', INDEX_SPLIT_NUM) + check_target_file_list.append(obj_key[INDEX_LAST]) - # ④ 受領チェック処理を行う - receive_timing = event_parameter['receive_timing'] - logger.info(f'I-04-01 Enciseデータ受領チェック ({receive_timing}) 処理開始') - object_prefix = f'{event_parameter["check_folder_prefix"]}{execute_month}/' - # 1.Enciseデータバックアップ保管バケットの処理稼働月に該当するサブフォルダにあるファイル一覧を取得する - logger.info( - f'I-04-02 オブジェクトリストの取得 取得先:{CHECK_BUCKET_NAME}/{object_prefix}' - ) - check_target_file_list_response = s3_client.list_objects_v2(CHECK_BUCKET_NAME, Prefix=object_prefix) - check_target_file_list = [] - for content in check_target_file_list_response[['Contents']]: - # オブジェクトのキーからファイル名を切り出してリストに追加 - obj_key = content['Key'].rsplit('/', INDEX_SPLIT_NUM) - check_target_file_list.append(obj_key[INDEX_LAST]) + # 2.I/Fファイルチェック処理 + logger.info(f'I-04-03 Enciseデータ({receive_timing}) ファイルチェック処理開始') + receive_monthly_file_name_body = io.TextIOWrapper(io.BytesIO(check_target_file_list_response["Body"].read()), encoding='utf-8') + match_count = 0 + row_count = 0 + for tsv_row in csv.reader(receive_monthly_file_name_body, delimiter='\t'): + # 「④1.」で取得したリストが「③」で読み込んだファイル内に存在するか確認する + is_file_not_exists = True + for file_name in check_target_file_list: + match_result = re.fullmatch(tsv_row[INDEX_REGEX], file_name) + # 「③」で読み込んだファイルに記載されている全てが「④1.」で取得したリストに存在した場合 + if match_result is not None: + # 存在したファイルの年月部分を抜き出し、チェック対象年月(処理稼働月-1)である場合 + if True: # TODO + logger.info(f'I-04-04 I/Fファイルの受領を確認しました ファイル名:{file_name}') + is_file_not_exists = False + match_count += 1 + else: + logger.info(f'I-04-07 I/Fファイルの年月がチェック対象年月と一致しません ファイル名:{file_name}') + mail_message += f'{MAIL_INDENT}{tsv_row[INDEX_DATA_NAME]}(受領年月が不正:{file_name})\n' + break + if is_file_not_exists: + logger.info(f'E-04-06 月次I/Fファイルに不足があります ファイル名:{tsv_row[INDEX_DATA_NAME]}') + mail_message += f'{MAIL_INDENT}{tsv_row[INDEX_DATA_NAME]}\n' - # 2.I/Fファイルチェック処理 - logger.info(f'I-04-03 Enciseデータ({receive_timing}) ファイルチェック処理開始') - receive_monthly_file_name_body = io.TextIOWrapper(io.BytesIO(check_target_file_list_response["Body"].read()), encoding='utf-8') - match_count = 0 - row_count = 0 - for tsv_row in csv.reader(receive_monthly_file_name_body, delimiter='\t'): - # 「④1.」で取得したリストが「③」で読み込んだファイル内に存在するか確認する - is_file_not_exists = True - for file_name in check_target_file_list: - match_result = re.fullmatch(tsv_row[INDEX_REGEX], file_name) - # 「③」で読み込んだファイルに記載されている全てが「④1.」で取得したリストに存在した場合 - if match_result is not None: - # 存在したファイルの年月部分を抜き出し、チェック対象年月(処理稼働月-1)である場合 - if True: # TODO - logger.info(f'I-04-04 I/Fファイルの受領を確認しました ファイル名:{file_name}') - is_file_not_exists = False - match_count += 1 - else: - logger.info(f'I-04-07 I/Fファイルの年月がチェック対象年月と一致しません ファイル名:{file_name}') - mail_message += f'{MAIL_INDENT}{tsv_row[INDEX_DATA_NAME]}(受領年月が不正:{file_name})\n' - break - if is_file_not_exists: - logger.info(f'E-04-06 月次I/Fファイルに不足があります ファイル名:{tsv_row[INDEX_DATA_NAME]}') - mail_message += f'{MAIL_INDENT}{tsv_row[INDEX_DATA_NAME]}\n' + row_count += 1 - row_count += 1 + if row_count == match_count: + logger.info('I-04-05 I/Fファイルは全て受領していることを確認しました') - if row_count == match_count: - logger.info('I-04-05 I/Fファイルは全て受領していることを確認しました') + # ⑤ 「①」でメモリ保持しているメール挿入用文言に出力内容が存在するか確認する + logger.info('I-05-01 メール送信処理開始') - # ⑤ 「①」でメモリ保持しているメール挿入用文言に出力内容が存在するか確認する - logger.info('I-05-01 メール送信処理開始') + if len(mail_message) == 0: + logger.info(f'I-05-09 {execute_month} {receive_timing}データI/Fファイルに不足が無いため、メール送信処理をスキップします') + batch_item_success.append(message_id) + continue + + # 1.存在した場合 + logger.info(f'I-05-02 {execute_month} {receive_timing}データI/Fファイルに不足があるため、メール送信処理を開始します') + + try: + logger.info( + 'I-05-03 ' + + f'通知メール(タイトル)テンプレートファイル読込 読込元:{CONFIG_BUCKET_NAME}/{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_title_template"]}' + ) + mail_title_response = s3_client.get_object( + Bucket=CONFIG_BUCKET_NAME, + Key=f'{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_title_template"]}' + ) + mail_title_template = mail_title_response['Body'].read().decode('utf-8') + # 改行を取り除く + mail_title = substitute_mail_template(mail_title_template, receive_timing, mail_message) + mail_title_without_line_break = mail_title.splitlines()[0] + logger.info('I-05-04 通知メール(タイトル)テンプレートファイルを読み込みました') + except Exception as e: + logger.error(f'E-05-01 通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') + batch_item_failures.append(make_failure_item_on_error(message_id)) + continue + + try: + logger.info( + 'I-05-05 ' + + f'通知メール(本文)テンプレートファイル読込 読込元:{CONFIG_BUCKET_NAME}/{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_body_template"]}' + ) + mail_body_template_response = s3_client.get_object( + Bucket=CONFIG_BUCKET_NAME, + Key=f'{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_body_template"]}' + ) + mail_body_template = mail_body_template_response['Body'].read().decode('utf-8') + # メール本文内のプレースホルダーを置き換える + mail_body = substitute_mail_template(mail_body_template, receive_timing, mail_message) + logger.info('I-05-06 通知メール(本文)テンプレートファイルを読み込みました') + except Exception as e: + logger.error(f'E-05-02 通知メール(本文)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') + batch_item_failures.append(make_failure_item_on_error(message_id)) + continue + + logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_NOTICE_TOPIC}') + params = { + 'TopicArn': MBJ_NOTICE_TOPIC, + 'Subject': mail_title_without_line_break, + 'Message': mail_body + } + sns_client.publish(**params) + logger.info('I-05-08 メール送信指示をしました') - if len(mail_message) == 0: - logger.info(f'I-05-09 {execute_month} {receive_timing}データI/Fファイルに不足が無いため、メール送信処理をスキップします') batch_item_success.append(message_id) - continue - - # 1.存在した場合 - logger.info(f'I-05-02 {execute_month} {receive_timing}データI/Fファイルに不足があるため、メール送信処理を開始します') - - try: - logger.info( - 'I-05-03 ' + - f'通知メール(タイトル)テンプレートファイル読込 読込元:{CONFIG_BUCKET_NAME}/{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_title_template"]}' - ) - mail_title_response = s3_client.get_object( - Bucket=CONFIG_BUCKET_NAME, - Key=f'{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_title_template"]}' - ) - mail_title_template = mail_title_response['Body'].read().decode('utf-8') - # 改行を取り除く - mail_title = substitute_mail_template(mail_title_template, receive_timing, mail_message) - mail_title_without_line_break = mail_title.splitlines()[0] - logger.info('I-05-04 通知メール(タイトル)テンプレートファイルを読み込みました') except Exception as e: - logger.error(f'E-05-01 通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') + logger.error(f'E-99 想定外のエラーが発生しました エラー内容:{e}') batch_item_failures.append(make_failure_item_on_error(message_id)) continue - try: - logger.info( - 'I-05-05 ' + - f'通知メール(本文)テンプレートファイル読込 読込元:{CONFIG_BUCKET_NAME}/{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_body_template"]}' - ) - mail_body_template_response = s3_client.get_object( - Bucket=CONFIG_BUCKET_NAME, - Key=f'{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_body_template"]}' - ) - mail_body_template = mail_body_template_response['Body'].read().decode('utf-8') - # メール本文内のプレースホルダーを置き換える - mail_body = substitute_mail_template(mail_body_template, receive_timing, mail_message) - logger.info('I-05-06 通知メール(本文)テンプレートファイルを読み込みました') - except Exception as e: - logger.error(f'E-05-02 通知メール(本文)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') - batch_item_failures.append(make_failure_item_on_error(message_id)) - continue - - logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_NOTICE_TOPIC}') - params = { - 'TopicArn': MBJ_NOTICE_TOPIC, - 'Subject': mail_title_without_line_break, - 'Message': mail_body - } - sns_client.publish(**params) - logger.info('I-05-08 メール送信指示をしました') - - batch_item_success.append(message_id) - return batch_item_failures, batch_item_success From 8ca83fcc74a1741a48ded89ffb4c6a1497ca6d64 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 13:34:46 +0900 Subject: [PATCH 10/31] =?UTF-8?q?style:=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index ca77d714..4e782b03 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -81,11 +81,6 @@ def is_duplicate_message(message_id: str) -> bool: )["Count"] != 0 -# Put the message to the DynamoDB Table. -# @input string batch_item_success -# -# @param batch_item_success of the message to put. -# @return Boolean def push_success_messages_to_dynamo_db(batch_item_success: list[str]) -> bool: """処理済みのSQSメッセージIdをDynamoDBにPushする @@ -104,7 +99,7 @@ def push_success_messages_to_dynamo_db(batch_item_success: list[str]) -> bool: def substitute_mail_template(mail_template: str, receive_timing: str, mail_msg: str) -> str: - """メールテンプレートのプレースホルダーを置き換えます + """メールテンプレートのプレースホルダーを置き換える Args: mail_template (str): 置き換え前のメールテンプレート From 41542bbd9d535e1936aac13e2ae9223f27aca66f Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 13:38:43 +0900 Subject: [PATCH 11/31] =?UTF-8?q?feat:=20lambda=5Fhandler=E3=81=AB?= =?UTF-8?q?=E4=BE=8B=E5=A4=96=E5=87=A6=E7=90=86=E8=BF=BD=E5=8A=A0=E3=80=81?= =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E8=BF=BD=E5=8A=A0=E3=80=81?= =?UTF-8?q?=E6=9C=AA=E5=8F=97=E9=A0=98=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF?= =?UTF-8?q?=E9=96=A2=E6=95=B0=E5=90=8D=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check.py | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index 4e782b03..dc6085cf 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -129,8 +129,16 @@ def make_failure_item_on_error(message_id: str) -> dict[str, str]: return {"itemIdentifier": message_id} -def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, str]], list[str]]: +def encise_data_unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, str]], list[str]]: + """Enciseデータ未受領チェック + Args: + records (list): SQS Eventのレコードリスト + execute_month (str): 処理起動年月 + + Returns: + tuple[list[dict[str, str]], list[str]]: 失敗メッセージIdのリスト, 成功メッセージIdのリスト + """ batch_item_failures = [] batch_item_success = [] @@ -146,7 +154,7 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s logger.info(f'受信したメッセージは既に処理済みのため、処理をスキップします。メッセージID: {message_id}') continue except Exception as e: - logger.error(f"E-02-01 メッセージ重複チェック処理に失敗しました エラー内容:{e}") + logger.exception(f"E-02-01 メッセージ重複チェック処理に失敗しました エラー内容:{e}") batch_item_failures.append(make_failure_item_on_error(message_id)) continue @@ -166,7 +174,7 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s ) logger.info('I-03-02 受領チェック対象ファイルリストを読み込みました') except Exception as e: - logger.error(f"E-03-01 受領チェック対象ファイルリストの読み込みに失敗しました エラー内容:{e}") + logger.exception(f"E-03-01 受領チェック対象ファイルリストの読み込みに失敗しました エラー内容:{e}") batch_item_failures.append(make_failure_item_on_error(message_id)) continue @@ -241,7 +249,7 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s mail_title_without_line_break = mail_title.splitlines()[0] logger.info('I-05-04 通知メール(タイトル)テンプレートファイルを読み込みました') except Exception as e: - logger.error(f'E-05-01 通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') + logger.exception(f'E-05-01 通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') batch_item_failures.append(make_failure_item_on_error(message_id)) continue @@ -259,7 +267,7 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s mail_body = substitute_mail_template(mail_body_template, receive_timing, mail_message) logger.info('I-05-06 通知メール(本文)テンプレートファイルを読み込みました') except Exception as e: - logger.error(f'E-05-02 通知メール(本文)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') + logger.exception(f'E-05-02 通知メール(本文)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') batch_item_failures.append(make_failure_item_on_error(message_id)) continue @@ -274,7 +282,7 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s batch_item_success.append(message_id) except Exception as e: - logger.error(f'E-99 想定外のエラーが発生しました エラー内容:{e}') + logger.exception(f'E-99 想定外のエラーが発生しました エラー内容:{e}') batch_item_failures.append(make_failure_item_on_error(message_id)) continue @@ -282,24 +290,29 @@ def unreceive_check(records: list, execute_month: str) -> tuple[list[dict[str, s def lambda_handler(event, context): - # ① 処理開始ログを出力する - logger.info('I-01-01 処理開始 Enciseデータ受領チェック処理') - # 処理稼働年月を取得しメモリに保持する - execute_date_today = datetime.date.today() - execute_month = execute_date_today.strftime('%Y/%m') - logger.info(f'I-01-02 処理稼働月:{execute_month}') - # 処理失敗メッセージIDリストをメモリに保持する(初期値=空のリスト) - batch_item_failures = [] - # 処理成功メッセージIDリストをメモリに保持する(初期値=空のリスト) - batch_item_success = [] - # ② SQSメッセージ重複排除処理を行う - logger.info('I-02-01 メッセージ処理開始') - batch_item_failures, batch_item_success = unreceive_check(event["Records"], execute_month) - logger.info('I-06-01 すべてのメッセージの処理完了') + try: + # ① 処理開始ログを出力する + logger.info('I-01-01 処理開始 Enciseデータ受領チェック処理') + # 処理稼働年月を取得しメモリに保持する + execute_date_today = datetime.date.today() + execute_month = execute_date_today.strftime('%Y/%m') + logger.info(f'I-01-02 処理稼働月:{execute_month}') + # 処理失敗メッセージIDリストをメモリに保持する(初期値=空のリスト) + batch_item_failures = [] + # 処理成功メッセージIDリストをメモリに保持する(初期値=空のリスト) + batch_item_success = [] + # ② SQSメッセージ重複排除処理を行う + logger.info('I-02-01 メッセージ処理開始') + batch_item_failures, batch_item_success = encise_data_unreceive_check(event["Records"], execute_month) + logger.info('I-06-01 すべてのメッセージの処理完了') - # ⑦ メッセージを処理済として、以下のDynamoDBテーブルに記録する - push_success_messages_to_dynamo_db(batch_item_success) - logger.info('I-07-01 処理済みメッセージIDの記録完了') - logger.info('I-07-02 処理終了 Enciseデータ受領チェック処理') + # ⑦ メッセージを処理済として、以下のDynamoDBテーブルに記録する + push_success_messages_to_dynamo_db(batch_item_success) + logger.info('I-07-01 処理済みメッセージIDの記録完了') + logger.info('I-07-02 処理終了 Enciseデータ受領チェック処理') + + except Exception as e: + logger.exception(f'E-99 想定外のエラーが発生しました エラー内容:{e}') + raise e return batch_item_failures From d3615e38c4e8daf62b792421901db30a0cdfff47 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 13:46:50 +0900 Subject: [PATCH 12/31] =?UTF-8?q?feat:=20NDS=E9=80=9A=E7=9F=A5SNS=E3=81=AF?= =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AA=E3=81=AE=E3=81=A7=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check/encise-data-unreceive-check.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index dc6085cf..130e0a9a 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -16,8 +16,6 @@ CONFIG_BUCKET_NAME = os.environ["CONFIG_BUCKET_NAME"] LOG_LEVEL = os.environ["LOG_LEVEL"] MAIL_TEMPLATE_FOLDER_PATH = os.environ["MAIL_TEMPLATE_FOLDER_PATH"] MBJ_NOTICE_TOPIC = os.environ["MBJ_NOTICE_TOPIC"] -NDS_NOTICE_TITLE = os.environ["NDS_NOTICE_TITLE"] -NDS_NOTICE_TOPIC = os.environ["NDS_NOTICE_TOPIC"] PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME = os.environ["PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME"] PROCESSED_MESSAGE_EXPIRES_PERIOD = os.environ["PROCESSED_MESSAGE_EXPIRES_PERIOD"] TZ = os.environ["TZ"] From 5cadb1a466be0371aa80f71c9c09635b742e3818 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 14:02:08 +0900 Subject: [PATCH 13/31] =?UTF-8?q?feat:=20DynamoDB=E3=83=86=E3=83=BC?= =?UTF-8?q?=E3=83=96=E3=83=AB=E3=81=AE=E3=83=AC=E3=82=B3=E3=83=BC=E3=83=89?= =?UTF-8?q?=E6=9C=89=E5=8A=B9=E6=9C=9F=E9=99=90=E7=94=A8=E3=81=AE=E9=A0=85?= =?UTF-8?q?=E7=9B=AE=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index 130e0a9a..e4e26de8 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -17,7 +17,7 @@ LOG_LEVEL = os.environ["LOG_LEVEL"] MAIL_TEMPLATE_FOLDER_PATH = os.environ["MAIL_TEMPLATE_FOLDER_PATH"] MBJ_NOTICE_TOPIC = os.environ["MBJ_NOTICE_TOPIC"] PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME = os.environ["PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME"] -PROCESSED_MESSAGE_EXPIRES_PERIOD = os.environ["PROCESSED_MESSAGE_EXPIRES_PERIOD"] +PROCESSED_MESSAGE_EXPIRES_PERIOD = int(os.environ["PROCESSED_MESSAGE_EXPIRES_PERIOD"]) TZ = os.environ["TZ"] # 定数 @@ -79,7 +79,7 @@ def is_duplicate_message(message_id: str) -> bool: )["Count"] != 0 -def push_success_messages_to_dynamo_db(batch_item_success: list[str]) -> bool: +def put_success_messages_to_dynamo_db(batch_item_success: list[str]) -> bool: """処理済みのSQSメッセージIdをDynamoDBにPushする Args: @@ -88,10 +88,19 @@ def push_success_messages_to_dynamo_db(batch_item_success: list[str]) -> bool: Returns: bool: 登録成功の場合、True """ + + # レコードの有効期限を算出 + now = datetime.datetime.now(ZoneInfo(TZ)) + record_expiration_datetime = now + datetime.timedelta(minutes=PROCESSED_MESSAGE_EXPIRES_PERIOD) + record_expiration_time = record_expiration_datetime.timestamp() + for message_id in batch_item_success: dynamodb_client.put_item( TableName=PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME, - Item={'message_id': {'S': message_id}} + Item={ + 'message_id': {'S': message_id}, + 'record_expiration_time': {'N': f'{record_expiration_time}'} + } ) return True @@ -305,7 +314,7 @@ def lambda_handler(event, context): logger.info('I-06-01 すべてのメッセージの処理完了') # ⑦ メッセージを処理済として、以下のDynamoDBテーブルに記録する - push_success_messages_to_dynamo_db(batch_item_success) + put_success_messages_to_dynamo_db(batch_item_success) logger.info('I-07-01 処理済みメッセージIDの記録完了') logger.info('I-07-02 処理終了 Enciseデータ受領チェック処理') From d2485a4afec4cd42fbe78c8ac793b6491a319ecf Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 14:45:58 +0900 Subject: [PATCH 14/31] =?UTF-8?q?feat:=20=E6=9C=AA=E5=8F=97=E9=A0=98?= =?UTF-8?q?=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E5=AF=BE=E8=B1=A1=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E3=83=AA=E3=82=B9=E3=83=88=E3=82=92?= =?UTF-8?q?=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../en_cluster_direct_file_list.config | 3 +++ .../en_cluster_file_list.config | 6 ++++++ .../unreceive_check_target/en_fixed_file_list.config | 11 +++++++++++ .../en_nation_direct_fixed_file_list.config | 1 + .../unreceive_check_target/en_quick_file_list.config | 10 ++++++++++ 5 files changed, 31 insertions(+) create mode 100644 s3/config/encise/unreceive_check_target/en_cluster_direct_file_list.config create mode 100644 s3/config/encise/unreceive_check_target/en_cluster_file_list.config create mode 100644 s3/config/encise/unreceive_check_target/en_fixed_file_list.config create mode 100644 s3/config/encise/unreceive_check_target/en_nation_direct_fixed_file_list.config create mode 100644 s3/config/encise/unreceive_check_target/en_quick_file_list.config diff --git a/s3/config/encise/unreceive_check_target/en_cluster_direct_file_list.config b/s3/config/encise/unreceive_check_target/en_cluster_direct_file_list.config new file mode 100644 index 00000000..44631a3a --- /dev/null +++ b/s3/config/encise/unreceive_check_target/en_cluster_direct_file_list.config @@ -0,0 +1,3 @@ +CLUFD_Merck02_496_([0-9]{6})D\.(CSV|csv) 直販分 En-Clusterデータ Merck02 +CLUFD_Merck03_496_([0-9]{6})D\.(CSV|csv) 直販分 En-Clusterデータ Merck03 +CLUFD_Merck04_496_([0-9]{6})D\.(CSV|csv) 直販分 En-Clusterデータ Merck04 diff --git a/s3/config/encise/unreceive_check_target/en_cluster_file_list.config b/s3/config/encise/unreceive_check_target/en_cluster_file_list.config new file mode 100644 index 00000000..33c31f6f --- /dev/null +++ b/s3/config/encise/unreceive_check_target/en_cluster_file_list.config @@ -0,0 +1,6 @@ +CLUFD_Merck02_496_([0-9]{6})\.(CSV|csv) En-Cluster データ Merck02 +CLUFD_Merck03_496_([0-9]{6})\.(CSV|csv) En-Cluster データ Merck03 +CLUFD_Merck04_496_([0-9]{6})\.(CSV|csv) En-Cluster データ Merck04 +CLUFM_CLUMST_496_01_([0-9]{6})\.(CSV|csv) En-Cluster クラスタマスタ +CLUFM_PROADD_496_([0-9]{6})\.(CSV|csv) En-Cluster 製品定義ファイル +CLUFM_PROLST_496_([0-9]{6})\.(CSV|csv) En-Cluster 製品マスタ diff --git a/s3/config/encise/unreceive_check_target/en_fixed_file_list.config b/s3/config/encise/unreceive_check_target/en_fixed_file_list.config new file mode 100644 index 00000000..edd0126b --- /dev/null +++ b/s3/config/encise/unreceive_check_target/en_fixed_file_list.config @@ -0,0 +1,11 @@ +NATFD_496_([0-9]{6})\.(CSV|csv) 確定版 En-Nation データ +NATFM_PROADD_496_([0-9]{6})\.(CSV|csv) 確定版 En-Nation 製品定義ファイル +NATFM_SEGMNT_496_([0-9]{6})\.(CSV|csv) 確定版 En-Nation セグメントマスタ +MST_BRAND([0-9]{6})\.(CSV|csv) 確定版 En-Nation ブランドマスタ +CITFD_Merck01_496_([0-9]{6})\.(CSV|csv) 確定版 En-City データ Merck01 +CITFD_Merck06_496_([0-9]{6})\.(CSV|csv) 確定版 En-City データ Merck06 +CITFD_Merck07_496_([0-9]{6})\.(CSV|csv) 確定版 En-City データ Merck07 +CITFM_PROADD_496_([0-9]{6})\.(CSV|csv) 確定版 En-City 製品定義ファイル +CITFM_PROLST_496_([0-9]{6})\.(CSV|csv) 確定版 En-City 製品マスタ +CITFM_REGION_496_([0-9]{6})\.(CSV|csv) 確定版 En-City 地域マスタ +CITFM_SEGMNT_496_([0-9]{6})\.(CSV|csv) 確定版 En-City セグメントマスタ diff --git a/s3/config/encise/unreceive_check_target/en_nation_direct_fixed_file_list.config b/s3/config/encise/unreceive_check_target/en_nation_direct_fixed_file_list.config new file mode 100644 index 00000000..dfb557e8 --- /dev/null +++ b/s3/config/encise/unreceive_check_target/en_nation_direct_fixed_file_list.config @@ -0,0 +1 @@ +NATFD_496_([0-9]{6})D\.(CSV|csv) 確定版 En-Nation データ 直販分 \ No newline at end of file diff --git a/s3/config/encise/unreceive_check_target/en_quick_file_list.config b/s3/config/encise/unreceive_check_target/en_quick_file_list.config new file mode 100644 index 00000000..ae8a4761 --- /dev/null +++ b/s3/config/encise/unreceive_check_target/en_quick_file_list.config @@ -0,0 +1,10 @@ +NATQD_496_([0-9]{6}\).(CSV|csv) 速報版 En-Nation データ +NATQM_PROADD_496_([0-9]{6})\.(CSV|csv) 速報版 En-Nation 製品定義ファイル +NATQM_SEGMNT_496_([0-9]{6})\.(CSV|csv) 速報版 En-Nation セグメントマスタ +CITQD_Merck01_496_([0-9]{6})\.(CSV|csv) 速報版 En-City データ Merck01 +CITQD_Merck06_496_([0-9]{6})\.(CSV|csv) 速報版 En-City データ Merck06 +CITQD_Merck07_496_([0-9]{6})\.(CSV|csv) 速報版 En-City データ Merck07 +CITQM_PROADD_496_([0-9]{6})\.(CSV|csv) 速報版 En-City 製品定義ファイル +CITQM_PROLST_496_([0-9]{6})\.(CSV|csv) 速報版 En-City 製品マスタ +CITQM_REGION_496_([0-9]{6})\.(CSV|csv) 速報版 En-City 地域マスタ +CITQM_SEGMNT_496_([0-9]{6})\.(CSV|csv) 速報版 En-City セグメントマスタ From 2b7031f685ef1f768f52a4ca7433fa4c9e1344c4 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 14:48:44 +0900 Subject: [PATCH 15/31] =?UTF-8?q?feat:=20=E3=80=81=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=83=AB=E3=83=86=E3=83=B3=E3=83=97=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92?= =?UTF-8?q?=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../en_direct_unreceive_notice_mail_body.config | 9 +++++++++ .../en_direct_unreceive_notice_mail_title.config | 1 + .../en_unreceive_notice_mail_body.config | 9 +++++++++ .../en_unreceive_notice_mail_title.config | 1 + 4 files changed, 20 insertions(+) create mode 100644 s3/config/encise/notice_mail_template/en_direct_unreceive_notice_mail_body.config create mode 100644 s3/config/encise/notice_mail_template/en_direct_unreceive_notice_mail_title.config create mode 100644 s3/config/encise/notice_mail_template/en_unreceive_notice_mail_body.config create mode 100644 s3/config/encise/notice_mail_template/en_unreceive_notice_mail_title.config diff --git a/s3/config/encise/notice_mail_template/en_direct_unreceive_notice_mail_body.config b/s3/config/encise/notice_mail_template/en_direct_unreceive_notice_mail_body.config new file mode 100644 index 00000000..9cf79023 --- /dev/null +++ b/s3/config/encise/notice_mail_template/en_direct_unreceive_notice_mail_body.config @@ -0,0 +1,9 @@ +宛先各位 + Encise {receive_timing}である以下のファイルを受領できておりません。 +{notice_file_names} + + + QlikSenseサーバー側の送信状況のご確認をお願いいたします。 + + 尚、本メールはシステム自動送信のため、返信は出来ません。 + 本件に関する問い合わせは、MeDaCA Enciseデータ担当者にお願いいたします。 diff --git a/s3/config/encise/notice_mail_template/en_direct_unreceive_notice_mail_title.config b/s3/config/encise/notice_mail_template/en_direct_unreceive_notice_mail_title.config new file mode 100644 index 00000000..d40c304d --- /dev/null +++ b/s3/config/encise/notice_mail_template/en_direct_unreceive_notice_mail_title.config @@ -0,0 +1 @@ +【MeDaCa連携エラー通知】Encise {receive_timing}ファイル未受領 \ No newline at end of file diff --git a/s3/config/encise/notice_mail_template/en_unreceive_notice_mail_body.config b/s3/config/encise/notice_mail_template/en_unreceive_notice_mail_body.config new file mode 100644 index 00000000..591d85cb --- /dev/null +++ b/s3/config/encise/notice_mail_template/en_unreceive_notice_mail_body.config @@ -0,0 +1,9 @@ +宛先各位 + Encise {receive_timing}である以下のファイルを受領できておりません。 +{notice_file_names} + + + Encise社へ送信状況のご確認をお願いいたします。 + + 尚、本メールはシステム自動送信のため、返信は出来ません。 + 本件に関する問い合わせは、MeDaCA Enciseデータ担当者にお願いいたします。 diff --git a/s3/config/encise/notice_mail_template/en_unreceive_notice_mail_title.config b/s3/config/encise/notice_mail_template/en_unreceive_notice_mail_title.config new file mode 100644 index 00000000..d40c304d --- /dev/null +++ b/s3/config/encise/notice_mail_template/en_unreceive_notice_mail_title.config @@ -0,0 +1 @@ +【MeDaCa連携エラー通知】Encise {receive_timing}ファイル未受領 \ No newline at end of file From 1153cfa8b52263ef55c7e3f011b2b8a0ca813909 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 16:21:44 +0900 Subject: [PATCH 16/31] =?UTF-8?q?fix:=20=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=81=AE=E6=AD=A3=E8=A6=8F=E8=A1=A8=E7=8F=BE=E3=81=AE?= =?UTF-8?q?=E3=83=9F=E3=82=B9=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise/unreceive_check_target/en_quick_file_list.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/s3/config/encise/unreceive_check_target/en_quick_file_list.config b/s3/config/encise/unreceive_check_target/en_quick_file_list.config index ae8a4761..e3f3ebcd 100644 --- a/s3/config/encise/unreceive_check_target/en_quick_file_list.config +++ b/s3/config/encise/unreceive_check_target/en_quick_file_list.config @@ -1,4 +1,4 @@ -NATQD_496_([0-9]{6}\).(CSV|csv) 速報版 En-Nation データ +NATQD_496_([0-9]{6})\.(CSV|csv) 速報版 En-Nation データ NATQM_PROADD_496_([0-9]{6})\.(CSV|csv) 速報版 En-Nation 製品定義ファイル NATQM_SEGMNT_496_([0-9]{6})\.(CSV|csv) 速報版 En-Nation セグメントマスタ CITQD_Merck01_496_([0-9]{6})\.(CSV|csv) 速報版 En-City データ Merck01 From f1ce484d1dda6e911fb9080997312e8e67221699 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 16:24:16 +0900 Subject: [PATCH 17/31] =?UTF-8?q?feat:=20=E3=83=81=E3=82=A7=E3=83=83?= =?UTF-8?q?=E3=82=AF=E5=AF=BE=E8=B1=A1=E5=B9=B4=E6=9C=88=E3=81=AE=E3=83=AD?= =?UTF-8?q?=E3=82=B8=E3=83=83=E3=82=AF=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check.py | 91 ++++++++++++------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index e4e26de8..1667f9e2 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -8,6 +8,7 @@ import re from zoneinfo import ZoneInfo import boto3 +from dateutil.relativedelta import relativedelta # 環境変数 CHECK_BUCKET_NAME = os.environ["CHECK_BUCKET_NAME"] @@ -17,7 +18,8 @@ LOG_LEVEL = os.environ["LOG_LEVEL"] MAIL_TEMPLATE_FOLDER_PATH = os.environ["MAIL_TEMPLATE_FOLDER_PATH"] MBJ_NOTICE_TOPIC = os.environ["MBJ_NOTICE_TOPIC"] PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME = os.environ["PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME"] -PROCESSED_MESSAGE_EXPIRES_PERIOD = int(os.environ["PROCESSED_MESSAGE_EXPIRES_PERIOD"]) +PROCESSED_MESSAGE_EXPIRES_PERIOD = int( + os.environ["PROCESSED_MESSAGE_EXPIRES_PERIOD"]) TZ = os.environ["TZ"] # 定数 @@ -91,7 +93,8 @@ def put_success_messages_to_dynamo_db(batch_item_success: list[str]) -> bool: # レコードの有効期限を算出 now = datetime.datetime.now(ZoneInfo(TZ)) - record_expiration_datetime = now + datetime.timedelta(minutes=PROCESSED_MESSAGE_EXPIRES_PERIOD) + record_expiration_datetime = now + \ + datetime.timedelta(minutes=PROCESSED_MESSAGE_EXPIRES_PERIOD) record_expiration_time = record_expiration_datetime.timestamp() for message_id in batch_item_success: @@ -158,11 +161,13 @@ def encise_data_unreceive_check(records: list, execute_month: str) -> tuple[list message_id = record["messageId"] # 2.DynamoDBテーブルからレコードを取得し、処理済みメッセージかどうかを判別する if is_duplicate_message(message_id): - logger.info(f'受信したメッセージは既に処理済みのため、処理をスキップします。メッセージID: {message_id}') + logger.info( + f'受信したメッセージは既に処理済みのため、処理をスキップします。メッセージID: {message_id}') continue except Exception as e: logger.exception(f"E-02-01 メッセージ重複チェック処理に失敗しました エラー内容:{e}") - batch_item_failures.append(make_failure_item_on_error(message_id)) + batch_item_failures.append( + make_failure_item_on_error(message_id)) continue # SQSパラメータをJSONシリアライズし、Pythonの辞書オブジェクト(イベントパラメータ)を取得する。 @@ -181,49 +186,60 @@ def encise_data_unreceive_check(records: list, execute_month: str) -> tuple[list ) logger.info('I-03-02 受領チェック対象ファイルリストを読み込みました') except Exception as e: - logger.exception(f"E-03-01 受領チェック対象ファイルリストの読み込みに失敗しました エラー内容:{e}") - batch_item_failures.append(make_failure_item_on_error(message_id)) + logger.exception( + f"E-03-01 受領チェック対象ファイルリストの読み込みに失敗しました エラー内容:{e}") + batch_item_failures.append( + make_failure_item_on_error(message_id)) continue # ④ 受領チェック処理を行う receive_timing = event_parameter['receive_timing'] logger.info(f'I-04-01 Enciseデータ受領チェック ({receive_timing}) 処理開始') - object_prefix = f'{event_parameter["check_folder_prefix"]}{execute_month}/' + object_prefix = f'{event_parameter["check_folder_prefix"]}/{execute_month}/' # 1.Enciseデータバックアップ保管バケットの処理稼働月に該当するサブフォルダにあるファイル一覧を取得する logger.info( f'I-04-02 オブジェクトリストの取得 取得先:{CHECK_BUCKET_NAME}/{object_prefix}' ) - check_target_file_list_response = s3_client.list_objects_v2(CHECK_BUCKET_NAME, Prefix=object_prefix) - check_target_file_list = [] - for content in check_target_file_list_response[['Contents']]: + receive_file_list_response = s3_client.list_objects_v2( + Bucket=CHECK_BUCKET_NAME, Prefix=object_prefix) + receive_file_list = [] + for content in receive_file_list_response.get('Contents', []): # オブジェクトのキーからファイル名を切り出してリストに追加 obj_key = content['Key'].rsplit('/', INDEX_SPLIT_NUM) - check_target_file_list.append(obj_key[INDEX_LAST]) + receive_file_list.append(obj_key[INDEX_LAST]) # 2.I/Fファイルチェック処理 logger.info(f'I-04-03 Enciseデータ({receive_timing}) ファイルチェック処理開始') - receive_monthly_file_name_body = io.TextIOWrapper(io.BytesIO(check_target_file_list_response["Body"].read()), encoding='utf-8') + check_target_file_name_body = io.TextIOWrapper(io.BytesIO( + check_target_file_list_response["Body"].read()), encoding='utf-8') match_count = 0 row_count = 0 - for tsv_row in csv.reader(receive_monthly_file_name_body, delimiter='\t'): + for tsv_row in csv.reader(check_target_file_name_body, delimiter='\t'): # 「④1.」で取得したリストが「③」で読み込んだファイル内に存在するか確認する is_file_not_exists = True - for file_name in check_target_file_list: - match_result = re.fullmatch(tsv_row[INDEX_REGEX], file_name) + for file_name in receive_file_list: + match_result = re.fullmatch( + tsv_row[INDEX_REGEX], file_name) # 「③」で読み込んだファイルに記載されている全てが「④1.」で取得したリストに存在した場合 if match_result is not None: # 存在したファイルの年月部分を抜き出し、チェック対象年月(処理稼働月-1)である場合 - if True: # TODO - logger.info(f'I-04-04 I/Fファイルの受領を確認しました ファイル名:{file_name}') + match_group_yyyymm = match_result.group(1) + check_target_yyyymm = (datetime.datetime.now( + ZoneInfo(TZ)) - relativedelta(month=1)).strftime('%Y%m') + if match_group_yyyymm == check_target_yyyymm: + logger.info( + f'I-04-04 I/Fファイルの受領を確認しました ファイル名:{file_name}') is_file_not_exists = False match_count += 1 else: - logger.info(f'I-04-07 I/Fファイルの年月がチェック対象年月と一致しません ファイル名:{file_name}') + logger.info( + f'I-04-07 I/Fファイルの年月がチェック対象年月と一致しません ファイル名:{file_name}') mail_message += f'{MAIL_INDENT}{tsv_row[INDEX_DATA_NAME]}(受領年月が不正:{file_name})\n' break - if is_file_not_exists: - logger.info(f'E-04-06 月次I/Fファイルに不足があります ファイル名:{tsv_row[INDEX_DATA_NAME]}') - mail_message += f'{MAIL_INDENT}{tsv_row[INDEX_DATA_NAME]}\n' + if is_file_not_exists: + logger.info( + f'E-04-06 月次I/Fファイルに不足があります ファイル名:{tsv_row[INDEX_DATA_NAME]}') + mail_message += f'{MAIL_INDENT}{tsv_row[INDEX_DATA_NAME]}\n' row_count += 1 @@ -234,12 +250,14 @@ def encise_data_unreceive_check(records: list, execute_month: str) -> tuple[list logger.info('I-05-01 メール送信処理開始') if len(mail_message) == 0: - logger.info(f'I-05-09 {execute_month} {receive_timing}データI/Fファイルに不足が無いため、メール送信処理をスキップします') + logger.info( + f'I-05-09 {execute_month} {receive_timing}データI/Fファイルに不足が無いため、メール送信処理をスキップします') batch_item_success.append(message_id) continue # 1.存在した場合 - logger.info(f'I-05-02 {execute_month} {receive_timing}データI/Fファイルに不足があるため、メール送信処理を開始します') + logger.info( + f'I-05-02 {execute_month} {receive_timing}データI/Fファイルに不足があるため、メール送信処理を開始します') try: logger.info( @@ -250,14 +268,18 @@ def encise_data_unreceive_check(records: list, execute_month: str) -> tuple[list Bucket=CONFIG_BUCKET_NAME, Key=f'{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_title_template"]}' ) - mail_title_template = mail_title_response['Body'].read().decode('utf-8') + mail_title_template = ( + mail_title_response['Body'].read().decode('utf-8')) # 改行を取り除く - mail_title = substitute_mail_template(mail_title_template, receive_timing, mail_message) + mail_title = substitute_mail_template( + mail_title_template, receive_timing, mail_message) mail_title_without_line_break = mail_title.splitlines()[0] logger.info('I-05-04 通知メール(タイトル)テンプレートファイルを読み込みました') except Exception as e: - logger.exception(f'E-05-01 通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') - batch_item_failures.append(make_failure_item_on_error(message_id)) + logger.exception( + f'E-05-01 通知メール(タイトル)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') + batch_item_failures.append( + make_failure_item_on_error(message_id)) continue try: @@ -269,13 +291,17 @@ def encise_data_unreceive_check(records: list, execute_month: str) -> tuple[list Bucket=CONFIG_BUCKET_NAME, Key=f'{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_body_template"]}' ) - mail_body_template = mail_body_template_response['Body'].read().decode('utf-8') + mail_body_template = mail_body_template_response['Body'].read().decode( + 'utf-8') # メール本文内のプレースホルダーを置き換える - mail_body = substitute_mail_template(mail_body_template, receive_timing, mail_message) + mail_body = substitute_mail_template( + mail_body_template, receive_timing, mail_message) logger.info('I-05-06 通知メール(本文)テンプレートファイルを読み込みました') except Exception as e: - logger.exception(f'E-05-02 通知メール(本文)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') - batch_item_failures.append(make_failure_item_on_error(message_id)) + logger.exception( + f'E-05-02 通知メール(本文)テンプレートファイルの読み込みに失敗しました エラー内容:{e}') + batch_item_failures.append( + make_failure_item_on_error(message_id)) continue logger.info(f'I-05-07 メール送信指示をします 送信先トピック:{MBJ_NOTICE_TOPIC}') @@ -310,7 +336,8 @@ def lambda_handler(event, context): batch_item_success = [] # ② SQSメッセージ重複排除処理を行う logger.info('I-02-01 メッセージ処理開始') - batch_item_failures, batch_item_success = encise_data_unreceive_check(event["Records"], execute_month) + batch_item_failures, batch_item_success = encise_data_unreceive_check( + event["Records"], execute_month) logger.info('I-06-01 すべてのメッセージの処理完了') # ⑦ メッセージを処理済として、以下のDynamoDBテーブルに記録する From d5d5b719f7ad3127df5cc25280aa4a233f7f1afa Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 16:26:20 +0900 Subject: [PATCH 18/31] =?UTF-8?q?feat:=20=E3=83=AD=E3=83=BC=E3=82=AB?= =?UTF-8?q?=E3=83=AB=E5=8B=95=E4=BD=9C=E7=A2=BA=E8=AA=8D=E7=94=A8=E3=81=AE?= =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=82=92=E3=82=B3=E3=83=A1=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=81=A7=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index 1667f9e2..9499d2b2 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -350,3 +350,27 @@ def lambda_handler(event, context): raise e return batch_item_failures + + +# 動作確認用のコード +# if __name__ == '__main__': +# lambda_handler({ +# "Records": [ +# { +# "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", +# "receiptHandle": "MessageReceiptHandle", +# "body": "{\"receive_timing\": \"速報\",\"check_folder_prefix\": \"encise\",\"check_target_file_list\": \"en_quick_file_list.config\",\"notice_mail_title_template\": \"en_unreceive_notice_mail_title.config\",\"notice_mail_body_template\": \"en_unreceive_notice_mail_body.config\"\r\n}", +# "attributes": { +# "ApproximateReceiveCount": "1", +# "SentTimestamp": "1523232000000", +# "SenderId": "123456789012", +# "ApproximateFirstReceiveTimestamp": "1523232000001" +# }, +# "messageAttributes": {}, +# "md5OfBody": "{{{md5_of_body}}}", +# "eventSource": "aws:sqs", +# "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", +# "awsRegion": "us-east-1" +# } +# ] +# }, {}) From b0f39ea79d8430d0bf5049f5b0df78cb8b084035 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Tue, 13 Feb 2024 17:12:31 +0900 Subject: [PATCH 19/31] =?UTF-8?q?style:=20=E3=83=95=E3=82=A9=E3=83=BC?= =?UTF-8?q?=E3=83=9E=E3=83=83=E3=83=88=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../encise-data-unreceive-check.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py index 9499d2b2..d762c275 100644 --- a/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py +++ b/lambda/encise-data-unreceive-check/encise-data-unreceive-check.py @@ -14,12 +14,12 @@ from dateutil.relativedelta import relativedelta CHECK_BUCKET_NAME = os.environ["CHECK_BUCKET_NAME"] CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH = os.environ["CHECK_TARGET_FILE_NAME_LIST_FOLDER_PATH"] CONFIG_BUCKET_NAME = os.environ["CONFIG_BUCKET_NAME"] -LOG_LEVEL = os.environ["LOG_LEVEL"] MAIL_TEMPLATE_FOLDER_PATH = os.environ["MAIL_TEMPLATE_FOLDER_PATH"] MBJ_NOTICE_TOPIC = os.environ["MBJ_NOTICE_TOPIC"] PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME = os.environ["PROCESSED_MESSAGE_DYNAMODB_TABLE_NAME"] PROCESSED_MESSAGE_EXPIRES_PERIOD = int( os.environ["PROCESSED_MESSAGE_EXPIRES_PERIOD"]) +LOG_LEVEL = os.environ["LOG_LEVEL"] TZ = os.environ["TZ"] # 定数 @@ -40,8 +40,6 @@ dynamodb_client = boto3.client('dynamodb') # logger設定 - - def log_datetime_convert_tz(*arg): """ログに出力するタイムスタンプのロケールを変更する(JST指定)""" return datetime.datetime.now(ZoneInfo(TZ)).timetuple() @@ -222,6 +220,7 @@ def encise_data_unreceive_check(records: list, execute_month: str) -> tuple[list tsv_row[INDEX_REGEX], file_name) # 「③」で読み込んだファイルに記載されている全てが「④1.」で取得したリストに存在した場合 if match_result is not None: + is_file_not_exists = False # 存在したファイルの年月部分を抜き出し、チェック対象年月(処理稼働月-1)である場合 match_group_yyyymm = match_result.group(1) check_target_yyyymm = (datetime.datetime.now( @@ -229,7 +228,6 @@ def encise_data_unreceive_check(records: list, execute_month: str) -> tuple[list if match_group_yyyymm == check_target_yyyymm: logger.info( f'I-04-04 I/Fファイルの受領を確認しました ファイル名:{file_name}') - is_file_not_exists = False match_count += 1 else: logger.info( @@ -291,8 +289,8 @@ def encise_data_unreceive_check(records: list, execute_month: str) -> tuple[list Bucket=CONFIG_BUCKET_NAME, Key=f'{MAIL_TEMPLATE_FOLDER_PATH}/{event_parameter["notice_mail_body_template"]}' ) - mail_body_template = mail_body_template_response['Body'].read().decode( - 'utf-8') + mail_body_template = ( + mail_body_template_response['Body'].read().decode('utf-8')) # メール本文内のプレースホルダーを置き換える mail_body = substitute_mail_template( mail_body_template, receive_timing, mail_message) From c8ad52db1f40c1ab2680a984317377aa3ecd766f Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Thu, 15 Feb 2024 13:55:20 +0900 Subject: [PATCH 20/31] =?UTF-8?q?r-export-dbdump=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stepfunctions/TOOLS/convert_config.yaml | 34 +++++++ .../r-export-dbdump-state.json | 95 +++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json diff --git a/stepfunctions/TOOLS/convert_config.yaml b/stepfunctions/TOOLS/convert_config.yaml index aa62a588..c0f62a09 100644 --- a/stepfunctions/TOOLS/convert_config.yaml +++ b/stepfunctions/TOOLS/convert_config.yaml @@ -26,6 +26,8 @@ resource: - &STG_SG_JSKULT_DBDUMP "sg-0967779af13538a8e" # セキュリティグループ(ecs-jskult-batch-laundering) - &STG_SG_JSKULT_BATCH_LAUNDERING "sg-00b9ea30c5c6bb77a" + # セキュリティグループ(ecs-export-dbdump) + - &STG_SG_EXPORT_DBDUMP "sg-03962e5f52b380186" # 本番環境 product: # サブネット(PrivateSubnet1) @@ -42,6 +44,8 @@ resource: - &PRD_SG_JSKULT_DBDUMP "sg-050ab3bc0d9ed261a" # セキュリティグループ(ecs-jskult-batch-laundering) - &PRD_SG_JSKULT_BATCH_LAUNDERING "sg-0d2bc30c1a2939c32" + # セキュリティグループ(ecs-export-dbdump) + - &PRD_SG_EXPORT_DBDUMP "sg-xxxxxxxxxxxxxxxx" config: # CRMデータ取得 r-crm-datafetch-state: @@ -177,3 +181,33 @@ config: SG_ECS_ALL: *PRD_SG_ECS_ALL # セキュリティグループ(ecs-jskut-batch-laundering) SG_JSKULT_BATCH_LAUNDERING: *PRD_SG_JSKULT_BATCH_LAUNDERING + # DBダンプ取得 + r-export-dbdump-state: + # ステージング環境 + staging: + # AWSアカウントID + AWS_ACCOUNT_ID: *AWS_ACCOUNT_ID + # 東京リージョン + REGION_AP_NORTHEAST_1: *REGION_AP_NORTHEAST_1 + # サブネット(PrivateSubnet1) + SUBNET_PRI_1A: *STG_SUBNET_PRI_1A + # サブネット(PrivateSubnet2) + SUBNET_PRI_1D: *STG_SUBNET_PRI_1D + # セキュリティグループ(ecs-all) + SG_ECS_ALL: *STG_SG_ECS_ALL + # セキュリティグループ(ecs-export-dbdump) + SG_EXPORT_DBDUMP: *STG_SG_EXPORT_DBDUMP + # 本番環境 + product: + # AWSアカウントID + AWS_ACCOUNT_ID: *AWS_ACCOUNT_ID + # 東京リージョン + REGION_AP_NORTHEAST_1: *REGION_AP_NORTHEAST_1 + # サブネット(PrivateSubnet1) + SUBNET_PRI_1A: *PRD_SUBNET_PRI_1A + # サブネット(PrivateSubnet2) + SUBNET_PRI_1D: *PRD_SUBNET_PRI_1D + # セキュリティグループ(ecs-all) + SG_ECS_ALL: *PRD_SG_ECS_ALL + # セキュリティグループ(ecs-export-dbdump) + SG_EXPORT_DBDUMP: *PRD_SG_EXPORT_DBDUMP diff --git a/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json new file mode 100644 index 00000000..3c708846 --- /dev/null +++ b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json @@ -0,0 +1,95 @@ +{ + "Comment": "DBダンプ取得ステートマシン", + "StartAt": "params", + "States": { + "params": { + "Comment": "パラメータ設定", + "Type": "Pass", + "Parameters": { + "sns": { + "TopicArn": "arn:aws:sns:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:mbj-notice-#{ENV_NAME}" + }, + "ecs": { + "LaunchType": "FARGATE", + "Cluster": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:cluster/mbj-newdwh2021-#{ENV_NAME}-export-dbdump-ecs", + "TaskDefinition": "ExportDatabaseDump", + "NetworkConfiguration": { + "AwsvpcConfiguration": { + "Subnets": [ + "#{SUBNET_PRI_1A}", + "#{SUBNET_PRI_1D}" + ], + "SecurityGroups": [ + "#{SG_ECS_ALL}", + "#{SG_EXPORT_DBDUMP}" + ], + "AssignPublicIp": "DISABLED" + } + }, + "Overrides": { + "ContainerOverrides": [ + { + "Name.$": "$.InputParams.ContainerName", + "Environment": [ + { + "Name": "DB_SCHEMA", + "Value.$": "$.params.eventBridge.DB_SCHEMA" + }, + { + "Name": "DUMP_BACKUP_BUCKET", + "Value.$": "$.params.eventBridge.DUMP_BACKUP_BUCKET" + } + ] + } + ] + } + } + }, + "ResultPath": "$.params", + "Next": "data-import" + }, + "data-import": { + "Type": "Task", + "Resource": "arn:aws:states:::ecs:runTask.sync", + "Parameters": { + "LaunchType.$": "$.params.ecs.LaunchType", + "Cluster.$": "$.params.ecs.Cluster", + "TaskDefinition.$": "$.params.ecs.TaskDefinition", + "NetworkConfiguration.$": "$.params.ecs.NetworkConfiguration", + "Overrides.$": "$.params.ecs.Overrides" + }, + "ResultPath": "$.result", + "Retry": [ + { + "ErrorEquals": [ + "States.ALL" + ], + "BackoffRate": 2, + "IntervalSeconds": 3, + "MaxAttempts": 3 + } + ], + "Catch": [ + { + "ErrorEquals": [ + "States.ALL" + ], + "Next": "CreateImportPendingFile", + "ResultPath": "$.result" + } + ], + "Next": "NormalEnd", + "Comment": "データ登録処理" + }, + "NormalEnd": { + "Comment": "正常終了", + "Type": "Succeed" + }, + "ErrorEnd": { + "Comment": "異常終了", + "Type": "Fail", + "Error": "StatesError", + "Cause": "StepFunctions ErrorEnd" + } + } +} \ No newline at end of file From 08df459ed8f258e75fbc332746d5fb5d931e22cc Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Thu, 15 Feb 2024 16:20:36 +0900 Subject: [PATCH 21/31] =?UTF-8?q?=E3=82=BF=E3=82=B9=E3=82=AF=E5=90=8D?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../r-export-dbdump-state/r-export-dbdump-state.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json index 3c708846..00bf15e0 100644 --- a/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json +++ b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json @@ -46,9 +46,9 @@ } }, "ResultPath": "$.params", - "Next": "data-import" + "Next": "export-dbdump" }, - "data-import": { + "export-dbdump": { "Type": "Task", "Resource": "arn:aws:states:::ecs:runTask.sync", "Parameters": { @@ -74,7 +74,7 @@ "ErrorEquals": [ "States.ALL" ], - "Next": "CreateImportPendingFile", + "Next": "ErrorEnd", "ResultPath": "$.result" } ], From 4900e1115d24be4f6aafb1cdf9a3230f44164697 Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Thu, 15 Feb 2024 16:45:46 +0900 Subject: [PATCH 22/31] =?UTF-8?q?=E6=8C=87=E6=91=98=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../r-export-dbdump-state.json | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json index 00bf15e0..d3cfb4a1 100644 --- a/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json +++ b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json @@ -6,13 +6,10 @@ "Comment": "パラメータ設定", "Type": "Pass", "Parameters": { - "sns": { - "TopicArn": "arn:aws:sns:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:mbj-notice-#{ENV_NAME}" - }, "ecs": { "LaunchType": "FARGATE", "Cluster": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:cluster/mbj-newdwh2021-#{ENV_NAME}-export-dbdump-ecs", - "TaskDefinition": "ExportDatabaseDump", + "TaskDefinition": "mbj-newdwh2021-#{ENV_NAME}-export-task-dbdump", "NetworkConfiguration": { "AwsvpcConfiguration": { "Subnets": [ @@ -29,15 +26,15 @@ "Overrides": { "ContainerOverrides": [ { - "Name.$": "$.InputParams.ContainerName", + "Name": "mbj-newdwh2021-#{ENV_NAME}-export-task-dbdump", "Environment": [ { "Name": "DB_SCHEMA", - "Value.$": "$.params.eventBridge.DB_SCHEMA" + "Value.$": "$.db_schema" }, { "Name": "DUMP_BACKUP_BUCKET", - "Value.$": "$.params.eventBridge.DUMP_BACKUP_BUCKET" + "Value.$": "$.dump_backup_bucket" } ] } @@ -46,9 +43,9 @@ } }, "ResultPath": "$.params", - "Next": "export-dbdump" + "Next": "exec-export-dbdump" }, - "export-dbdump": { + "exec-export-dbdump": { "Type": "Task", "Resource": "arn:aws:states:::ecs:runTask.sync", "Parameters": { From 511feeb15f1637ea215258fcb60dcb1915033425 Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Fri, 16 Feb 2024 10:17:59 +0900 Subject: [PATCH 23/31] =?UTF-8?q?TaskDefinition=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json index d3cfb4a1..248036ea 100644 --- a/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json +++ b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json @@ -9,7 +9,7 @@ "ecs": { "LaunchType": "FARGATE", "Cluster": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:cluster/mbj-newdwh2021-#{ENV_NAME}-export-dbdump-ecs", - "TaskDefinition": "mbj-newdwh2021-#{ENV_NAME}-export-task-dbdump", + "TaskDefinition": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:task-definition/mbj-newdwh2021-#{ENV_NAME}-export-task-dbdump", "NetworkConfiguration": { "AwsvpcConfiguration": { "Subnets": [ From 4b5197bebe4db5886ea33bcda515ec76723d1d10 Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Tue, 20 Feb 2024 18:00:49 +0900 Subject: [PATCH 24/31] dbdump-first-commit --- ecs/export-dbdump/.dockerignore | 12 + ecs/export-dbdump/.env.example | 14 ++ ecs/export-dbdump/.gitignore | 11 + ecs/export-dbdump/.vscode/launch.json | 16 ++ .../.vscode/recommended_settings.json | 31 +++ ecs/export-dbdump/Dockerfile | 40 ++++ ecs/export-dbdump/Pipfile | 19 ++ ecs/export-dbdump/Pipfile.lock | 213 ++++++++++++++++++ ecs/export-dbdump/README.md | 48 ++++ ecs/export-dbdump/entrypoint.py | 10 + ecs/export-dbdump/mysql_dpkg_selection.txt | 3 + ecs/export-dbdump/src/__init__.py | 0 .../src/batch/batch_functions.py | 131 +++++++++++ .../src/batch/common/__init__.py | 0 ecs/export-dbdump/src/db/__init__.py | 0 ecs/export-dbdump/src/db/database.py | 180 +++++++++++++++ ecs/export-dbdump/src/error/__init__.py | 0 ecs/export-dbdump/src/error/exceptions.py | 10 + ecs/export-dbdump/src/jobctrl_dbdump.py | 126 +++++++++++ ecs/export-dbdump/src/logging/get_logger.py | 37 +++ ecs/export-dbdump/src/system_var/__init__.py | 0 ecs/export-dbdump/src/system_var/constants.py | 15 ++ .../src/system_var/environment.py | 19 ++ 23 files changed, 935 insertions(+) create mode 100644 ecs/export-dbdump/.dockerignore create mode 100644 ecs/export-dbdump/.env.example create mode 100644 ecs/export-dbdump/.gitignore create mode 100644 ecs/export-dbdump/.vscode/launch.json create mode 100644 ecs/export-dbdump/.vscode/recommended_settings.json create mode 100644 ecs/export-dbdump/Dockerfile create mode 100644 ecs/export-dbdump/Pipfile create mode 100644 ecs/export-dbdump/Pipfile.lock create mode 100644 ecs/export-dbdump/README.md create mode 100644 ecs/export-dbdump/entrypoint.py create mode 100644 ecs/export-dbdump/mysql_dpkg_selection.txt create mode 100644 ecs/export-dbdump/src/__init__.py create mode 100644 ecs/export-dbdump/src/batch/batch_functions.py create mode 100644 ecs/export-dbdump/src/batch/common/__init__.py create mode 100644 ecs/export-dbdump/src/db/__init__.py create mode 100644 ecs/export-dbdump/src/db/database.py create mode 100644 ecs/export-dbdump/src/error/__init__.py create mode 100644 ecs/export-dbdump/src/error/exceptions.py create mode 100644 ecs/export-dbdump/src/jobctrl_dbdump.py create mode 100644 ecs/export-dbdump/src/logging/get_logger.py create mode 100644 ecs/export-dbdump/src/system_var/__init__.py create mode 100644 ecs/export-dbdump/src/system_var/constants.py create mode 100644 ecs/export-dbdump/src/system_var/environment.py diff --git a/ecs/export-dbdump/.dockerignore b/ecs/export-dbdump/.dockerignore new file mode 100644 index 00000000..8b9da402 --- /dev/null +++ b/ecs/export-dbdump/.dockerignore @@ -0,0 +1,12 @@ +tests/* +.coverage +.env +.env.example +.report/* +.vscode/* +.pytest_cache/* +*/__pychache__/* +Dockerfile +pytest.ini +README.md +*.sql diff --git a/ecs/export-dbdump/.env.example b/ecs/export-dbdump/.env.example new file mode 100644 index 00000000..c4e1f427 --- /dev/null +++ b/ecs/export-dbdump/.env.example @@ -0,0 +1,14 @@ +DB_HOST=************ +DB_PORT=3306 +DB_USERNAME=************ +DB_PASSWORD=************ +DB_SCHEMA=***** + +DUMP_BACKUP_BUCKET=************ +DUMP_BACKUP_FOLDER=dump + +LOG_LEVEL=INFO +DB_CONNECTION_MAX_RETRY_ATTEMPT=************ +DB_CONNECTION_RETRY_INTERVAL_INIT=************ +DB_CONNECTION_RETRY_INTERVAL_MIN_SECONDS=************ +DB_CONNECTION_RETRY_INTERVAL_MAX_SECONDS=************ diff --git a/ecs/export-dbdump/.gitignore b/ecs/export-dbdump/.gitignore new file mode 100644 index 00000000..cf44449e --- /dev/null +++ b/ecs/export-dbdump/.gitignore @@ -0,0 +1,11 @@ +.vscode/settings.json +.env +my.cnf + +# python +__pycache__ + +# python test +.pytest_cache +.coverage +.report/ \ No newline at end of file diff --git a/ecs/export-dbdump/.vscode/launch.json b/ecs/export-dbdump/.vscode/launch.json new file mode 100644 index 00000000..ac80ce4f --- /dev/null +++ b/ecs/export-dbdump/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // IntelliSense を使用して利用可能な属性を学べます。 + // 既存の属性の説明をホバーして表示します。 + // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(DEBUG)jskult batch laundering", + "type": "python", + "request": "launch", + "program": "entrypoint.py", + "console": "integratedTerminal", + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/ecs/export-dbdump/.vscode/recommended_settings.json b/ecs/export-dbdump/.vscode/recommended_settings.json new file mode 100644 index 00000000..b5e79d73 --- /dev/null +++ b/ecs/export-dbdump/.vscode/recommended_settings.json @@ -0,0 +1,31 @@ +{ + "[python]": { + "editor.defaultFormatter": null, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true + } + }, + // 自身の環境に合わせて変えてください + "python.defaultInterpreterPath": "", + "python.linting.lintOnSave": true, + "python.linting.enabled": true, + "python.linting.pylintEnabled": false, + "python.linting.flake8Enabled": true, + "python.linting.flake8Args": [ + "--max-line-length=200", + "--ignore=F541" + ], + "python.formatting.provider": "autopep8", + "python.formatting.autopep8Path": "autopep8", + "python.formatting.autopep8Args": [ + "--max-line-length", "200", + "--ignore=F541" + ], + "python.testing.pytestArgs": [ + "tests/batch/ultmarc" + ], + + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} diff --git a/ecs/export-dbdump/Dockerfile b/ecs/export-dbdump/Dockerfile new file mode 100644 index 00000000..140de9c6 --- /dev/null +++ b/ecs/export-dbdump/Dockerfile @@ -0,0 +1,40 @@ +FROM python:3.9-bullseye + +ENV TZ="Asia/Tokyo" + +WORKDIR /usr/src/app +COPY Pipfile Pipfile.lock ./ +# mysql-apt-config をdpkgでインストールする際に標準出力に渡す文字列ファイルをコピー +COPY mysql_dpkg_selection.txt ./ +# 必要なパッケージインストール +RUN apt update && apt install -y less vim curl wget gzip unzip sudo lsb-release + +# mysqlをインストール +RUN \ + wget https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb && \ + dpkg -i mysql-apt-config_0.8.29-1_all.deb < mysql_dpkg_selection.txt && \ + apt update && \ + apt install -y mysql-client + +# aws cli v2 のインストール +RUN \ + curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \ + unzip awscliv2.zip && \ + sudo ./aws/install + +# python関連のライブラリインストール +RUN \ + pip install --upgrade pip wheel setuptools && \ + pip install pipenv --no-cache-dir && \ + pipenv install --system --deploy && \ + pip uninstall -y pipenv virtualenv-clone virtualenv + +# パッケージのセキュリティアップデートのみを適用するコマンドを実行 +RUN \ + apt install -y unattended-upgrades && \ + unattended-upgrades + +COPY src ./src +COPY entrypoint.py entrypoint.py + +CMD ["python", "entrypoint.py"] diff --git a/ecs/export-dbdump/Pipfile b/ecs/export-dbdump/Pipfile new file mode 100644 index 00000000..65b30a51 --- /dev/null +++ b/ecs/export-dbdump/Pipfile @@ -0,0 +1,19 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +sqlalchemy = "*" +tenacity = "*" +pymysql = "*" + +[dev-packages] +autopep8 = "*" +flake8 = "*" + +[requires] +python_version = "3.9" + +[pipenv] +allow_prereleases = true diff --git a/ecs/export-dbdump/Pipfile.lock b/ecs/export-dbdump/Pipfile.lock new file mode 100644 index 00000000..fb6db2d1 --- /dev/null +++ b/ecs/export-dbdump/Pipfile.lock @@ -0,0 +1,213 @@ +{ + "_meta": { + "hash": { + "sha256": "e2e2efd7ebd6ad719931983f277105dd94c4ebb6363f7c00e75dd8ca05523713" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "greenlet": { + "hashes": [ + "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", + "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", + "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", + "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", + "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", + "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", + "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", + "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", + "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", + "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", + "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", + "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", + "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", + "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", + "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", + "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", + "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", + "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", + "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", + "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", + "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", + "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", + "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", + "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", + "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", + "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", + "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", + "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", + "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", + "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", + "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", + "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", + "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", + "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", + "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", + "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", + "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", + "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", + "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", + "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", + "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", + "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", + "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", + "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", + "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", + "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", + "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", + "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", + "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", + "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", + "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", + "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", + "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", + "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", + "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", + "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", + "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", + "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" + ], + "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": "==3.0.3" + }, + "pymysql": { + "hashes": [ + "sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96", + "sha256:8969ec6d763c856f7073c4c64662882675702efcb114b4bcbb955aea3a069fa7" + ], + "index": "pypi", + "version": "==1.1.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9", + "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d", + "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e", + "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669", + "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d", + "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5", + "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002", + "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e", + "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd", + "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215", + "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24", + "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39", + "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62", + "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900", + "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf", + "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735", + "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4", + "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23", + "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9", + "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c", + "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de", + "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7", + "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625", + "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f", + "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a", + "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4", + "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643", + "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018", + "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9", + "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5", + "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08", + "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3", + "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3", + "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed", + "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95", + "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6", + "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0", + "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84", + "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f", + "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d", + "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2", + "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5", + "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570", + "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7", + "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c", + "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf", + "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3", + "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed", + "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5" + ], + "index": "pypi", + "version": "==2.0.25" + }, + "tenacity": { + "hashes": [ + "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a", + "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c" + ], + "index": "pypi", + "version": "==8.2.3" + }, + "typing-extensions": { + "hashes": [ + "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", + "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" + ], + "markers": "python_version >= '3.8'", + "version": "==4.9.0" + } + }, + "develop": { + "autopep8": { + "hashes": [ + "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb", + "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c" + ], + "index": "pypi", + "version": "==2.0.4" + }, + "flake8": { + "hashes": [ + "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132", + "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3" + ], + "index": "pypi", + "version": "==7.0.0" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", + "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" + ], + "markers": "python_version >= '3.8'", + "version": "==2.11.1" + }, + "pyflakes": { + "hashes": [ + "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", + "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a" + ], + "markers": "python_version >= '3.8'", + "version": "==3.2.0" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version < '3.11'", + "version": "==2.0.1" + } + } +} diff --git a/ecs/export-dbdump/README.md b/ecs/export-dbdump/README.md new file mode 100644 index 00000000..7d8f0ccd --- /dev/null +++ b/ecs/export-dbdump/README.md @@ -0,0 +1,48 @@ +# 日次バッチ処理前DBダンプ取得  + +## 概要 + +日次バッチ処理前DBダンプ取得処理。 + +## 環境情報 + +- Python 3.9 +- MySQL 8.23 +- VSCode + +## 環境構築 + +- Python の構築 + + - Merck_NewDWH 開発 2021 の Wiki、[Python 環境構築](https://nds-tyo.backlog.com/alias/wiki/1874930)を参照 + - 「Pipenv の導入」までを行っておくこと + - 構築完了後、プロジェクト配下で以下のコマンドを実行し、Python の仮想環境を作成する + - `pipenv install --dev --python ` + - この手順で出力される仮想環境のパスは、後述する VSCode の設定手順で使用するため、控えておく + +- MySQL の環境構築 + - Windows の場合、以下のリンクからダウンロードする + - + - 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」キーを押下すると、バッチ処理が起動する。 +- 「entrypoint.py」が、バッチ処理のエントリーポイント。 +- 実際の処理は、「src/jobctrl_dbdump.py」で行っている。 + + +## フォルダ構成(工事中) diff --git a/ecs/export-dbdump/entrypoint.py b/ecs/export-dbdump/entrypoint.py new file mode 100644 index 00000000..543cb134 --- /dev/null +++ b/ecs/export-dbdump/entrypoint.py @@ -0,0 +1,10 @@ +"""実消化&アルトマーク 日次バッチ処理前DBダンプ取得のエントリーポイント""" +from src import jobctrl_dbdump + +if __name__ == '__main__': + try: + exit(jobctrl_dbdump.exec()) + except Exception: + # エラーが起きても、正常系のコードで返す。 + # エラーが起きた事実はbatch_process内でログを出す。 + exit(0) diff --git a/ecs/export-dbdump/mysql_dpkg_selection.txt b/ecs/export-dbdump/mysql_dpkg_selection.txt new file mode 100644 index 00000000..d3cb46f9 --- /dev/null +++ b/ecs/export-dbdump/mysql_dpkg_selection.txt @@ -0,0 +1,3 @@ +1 +1 +4 \ No newline at end of file diff --git a/ecs/export-dbdump/src/__init__.py b/ecs/export-dbdump/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/export-dbdump/src/batch/batch_functions.py b/ecs/export-dbdump/src/batch/batch_functions.py new file mode 100644 index 00000000..eece0536 --- /dev/null +++ b/ecs/export-dbdump/src/batch/batch_functions.py @@ -0,0 +1,131 @@ +"""バッチ処理の共通関数""" +import logging +import textwrap + +from src.db.database import Database +from src.error.exceptions import BatchOperationException, DBException +from src.system_var import constants + + +def get_batch_statuses() -> tuple[str, str]: + """日付テーブルから、以下を取得して返す。 + - バッチ処理中フラグ + - dump取得状況区分 + + Raises: + BatchOperationException: 日付テーブルが取得できないとき、何らかのエラーが発生したとき + + Returns: + tuple[str, str]: [0]バッチ処理中フラグ,[1]dump取得状況区分 + """ + db = Database.get_instance() + sql = 'SELECT bch_actf, dump_sts_kbn FROM src05.hdke_tbl' + try: + db.connect() + hdke_tbl_result = db.execute_select(sql) + except DBException as e: + raise BatchOperationException(e) + finally: + db.disconnect() + + if len(hdke_tbl_result) == 0: + raise BatchOperationException('日付テーブルが取得できませんでした') + + # 必ず1件取れる + hdke_tbl_record = hdke_tbl_result[0] + batch_processing_flag = hdke_tbl_record['bch_actf'] + dump_status_kbn = hdke_tbl_record['dump_sts_kbn'] + + return batch_processing_flag, dump_status_kbn + + +def update_dump_status_kbn_in_processing() -> None: + """dump取得状況区分を処理中に更新する + + Raises: + BatchOperationException: DB操作の何らかのエラー + """ + db = Database.get_instance() + sql = """\ + UPDATE src05.hdke_tbl + SET + dump_sts_kbn = :in_processing, + updater = CURRENT_USER(), + update_date = NOW() + """ + try: + db.connect() + db.to_jst() + db.execute(sql, {'in_processing': constants.BATCH_ACTF_BATCH_IN_PROCESSING}) + except DBException as e: + raise BatchOperationException(e) + finally: + db.disconnect() + + return + + +def update_dump_status_kbn_error() -> None: + """dump取得状況区分をエラーに更新する + + Raises: + BatchOperationException: DB操作の何らかのエラー + """ + db = Database.get_instance() + sql = """\ + UPDATE src05.hdke_tbl + SET + dump_sts_kbn = :dump_unprocessed, + updater = CURRENT_USER(), + update_date = NOW() + """ + try: + db.connect() + db.to_jst() + db.execute(sql, { + 'dump_unprocessed': constants.DUMP_STATUS_KBN_ERROR + }) + except DBException as e: + raise BatchOperationException(e) + finally: + db.disconnect() + + return + + +def update_dump_status_kbn_complete() -> None: + """dump取得状況区分を正常終了に更新する + + Raises: + BatchOperationException: DB操作の何らかのエラー + """ + db = Database.get_instance() + sql = """\ + UPDATE src05.hdke_tbl + SET + dump_sts_kbn = :dump_unprocessed, + updater = CURRENT_USER(), + update_date = NOW() + """ + try: + db.connect() + db.to_jst() + db.execute(sql, { + 'dump_unprocessed': constants.DUMP_STATUS_KBN_COMPLETE + }) + except DBException as e: + raise BatchOperationException(e) + finally: + db.disconnect() + + return + + +def logging_sql(logger: logging.Logger, sql: str) -> None: + """SQL文をデバッグログで出力する + + Args: + logger (logging.Logger): ロガー + sql (str): SQL文 + """ + logger.debug(f'\n{"-" * 15}\n{textwrap.dedent(sql)[1:-1]}\n{"-" * 15}') diff --git a/ecs/export-dbdump/src/batch/common/__init__.py b/ecs/export-dbdump/src/batch/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/export-dbdump/src/db/__init__.py b/ecs/export-dbdump/src/db/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/export-dbdump/src/db/database.py b/ecs/export-dbdump/src/db/database.py new file mode 100644 index 00000000..24bb6061 --- /dev/null +++ b/ecs/export-dbdump/src/db/database.py @@ -0,0 +1,180 @@ +from sqlalchemy import (Connection, CursorResult, Engine, QueuePool, + create_engine, text) +from sqlalchemy.engine.url import URL +from tenacity import retry, stop_after_attempt, wait_exponential + +from src.error.exceptions import DBException +from src.system_var import environment + + +class Database: + """データベース操作クラス""" + __connection: Connection = None + __engine: Engine = None + __host: str = None + __port: str = None + __username: str = None + __password: str = None + __schema: str = None + __connection_string: str = None + + def __init__(self, username: str, password: str, host: str, port: int, schema: str) -> None: + """このクラスの新たなインスタンスを初期化します + + Args: + username (str): DBユーザー名 + password (str): DBパスワード + host (str): DBホスト名 + port (int): DBポート + schema (str): DBスキーマ名 + """ + self.__username = username + self.__password = password + self.__host = host + self.__port = int(port) + self.__schema = schema + + self.__connection_string = URL.create( + drivername='mysql+pymysql', + username=self.__username, + password=self.__password, + host=self.__host, + port=self.__port, + database=self.__schema, + query={"charset": "utf8mb4"} + ) + + self.__engine = create_engine( + self.__connection_string, + pool_timeout=5, + poolclass=QueuePool + ) + + @classmethod + def get_instance(cls): + """インスタンスを取得します + + Returns: + Database: DB操作クラスインスタンス + """ + return cls( + username=environment.DB_USERNAME, + password=environment.DB_PASSWORD, + host=environment.DB_HOST, + port=environment.DB_PORT, + schema=environment.DB_SCHEMA + ) + + @retry( + wait=wait_exponential( + multiplier=environment.DB_CONNECTION_RETRY_INTERVAL_INIT, + min=environment.DB_CONNECTION_RETRY_INTERVAL_MIN_SECONDS, + max=environment.DB_CONNECTION_RETRY_INTERVAL_MAX_SECONDS + ), + stop=stop_after_attempt(environment.DB_CONNECTION_MAX_RETRY_ATTEMPT), + retry_error_cls=DBException + ) + def connect(self): + """ + DBに接続します。接続に失敗した場合、リトライします。 + Raises: + DBException: 接続失敗 + """ + 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クエリを実行します。 + + Args: + select_query (str): SELECT文 + parameters (dict, optional): クエリのプレースホルダーに埋め込む変数の辞書. Defaults to None. + + Raises: + DBException: DBエラー + + Returns: + list[dict]: カラム名: 値の辞書リスト + """ + if self.__connection is None: + raise DBException('DBに接続していません') + + result = None + try: + # トランザクションが開始している場合は、トランザクションを引き継ぐ + if self.__connection.in_transaction(): + result = self.__connection.execute(text(select_query), parameters) + else: + # トランザクションが明示的に開始していない場合は、クエリ単位でトランザクションをbegin-commitする。 + result = self.__execute_with_transaction(select_query, parameters) + except Exception as e: + raise DBException(f'SQL Error: {e}') + + result_rows = result.mappings().all() + return result_rows + + def execute(self, query: str, parameters=None) -> CursorResult: + """SQLクエリを実行します。 + + Args: + query (str): SQL文 + parameters (dict, optional): クエリのプレースホルダーに埋め込む変数の辞書. Defaults to None. + + Raises: + DBException: DBエラー + + Returns: + CursorResult: 取得結果 + """ + if self.__connection is None: + raise DBException('DBに接続していません') + + result = None + try: + # トランザクションが開始している場合は、トランザクションを引き継ぐ + if self.__connection.in_transaction(): + result = self.__connection.execute(text(query), parameters) + else: + # トランザクションが明示的に開始していない場合は、クエリ単位でトランザクションをbegin-commitする。 + result = self.__execute_with_transaction(query, parameters) + except Exception as e: + raise DBException(f'SQL Error: {e}') + + return result + + def begin(self): + """トランザクションを開始します。""" + if not self.__connection.in_transaction(): + self.__connection.begin() + + def commit(self): + """トランザクションをコミットします""" + if self.__connection.in_transaction(): + self.__connection.commit() + + def rollback(self): + """トランザクションをロールバックします""" + if self.__connection.in_transaction(): + self.__connection.rollback() + + def disconnect(self): + """DB接続を切断します。""" + if self.__connection is not None: + self.__connection.close() + self.__connection = None + + def to_jst(self): + self.execute('SET time_zone = "+9:00"') + + def __execute_with_transaction(self, query: str, parameters: dict): + # トランザクションを開始してクエリを実行する + with self.__connection.begin(): + try: + result = self.__connection.execute(text(query), parameters=parameters) + except Exception as e: + self.__connection.rollback() + raise e + # ここでコミットされる + return result diff --git a/ecs/export-dbdump/src/error/__init__.py b/ecs/export-dbdump/src/error/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/export-dbdump/src/error/exceptions.py b/ecs/export-dbdump/src/error/exceptions.py new file mode 100644 index 00000000..055c24f6 --- /dev/null +++ b/ecs/export-dbdump/src/error/exceptions.py @@ -0,0 +1,10 @@ +class MeDaCaException(Exception): + pass + + +class DBException(MeDaCaException): + pass + + +class BatchOperationException(MeDaCaException): + pass diff --git a/ecs/export-dbdump/src/jobctrl_dbdump.py b/ecs/export-dbdump/src/jobctrl_dbdump.py new file mode 100644 index 00000000..b6e27728 --- /dev/null +++ b/ecs/export-dbdump/src/jobctrl_dbdump.py @@ -0,0 +1,126 @@ +"""DBダンプ取得""" + +import datetime +import os +import subprocess +import textwrap + +from src.batch.batch_functions import (get_batch_statuses, + update_dump_status_kbn_complete, + update_dump_status_kbn_error, + update_dump_status_kbn_in_processing) +from src.error.exceptions import BatchOperationException +from src.logging.get_logger import get_logger +from src.system_var import constants, environment + +logger = get_logger('DBダンプ取得') + + +def exec(): + try: + logger.info('DBダンプ取得:開始') + try: + # 日次バッチ処置中フラグ、dump処理状態区分を取得 + batch_processing_flag, dump_status_kbn = get_batch_statuses() + except BatchOperationException as e: + raise BatchOperationException(f'日付テーブル取得エラー(異常終了):{e}') + + # 日次バッチ処理中の場合、処理は行わない + if batch_processing_flag == constants.BATCH_ACTF_BATCH_IN_PROCESSING: + logger.error('日次バッチ処理中の為、DBダンプ取得を終了します。') + return constants.BATCH_EXIT_CODE_SUCCESS + + # dump処理状態区分が処理中の場合、処理は行わない + if dump_status_kbn == constants.DUMP_STATUS_KBN_PROCESSED: + logger.error(f'ダンプ取得中の為、DBダンプ取得を終了します。 dump処理状態区分={dump_status_kbn}') + return constants.BATCH_EXIT_CODE_SUCCESS + + # dump処理状態区分がエラーの場合、処理は行わない + if dump_status_kbn == constants.DUMP_STATUS_KBN_ERROR: + logger.error(f'ダンプ取得が実行不可能な状態の為、DBダンプ取得を終了します。 dump処理状態区分={dump_status_kbn}') + return constants.BATCH_EXIT_CODE_SUCCESS + + # dump処理状態区分を処理中に更新 + try: + update_dump_status_kbn_in_processing() + except BatchOperationException as e: + raise BatchOperationException(f'dump処理状態区分更新(未処理→処理中) エラー(異常終了):{e}') + + # MySQL接続情報を作成する + my_cnf_file_content = f""" + [client] + user={environment.DB_USERNAME} + password={environment.DB_PASSWORD} + host={environment.DB_HOST} + """ + # my.cnfファイルのパス + my_cnf_path = os.path.join('my.cnf') + + # my.cnfファイルを生成する + with open(my_cnf_path, 'w') as f: + f.write(textwrap.dedent(my_cnf_file_content)[1:-1]) + + # ファイルのパーミッションが強いとmysqldumpコマンドが実行できないため + # my.cnfファイルのパーミッションをread-onlyに設定 + os.chmod(my_cnf_path, 0o444) + + dt_now = datetime.datetime.now() + converted_value = dt_now.strftime('%Y%m%d%H%M%S') + dump_file_name = f'backup_rds_{environment.DB_SCHEMA}_{converted_value}.gz' + s3_file_path = f's3://{environment.DUMP_BACKUP_BUCKET}/dump/{dt_now.year}/{dt_now.strftime("%m")}/{dt_now.strftime("%d")}/{dump_file_name}' + + # mysqldumpコマンドを実行し、dumpを取得する + command = [ + 'mysqldump', + f'--defaults-file={my_cnf_path}', + '-P', + f"{environment.DB_PORT}", + '--no-tablespaces', + '--skip-column-statistics', + '--single-transaction', + '--set-gtid-purged=OFF', + environment.DB_SCHEMA + ] + + mysqldump_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # gzipコマンドを実行してdump結果を圧縮する + gzip_process = subprocess.Popen(['gzip', '-c'], stdin=mysqldump_process.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # aws s3 cpコマンドを実行してアップロードする + s3_cp_process = subprocess.Popen(['aws', 's3', 'cp', '-', s3_file_path], stdin=gzip_process.stdout, stderr=subprocess.PIPE) + # mysqldumpの標準出力をgzipに接続したため、標準出力をクローズする + mysqldump_process.stdout.close() + # gzipの標準出力をaws s3 cpに接続したため、標準出力をクローズする + gzip_process.stdout.close() + + # パイプラインを実行し、エラーハンドリング + _, error = mysqldump_process.communicate() + if mysqldump_process.returncode != 0: + raise BatchOperationException(f'`mysqldump`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + + _, error = gzip_process.communicate() + if gzip_process.returncode != 0: + raise BatchOperationException(f'`gzip`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + + _, error = s3_cp_process.communicate() + if s3_cp_process.returncode != 0: + raise BatchOperationException(f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + + # dump処理状態区分を正常終了に更新 + try: + update_dump_status_kbn_complete() + except BatchOperationException as e: + raise BatchOperationException(f'dump処理状態区分更新(処理中→正常終了) エラー(異常終了):{e}') + + logger.info('DBダンプ取得:終了(正常終了)') + logger.info(f'出力ファイルパス: {s3_file_path}') + return constants.BATCH_EXIT_CODE_SUCCESS + + except Exception as e: + # dump処理状態区分をエラーに更新 + try: + update_dump_status_kbn_error() + except BatchOperationException as e: + logger.exception(f'dump処理状態区分更新(処理中→エラー) エラー(異常終了):{e}') + return constants.BATCH_EXIT_CODE_SUCCESS + logger.exception(f'DBダンプ取得中に想定外のエラーが発生しました :{e}') + return constants.BATCH_EXIT_CODE_SUCCESS diff --git a/ecs/export-dbdump/src/logging/get_logger.py b/ecs/export-dbdump/src/logging/get_logger.py new file mode 100644 index 00000000..f36f1199 --- /dev/null +++ b/ecs/export-dbdump/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 diff --git a/ecs/export-dbdump/src/system_var/__init__.py b/ecs/export-dbdump/src/system_var/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/export-dbdump/src/system_var/constants.py b/ecs/export-dbdump/src/system_var/constants.py new file mode 100644 index 00000000..993e5a7d --- /dev/null +++ b/ecs/export-dbdump/src/system_var/constants.py @@ -0,0 +1,15 @@ +# バッチ正常終了コード +BATCH_EXIT_CODE_SUCCESS = 0 + +# バッチ処理中フラグ:未処理 +BATCH_ACTF_BATCH_UNPROCESSED = '0' +# バッチ処理中フラグ:処理中 +BATCH_ACTF_BATCH_IN_PROCESSING = '1' +# dump取得状態区分:未処理 +DUMP_STATUS_KBN_UNPROCESSED = '0' +# dump取得状態区分:処理中 +DUMP_STATUS_KBN_PROCESSED = '1' +# dump取得状態区分:正常終了 +DUMP_STATUS_KBN_COMPLETE = '2' +# dump取得状態区分:エラー +DUMP_STATUS_KBN_ERROR = '9' diff --git a/ecs/export-dbdump/src/system_var/environment.py b/ecs/export-dbdump/src/system_var/environment.py new file mode 100644 index 00000000..61fdd693 --- /dev/null +++ b/ecs/export-dbdump/src/system_var/environment.py @@ -0,0 +1,19 @@ +import os + +# Database +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'] + +# AWS +DUMP_BACKUP_BUCKET = os.environ['DUMP_BACKUP_BUCKET'] + +# 初期値がある環境変数 +LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO') +DB_CONNECTION_MAX_RETRY_ATTEMPT = int(os.environ.get('DB_CONNECTION_MAX_RETRY_ATTEMPT', 4)) +DB_CONNECTION_RETRY_INTERVAL_INIT = int(os.environ.get('DB_CONNECTION_RETRY_INTERVAL', 5)) +DB_CONNECTION_RETRY_INTERVAL_MIN_SECONDS = int(os.environ.get('DB_CONNECTION_RETRY_MIN_SECONDS', 5)) +DB_CONNECTION_RETRY_INTERVAL_MAX_SECONDS = int(os.environ.get('DB_CONNECTION_RETRY_MAX_SECONDS', 50)) + From 22d8ee44655efa4814cd5555af4364445528ef9f Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Wed, 21 Feb 2024 14:26:04 +0900 Subject: [PATCH 25/31] dbrestore-first-commit --- ecs/restore-dbdump/.dockerignore | 13 ++++ ecs/restore-dbdump/.env.example | 6 ++ ecs/restore-dbdump/.gitignore | 16 +++++ ecs/restore-dbdump/.vscode/launch.json | 17 +++++ .../.vscode/recommended_settings.json | 31 ++++++++ ecs/restore-dbdump/Dockerfile | 40 +++++++++++ ecs/restore-dbdump/Pipfile | 16 +++++ ecs/restore-dbdump/Pipfile.lock | 71 +++++++++++++++++++ ecs/restore-dbdump/README.md | 67 +++++++++++++++++ ecs/restore-dbdump/entrypoint.py | 10 +++ ecs/restore-dbdump/mysql_dpkg_selection.txt | 3 + ecs/restore-dbdump/src/__init__.py | 0 ecs/restore-dbdump/src/logging/get_logger.py | 37 ++++++++++ ecs/restore-dbdump/src/restore_backup.py | 64 +++++++++++++++++ ecs/restore-dbdump/src/system_var/__init__.py | 0 .../src/system_var/constants.py | 2 + .../src/system_var/environment.py | 12 ++++ 17 files changed, 405 insertions(+) create mode 100644 ecs/restore-dbdump/.dockerignore create mode 100644 ecs/restore-dbdump/.env.example create mode 100644 ecs/restore-dbdump/.gitignore create mode 100644 ecs/restore-dbdump/.vscode/launch.json create mode 100644 ecs/restore-dbdump/.vscode/recommended_settings.json create mode 100644 ecs/restore-dbdump/Dockerfile create mode 100644 ecs/restore-dbdump/Pipfile create mode 100644 ecs/restore-dbdump/Pipfile.lock create mode 100644 ecs/restore-dbdump/README.md create mode 100644 ecs/restore-dbdump/entrypoint.py create mode 100644 ecs/restore-dbdump/mysql_dpkg_selection.txt create mode 100644 ecs/restore-dbdump/src/__init__.py create mode 100644 ecs/restore-dbdump/src/logging/get_logger.py create mode 100644 ecs/restore-dbdump/src/restore_backup.py create mode 100644 ecs/restore-dbdump/src/system_var/__init__.py create mode 100644 ecs/restore-dbdump/src/system_var/constants.py create mode 100644 ecs/restore-dbdump/src/system_var/environment.py diff --git a/ecs/restore-dbdump/.dockerignore b/ecs/restore-dbdump/.dockerignore new file mode 100644 index 00000000..d826198a --- /dev/null +++ b/ecs/restore-dbdump/.dockerignore @@ -0,0 +1,13 @@ +tests/* +.coverage +.env +.env.example +.report/* +.vscode/* +.pytest_cache/* +*/__pychache__/* +Dockerfile +pytest.ini +README.md +*.sql +*.gz diff --git a/ecs/restore-dbdump/.env.example b/ecs/restore-dbdump/.env.example new file mode 100644 index 00000000..7272a711 --- /dev/null +++ b/ecs/restore-dbdump/.env.example @@ -0,0 +1,6 @@ +DB_HOST=************ +DB_PORT=3306 +DB_USERNAME=************ +DB_PASSWORD=************ +DB_SCHEMA=src05 +DUMP_FILE_S3_PATH=******************* \ No newline at end of file diff --git a/ecs/restore-dbdump/.gitignore b/ecs/restore-dbdump/.gitignore new file mode 100644 index 00000000..339d80cc --- /dev/null +++ b/ecs/restore-dbdump/.gitignore @@ -0,0 +1,16 @@ +.vscode/settings.json +.env + +# python +__pycache__ + +# python test +.pytest_cache +.coverage +.report/ + +# mysql config file +my.cnf + +# compress file +*.gz \ No newline at end of file diff --git a/ecs/restore-dbdump/.vscode/launch.json b/ecs/restore-dbdump/.vscode/launch.json new file mode 100644 index 00000000..23c4b05f --- /dev/null +++ b/ecs/restore-dbdump/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // IntelliSense を使用して利用可能な属性を学べます。 + // 既存の属性の説明をホバーして表示します。 + // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "(DEBUG)restore dbdump", + "type": "python", + "request": "launch", + "program": "entrypoint.py", + "console": "integratedTerminal", + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/ecs/restore-dbdump/.vscode/recommended_settings.json b/ecs/restore-dbdump/.vscode/recommended_settings.json new file mode 100644 index 00000000..b5e79d73 --- /dev/null +++ b/ecs/restore-dbdump/.vscode/recommended_settings.json @@ -0,0 +1,31 @@ +{ + "[python]": { + "editor.defaultFormatter": null, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true + } + }, + // 自身の環境に合わせて変えてください + "python.defaultInterpreterPath": "", + "python.linting.lintOnSave": true, + "python.linting.enabled": true, + "python.linting.pylintEnabled": false, + "python.linting.flake8Enabled": true, + "python.linting.flake8Args": [ + "--max-line-length=200", + "--ignore=F541" + ], + "python.formatting.provider": "autopep8", + "python.formatting.autopep8Path": "autopep8", + "python.formatting.autopep8Args": [ + "--max-line-length", "200", + "--ignore=F541" + ], + "python.testing.pytestArgs": [ + "tests/batch/ultmarc" + ], + + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} diff --git a/ecs/restore-dbdump/Dockerfile b/ecs/restore-dbdump/Dockerfile new file mode 100644 index 00000000..140de9c6 --- /dev/null +++ b/ecs/restore-dbdump/Dockerfile @@ -0,0 +1,40 @@ +FROM python:3.9-bullseye + +ENV TZ="Asia/Tokyo" + +WORKDIR /usr/src/app +COPY Pipfile Pipfile.lock ./ +# mysql-apt-config をdpkgでインストールする際に標準出力に渡す文字列ファイルをコピー +COPY mysql_dpkg_selection.txt ./ +# 必要なパッケージインストール +RUN apt update && apt install -y less vim curl wget gzip unzip sudo lsb-release + +# mysqlをインストール +RUN \ + wget https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb && \ + dpkg -i mysql-apt-config_0.8.29-1_all.deb < mysql_dpkg_selection.txt && \ + apt update && \ + apt install -y mysql-client + +# aws cli v2 のインストール +RUN \ + curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \ + unzip awscliv2.zip && \ + sudo ./aws/install + +# python関連のライブラリインストール +RUN \ + pip install --upgrade pip wheel setuptools && \ + pip install pipenv --no-cache-dir && \ + pipenv install --system --deploy && \ + pip uninstall -y pipenv virtualenv-clone virtualenv + +# パッケージのセキュリティアップデートのみを適用するコマンドを実行 +RUN \ + apt install -y unattended-upgrades && \ + unattended-upgrades + +COPY src ./src +COPY entrypoint.py entrypoint.py + +CMD ["python", "entrypoint.py"] diff --git a/ecs/restore-dbdump/Pipfile b/ecs/restore-dbdump/Pipfile new file mode 100644 index 00000000..f032c7ea --- /dev/null +++ b/ecs/restore-dbdump/Pipfile @@ -0,0 +1,16 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] + +[dev-packages] +autopep8 = "*" +flake8 = "*" + +[requires] +python_version = "3.9" + +[pipenv] +allow_prereleases = true diff --git a/ecs/restore-dbdump/Pipfile.lock b/ecs/restore-dbdump/Pipfile.lock new file mode 100644 index 00000000..58c4d231 --- /dev/null +++ b/ecs/restore-dbdump/Pipfile.lock @@ -0,0 +1,71 @@ +{ + "_meta": { + "hash": { + "sha256": "cc5f54bfb2073051a26f113ceac64e12fdd0bf8faa36f1a42210cc9c921c134b" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": { + "autopep8": { + "hashes": [ + "sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb", + "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==2.0.4" + }, + "flake8": { + "hashes": [ + "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132", + "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3" + ], + "index": "pypi", + "markers": "python_full_version >= '3.8.1'", + "version": "==7.0.0" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", + "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" + ], + "markers": "python_version >= '3.8'", + "version": "==2.11.1" + }, + "pyflakes": { + "hashes": [ + "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", + "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a" + ], + "markers": "python_version >= '3.8'", + "version": "==3.2.0" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version < '3.11'", + "version": "==2.0.1" + } + } +} diff --git a/ecs/restore-dbdump/README.md b/ecs/restore-dbdump/README.md new file mode 100644 index 00000000..dee336e4 --- /dev/null +++ b/ecs/restore-dbdump/README.md @@ -0,0 +1,67 @@ +# ダンプ復元スクリプト + +## 概要 + +ダンプ復元スクリプト + +## 環境情報 + +- Python 3.9 +- MySQL 8.23 +- VSCode + +## 環境構築 + +- Python の構築 + + - Merck_NewDWH 開発 2021 の Wiki、[Python 環境構築](https://nds-tyo.backlog.com/alias/wiki/1874930)を参照 + - 「Pipenv の導入」までを行っておくこと + - 構築完了後、プロジェクト配下で以下のコマンドを実行し、Python の仮想環境を作成する + - `pipenv install --dev --python ` + - この手順で出力される仮想環境のパスは、後述する VSCode の設定手順で使用するため、控えておく + +- MySQL の環境構築 + - Windows の場合、以下のリンクからダウンロードする + - + - 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」キーを押下すると、バッチ処理が起動する。 +- 「entrypoint.py」が、バッチ処理のエントリーポイント。 +- 実際の処理は、「src/restore_backup.py」で行っている。 + +## フォルダ構成 + +```txt +. +├── .env.example -- ローカル実行用の環境変数のサンプル値。 +├── .dockerignore -- docker build時のコンテキストに含めるファイルの抑制リスト +├── .gitignore -- Git差分管理除外リスト +├── Dockerfile -- Dockerイメージ作成用 +├── Pipfile -- pythonの依存関係管理 +├── Pipfile.lock -- 依存関係バージョン固定 +├── README.md -- 当ファイル +├── entrypoint.py -- エントリーポイントとなるファイル +├── mysql_dpkg_selection.txt -- Dockerイメージでdpkgを使うときに外部から注入する選択値 +└── src -- ソースコードフォルダ + ├── logging + │ └── get_logger.py -- ロガー + ├── restore_backup.py -- dump復元処理本体 + └── system_var + ├── constants.py -- 定数ファイル + └── environment.py -- 環境変数ファイル +``` diff --git a/ecs/restore-dbdump/entrypoint.py b/ecs/restore-dbdump/entrypoint.py new file mode 100644 index 00000000..70f48060 --- /dev/null +++ b/ecs/restore-dbdump/entrypoint.py @@ -0,0 +1,10 @@ +"""実消化&アルトマーク DBダンプ復元のエントリーポイント""" +from src import restore_backup + +if __name__ == '__main__': + try: + exit(restore_backup.exec()) + except Exception: + # エラーが起きても、正常系のコードで返す。 + # エラーが起きた事実はbatch_process内でログを出す。 + exit(0) diff --git a/ecs/restore-dbdump/mysql_dpkg_selection.txt b/ecs/restore-dbdump/mysql_dpkg_selection.txt new file mode 100644 index 00000000..d3cb46f9 --- /dev/null +++ b/ecs/restore-dbdump/mysql_dpkg_selection.txt @@ -0,0 +1,3 @@ +1 +1 +4 \ No newline at end of file diff --git a/ecs/restore-dbdump/src/__init__.py b/ecs/restore-dbdump/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/restore-dbdump/src/logging/get_logger.py b/ecs/restore-dbdump/src/logging/get_logger.py new file mode 100644 index 00000000..f36f1199 --- /dev/null +++ b/ecs/restore-dbdump/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 diff --git a/ecs/restore-dbdump/src/restore_backup.py b/ecs/restore-dbdump/src/restore_backup.py new file mode 100644 index 00000000..25ce0b19 --- /dev/null +++ b/ecs/restore-dbdump/src/restore_backup.py @@ -0,0 +1,64 @@ +"""ダンプ復元スクリプト""" + +import os +import subprocess +import textwrap + +### from src.logging.get_logger import get_logger +from src.system_var import constants, environment + +### logger = get_logger('ダンプ復元スクリプト') + + +def exec(): + try: + ### logger.info('ダンプ復元スクリプト:開始') + + # MySQL接続情報を作成する + my_cnf_file_content = f""" + [client] + user={environment.DB_USERNAME} + password={environment.DB_PASSWORD} + host={environment.DB_HOST} + """ + # my.cnfファイルのパス + my_cnf_path = os.path.join('my.cnf') + + # my.cnfファイルを生成する + with open(my_cnf_path, 'w') as f: + f.write(textwrap.dedent(my_cnf_file_content)[1:-1]) + + os.chmod(my_cnf_path, 0o444) + + # 復元対象のダンプファイルを特定 + s3_file_path = environment.DUMP_FILE_S3_PATH + + # aws s3 cpコマンドを実行してdumpファイルをローカルにダウンロードする + s3_cp_process = subprocess.Popen(['aws', 's3', 'cp', s3_file_path, './dump.gz'], stderr=subprocess.PIPE) + _, error = s3_cp_process.communicate() + if s3_cp_process.returncode != 0: + ### logger.error(f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + return constants.BATCH_EXIT_CODE_SUCCESS + # S3コマンドの標準エラーはクローズしておく + s3_cp_process.stderr.close() + + # gzipコマンドを実行してdumpファイルを解凍する + gzip_process = subprocess.Popen(['gunzip', '-c', './dump.gz'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # mysqlコマンドを実行し、dumpを復元する + mysql_process = subprocess.Popen( + ['mysql', f'--defaults-file={my_cnf_path}', '-P', f"{environment.DB_PORT}", environment.DB_SCHEMA], + stdin=gzip_process.stdout, stderr=subprocess.PIPE + ) + # gzipの標準出力をmysqlに接続したため、標準出力をクローズする + gzip_process.stdout.close() + + _, error = mysql_process.communicate() + if mysql_process.returncode != 0: + ### logger.error(f'コマンド実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + return constants.BATCH_EXIT_CODE_SUCCESS + ### logger.info('[NOTICE]ダンプ復元スクリプト:終了(正常終了)') + return constants.BATCH_EXIT_CODE_SUCCESS + + except Exception as e: + ### logger.exception(f'ダンプ復元スクリプト中に想定外のエラーが発生しました :{e}') + return constants.BATCH_EXIT_CODE_SUCCESS diff --git a/ecs/restore-dbdump/src/system_var/__init__.py b/ecs/restore-dbdump/src/system_var/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/restore-dbdump/src/system_var/constants.py b/ecs/restore-dbdump/src/system_var/constants.py new file mode 100644 index 00000000..8a555af3 --- /dev/null +++ b/ecs/restore-dbdump/src/system_var/constants.py @@ -0,0 +1,2 @@ +# バッチ正常終了コード +BATCH_EXIT_CODE_SUCCESS = 0 diff --git a/ecs/restore-dbdump/src/system_var/environment.py b/ecs/restore-dbdump/src/system_var/environment.py new file mode 100644 index 00000000..65664807 --- /dev/null +++ b/ecs/restore-dbdump/src/system_var/environment.py @@ -0,0 +1,12 @@ +import os + +# Database +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'] + +# AWS +DUMP_FILE_S3_PATH = os.environ['DUMP_FILE_S3_PATH'] + From 0459668032eec8a1a11dadbdd75e16283a1dae21 Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Wed, 21 Feb 2024 16:19:23 +0900 Subject: [PATCH 26/31] =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=83=A2=E3=82=B8?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=83=AB=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/export-dbdump/.env.example | 5 - ecs/export-dbdump/.vscode/launch.json | 2 +- ecs/export-dbdump/Pipfile | 3 - ecs/export-dbdump/Pipfile.lock | 150 +-------------- ecs/export-dbdump/README.md | 4 +- ecs/export-dbdump/entrypoint.py | 2 +- .../src/batch/batch_functions.py | 131 ------------- .../src/batch/common/__init__.py | 0 ecs/export-dbdump/src/db/__init__.py | 0 ecs/export-dbdump/src/db/database.py | 180 ------------------ ecs/export-dbdump/src/error/__init__.py | 0 ecs/export-dbdump/src/error/exceptions.py | 10 - ecs/export-dbdump/src/jobctrl_dbdump.py | 53 +----- ecs/export-dbdump/src/system_var/constants.py | 14 +- 14 files changed, 15 insertions(+), 539 deletions(-) delete mode 100644 ecs/export-dbdump/src/batch/batch_functions.py delete mode 100644 ecs/export-dbdump/src/batch/common/__init__.py delete mode 100644 ecs/export-dbdump/src/db/__init__.py delete mode 100644 ecs/export-dbdump/src/db/database.py delete mode 100644 ecs/export-dbdump/src/error/__init__.py delete mode 100644 ecs/export-dbdump/src/error/exceptions.py diff --git a/ecs/export-dbdump/.env.example b/ecs/export-dbdump/.env.example index c4e1f427..9782a3ce 100644 --- a/ecs/export-dbdump/.env.example +++ b/ecs/export-dbdump/.env.example @@ -5,10 +5,5 @@ DB_PASSWORD=************ DB_SCHEMA=***** DUMP_BACKUP_BUCKET=************ -DUMP_BACKUP_FOLDER=dump LOG_LEVEL=INFO -DB_CONNECTION_MAX_RETRY_ATTEMPT=************ -DB_CONNECTION_RETRY_INTERVAL_INIT=************ -DB_CONNECTION_RETRY_INTERVAL_MIN_SECONDS=************ -DB_CONNECTION_RETRY_INTERVAL_MAX_SECONDS=************ diff --git a/ecs/export-dbdump/.vscode/launch.json b/ecs/export-dbdump/.vscode/launch.json index ac80ce4f..2121f066 100644 --- a/ecs/export-dbdump/.vscode/launch.json +++ b/ecs/export-dbdump/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "name": "(DEBUG)jskult batch laundering", + "name": "(DEBUG) export dbdump", "type": "python", "request": "launch", "program": "entrypoint.py", diff --git a/ecs/export-dbdump/Pipfile b/ecs/export-dbdump/Pipfile index 65b30a51..f032c7ea 100644 --- a/ecs/export-dbdump/Pipfile +++ b/ecs/export-dbdump/Pipfile @@ -4,9 +4,6 @@ verify_ssl = true name = "pypi" [packages] -sqlalchemy = "*" -tenacity = "*" -pymysql = "*" [dev-packages] autopep8 = "*" diff --git a/ecs/export-dbdump/Pipfile.lock b/ecs/export-dbdump/Pipfile.lock index fb6db2d1..58c4d231 100644 --- a/ecs/export-dbdump/Pipfile.lock +++ b/ecs/export-dbdump/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e2e2efd7ebd6ad719931983f277105dd94c4ebb6363f7c00e75dd8ca05523713" + "sha256": "cc5f54bfb2073051a26f113ceac64e12fdd0bf8faa36f1a42210cc9c921c134b" }, "pipfile-spec": 6, "requires": { @@ -15,151 +15,7 @@ } ] }, - "default": { - "greenlet": { - "hashes": [ - "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", - "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", - "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", - "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", - "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", - "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", - "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", - "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", - "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", - "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", - "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", - "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", - "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", - "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", - "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", - "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", - "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", - "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", - "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", - "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", - "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", - "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", - "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", - "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", - "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", - "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", - "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", - "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", - "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", - "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", - "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", - "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", - "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", - "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", - "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", - "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", - "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", - "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", - "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", - "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", - "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", - "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", - "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", - "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", - "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", - "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", - "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", - "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", - "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", - "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", - "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", - "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", - "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", - "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", - "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", - "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", - "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", - "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" - ], - "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": "==3.0.3" - }, - "pymysql": { - "hashes": [ - "sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96", - "sha256:8969ec6d763c856f7073c4c64662882675702efcb114b4bcbb955aea3a069fa7" - ], - "index": "pypi", - "version": "==1.1.0" - }, - "sqlalchemy": { - "hashes": [ - "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9", - "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d", - "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e", - "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669", - "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d", - "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5", - "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002", - "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e", - "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd", - "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215", - "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24", - "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39", - "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62", - "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900", - "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf", - "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735", - "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4", - "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23", - "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9", - "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c", - "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de", - "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7", - "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625", - "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f", - "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a", - "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4", - "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643", - "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018", - "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9", - "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5", - "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08", - "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3", - "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3", - "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed", - "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95", - "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6", - "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0", - "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84", - "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f", - "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d", - "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2", - "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5", - "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570", - "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7", - "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c", - "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf", - "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3", - "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed", - "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5" - ], - "index": "pypi", - "version": "==2.0.25" - }, - "tenacity": { - "hashes": [ - "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a", - "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c" - ], - "index": "pypi", - "version": "==8.2.3" - }, - "typing-extensions": { - "hashes": [ - "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783", - "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd" - ], - "markers": "python_version >= '3.8'", - "version": "==4.9.0" - } - }, + "default": {}, "develop": { "autopep8": { "hashes": [ @@ -167,6 +23,7 @@ "sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==2.0.4" }, "flake8": { @@ -175,6 +32,7 @@ "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3" ], "index": "pypi", + "markers": "python_full_version >= '3.8.1'", "version": "==7.0.0" }, "mccabe": { diff --git a/ecs/export-dbdump/README.md b/ecs/export-dbdump/README.md index 7d8f0ccd..4b3afad4 100644 --- a/ecs/export-dbdump/README.md +++ b/ecs/export-dbdump/README.md @@ -1,8 +1,8 @@ -# 日次バッチ処理前DBダンプ取得  +# 【共通】DBダンプ取得  ## 概要 -日次バッチ処理前DBダンプ取得処理。 +【共通】DBダンプ取得処理。 ## 環境情報 diff --git a/ecs/export-dbdump/entrypoint.py b/ecs/export-dbdump/entrypoint.py index 543cb134..221f3770 100644 --- a/ecs/export-dbdump/entrypoint.py +++ b/ecs/export-dbdump/entrypoint.py @@ -1,4 +1,4 @@ -"""実消化&アルトマーク 日次バッチ処理前DBダンプ取得のエントリーポイント""" +"""【共通】DBダンプ取得処理のエントリーポイント""" from src import jobctrl_dbdump if __name__ == '__main__': diff --git a/ecs/export-dbdump/src/batch/batch_functions.py b/ecs/export-dbdump/src/batch/batch_functions.py deleted file mode 100644 index eece0536..00000000 --- a/ecs/export-dbdump/src/batch/batch_functions.py +++ /dev/null @@ -1,131 +0,0 @@ -"""バッチ処理の共通関数""" -import logging -import textwrap - -from src.db.database import Database -from src.error.exceptions import BatchOperationException, DBException -from src.system_var import constants - - -def get_batch_statuses() -> tuple[str, str]: - """日付テーブルから、以下を取得して返す。 - - バッチ処理中フラグ - - dump取得状況区分 - - Raises: - BatchOperationException: 日付テーブルが取得できないとき、何らかのエラーが発生したとき - - Returns: - tuple[str, str]: [0]バッチ処理中フラグ,[1]dump取得状況区分 - """ - db = Database.get_instance() - sql = 'SELECT bch_actf, dump_sts_kbn FROM src05.hdke_tbl' - try: - db.connect() - hdke_tbl_result = db.execute_select(sql) - except DBException as e: - raise BatchOperationException(e) - finally: - db.disconnect() - - if len(hdke_tbl_result) == 0: - raise BatchOperationException('日付テーブルが取得できませんでした') - - # 必ず1件取れる - hdke_tbl_record = hdke_tbl_result[0] - batch_processing_flag = hdke_tbl_record['bch_actf'] - dump_status_kbn = hdke_tbl_record['dump_sts_kbn'] - - return batch_processing_flag, dump_status_kbn - - -def update_dump_status_kbn_in_processing() -> None: - """dump取得状況区分を処理中に更新する - - Raises: - BatchOperationException: DB操作の何らかのエラー - """ - db = Database.get_instance() - sql = """\ - UPDATE src05.hdke_tbl - SET - dump_sts_kbn = :in_processing, - updater = CURRENT_USER(), - update_date = NOW() - """ - try: - db.connect() - db.to_jst() - db.execute(sql, {'in_processing': constants.BATCH_ACTF_BATCH_IN_PROCESSING}) - except DBException as e: - raise BatchOperationException(e) - finally: - db.disconnect() - - return - - -def update_dump_status_kbn_error() -> None: - """dump取得状況区分をエラーに更新する - - Raises: - BatchOperationException: DB操作の何らかのエラー - """ - db = Database.get_instance() - sql = """\ - UPDATE src05.hdke_tbl - SET - dump_sts_kbn = :dump_unprocessed, - updater = CURRENT_USER(), - update_date = NOW() - """ - try: - db.connect() - db.to_jst() - db.execute(sql, { - 'dump_unprocessed': constants.DUMP_STATUS_KBN_ERROR - }) - except DBException as e: - raise BatchOperationException(e) - finally: - db.disconnect() - - return - - -def update_dump_status_kbn_complete() -> None: - """dump取得状況区分を正常終了に更新する - - Raises: - BatchOperationException: DB操作の何らかのエラー - """ - db = Database.get_instance() - sql = """\ - UPDATE src05.hdke_tbl - SET - dump_sts_kbn = :dump_unprocessed, - updater = CURRENT_USER(), - update_date = NOW() - """ - try: - db.connect() - db.to_jst() - db.execute(sql, { - 'dump_unprocessed': constants.DUMP_STATUS_KBN_COMPLETE - }) - except DBException as e: - raise BatchOperationException(e) - finally: - db.disconnect() - - return - - -def logging_sql(logger: logging.Logger, sql: str) -> None: - """SQL文をデバッグログで出力する - - Args: - logger (logging.Logger): ロガー - sql (str): SQL文 - """ - logger.debug(f'\n{"-" * 15}\n{textwrap.dedent(sql)[1:-1]}\n{"-" * 15}') diff --git a/ecs/export-dbdump/src/batch/common/__init__.py b/ecs/export-dbdump/src/batch/common/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ecs/export-dbdump/src/db/__init__.py b/ecs/export-dbdump/src/db/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ecs/export-dbdump/src/db/database.py b/ecs/export-dbdump/src/db/database.py deleted file mode 100644 index 24bb6061..00000000 --- a/ecs/export-dbdump/src/db/database.py +++ /dev/null @@ -1,180 +0,0 @@ -from sqlalchemy import (Connection, CursorResult, Engine, QueuePool, - create_engine, text) -from sqlalchemy.engine.url import URL -from tenacity import retry, stop_after_attempt, wait_exponential - -from src.error.exceptions import DBException -from src.system_var import environment - - -class Database: - """データベース操作クラス""" - __connection: Connection = None - __engine: Engine = None - __host: str = None - __port: str = None - __username: str = None - __password: str = None - __schema: str = None - __connection_string: str = None - - def __init__(self, username: str, password: str, host: str, port: int, schema: str) -> None: - """このクラスの新たなインスタンスを初期化します - - Args: - username (str): DBユーザー名 - password (str): DBパスワード - host (str): DBホスト名 - port (int): DBポート - schema (str): DBスキーマ名 - """ - self.__username = username - self.__password = password - self.__host = host - self.__port = int(port) - self.__schema = schema - - self.__connection_string = URL.create( - drivername='mysql+pymysql', - username=self.__username, - password=self.__password, - host=self.__host, - port=self.__port, - database=self.__schema, - query={"charset": "utf8mb4"} - ) - - self.__engine = create_engine( - self.__connection_string, - pool_timeout=5, - poolclass=QueuePool - ) - - @classmethod - def get_instance(cls): - """インスタンスを取得します - - Returns: - Database: DB操作クラスインスタンス - """ - return cls( - username=environment.DB_USERNAME, - password=environment.DB_PASSWORD, - host=environment.DB_HOST, - port=environment.DB_PORT, - schema=environment.DB_SCHEMA - ) - - @retry( - wait=wait_exponential( - multiplier=environment.DB_CONNECTION_RETRY_INTERVAL_INIT, - min=environment.DB_CONNECTION_RETRY_INTERVAL_MIN_SECONDS, - max=environment.DB_CONNECTION_RETRY_INTERVAL_MAX_SECONDS - ), - stop=stop_after_attempt(environment.DB_CONNECTION_MAX_RETRY_ATTEMPT), - retry_error_cls=DBException - ) - def connect(self): - """ - DBに接続します。接続に失敗した場合、リトライします。 - Raises: - DBException: 接続失敗 - """ - 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クエリを実行します。 - - Args: - select_query (str): SELECT文 - parameters (dict, optional): クエリのプレースホルダーに埋め込む変数の辞書. Defaults to None. - - Raises: - DBException: DBエラー - - Returns: - list[dict]: カラム名: 値の辞書リスト - """ - if self.__connection is None: - raise DBException('DBに接続していません') - - result = None - try: - # トランザクションが開始している場合は、トランザクションを引き継ぐ - if self.__connection.in_transaction(): - result = self.__connection.execute(text(select_query), parameters) - else: - # トランザクションが明示的に開始していない場合は、クエリ単位でトランザクションをbegin-commitする。 - result = self.__execute_with_transaction(select_query, parameters) - except Exception as e: - raise DBException(f'SQL Error: {e}') - - result_rows = result.mappings().all() - return result_rows - - def execute(self, query: str, parameters=None) -> CursorResult: - """SQLクエリを実行します。 - - Args: - query (str): SQL文 - parameters (dict, optional): クエリのプレースホルダーに埋め込む変数の辞書. Defaults to None. - - Raises: - DBException: DBエラー - - Returns: - CursorResult: 取得結果 - """ - if self.__connection is None: - raise DBException('DBに接続していません') - - result = None - try: - # トランザクションが開始している場合は、トランザクションを引き継ぐ - if self.__connection.in_transaction(): - result = self.__connection.execute(text(query), parameters) - else: - # トランザクションが明示的に開始していない場合は、クエリ単位でトランザクションをbegin-commitする。 - result = self.__execute_with_transaction(query, parameters) - except Exception as e: - raise DBException(f'SQL Error: {e}') - - return result - - def begin(self): - """トランザクションを開始します。""" - if not self.__connection.in_transaction(): - self.__connection.begin() - - def commit(self): - """トランザクションをコミットします""" - if self.__connection.in_transaction(): - self.__connection.commit() - - def rollback(self): - """トランザクションをロールバックします""" - if self.__connection.in_transaction(): - self.__connection.rollback() - - def disconnect(self): - """DB接続を切断します。""" - if self.__connection is not None: - self.__connection.close() - self.__connection = None - - def to_jst(self): - self.execute('SET time_zone = "+9:00"') - - def __execute_with_transaction(self, query: str, parameters: dict): - # トランザクションを開始してクエリを実行する - with self.__connection.begin(): - try: - result = self.__connection.execute(text(query), parameters=parameters) - except Exception as e: - self.__connection.rollback() - raise e - # ここでコミットされる - return result diff --git a/ecs/export-dbdump/src/error/__init__.py b/ecs/export-dbdump/src/error/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ecs/export-dbdump/src/error/exceptions.py b/ecs/export-dbdump/src/error/exceptions.py deleted file mode 100644 index 055c24f6..00000000 --- a/ecs/export-dbdump/src/error/exceptions.py +++ /dev/null @@ -1,10 +0,0 @@ -class MeDaCaException(Exception): - pass - - -class DBException(MeDaCaException): - pass - - -class BatchOperationException(MeDaCaException): - pass diff --git a/ecs/export-dbdump/src/jobctrl_dbdump.py b/ecs/export-dbdump/src/jobctrl_dbdump.py index b6e27728..0612e4f7 100644 --- a/ecs/export-dbdump/src/jobctrl_dbdump.py +++ b/ecs/export-dbdump/src/jobctrl_dbdump.py @@ -5,11 +5,6 @@ import os import subprocess import textwrap -from src.batch.batch_functions import (get_batch_statuses, - update_dump_status_kbn_complete, - update_dump_status_kbn_error, - update_dump_status_kbn_in_processing) -from src.error.exceptions import BatchOperationException from src.logging.get_logger import get_logger from src.system_var import constants, environment @@ -19,32 +14,6 @@ logger = get_logger('DBダンプ取得') def exec(): try: logger.info('DBダンプ取得:開始') - try: - # 日次バッチ処置中フラグ、dump処理状態区分を取得 - batch_processing_flag, dump_status_kbn = get_batch_statuses() - except BatchOperationException as e: - raise BatchOperationException(f'日付テーブル取得エラー(異常終了):{e}') - - # 日次バッチ処理中の場合、処理は行わない - if batch_processing_flag == constants.BATCH_ACTF_BATCH_IN_PROCESSING: - logger.error('日次バッチ処理中の為、DBダンプ取得を終了します。') - return constants.BATCH_EXIT_CODE_SUCCESS - - # dump処理状態区分が処理中の場合、処理は行わない - if dump_status_kbn == constants.DUMP_STATUS_KBN_PROCESSED: - logger.error(f'ダンプ取得中の為、DBダンプ取得を終了します。 dump処理状態区分={dump_status_kbn}') - return constants.BATCH_EXIT_CODE_SUCCESS - - # dump処理状態区分がエラーの場合、処理は行わない - if dump_status_kbn == constants.DUMP_STATUS_KBN_ERROR: - logger.error(f'ダンプ取得が実行不可能な状態の為、DBダンプ取得を終了します。 dump処理状態区分={dump_status_kbn}') - return constants.BATCH_EXIT_CODE_SUCCESS - - # dump処理状態区分を処理中に更新 - try: - update_dump_status_kbn_in_processing() - except BatchOperationException as e: - raise BatchOperationException(f'dump処理状態区分更新(未処理→処理中) エラー(異常終了):{e}') # MySQL接続情報を作成する my_cnf_file_content = f""" @@ -65,9 +34,9 @@ def exec(): os.chmod(my_cnf_path, 0o444) dt_now = datetime.datetime.now() - converted_value = dt_now.strftime('%Y%m%d%H%M%S') + converted_value = dt_now.strftime('%Y%m%d%H%M%S%f') dump_file_name = f'backup_rds_{environment.DB_SCHEMA}_{converted_value}.gz' - s3_file_path = f's3://{environment.DUMP_BACKUP_BUCKET}/dump/{dt_now.year}/{dt_now.strftime("%m")}/{dt_now.strftime("%d")}/{dump_file_name}' + s3_file_path = f's3://{environment.DUMP_BACKUP_BUCKET}/{constants.DUMP_BACKUP_FOLDER}/{dt_now.year}/{dt_now.strftime("%m")}/{dt_now.strftime("%d")}/{dump_file_name}' # mysqldumpコマンドを実行し、dumpを取得する command = [ @@ -95,32 +64,20 @@ def exec(): # パイプラインを実行し、エラーハンドリング _, error = mysqldump_process.communicate() if mysqldump_process.returncode != 0: - raise BatchOperationException(f'`mysqldump`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + raise Exception(f'`mysqldump`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') _, error = gzip_process.communicate() if gzip_process.returncode != 0: - raise BatchOperationException(f'`gzip`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + raise Exception(f'`gzip`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') _, error = s3_cp_process.communicate() if s3_cp_process.returncode != 0: - raise BatchOperationException(f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') - - # dump処理状態区分を正常終了に更新 - try: - update_dump_status_kbn_complete() - except BatchOperationException as e: - raise BatchOperationException(f'dump処理状態区分更新(処理中→正常終了) エラー(異常終了):{e}') + raise Exception(f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') logger.info('DBダンプ取得:終了(正常終了)') logger.info(f'出力ファイルパス: {s3_file_path}') return constants.BATCH_EXIT_CODE_SUCCESS except Exception as e: - # dump処理状態区分をエラーに更新 - try: - update_dump_status_kbn_error() - except BatchOperationException as e: - logger.exception(f'dump処理状態区分更新(処理中→エラー) エラー(異常終了):{e}') - return constants.BATCH_EXIT_CODE_SUCCESS logger.exception(f'DBダンプ取得中に想定外のエラーが発生しました :{e}') return constants.BATCH_EXIT_CODE_SUCCESS diff --git a/ecs/export-dbdump/src/system_var/constants.py b/ecs/export-dbdump/src/system_var/constants.py index 993e5a7d..4d288bbd 100644 --- a/ecs/export-dbdump/src/system_var/constants.py +++ b/ecs/export-dbdump/src/system_var/constants.py @@ -1,15 +1,5 @@ # バッチ正常終了コード BATCH_EXIT_CODE_SUCCESS = 0 -# バッチ処理中フラグ:未処理 -BATCH_ACTF_BATCH_UNPROCESSED = '0' -# バッチ処理中フラグ:処理中 -BATCH_ACTF_BATCH_IN_PROCESSING = '1' -# dump取得状態区分:未処理 -DUMP_STATUS_KBN_UNPROCESSED = '0' -# dump取得状態区分:処理中 -DUMP_STATUS_KBN_PROCESSED = '1' -# dump取得状態区分:正常終了 -DUMP_STATUS_KBN_COMPLETE = '2' -# dump取得状態区分:エラー -DUMP_STATUS_KBN_ERROR = '9' +# ダンプバックアップフォルダー +DUMP_BACKUP_FOLDER = 'dump' \ No newline at end of file From 9400a7f5ff43c3cd713ad8a3c1aebaf0b5858672 Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Wed, 21 Feb 2024 16:48:12 +0900 Subject: [PATCH 27/31] =?UTF-8?q?=E6=8C=87=E6=91=98=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E3=83=BBprepostexec=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/restore-dbdump/.env.example | 3 +- ecs/restore-dbdump/README.md | 2 +- ecs/restore-dbdump/src/restore_backup.py | 40 +++++++++++++++---- .../src/system_var/environment.py | 2 +- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/ecs/restore-dbdump/.env.example b/ecs/restore-dbdump/.env.example index 7272a711..1a407863 100644 --- a/ecs/restore-dbdump/.env.example +++ b/ecs/restore-dbdump/.env.example @@ -3,4 +3,5 @@ DB_PORT=3306 DB_USERNAME=************ DB_PASSWORD=************ DB_SCHEMA=src05 -DUMP_FILE_S3_PATH=******************* \ No newline at end of file +DUMP_FILE_S3_PATH=******************* +LOG_LEVEL=INFO \ No newline at end of file diff --git a/ecs/restore-dbdump/README.md b/ecs/restore-dbdump/README.md index dee336e4..187692af 100644 --- a/ecs/restore-dbdump/README.md +++ b/ecs/restore-dbdump/README.md @@ -2,7 +2,7 @@ ## 概要 -ダンプ復元スクリプト +当処理は特定の機能で利用するものではなく、共通処理として要件に応じて実行することを想定している。 ## 環境情報 diff --git a/ecs/restore-dbdump/src/restore_backup.py b/ecs/restore-dbdump/src/restore_backup.py index 25ce0b19..2b015629 100644 --- a/ecs/restore-dbdump/src/restore_backup.py +++ b/ecs/restore-dbdump/src/restore_backup.py @@ -4,16 +4,20 @@ import os import subprocess import textwrap -### from src.logging.get_logger import get_logger +from src.logging.get_logger import get_logger from src.system_var import constants, environment -### logger = get_logger('ダンプ復元スクリプト') +logger = get_logger('ダンプ復元スクリプト') def exec(): try: - ### logger.info('ダンプ復元スクリプト:開始') + logger.info('ダンプ復元スクリプト:開始') + # 事前処理(共通処理としては空振りする) + _pre_exec() + + #メイン処理 # MySQL接続情報を作成する my_cnf_file_content = f""" [client] @@ -37,7 +41,7 @@ def exec(): s3_cp_process = subprocess.Popen(['aws', 's3', 'cp', s3_file_path, './dump.gz'], stderr=subprocess.PIPE) _, error = s3_cp_process.communicate() if s3_cp_process.returncode != 0: - ### logger.error(f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + logger.error(f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') return constants.BATCH_EXIT_CODE_SUCCESS # S3コマンドの標準エラーはクローズしておく s3_cp_process.stderr.close() @@ -54,11 +58,33 @@ def exec(): _, error = mysql_process.communicate() if mysql_process.returncode != 0: - ### logger.error(f'コマンド実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + logger.error(f'コマンド実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') return constants.BATCH_EXIT_CODE_SUCCESS - ### logger.info('[NOTICE]ダンプ復元スクリプト:終了(正常終了)') + + # 事後処理(共通処理としては空振りする) + _post_exec() + + logger.info('[NOTICE]ダンプ復元スクリプト:終了(正常終了)') return constants.BATCH_EXIT_CODE_SUCCESS except Exception as e: - ### logger.exception(f'ダンプ復元スクリプト中に想定外のエラーが発生しました :{e}') + logger.exception(f'ダンプ復元スクリプト中に想定外のエラーが発生しました :{e}') return constants.BATCH_EXIT_CODE_SUCCESS + +def _pre_exec(): + """ + ダンプ復元 事前処理 + 共通機能としては事前処理を実装しない。 + 事前処理が必要なダンプ復元処理を実装する場合、当ロジックをコピーする。 + """ + print('pre') + pass + +def _post_exec(): + """ + ダンプ復元 事後処理 + 共通機能としては事後処理を実装しない。 + 事後処理が必要なダンプ復元処理を実装する場合、当ロジックをコピーする。 + """ + print('post') + pass diff --git a/ecs/restore-dbdump/src/system_var/environment.py b/ecs/restore-dbdump/src/system_var/environment.py index 65664807..bddc0a38 100644 --- a/ecs/restore-dbdump/src/system_var/environment.py +++ b/ecs/restore-dbdump/src/system_var/environment.py @@ -9,4 +9,4 @@ DB_SCHEMA = os.environ['DB_SCHEMA'] # AWS DUMP_FILE_S3_PATH = os.environ['DUMP_FILE_S3_PATH'] - +LOG_LEVEL = os.environ['LOG_LEVEL'] From b480424354720d12f760cfc4bd428feb62f9d786 Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Wed, 21 Feb 2024 16:52:19 +0900 Subject: [PATCH 28/31] =?UTF-8?q?=E7=A2=BA=E8=AA=8D=E7=94=A8=E4=B8=8D?= =?UTF-8?q?=E8=A6=81=E3=82=B3=E3=83=BC=E3=83=89=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/restore-dbdump/src/restore_backup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ecs/restore-dbdump/src/restore_backup.py b/ecs/restore-dbdump/src/restore_backup.py index 2b015629..d047703a 100644 --- a/ecs/restore-dbdump/src/restore_backup.py +++ b/ecs/restore-dbdump/src/restore_backup.py @@ -63,7 +63,7 @@ def exec(): # 事後処理(共通処理としては空振りする) _post_exec() - + logger.info('[NOTICE]ダンプ復元スクリプト:終了(正常終了)') return constants.BATCH_EXIT_CODE_SUCCESS @@ -77,7 +77,6 @@ def _pre_exec(): 共通機能としては事前処理を実装しない。 事前処理が必要なダンプ復元処理を実装する場合、当ロジックをコピーする。 """ - print('pre') pass def _post_exec(): @@ -86,5 +85,4 @@ def _post_exec(): 共通機能としては事後処理を実装しない。 事後処理が必要なダンプ復元処理を実装する場合、当ロジックをコピーする。 """ - print('post') pass From 37b9bc611208ae23d01e6177617c7a942d531800 Mon Sep 17 00:00:00 2001 From: "nik.n" Date: Wed, 21 Feb 2024 17:07:24 +0900 Subject: [PATCH 29/31] =?UTF-8?q?=E5=89=8D=E5=BE=8C=E5=87=A6=E7=90=86prepo?= =?UTF-8?q?st=5Fexec=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/export-dbdump/README.md | 2 +- ecs/export-dbdump/src/jobctrl_dbdump.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/ecs/export-dbdump/README.md b/ecs/export-dbdump/README.md index 4b3afad4..a3764e33 100644 --- a/ecs/export-dbdump/README.md +++ b/ecs/export-dbdump/README.md @@ -2,7 +2,7 @@ ## 概要 -【共通】DBダンプ取得処理。 +当処理は特定の機能で利用するものではなく、共通処理として要件に応じて実行することを想定している。 ## 環境情報 diff --git a/ecs/export-dbdump/src/jobctrl_dbdump.py b/ecs/export-dbdump/src/jobctrl_dbdump.py index 0612e4f7..a82c155f 100644 --- a/ecs/export-dbdump/src/jobctrl_dbdump.py +++ b/ecs/export-dbdump/src/jobctrl_dbdump.py @@ -15,6 +15,10 @@ def exec(): try: logger.info('DBダンプ取得:開始') + # 事前処理(共通処理としては空振りする) + _pre_exec() + + # メイン処理 # MySQL接続情報を作成する my_cnf_file_content = f""" [client] @@ -74,6 +78,9 @@ def exec(): if s3_cp_process.returncode != 0: raise Exception(f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + # 事後処理(共通処理としては空振りする) + _post_exec() + logger.info('DBダンプ取得:終了(正常終了)') logger.info(f'出力ファイルパス: {s3_file_path}') return constants.BATCH_EXIT_CODE_SUCCESS @@ -81,3 +88,19 @@ def exec(): except Exception as e: logger.exception(f'DBダンプ取得中に想定外のエラーが発生しました :{e}') return constants.BATCH_EXIT_CODE_SUCCESS + +def _pre_exec(): + """ + ダンプ復元 事前処理 + 共通機能としては事前処理を実装しない。 + 事前処理が必要なダンプ復元処理を実装する場合、当ロジックをコピーする。 + """ + pass + +def _post_exec(): + """ + ダンプ復元 事後処理 + 共通機能としては事後処理を実装しない。 + 事後処理が必要なダンプ復元処理を実装する場合、当ロジックをコピーする。 + """ + pass \ No newline at end of file From 522c48252c4fea62a27b41cbe2b6fbc78681bdb1 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Mon, 26 Feb 2024 17:41:00 +0900 Subject: [PATCH 30/31] =?UTF-8?q?fix:=20=E3=82=B9=E3=83=86=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=83=9E=E3=82=B7=E3=83=B3=E5=AE=9A=E7=BE=A9=E3=81=AE?= =?UTF-8?q?=E8=AA=A4=E3=82=8A=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../r-export-dbdump-state/r-export-dbdump-state.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json index 248036ea..94ea3d86 100644 --- a/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json +++ b/stepfunctions/r-export-dbdump-state/r-export-dbdump-state.json @@ -9,7 +9,7 @@ "ecs": { "LaunchType": "FARGATE", "Cluster": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:cluster/mbj-newdwh2021-#{ENV_NAME}-export-dbdump-ecs", - "TaskDefinition": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:task-definition/mbj-newdwh2021-#{ENV_NAME}-export-task-dbdump", + "TaskDefinition": "arn:aws:ecs:#{REGION_AP_NORTHEAST_1}:#{AWS_ACCOUNT_ID}:task-definition/mbj-newdwh2021-#{ENV_NAME}-task-export-dbdump", "NetworkConfiguration": { "AwsvpcConfiguration": { "Subnets": [ @@ -26,7 +26,7 @@ "Overrides": { "ContainerOverrides": [ { - "Name": "mbj-newdwh2021-#{ENV_NAME}-export-task-dbdump", + "Name": "mbj-newdwh2021-#{ENV_NAME}-container-export-dbdump", "Environment": [ { "Name": "DB_SCHEMA", From 0c000743806bfbb155365e6823f71a5632fd10f2 Mon Sep 17 00:00:00 2001 From: "shimoda.m@nds-tyo.co.jp" Date: Mon, 26 Feb 2024 18:57:42 +0900 Subject: [PATCH 31/31] =?UTF-8?q?fix:=20DB=E6=8E=A5=E7=B6=9A=E3=82=92?= =?UTF-8?q?=E4=BA=8B=E5=89=8D=E3=81=AB=E8=A1=8C=E3=81=84=E3=80=81=E3=82=A8?= =?UTF-8?q?=E3=83=A9=E3=83=BC=E3=82=92=E6=8A=91=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ecs/restore-dbdump/src/restore_backup.py | 40 ++++++++++++++++++------ 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/ecs/restore-dbdump/src/restore_backup.py b/ecs/restore-dbdump/src/restore_backup.py index d047703a..39b92f23 100644 --- a/ecs/restore-dbdump/src/restore_backup.py +++ b/ecs/restore-dbdump/src/restore_backup.py @@ -17,7 +17,7 @@ def exec(): # 事前処理(共通処理としては空振りする) _pre_exec() - #メイン処理 + # メイン処理 # MySQL接続情報を作成する my_cnf_file_content = f""" [client] @@ -34,23 +34,40 @@ def exec(): os.chmod(my_cnf_path, 0o444) + # DBへの接続エラーを早期に検出するため、事前にMySQLサーバーに接続 + mysql_pre_process = subprocess.Popen( + ['mysql', f'--defaults-file={my_cnf_path}', '-P', f"{environment.DB_PORT}", + environment.DB_SCHEMA, '-N', '-e', 'SELECT 1;'], + stderr=subprocess.PIPE + ) + + _, error = mysql_pre_process.communicate() + if mysql_pre_process.returncode != 0: + logger.error( + f'MySQLサーバーへの接続に失敗しました。{"" if error is None else error.decode("utf-8")}') + return constants.BATCH_EXIT_CODE_SUCCESS + # 復元対象のダンプファイルを特定 s3_file_path = environment.DUMP_FILE_S3_PATH # aws s3 cpコマンドを実行してdumpファイルをローカルにダウンロードする - s3_cp_process = subprocess.Popen(['aws', 's3', 'cp', s3_file_path, './dump.gz'], stderr=subprocess.PIPE) + s3_cp_process = subprocess.Popen( + ['aws', 's3', 'cp', s3_file_path, './dump.gz'], stderr=subprocess.PIPE) _, error = s3_cp_process.communicate() if s3_cp_process.returncode != 0: - logger.error(f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + logger.error( + f'`aws s3 cp`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') return constants.BATCH_EXIT_CODE_SUCCESS # S3コマンドの標準エラーはクローズしておく s3_cp_process.stderr.close() # gzipコマンドを実行してdumpファイルを解凍する - gzip_process = subprocess.Popen(['gunzip', '-c', './dump.gz'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + gzip_process = subprocess.Popen( + ['gunzip', '-c', './dump.gz'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) # mysqlコマンドを実行し、dumpを復元する mysql_process = subprocess.Popen( - ['mysql', f'--defaults-file={my_cnf_path}', '-P', f"{environment.DB_PORT}", environment.DB_SCHEMA], + ['mysql', f'--defaults-file={my_cnf_path}', '-P', + f"{environment.DB_PORT}", environment.DB_SCHEMA], stdin=gzip_process.stdout, stderr=subprocess.PIPE ) # gzipの標準出力をmysqlに接続したため、標準出力をクローズする @@ -58,9 +75,10 @@ def exec(): _, error = mysql_process.communicate() if mysql_process.returncode != 0: - logger.error(f'コマンド実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') + logger.error( + f'コマンド実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}') return constants.BATCH_EXIT_CODE_SUCCESS - + # 事後処理(共通処理としては空振りする) _post_exec() @@ -70,19 +88,21 @@ def exec(): except Exception as e: logger.exception(f'ダンプ復元スクリプト中に想定外のエラーが発生しました :{e}') return constants.BATCH_EXIT_CODE_SUCCESS - + + def _pre_exec(): """ ダンプ復元 事前処理 - 共通機能としては事前処理を実装しない。 + 共通機能としては事前処理を実装しない。 事前処理が必要なダンプ復元処理を実装する場合、当ロジックをコピーする。 """ pass + def _post_exec(): """ ダンプ復元 事後処理 - 共通機能としては事後処理を実装しない。 + 共通機能としては事後処理を実装しない。 事後処理が必要なダンプ復元処理を実装する場合、当ロジックをコピーする。 """ pass