Merge branch 'develop' into feature-NEWDWH2021-880

This commit is contained in:
Nik Afiq 2024-05-08 13:53:43 +09:00
commit 1dc58e577f
1017 changed files with 51971 additions and 949 deletions

3
.gitignore vendored
View File

@ -15,3 +15,6 @@ stepfunctions/*/build
# python test
.coverage
.report/
# log
.log

20
ecs/crm-datafetch/.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
# Node.jsで実装されたLambdaの管理対象外ファイル群
package-lock.json
node_modules/
# ローカル確認用環境変数ファイル
.env
# Pythonの仮想環境ファイル
.venv
# pythonのキャッシュファイル
__pycache__/
# StepFunctionsステートメント定義変換後のフォルダ
stepfunctions/*/build
**/.vscode/settings.json
# python test
.coverage
.report/
# log
.log

View File

@ -1,4 +1,4 @@
FROM python:3.8
FROM python:3.9
ENV TZ="Asia/Tokyo"

View File

@ -11,7 +11,7 @@ test = "pytest tests/"
[packages]
boto3 = "*"
simple-salesforce = "*"
simple-salesforce = "==1.12.4"
tenacity = "*"
[dev-packages]
@ -23,4 +23,4 @@ pytest-html = "*"
moto = "*"
[requires]
python_version = "3.8"
python_version = "3.9"

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
### ツールのバージョン
- Python 3.8.x
- Python 3.9.x
- PipEnv(Pythonの依存関係管理用モジュール)
### 開発環境

View File

@ -1,3 +1,4 @@
from collections import OrderedDict
from src.config.objects import TargetObject
from src.converter.convert_strategy import ConvertStrategyFactory
@ -25,10 +26,11 @@ class CSVStringConverter:
json_object = self.__extract_necessary_props_from(json_object)
csv_row = []
for column in columns:
v = json_object[column.upper()]
column_name = column.upper()
column_value = self.__get_column_value(json_object, column_name)
convert_strategy = self.__convert_strategy_factory.create(v)
converted_value = convert_strategy.convert_value(v)
convert_strategy = self.__convert_strategy_factory.create(column_value)
converted_value = convert_strategy.convert_value(column_value)
csv_row.append(converted_value)
@ -38,3 +40,31 @@ class CSVStringConverter:
except Exception as e:
raise Exception(
f'CSV変換に失敗しました カラム名:[{column}] 行番号: [{i}] エラー内容:[{e}]')
def __get_column_value(self, json_object: dict, column_name: str) -> str:
# 参照を辿らない通常の項目の場合、カラム名が一致するためそのまま取得
if '.' not in column_name:
return json_object[column_name]
# カラム名に`.`が含まれている場合、オブジェクトの参照を辿って終端を取得する
relationship_columns = column_name.split('.')
return self.__get_column_value_by_relationship(json_object, relationship_columns)
def __get_column_value_by_relationship(self, json_object: dict, relationship_columns: str, recurs: int = 0) -> str:
# 参照関係の終端を取得しきるまで再帰的に深掘りする
# REVIEW: 参照の終端の項目型が住所型の場合、レスポンスが辞書型になるため大抵の場合Noneになる
relationship_name = relationship_columns[recurs]
relationship_item = json_object.get(relationship_name)
# 項目が取得できなかったらNoneを返す
if relationship_item is None:
return None
# 参照が辿りきれていない場合、再帰的に深掘りする
if type(relationship_item) == dict or type(relationship_item) == OrderedDict:
# 取り回しを良くするために、辞書のキーをアッパーケースにしておく
relationship_item_upper = {k.upper(): v for k, v in relationship_item.items()}
return self.__get_column_value_by_relationship(relationship_item_upper, relationship_columns, recurs + 1)
# 終端のデータを取得
return relationship_item

View File

@ -11,7 +11,8 @@ class TestCSVStringConverter:
def test_convert(self) -> str:
"""
Cases:
入力データがCSV形式の文字列で出力されること
- 入力データがCSV形式の文字列で出力されること
- 参照関係を辿った項目の終端が取得されていること
Arranges:
- オブジェクト情報の作成
- データの作成
@ -35,7 +36,10 @@ class TestCSVStringConverter:
"RowCause",
"LastModifiedDate",
"LastModifiedById",
"IsDeleted"
"IsDeleted",
"Account.Name",
"Account.attributes.type",
"Account.attributes.url"
],
"is_skip": False,
"is_update_last_fetch_datetime": False,
@ -57,7 +61,8 @@ class TestCSVStringConverter:
('RowCause', 'テストのため1'),
('LastModifiedDate', '2022-06-01T00:00:00.000+0000'),
('LastModifiedById', 1.234567E+6),
('IsDeleted', False)
('IsDeleted', False),
('Account', None)
]),
OrderedDict([
('attributes', OrderedDict([('type', 'AccountShare'), ('url', '/services/data/v1.0/sobjects/AccountShare/test1')])),
@ -71,7 +76,8 @@ class TestCSVStringConverter:
('RowCause', 'テストのため2'),
('LastModifiedDate', '2022-06-02T16:30:30.000+0000'),
('LastModifiedById', 2.23E+0),
('IsDeleted', True)
('IsDeleted', True),
('Account', None)
]),
OrderedDict([
('attributes', OrderedDict([('type', 'AccountShare'), ('url', '/services/data/v1.0/sobjects/AccountShare/test1')])),
@ -85,7 +91,26 @@ class TestCSVStringConverter:
('RowCause', 'テストのため3'),
('LastModifiedDate', '2022-06-03T23:50:50.000+0000'),
('LastModifiedById', 3.234567),
('IsDeleted', False)
('IsDeleted', True),
('Account', None)
]),
OrderedDict([
('attributes', OrderedDict([('type', 'AccountShare'), ('url', '/services/data/v1.0/sobjects/AccountShare/test1')])),
('Id', 'TEST004'),
('AccountId', 'test004'),
('UserOrGroupId', None),
('AccountAccessLevel', 13),
('OpportunityAccessLevel', 14),
('CaseAccessLevel', 15),
('ContactAccessLevel', 16),
('RowCause', 'テストのため4'),
('LastModifiedDate', '2022-06-03T23:50:50.000+0000'),
('LastModifiedById', 3.234567),
('IsDeleted', False),
('Account', OrderedDict([
('attributes', OrderedDict([('type', 'Account'), ('url', '/services/data/v1.0/sobjects/Account/test4')])),
('Name', 'テスト取引先'),
]))
])
]
@ -99,10 +124,13 @@ class TestCSVStringConverter:
# Expects
expect = [
["Id", "AccountId", "UserOrGroupId", "AccountAccessLevel", "OpportunityAccessLevel", "CaseAccessLevel",
"ContactAccessLevel", "RowCause", "LastModifiedDate", "LastModifiedById", "IsDeleted"],
["TEST001", "test001", "", 1, 2, 3, 4, "テストのため1", "2022-06-01 09:00:00", 1234567.0, 0],
["TEST002", "test002", "", 5, 6, 7, 8, "テストのため2", "2022-06-03 01:30:30", 2.23, 1],
["TEST003", "test003", "", 9, 10, 11, 12, "テストのため3", "2022-06-04 08:50:50", 3.234567, 0]
"ContactAccessLevel", "RowCause", "LastModifiedDate", "LastModifiedById", "IsDeleted",
"Account.Name", "Account.attributes.type", "Account.attributes.url"],
["TEST001", "test001", "", 1, 2, 3, 4, "テストのため1", "2022-06-01 09:00:00", 1234567.0, 0, "", "", ""],
["TEST002", "test002", "", 5, 6, 7, 8, "テストのため2", "2022-06-03 01:30:30", 2.23, 1, "", "", ""],
["TEST003", "test003", "", 9, 10, 11, 12, "テストのため3", "2022-06-04 08:50:50", 3.234567, 1, "", "", ""],
["TEST004", "test004", "", 13, 14, 15, 16, "テストのため4", "2022-06-04 08:50:50",
3.234567, 0, "テスト取引先", "Account", "/services/data/v1.0/sobjects/Account/test4"]
]
assert actual == expect
@ -184,7 +212,12 @@ class TestCSVStringConverter:
('RowCause', 'テストのため3'),
('LastModifiedDate', '2022-06-03T23:50:50.000+0000'),
('LastModifiedById', 3.234567E+6),
('IsDeleted', False)
('IsDeleted', False),
('Account', OrderedDict([
('attributes', OrderedDict([('type', 'Account'), ('url', '/services/data/v1.0/sobjects/Account/test3')])),
('Name', 'テスト取引先'),
])
),
])
]

View File

@ -286,6 +286,56 @@ class TestSalesforceApiClient:
actual = sut.fetch_sf_data(soql)
assert len(actual) >= 0
def test_fetch_sf_data_relationship_object_depth_1(self):
"""
Cases:
参照関係を1回辿るSOQLを実行しSalesforceからデータが取得できること
Arranges:
Salesforceの以下のオブジェクトにレコードを作成する(手作業コード上では行わない)
- RelationShipTest__c
Expects:
取得結果が期待値と一致すること
"""
soql = """SELECT
Id,
Name,
RecordTypeId,
RecordType.DeveloperName
FROM
RelationShipTest__c
ORDER BY Name ASC
"""
sut = SalesforceApiClient()
actual = sut.fetch_sf_data(soql)
assert len(actual) > 0
assert dict(actual[0])["RecordType"]["DeveloperName"] == "RecordTypeNormal"
def test_fetch_sf_data_relationship_object_depth_2(self):
"""
Cases:
参照関係を2回辿るSOQLを実行しSalesforceからデータが取得できること
Arranges:
Salesforceの以下のオブジェクトにレコードを作成する(手作業コード上では行わない)
- RelationShipTest__c
- RelationShipTest_Child__c
Expects:
取得結果が期待値と一致すること
"""
soql = """SELECT
Id,
Name,
RelationShipTest__r.RecordType.DeveloperName
FROM
RelationShipTest_Child__c
ORDER BY Name ASC
"""
sut = SalesforceApiClient()
actual = sut.fetch_sf_data(soql)
assert len(actual) > 0
assert dict(actual[0])["RelationshipTest__r"]["RecordType"]["DeveloperName"] == "RecordTypeNormal"
def test_fetch_sf_data_by_soql_builder_system_modstamp_to_ge(self):
"""
Cases:
@ -532,6 +582,78 @@ class TestSalesforceApiClient:
assert len(actual) == 17
# 内容の確認は別のケースで行っているため省略
def test_fetch_sf_data_by_soql_builder_relationship_object_depth_1(self):
"""
Cases:
- SOQLBuilderから生成したSOQLでSalesforceから参照関係を1回辿ったオブジェクト項目が取得できること
Arranges:
- Salesforceの以下のオブジェクトにレコードを作成する(手作業コード上では行わない)
- RelationShipTest__c
- RelationShipTest_Child__c
- LastFetchDatetimeのFromに2000年1月1日を指定する
- LastFetchDatetimeのToに2100年12月31日を指定する
Expects:
取得できたオブジェクトの1件をサンプリング確認しレコードタイプ名(DeveloperName)が含まれている
"""
execute_datetime = ExecuteDateTime()
last_fetch_datetime = LastFetchDatetime({
'last_fetch_datetime_from': '2000-01-01T00:00:00.000Z',
'last_fetch_datetime_to': '2100-12-31T23:59:59.000Z',
}, execute_datetime)
target_object = TargetObject({
'object_name': 'RelationShipTest__c',
'columns': [
'Id',
'Name',
'RecordTypeId',
'RecordType.DeveloperName'
]
}, execute_datetime)
soql_builder = SOQLBuilder(target_object, last_fetch_datetime)
soql = soql_builder.create_fetch_soql()
sut = SalesforceApiClient()
actual = sut.fetch_sf_data(soql)
assert len(actual) > 0
assert dict(actual[0])["RecordType"]["DeveloperName"] == "RecordTypeNormal"
...
def test_fetch_sf_data_by_soql_builder_relationship_object_depth_2(self):
"""
Cases:
- SOQLBuilderから生成したSOQLでSalesforceから参照関係を2回辿ったオブジェクト項目が取得できること
Arranges:
- Salesforceの以下のオブジェクトにレコードを作成する(手作業コード上では行わない)
- RelationShipTest__c
- RelationShipTest_Child__c
- LastFetchDatetimeのFromに2000年1月1日を指定する
- LastFetchDatetimeのToに2100年12月31日を指定する
Expects:
取得できたオブジェクトの1件をサンプリング確認しレコードタイプ名(DeveloperName)が含まれている
"""
execute_datetime = ExecuteDateTime()
last_fetch_datetime = LastFetchDatetime({
'last_fetch_datetime_from': '2000-01-01T00:00:00.000Z',
'last_fetch_datetime_to': '2100-12-31T23:59:59.000Z',
}, execute_datetime)
target_object = TargetObject({
'object_name': 'RelationShipTest_Child__c',
'columns': [
'Id',
'Name',
'RelationShipTest__r.RecordType.DeveloperName'
]
}, execute_datetime)
soql_builder = SOQLBuilder(target_object, last_fetch_datetime)
soql = soql_builder.create_fetch_soql()
sut = SalesforceApiClient()
actual = sut.fetch_sf_data(soql)
assert len(actual) > 0
assert dict(actual[0])["RelationshipTest__r"]["RecordType"]["DeveloperName"] == "RecordTypeSpecial"
def test_raise_create_instance_cause_auth_failed(self, monkeypatch):
"""
Cases:

View File

@ -6,6 +6,7 @@ from datetime import datetime, timezone
import boto3
import pytest
from src.controller import controller
from src.parser.json_parser import JsonParser
from src.system_var.constants import YYYYMMDDTHHMMSSTZ
@ -114,6 +115,10 @@ def test_walk_through(s3_test, s3_client, monkeypatch, caplog):
logger.info(f'##########################')
# Assertion
log_messages = caplog.messages
# ログの目視確認を容易にするため、ローカルファイルに書き出す。
with open('crm_datafetch_test_walk_through_diff.log', 'w', encoding='utf8') as f:
f.write('\n'.join(log_messages))
# ループ前のログ確認
assert 'I-CTRL-01 CRMデータ取得処理を開始します' in log_messages
assert 'I-CTRL-02 データ取得準備処理呼び出し' in log_messages
@ -170,6 +175,10 @@ def test_walk_through(s3_test, s3_client, monkeypatch, caplog):
logger.info(f'##########################')
# ログ再取得
log_messages_all = caplog.messages
# ログの目視確認を容易にするため、ローカルファイルに書き出す。
with open('crm_datafetch_test_walk_through_all.log', 'w', encoding='utf8') as f:
f.write('\n'.join(log_messages_all))
object_info_list_all = object_info_files[1]
# 開始ログなどはテスト済みなのでチェックを省く
for object_info in object_info_list_all['objects']:

View File

@ -0,0 +1,12 @@
tests/*
.coverage
.env
.env.example
.report/*
.vscode/*
.pytest_cache/*
*/__pychache__/*
Dockerfile
pytest.ini
README.md
*.sql

View File

@ -0,0 +1,9 @@
DB_HOST=************
DB_PORT=3306
DB_USERNAME=************
DB_PASSWORD=************
DB_SCHEMA=*****
DUMP_BACKUP_BUCKET=************
LOG_LEVEL=INFO

11
ecs/export-dbdump/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.vscode/settings.json
.env
my.cnf
# python
__pycache__
# python test
.pytest_cache
.coverage
.report/

16
ecs/export-dbdump/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// IntelliSense 使
//
// : https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(DEBUG) export dbdump",
"type": "python",
"request": "launch",
"program": "entrypoint.py",
"console": "integratedTerminal",
"justMyCode": true
}
]
}

View File

@ -0,0 +1,31 @@
{
"[python]": {
"editor.defaultFormatter": null,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
//
"python.defaultInterpreterPath": "<pythonインタプリターのパス>",
"python.linting.lintOnSave": true,
"python.linting.enabled": true,
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true,
"python.linting.flake8Args": [
"--max-line-length=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
}

View File

@ -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"]

16
ecs/export-dbdump/Pipfile Normal file
View File

@ -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

71
ecs/export-dbdump/Pipfile.lock generated Normal file
View File

@ -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"
}
}
}

View File

@ -0,0 +1,48 @@
# 【共通】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 <pyenvでインストールしたpythonバージョン>`
- この手順で出力される仮想環境のパスは、後述する VSCode の設定手順で使用するため、控えておく
- MySQL の環境構築
- Windows の場合、以下のリンクからダウンロードする
- <https://dev.mysql.com/downloads/installer/>
- Docker を利用する場合、「newsdwh-tools」リポジトリの MySQL 設定を使用すると便利
- 「crm-table-to-ddl」フォルダ内で以下のコマンドを実行すると
- `docker-compose up -d`
- Docker の構築手順は、[Docker のセットアップ手順](https://nds-tyo.backlog.com/alias/wiki/1754332)を参照のこと
- データを投入する
- 立ち上げたデータベースに「src05」スキーマを作成する
- [ローカル開発用データ](https://ndstokyo.sharepoint.com/:f:/r/sites/merck-new-dwh-team/Shared%20Documents/03.NewDWH%E6%A7%8B%E7%AF%89%E3%83%95%E3%82%A7%E3%83%BC%E3%82%BA3/02.%E9%96%8B%E7%99%BA/90.%E9%96%8B%E7%99%BA%E5%85%B1%E6%9C%89/%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E9%96%8B%E7%99%BA%E7%94%A8%E3%83%87%E3%83%BC%E3%82%BF?csf=1&web=1&e=VVcRUs)をダウンロードし、mysql コマンドを使用して復元する
- `mysql -h <ホスト名> -P <ポート> -u <ユーザー名> -p src05 < src05_dump.sql`
- 環境変数の設定
- 「.env.example」ファイルをコピーし、「.env」ファイルを作成する
- 環境変数を設定する。設定内容は PRJ メンバーより共有を受けてください
- VSCode の設定
- 「.vscode/recommended_settings.json」ファイルをコピーし、「settings.json」ファイルを作成する
- 「python.defaultInterpreterPath」を、Python の構築手順で作成した仮想環境のパスに変更する
## 実行
- VSCode 上で「F5」キーを押下すると、バッチ処理が起動する。
- 「entrypoint.py」が、バッチ処理のエントリーポイント。
- 実際の処理は、「src/jobctrl_dbdump.py」で行っている。
## フォルダ構成(工事中)

View File

@ -0,0 +1,10 @@
"""【共通】DBダンプ取得処理のエントリーポイント"""
from src import jobctrl_dbdump
if __name__ == '__main__':
try:
exit(jobctrl_dbdump.exec())
except Exception:
# エラーが起きても、正常系のコードで返す。
# エラーが起きた事実はbatch_process内でログを出す。
exit(0)

View File

@ -0,0 +1,3 @@
1
1
4

View File

View File

@ -0,0 +1,106 @@
"""DBダンプ取得"""
import datetime
import os
import subprocess
import textwrap
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ダンプ取得開始')
# 事前処理(共通処理としては空振りする)
_pre_exec()
# メイン処理
# 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%f')
dump_file_name = f'backup_rds_{environment.DB_SCHEMA}_{converted_value}.gz'
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 = [
'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 Exception(f'`mysqldump`実行時にエラーが発生しました。{"" if error is None else error.decode("utf-8")}')
_, error = gzip_process.communicate()
if gzip_process.returncode != 0:
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 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
except Exception as e:
logger.exception(f'DBダンプ取得中に想定外のエラーが発生しました :{e}')
return constants.BATCH_EXIT_CODE_SUCCESS
def _pre_exec():
"""
ダンプ復元 事前処理
共通機能としては事前処理を実装しない
事前処理が必要なダンプ復元処理を実装する場合当ロジックをコピーする
"""
pass
def _post_exec():
"""
ダンプ復元 事後処理
共通機能としては事後処理を実装しない
事後処理が必要なダンプ復元処理を実装する場合当ロジックをコピーする
"""
pass

View File

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

View File

@ -0,0 +1,5 @@
# バッチ正常終了コード
BATCH_EXIT_CODE_SUCCESS = 0
# ダンプバックアップフォルダー
DUMP_BACKUP_FOLDER = 'dump'

View File

@ -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))

View File

@ -0,0 +1,12 @@
tests/*
.coverage
.env
.env.example
.report/*
.vscode/*
.pytest_cache/*
*/__pychache__/*
Dockerfile
pytest.ini
README.md
*.sql

View File

@ -0,0 +1,26 @@
DB_HOST=************
DB_PORT=************
DB_USERNAME=************
DB_PASSWORD=************
DB_SCHEMA=src05
LOG_LEVEL=INFO
ULTMARC_DATA_BUCKET=****************
ULTMARC_DATA_FOLDER=recv
JSKULT_BACKUP_BUCKET=****************
ULTMARC_BACKUP_FOLDER=ultmarc
VJSK_BACKUP_FOLDER=vjsk
JSKULT_CONFIG_BUCKET=**********************
JSKULT_CONFIG_CALENDAR_FOLDER=jskult/calendar
JSKULT_CONFIG_CALENDAR_HOLIDAY_LIST_FILE_NAME=jskult_holiday_list.txt
VJSK_DATA_SEND_FOLDER=send
VJSK_DATA_RECEIVE_FOLDER=recv
VJSK_DATA_BUCKET=*************
JSKULT_CONFIG_CALENDAR_WHOLESALER_STOCK_FILE_NAME=jskult_wholesaler_stock_input_day_list.txt
JSKULT_CONFIG_CONVERT_FOLDER=jskult/convert
JSKULT_ULTMARC_HEX_CONVERT_CONFIG_FILE_NAME=ultmarc_hex_convert_config.json
# 連携データ抽出期間
SALES_LAUNDERING_EXTRACT_DATE_PERIOD=0
# 洗替対象テーブル名
SALES_LAUNDERING_TARGET_TABLE_NAME=src05.sales_lau
# 卸実績洗替で作成するデータの期間(年単位)
SALES_LAUNDERING_TARGET_YEAR_OFFSET=5

10
ecs/jskult-batch-daily/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.vscode/settings.json
.env
# python
__pycache__
# python test
.pytest_cache
.coverage
.report/

View File

@ -0,0 +1,16 @@
{
// IntelliSense 使
//
// : https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(DEBUG)jskult batch daily",
"type": "python",
"request": "launch",
"program": "entrypoint.py",
"console": "integratedTerminal",
"justMyCode": true
}
]
}

View File

@ -0,0 +1,31 @@
{
"[python]": {
"editor.defaultFormatter": null,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
//
"python.defaultInterpreterPath": "<pythonインタプリターのパス>",
"python.linting.lintOnSave": true,
"python.linting.enabled": true,
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true,
"python.linting.flake8Args": [
"--max-line-length=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/"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}

View File

@ -0,0 +1,20 @@
FROM python:3.9
ENV TZ="Asia/Tokyo"
WORKDIR /usr/src/app
COPY Pipfile Pipfile.lock ./
RUN \
apt update -y && \
# パッケージのセキュリティアップデートのみを適用するコマンド
apt install -y unattended-upgrades && \
unattended-upgrades && \
pip install --upgrade pip wheel setuptools && \
pip install pipenv --no-cache-dir && \
pipenv install --system --deploy && \
pip uninstall -y pipenv virtualenv-clone virtualenv
COPY src ./src
COPY entrypoint.py entrypoint.py
CMD ["python", "entrypoint.py"]

View File

@ -0,0 +1,29 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[scripts]
"test:ultmarc" = "pytest tests/batch/ultmarc/"
"test:ultmarc:cov" = "pytest --cov=src/batch/ultmarc/ --cov-branch --cov-report=term-missing tests/batch/ultmarc/"
"test:vjsk" = "pytest tests/batch/vjsk/"
"test:vjsk:cov" = "pytest --cov=src/batch/vjsk/ --cov-branch --cov-report=term-missing tests/batch/vjsk/"
[packages]
boto3 = "*"
PyMySQL = "*"
sqlalchemy = "*"
tenacity = "*"
[dev-packages]
autopep8 = "*"
flake8 = "*"
pytest = "*"
pytest-cov = "*"
boto3 = "*"
[requires]
python_version = "3.9"
[pipenv]
allow_prereleases = true

442
ecs/jskult-batch-daily/Pipfile.lock generated Normal file
View File

@ -0,0 +1,442 @@
{
"_meta": {
"hash": {
"sha256": "df8b09869c6ad0daff24cf808bac56f528d8ae5835fe70a50d58c2bed724e717"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.9"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"boto3": {
"hashes": [
"sha256:4b40bf2c8494647c9e88c180537dc9fc0c1047a9fffbb1e5b0da6596f1e59b7b",
"sha256:992e994c7e481a5d3259c699574882b79d631a46f7c369bea350b7ccb0651317"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.34.61"
},
"botocore": {
"hashes": [
"sha256:079f3288d38f97fd5656c25c44a94bea0e7090b938abfdeea463eaadb210c4a0",
"sha256:72df4af7e4e6392552c882d48c74e4be9bf7be4cd8d829711b312fbae13d7034"
],
"markers": "python_version >= '3.8'",
"version": "==1.34.61"
},
"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"
},
"jmespath": {
"hashes": [
"sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980",
"sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"
],
"markers": "python_version >= '3.7'",
"version": "==1.0.1"
},
"pymysql": {
"hashes": [
"sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96",
"sha256:8969ec6d763c856f7073c4c64662882675702efcb114b4bcbb955aea3a069fa7"
],
"markers": "python_version >= '3.7'",
"version": "==1.1.0"
},
"python-dateutil": {
"hashes": [
"sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
"sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.9.0.post0"
},
"s3transfer": {
"hashes": [
"sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e",
"sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b"
],
"markers": "python_version >= '3.8'",
"version": "==0.10.0"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"sqlalchemy": {
"hashes": [
"sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2",
"sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa",
"sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462",
"sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d",
"sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b",
"sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526",
"sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b",
"sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53",
"sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d",
"sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4",
"sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750",
"sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db",
"sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc",
"sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da",
"sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2",
"sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368",
"sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f",
"sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5",
"sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d",
"sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986",
"sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5",
"sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197",
"sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf",
"sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7",
"sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7",
"sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc",
"sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075",
"sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5",
"sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b",
"sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c",
"sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b",
"sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6",
"sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9",
"sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385",
"sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c",
"sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9",
"sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67",
"sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02",
"sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a",
"sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097",
"sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133",
"sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6",
"sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8",
"sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75",
"sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252",
"sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9",
"sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05",
"sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71",
"sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==2.0.28"
},
"tenacity": {
"hashes": [
"sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a",
"sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==8.2.3"
},
"typing-extensions": {
"hashes": [
"sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475",
"sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"
],
"markers": "python_version >= '3.8'",
"version": "==4.10.0"
},
"urllib3": {
"hashes": [
"sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07",
"sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"
],
"markers": "python_version < '3.10'",
"version": "==1.26.18"
}
},
"develop": {
"autopep8": {
"hashes": [
"sha256:067959ca4a07b24dbd5345efa8325f5f58da4298dab0dde0443d5ed765de80cb",
"sha256:2913064abd97b3419d1cc83ea71f042cb821f87e45b9c88cad5ad3c4ea87fe0c"
],
"index": "pypi",
"markers": "python_version >= '3.6'",
"version": "==2.0.4"
},
"boto3": {
"hashes": [
"sha256:4b40bf2c8494647c9e88c180537dc9fc0c1047a9fffbb1e5b0da6596f1e59b7b",
"sha256:992e994c7e481a5d3259c699574882b79d631a46f7c369bea350b7ccb0651317"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.34.61"
},
"botocore": {
"hashes": [
"sha256:079f3288d38f97fd5656c25c44a94bea0e7090b938abfdeea463eaadb210c4a0",
"sha256:72df4af7e4e6392552c882d48c74e4be9bf7be4cd8d829711b312fbae13d7034"
],
"markers": "python_version >= '3.8'",
"version": "==1.34.61"
},
"coverage": {
"extras": [
"toml"
],
"hashes": [
"sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa",
"sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003",
"sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f",
"sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c",
"sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e",
"sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0",
"sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9",
"sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52",
"sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e",
"sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454",
"sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0",
"sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079",
"sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352",
"sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f",
"sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30",
"sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe",
"sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113",
"sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765",
"sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc",
"sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e",
"sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501",
"sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7",
"sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2",
"sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f",
"sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4",
"sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524",
"sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c",
"sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51",
"sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840",
"sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6",
"sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee",
"sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e",
"sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45",
"sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba",
"sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d",
"sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3",
"sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10",
"sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e",
"sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb",
"sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9",
"sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a",
"sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47",
"sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1",
"sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3",
"sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914",
"sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328",
"sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6",
"sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d",
"sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0",
"sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94",
"sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc",
"sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"
],
"markers": "python_version >= '3.8'",
"version": "==7.4.3"
},
"exceptiongroup": {
"hashes": [
"sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14",
"sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"
],
"markers": "python_version < '3.11'",
"version": "==1.2.0"
},
"flake8": {
"hashes": [
"sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132",
"sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"
],
"index": "pypi",
"markers": "python_full_version >= '3.8.1'",
"version": "==7.0.0"
},
"iniconfig": {
"hashes": [
"sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3",
"sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"
],
"markers": "python_version >= '3.7'",
"version": "==2.0.0"
},
"jmespath": {
"hashes": [
"sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980",
"sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"
],
"markers": "python_version >= '3.7'",
"version": "==1.0.1"
},
"mccabe": {
"hashes": [
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
],
"markers": "python_version >= '3.6'",
"version": "==0.7.0"
},
"packaging": {
"hashes": [
"sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
"sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"
],
"markers": "python_version >= '3.7'",
"version": "==24.0"
},
"pluggy": {
"hashes": [
"sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981",
"sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"
],
"markers": "python_version >= '3.8'",
"version": "==1.4.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"
},
"pytest": {
"hashes": [
"sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7",
"sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==8.1.1"
},
"pytest-cov": {
"hashes": [
"sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6",
"sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==4.1.0"
},
"python-dateutil": {
"hashes": [
"sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
"sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.9.0.post0"
},
"s3transfer": {
"hashes": [
"sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e",
"sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b"
],
"markers": "python_version >= '3.8'",
"version": "==0.10.0"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"tomli": {
"hashes": [
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
],
"markers": "python_version < '3.11'",
"version": "==2.0.1"
},
"urllib3": {
"hashes": [
"sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07",
"sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"
],
"markers": "python_version < '3.10'",
"version": "==1.26.18"
}
}
}

View File

@ -0,0 +1,292 @@
# 実消化&アルトマーク 日次バッチ
## 概要
実消化&アルトマークの日次バッチ処理。
## 環境情報
- 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 <pyenvでインストールしたpythonバージョン>`
- この手順で出力される仮想環境のパスは、後述する VSCode の設定手順で使用するため、控えておく
- MySQL の環境構築
- Windows の場合、以下のリンクからダウンロードする
- <https://dev.mysql.com/downloads/installer/>
- Docker を利用する場合、「newsdwh-tools」リポジトリの MySQL 設定を使用すると便利
- 「crm-table-to-ddl」フォルダ内で以下のコマンドを実行すると
- `docker-compose up -d`
- Docker の構築手順は、[Docker のセットアップ手順](https://nds-tyo.backlog.com/alias/wiki/1754332)を参照のこと
- データを投入する
- 立ち上げたデータベースに「src05」スキーマを作成する
- [ローカル開発用データ](https://ndstokyo.sharepoint.com/:f:/r/sites/merck-new-dwh-team/Shared%20Documents/03.NewDWH%E6%A7%8B%E7%AF%89%E3%83%95%E3%82%A7%E3%83%BC%E3%82%BA3/02.%E9%96%8B%E7%99%BA/90.%E9%96%8B%E7%99%BA%E5%85%B1%E6%9C%89/%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E9%96%8B%E7%99%BA%E7%94%A8%E3%83%87%E3%83%BC%E3%82%BF?csf=1&web=1&e=VVcRUs)をダウンロードし、mysql コマンドを使用して復元する
- `mysql -h <ホスト名> -P <ポート> -u <ユーザー名> -p src05 < src05_dump.sql`
- 環境変数の設定
- 「.env.example」ファイルをコピーし、「.env」ファイルを作成する
- 環境変数を設定する。設定内容は PRJ メンバーより共有を受けてください
- VSCode の設定
- 「.vscode/recommended_settings.json」ファイルをコピーし、「settings.json」ファイルを作成する
- 「python.defaultInterpreterPath」を、Python の構築手順で作成した仮想環境のパスに変更する
## 実行
- VSCode 上で「F5」キーを押下すると、バッチ処理が起動する。
- 「entrypoint.py」が、バッチ処理のエントリーポイント。
- 実際の処理は、「src/jobctrl_daily.py」で行っている。
## 単体テスト(アルトマーク取込処理)
アルトマーク取込処理は、単体テストコードを使用してテスト自動化を行う
### テスト準備
- VSCodeで以下の拡張機能をインストールする
- Python
- Python Test Explorer for Visual Studio Code
- Test Explorer UI
- VSCode 上でショートカット「ctrl」+「shift」+「P」でコマンドパレットを開く
- コマンドパレットの検索窓に「Python」と入力し、「Python: テストを構成する」を押下する
- 現在のワークスペースを選び、「pytest」を選択する
- 「tests」フォルダを選択する
- バックグランドで、pytest モジュールのインストールが始まれば成功
### テスト用のサブコマンド一覧
- `pipenv run`のあとに、サブコマンドとしてユーザー定義スクリプトを実行できる
- `Pipfile`内の「scripts」セクションに宣言されている
| コマンド | 概要 |
| ---------------- | -------------------------------------------------------------------------------------------- |
| test:ultmarc | tests/batch/ultmarc フォルダ配下のユニットテストを実行する |
| test:ultmarc:cov | tests/batch/ultmarc フォルダ配下のユニットテストを実行し、テストカバレッジを取得する(C0, C1) |
### テスト共通関数の仕様
- tests/testing_utility.py内の共通関数の仕様について記載する
#### create_ultmarc_test_data_from_csv
- 引数
- file_path: str
- 戻り値
- src.batch.ultmarc.datfile.DatFileのインスタンス
- 処理概要
- CSVファイルから、アルトマークのインプットデータを作成する
- データフォーマットは以下
- 文字コード: UTF-8
- 改行コードLF
- ヘッダ: なし
- 値囲い: ダブルクォート
- アルトマークデータと文字コードを合わせるため、指定されたファイルを一時ディレクトリに、文字コード「cp932」で書き出してからテストデータとして読み込む
- テストデータそのものはUTF-8の文字コードで作成すること
### create_db_data_from_csv
- 引数
- file_path: str
- 戻り値
- テーブルのレコードに相当する辞書のリスト
- 処理概要
- CSVファイルから、アルトマークテーブルに相当するテストデータを作成する
- テストの初期データ、期待値データを作成するのに利用する
- データフォーマットは以下
- 文字コード: UTF-8
- 改行コードLF
- ヘッダ: なし
- 値囲い: ダブルクォート
- ファイル内の、以下の形式のデータを自動的に変換する
- `NULL`
- `None`に変換される
- `yyyy-mm-dd`もしくは、`yyyy/mm/dd`の文字
- Date型に変換される
- `yyyy-mm-dd hh:mm:ss`もしくは、`yyyy/mm/dd hh:mm:ss`の文字
- DateTime型に変換される
### create_insert_sql_with_parameter
- 引数
- table_name: str テーブル名
- column_names: list[str] カラム名のリスト
- test_data: list[str]: 値のリスト
- 戻り値
- INSERT文とバインドパラメータ辞書
- 処理概要
- 引数を使用して、`src.db.Database#execute`メソッドで実行可能な形でINSERT文、バインドパラメータを作成する
### create_delete_sql_with_parameter
- 引数
- table_name: str テーブル名
- column_names: list[str] カラム名のリスト
- test_data: list[str]: 値のリスト
- 戻り値
- DELETE文とバインドパラメータ辞書
- 処理概要
- 引数を使用して、`src.db.Database#execute`メソッドで実行可能な形でDELETE文、バインドパラメータを作成する
### create_ultmarc_table_mapper_sut
- 引数
- line: src.batch.ultmarc.datfile.DatFileLine アルトマークデータファイルの1行
- db: src.db.Database データベース操作クラス
- 戻り値
- マッパークラス
- 処理概要
- src.batch.ultmarc.utmp_tables.ultmarc_table_mapper_factory.UltmarcTableMapperFactoryを通じて、テスト対象のマッパークラスを生成して返す
### assert_table_results
- 引数
- actual_rows: list[dict] テスト結果の辞書リスト
- expect_rows: list[dict] 期待値の辞書リスト
- ignore_col_name: list 比較を無視するDBのカラム名. Default None.
- 戻り値
- なし
- 処理概要
- テスト結果データと期待値データを突き合わせ、期待値どおりとなっているかを確認する
- ignore_col_nameに指定したカラムは、呼び出し元のテストコード内で個別に突き合わせする
## 単体テスト(実消化データ取込処理)
実消化データは、単体テストコードを使用してテスト自動化を行う
### テスト準備
※単体テスト(アルトマーク取込処理)と同じ
### テスト用のサブコマンド一覧
- `pipenv run`のあとに、サブコマンドとしてユーザー定義スクリプトを実行できる
- `Pipfile`内の「scripts」セクションに宣言されている
| コマンド | 概要 |
| ---------------- | -------------------------------------------------------------------------------------------- |
| test:vjsk | tests/batch/vjsk フォルダ配下のユニットテストを実行する |
| test:vjsk:cov | tests/batch/vjsk フォルダ配下のユニットテストを実行し、テストカバレッジを取得する(C0, C1) |
### テスト共通関数の仕様
- tests/testing_vjsk_utility.py内の共通関数の仕様について記載する
#### create_vjsk_assertion_list
- 概要
- DB登録期待値リストを作成する
- Args:
- file_path (str): DB登録期待値ファイル(tsvファイル)のパス
- memo: ※DB登録期待値ファイルの前提
- memo: 受領データファイルと同じ
- memo: BOM付きtsv形式
- memo: 一行目はカラム名になっているヘッダ行
- Returns:
- List(dict) DB登録期待値辞書リスト
## フォルダ構成
```text
.
├── Pipfile -- Pythonモジュールの依存関係を管理するファイル
├── Dockerfile -- Dockerイメージを作成するためのファイル
├── Pipfile -- Pythonモジュールの依存関係を管理するファイル
├── Pipfile.lock -- Pythonモジュールの依存関係バージョン固定用ファイル
├── README.md -- 当ファイル
├── entrypoint.py -- バッチ処理のエントリーポイントになるpythonファイル
├── src -- ソースコードの保管場所
│ ├── aws -- AWS関連処理
│ │ └── s3.py -- S3クライアントとバケット処理
│ ├── batch -- バッチ処理関連ソース置き場
│ │ ├── batch_functions.py -- バッチ処理共通関数置き場
│ │ ├── datachange -- 実績洗替関連ソース置き場
│ │ │ └── emp_chg_inst_lau.py -- 施設担当者マスタ洗替
│ │ └── jissekiaraigae.py -- 実績洗替処理のエントリーポイント
│ │ └── ultmarc -- アルトマーク関連処理
│ │ ├── ultmarc_process.py -- アルトマーク関連処理のエントリーポイント
│ │ ├── datfile.py -- データファイル読込
│ │ └── utmp_tables -- アルトマークテーブルへの登録関連
│ │ ├── table_mapper -- テーブルへのデータマッピング処理
│ │ │ ├── concrete -- テーブルマッパーのマッピング処理を行う具象クラス(全テーブル分)
│ │ │ │ ├── com_alma_mapper.py
│ │ │ │ ├── ...
│ │ │ │ └── null_mapper.py -- テスト用、空振りするマッパークラス
│ │ │ └── ultmarc_table_mapper.py -- テーブルへの登録処理を行う抽象クラス
│ │ ├── tables -- アルトマークデータのDTOクラス(全テーブル分)
│ │ │ ├── com_alma.py
│ │ │ ├── ...
│ │ │ └── ultmarc_table.py -- アルトマークテーブルの抽象クラス
│ │ └── ultmarc_table_mapper_factory.py -- テーブルマッパー生成クラス
│ ├── db
│ │ └── database.py -- データベース操作共通処理
│ ├── error
│ │ └── exceptions.py -- カスタム例外
│ ├── jobctrl_daily.py -- 日次バッチ処理のエントリーポイント。「entrypoint.py」 から呼ばれる。
│ ├── logging
│ │ └── get_logger.py -- ログ出力の共通処理
│ ├── system_var
│ │ └── environment.py -- 環境変数
│ └── time
│ └── elapsed_time.py -- 実行時間計測用
└── tests -- ユニットテストのルートディレクト
├── batch
│ └── ultmarc -- アルトマーク関連のユニットテストを格納する
│ │ └── utmp_tables
│ │ └── table_mapper -- 以下、マッパークラス単位でフォルダを切る
│ │ └── com_alma
│ │ ├── test_com_alma_mapper.py -- テストコード本体
│ │ ├── com_alma_insert.csv -- S3に配置される想定のテストCSVデータ。ケースごとに用意する。
│ │ ...
│ │ ├── db_com_alma_before_update.csv -- テスト時に事前にDBに登録しておくデータ。CSVで用意する。
│ │ ...
│ │ ├── expect_com_alma_insert.csv -- テストの期待値データ。CSVで用意する。
│ │ ...
│ └─vjsk -- 実消化データ取込処理関連のユニットテストを格納する
│ │
│ ├─vjsk_file_check -- 受領ファイルチェック処理関連のユニットテストを格納する
│ │ ├─conftest.py -- テスト内で共通利用できるフィクスチャの宣言
│ │ └─test_vjsk_file_check.py -- テストクラス本体
│ │
│ └─vjsk_load -- 受領データ登録処理関連のユニットテストを格納する
│ │ conftest.py -- テスト内で共通利用できるフィクスチャの宣言
│ │ test_vjsk_load.py -- テストクラス本体
│ │
│ └─testdata -- テストモジュールが使用するテストデータを格納する
│ │ bio_slip_data_202304280000.tsv -- 正常ケースの単体確認用
│ │ ... -- *20230428* は新規4件の登録確認用
│ │ whs_mst_202304290000.tsv -- *20230429* は更新2件+追加新規2件の登録確認用
│ │
│ ├─NoData -- 正常ケースの単体確認用
│ │ bio_slip_data_nodatarecord.tsv -- ヘッダ行のみでデータが0件の動作確認用
│ │ ...
│ │ whs_mst_nodatarecord.tsv
│ │
│ ├─TestFormatErrorFile -- 異常ケースの単体確認用
│ │ bio_slip_data_formaterror.tsv -- 末尾行のタブ数が想定と異なる(ファイル欠落がある)ときの動作確認用
│ │ ...
│ │ whs_mst_formaterror.tsv
│ │
│ ├─TestImportFileToDb -- 正常ケースの単体確認用
│ │ bio_slip_data_202304270000.gz -- 対向元システムから送られてきた状態(gz圧縮)の受領データファイルの動作確認用
│ │ ...
│ │ whs_mst_202304270000.gz
│ │
│ └─UnzipError -- 異常ケースの単体確認用
│ bio_slip_data_202304270000.gz -- gz圧縮ファイルが解凍できないときの動作確認用
│ ...
│ whs_mst_202304270000.gz
├── conftest.py -- テスト内で共通利用できるフィクスチャを宣言する(執筆時点ではDBのみ)
├── testing_utility.py -- テストの共通関数
└── testing_vjsk_utility.py -- テストの共通関数(実消化データ取込処理関連)
```

View File

@ -0,0 +1,10 @@
"""実消化&アルトマーク 日次バッチのエントリーポイント"""
from src import jobctrl_daily
if __name__ == '__main__':
try:
exit(jobctrl_daily.exec())
except Exception:
# エラーが起きても、正常系のコードで返す。
# エラーが起きた事実はbatch_process内でログを出す。
exit(0)

View File

@ -0,0 +1,3 @@
[pytest]
log_format = %(levelname)s %(asctime)s %(message)s
log_date_format = %Y-%m-%d %H:%M:%S

View File

View File

@ -0,0 +1,185 @@
import gzip
import os
import os.path as path
import shutil
import tempfile
import boto3
from src.system_var import environment
class S3Client:
__s3_client = boto3.client('s3')
_bucket_name: str
def list_objects(self, bucket_name: str, folder_name: str):
response = self.__s3_client.list_objects_v2(Bucket=bucket_name, Prefix=folder_name)
if response['KeyCount'] == 0:
return []
contents = response['Contents']
# 末尾がスラッシュで終わるものはフォルダとみなしてスキップする
objects = [{'filename': content['Key'], 'size': content['Size']}
for content in contents if not content['Key'].endswith('/')]
return objects
def copy(self, src_bucket: str, src_key: str, dest_bucket: str, dest_key: str) -> None:
copy_source = {'Bucket': src_bucket, 'Key': src_key}
self.__s3_client.copy(copy_source, dest_bucket, dest_key)
return
def download_file(self, bucket_name: str, file_key: str, file):
self.__s3_client.download_fileobj(
Bucket=bucket_name,
Key=file_key,
Fileobj=file
)
return
def upload_file(self, local_file_path: str, bucket_name: str, file_key: str):
self.__s3_client.upload_file(
local_file_path,
Bucket=bucket_name,
Key=file_key
)
def delete_file(self, bucket_name: str, file_key: str):
self.__s3_client.delete_object(
Bucket=bucket_name,
Key=file_key
)
class S3Bucket():
_s3_client = S3Client()
_bucket_name: str = None
class UltmarcBucket(S3Bucket):
_bucket_name = environment.ULTMARC_DATA_BUCKET
_folder = environment.ULTMARC_DATA_FOLDER
def list_dat_file(self):
return self._s3_client.list_objects(self._bucket_name, self._folder)
def download_dat_file(self, dat_filename: str):
# 一時ファイルとして保存する
temporary_dir = tempfile.mkdtemp()
temporary_file_path = path.join(temporary_dir, f'{dat_filename.replace(f"{self._folder}/", "")}')
with open(temporary_file_path, mode='wb') as f:
self._s3_client.download_file(self._bucket_name, dat_filename, f)
f.seek(0)
return temporary_file_path
def backup_dat_file(self, dat_file_key: str, datetime_key: str):
# バックアップバケットにコピー
ultmarc_backup_bucket = UltmarcBackupBucket()
backup_key = f'{ultmarc_backup_bucket._folder}/{datetime_key}/{dat_file_key.replace(f"{self._folder}/", "")}'
self._s3_client.copy(self._bucket_name, dat_file_key, ultmarc_backup_bucket._bucket_name, backup_key)
# コピー元のファイルを削除
self._s3_client.delete_file(self._bucket_name, dat_file_key)
class ConfigBucket(S3Bucket):
_bucket_name = environment.JSKULT_CONFIG_BUCKET
def download_holiday_list(self):
# 一時ファイルとして保存する
temporary_dir = tempfile.mkdtemp()
temporary_file_path = path.join(temporary_dir, environment.JSKULT_CONFIG_CALENDAR_HOLIDAY_LIST_FILE_NAME)
holiday_list_key = f'{environment.JSKULT_CONFIG_CALENDAR_FOLDER}/{environment.JSKULT_CONFIG_CALENDAR_HOLIDAY_LIST_FILE_NAME}'
with open(temporary_file_path, mode='wb') as f:
self._s3_client.download_file(self._bucket_name, holiday_list_key, f)
f.seek(0)
return temporary_file_path
def download_wholesaler_stock_input_day_list(self):
# 一時ファイルとして保存する
temporary_dir = tempfile.mkdtemp()
temporary_file_path = path.join(temporary_dir, environment.JSKULT_CONFIG_CALENDAR_WHOLESALER_STOCK_FILE_NAME)
wholesaler_stock_input_day_list_key = f'{environment.JSKULT_CONFIG_CALENDAR_FOLDER}/{environment.JSKULT_CONFIG_CALENDAR_WHOLESALER_STOCK_FILE_NAME}'
with open(temporary_file_path, mode='wb') as f:
self._s3_client.download_file(self._bucket_name, wholesaler_stock_input_day_list_key, f)
f.seek(0)
return temporary_file_path
def download_ultmarc_hex_convert_config(self):
# 一時ファイルとして保存する
temporary_dir = tempfile.mkdtemp()
temporary_file_path = path.join(temporary_dir, environment.JSKULT_ULTMARC_HEX_CONVERT_CONFIG_FILE_NAME)
hex_convert_config_key = f'{environment.JSKULT_CONFIG_CONVERT_FOLDER}/{environment.JSKULT_ULTMARC_HEX_CONVERT_CONFIG_FILE_NAME}'
with open(temporary_file_path, mode='wb') as f:
self._s3_client.download_file(self._bucket_name, hex_convert_config_key, f)
f.seek(0)
return temporary_file_path
class JskUltBackupBucket(S3Bucket):
_bucket_name = environment.JSKULT_BACKUP_BUCKET
class UltmarcBackupBucket(JskUltBackupBucket):
_folder = environment.ULTMARC_BACKUP_FOLDER
class VjskBackupBucket(JskUltBackupBucket):
_folder = environment.VJSK_BACKUP_FOLDER
class VjskReceiveBucket(S3Bucket):
_bucket_name = environment.VJSK_DATA_BUCKET
_recv_folder = environment.VJSK_DATA_RECEIVE_FOLDER
_s3_file_list = None
def get_s3_file_list(self):
self._s3_file_list = self._s3_client.list_objects(self._bucket_name, self._recv_folder)
return self._s3_file_list
def download_data_file(self, data_filename: str):
temporary_dir = tempfile.mkdtemp()
temporary_file_path = path.join(temporary_dir, f'{data_filename.replace(f"{self._recv_folder}/", "")}')
with open(temporary_file_path, mode='wb') as f:
self._s3_client.download_file(self._bucket_name, data_filename, f)
f.seek(0)
return temporary_file_path
def unzip_data_file(self, filename: str):
temp_dir = os.path.dirname(filename)
decompress_filename = os.path.basename(filename).replace('.gz', '')
decompress_file_path = os.path.join(temp_dir, decompress_filename)
with gzip.open(filename, 'rb') as gz:
with open(decompress_file_path, 'wb') as decompressed_file:
shutil.copyfileobj(gz, decompressed_file)
ret = [decompress_file_path]
return ret
def backup_dat_file(self, target_files: list, datetime_key: str):
jskult_backup_bucket = VjskBackupBucket()
for target_file in target_files:
backup_from_file_path = target_file.get("filename")
backup_to_filename = backup_from_file_path.replace(f"{self._recv_folder}/", "")
backup_key = f'{jskult_backup_bucket._folder}/{datetime_key}/{backup_to_filename}'
self._s3_client.copy(self._bucket_name, backup_from_file_path,
jskult_backup_bucket._bucket_name, backup_key)
self._s3_client.delete_file(self._bucket_name, backup_from_file_path)
class VjskSendBucket(S3Bucket):
_bucket_name = environment.VJSK_DATA_BUCKET
_send_folder = environment.VJSK_DATA_SEND_FOLDER
def upload_inst_pharm_csv_file(self, vjsk_create_csv: str, csv_file_path: str):
# S3バケットにファイルを移動
csv_file_name = f'{self._send_folder}/{vjsk_create_csv}'
s3_client = S3Client()
s3_client.upload_file(csv_file_path, self._bucket_name, csv_file_name)
return
def backup_inst_pharm_csv_file(self, dat_file_key: str, datetime_key: str):
# バックアップバケットにコピー
vjsk_backup_bucket = VjskBackupBucket()
dat_key = f'{self._send_folder}/{dat_file_key}'
backup_key = f'{vjsk_backup_bucket._folder}/{self._send_folder}/{datetime_key}/{dat_file_key.replace(f"{self._send_folder}/", "")}'
self._s3_client.copy(self._bucket_name, dat_key, vjsk_backup_bucket._bucket_name, backup_key)

View File

@ -0,0 +1,111 @@
"""バッチ処理の共通関数"""
import logging
import textwrap
from datetime import datetime
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, str]:
"""日付テーブルから、以下を取得して返す。
- 日次バッチ処理中フラグ
- dump取得状況区分
- 処理日YYYY/MM/DD
Raises:
BatchOperationException: 日付テーブルが取得できないとき何らかのエラーが発生したとき
Returns:
tuple[str, str]: [0]日次バッチ処理中フラグdump取得状況区分
"""
db = Database.get_instance()
sql = 'SELECT bch_actf, dump_sts_kbn, src05.get_syor_date() AS syor_date 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']
syor_date = hdke_tbl_record['syor_date']
# 処理日を文字列に変換する
syor_date_str = datetime.strftime(syor_date, '%Y/%m/%d')
return batch_processing_flag, dump_status_kbn, syor_date_str
def update_batch_processing_flag_in_processing() -> None:
"""バッチ処理中フラグを処理中に更新する
Raises:
BatchOperationException: DB操作の何らかのエラー
"""
db = Database.get_instance()
sql = """\
UPDATE src05.hdke_tbl
SET
bch_actf = :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_batch_process_complete() -> None:
"""バッチ処理を完了とし、処理日、バッチ処理中フラグ、dump処理状態区分を更新する
Raises:
BatchOperationException: DB操作の何らかのエラー
"""
db = Database.get_instance()
sql = """\
UPDATE src05.hdke_tbl
SET
bch_actf = :batch_complete,
dump_sts_kbn = :dump_unprocessed,
syor_date = DATE_FORMAT((src05.get_syor_date() + interval 1 day), '%Y%m%d'), -- +1
updater = CURRENT_USER(),
update_date = NOW()
"""
try:
db.connect()
db.to_jst()
db.execute(sql, {
'batch_complete': constants.BATCH_ACTF_BATCH_UNPROCESSED,
'dump_unprocessed': constants.DUMP_STATUS_KBN_UNPROCESSED
})
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}')

View File

@ -0,0 +1,398 @@
from src.batch.batch_functions import logging_sql
from src.batch.common.batch_context import BatchContext
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger
from src.time.elapsed_time import ElapsedTime
batch_context = BatchContext.get_instance()
logger = get_logger('生物由来卸販売ロット分解')
def exec():
"""生物由来卸販売ロット分解"""
logger.debug('生物由来卸販売ロット分解処理開始')
# 営業日ではない場合、処理をスキップする
if batch_context.is_not_business_day:
logger.info('営業日ではないため、生物由来卸販売ロット分解処理をスキップします。')
return
db = Database.get_instance()
try:
db.connect()
db.begin()
# 生物由来ロット分解データの未確定データを削除する
_delete_not_confirm_data_in_bio_sales_lot(db)
# 生物由来ロット分解データを作成する
_insert_bio_sales_lot(db)
# 生物由来ロット分解データの不要レコードを削除する
_delete_empty_lot_record(db)
# MDB変換マスタビュー生物由来ロット分解処理用、メルク施設マスタから施設情報を生物由来ロット分解データにセットする
_set_inst_info_from_mdb_or_mst_inst(db)
# V製品マスタから製品情報を生物由来ロット分解データにセットする
_set_prd_info_from_v_prd_mst(db)
# 製造ロット管理番号マスタから有効期限を生物由来ロット分解データにセットする
_set_expr_dt_from_lot_num_mst(db)
db.commit()
logger.debug('生物由来卸販売ロット分解処理終了')
return
except Exception as e:
db.rollback()
raise BatchOperationException(e)
finally:
db.disconnect()
def _delete_not_confirm_data_in_bio_sales_lot(db: Database):
logger.debug('生物由来ロット分解データの未確定データ削除開始')
try:
elapsed_time = ElapsedTime()
sql = """
DELETE lot FROM src05.bio_sales_lot AS lot
INNER JOIN src05.bio_sales AS bio
ON bio.slip_mgt_num = lot.slip_mgt_num
AND DATE(bio.dwh_upd_dt) >= src05.get_syor_date()
"""
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'Query OK, {res.rowcount} rows affected ({elapsed_time.of})')
except Exception as e:
logger.debug('生物由来ロット分解データの未確定データ削除に失敗')
raise e
logger.debug('生物由来ロット分解データの未確定データ削除に成功')
def _insert_bio_sales_lot(db: Database):
logger.debug('生物由来ロット分解データの作成開始')
try:
elapsed_time = ElapsedTime()
sql = """
INSERT INTO src05.bio_sales_lot
SELECT
bio.slip_mgt_num AS slip_mgt_num,
conv.conv_cd AS conv_cd,
bio.rec_whs_cd AS rec_whs_cd,
bio.rec_whs_sub_cd AS rec_whs_sub_cd,
bio.rec_whs_org_cd AS rec_whs_org_cd,
bio.rec_comm_cd AS rec_comm_cd,
bio.rec_tran_kbn AS rec_tran_kbn,
bio.rev_hsdnymd_srk AS rev_hsdnymd_srk,
bio.rec_urag_num AS rec_urag_num,
bio.rec_comm_name AS rec_comm_name,
bio.rec_nonyu_fcl_name AS rec_nonyu_fcl_name,
bio.rec_nonyu_fcl_addr AS rec_nonyu_fcl_addr,
-- 3レコードに分解する
CASE conv.conv_cd
WHEN 1 THEN bio.rec_lot_num1
WHEN 2 THEN bio.rec_lot_num2
WHEN 3 THEN bio.rec_lot_num3
END AS rec_lot_num,
bio.rec_ymd AS rec_ymd,
bio.v_tran_cd AS v_tran_cd,
bio.tran_kbn_name AS tran_kbn_name,
bio.whs_org_cd AS whs_org_cd,
bio.v_whsorg_cd AS v_whsorg_cd,
bio.whs_org_name AS whs_org_name,
bio.v_whs_cd AS v_whs_cd,
bio.whs_name AS whs_name,
bio.nonyu_fcl_cd AS nonyu_fcl_cd,
bio.v_inst_cd AS v_inst_cd,
bio.v_inst_kn AS v_inst_kn,
bio.v_inst_name AS v_inst_name,
bio.v_inst_addr AS v_inst_addr,
bio.comm_cd AS comm_cd,
bio.product_name AS product_name,
bio.whs_rep_comm_name AS whs_rep_comm_name,
bio.whs_rep_nonyu_fcl_name AS whs_rep_nonyu_fcl_name,
bio.whs_rep_nonyu_fcl_addr AS whs_rep_nonyu_fcl_addr,
/* 製品名と製品コードは後ほどV製品マスタからセットする */
-- 製品名
NULL AS mkr_inf_1,
-- 製品コード
NULL AS mkr_cd,
-- 数量
-- Veeva取引区分の先頭が2の場合マイナス表示にする
CASE conv.conv_cd
WHEN 1 THEN
CASE
WHEN (LEFT(bio.v_tran_cd, 1) = 2 AND bio.qty1 >= 1) THEN -bio.qty1
ELSE bio.qty1
END
WHEN 2 THEN
CASE
WHEN (LEFT(bio.v_tran_cd, 1) = 2 AND bio.qty2 >= 1) THEN -bio.qty2
ELSE bio.qty2
END
WHEN 3 THEN
CASE
WHEN (LEFT(bio.v_tran_cd, 1) = 2 AND bio.qty3 >= 1) THEN -bio.qty3
ELSE bio.qty3
END
END AS qty,
bio.slip_org_kbn AS slip_org_kbn,
bio.bef_slip_mgt_num AS bef_slip_mgt_num,
CASE conv.conv_cd
WHEN 1 THEN bio.err_flg11
WHEN 2 THEN bio.err_flg12
WHEN 3 THEN bio.err_flg13
END AS lot_no_err_flg,
CASE bio.err_flg20
WHEN 'M' THEN '*'
ELSE NULL
END AS iko_flg,
CASE bio.rec_sts_kbn
WHEN '0' THEN bio.rec_sts_kbn
WHEN '1' THEN
CASE conv.conv_cd
WHEN 1 THEN bio.err_flg11
WHEN 2 THEN bio.err_flg12
WHEN 3 THEN bio.err_flg13
END
END AS rec_sts_kbn,
CASE
WHEN bio.bef_slip_mgt_num IS NOT NULL THEN bio.ins_dt
ELSE NULL
END AS ins_dt,
CASE
WHEN bio.bef_slip_mgt_num IS NOT NULL THEN bio.ins_usr
ELSE NULL
END AS ins_usr,
bio.dwh_upd_dt AS dwh_upd_dt,
/* 施設情報は後ほどセットする */
-- 施設コード
NULL AS inst_cd,
-- 正式施設名漢字
NULL AS inst_name_form,
-- 施設住所
NULL AS address,
-- 施設電話番号
NULL AS tel_num,
CASE conv.conv_cd
WHEN 1 THEN
CASE bio.err_flg11
WHEN '0' THEN '正常'
WHEN '1' THEN 'ロットエラー'
WHEN '2' THEN 'ロットエラー'
WHEN '3' THEN 'エラー(解消済)'
WHEN '4' THEN 'ロット不明'
WHEN '5' THEN 'エラー(解消済)'
WHEN '6' THEN 'ロット不明'
WHEN '7' THEN '除外'
WHEN '8' THEN '除外'
WHEN 'Z' THEN '除外'
END
WHEN 2 THEN
CASE bio.err_flg12
WHEN '0' THEN '正常'
WHEN '1' THEN 'ロットエラー'
WHEN '2' THEN 'ロットエラー'
WHEN '3' THEN 'エラー(解消済)'
WHEN '4' THEN 'ロット不明'
WHEN '5' THEN 'エラー(解消済)'
WHEN '6' THEN 'ロット不明'
WHEN '7' THEN '除外'
WHEN '8' THEN '除外'
WHEN 'Z' THEN '除外'
END
WHEN 3 THEN
CASE bio.err_flg13
WHEN '0' THEN '正常'
WHEN '1' THEN 'ロットエラー'
WHEN '2' THEN 'ロットエラー'
WHEN '3' THEN 'エラー(解消済)'
WHEN '4' THEN 'ロット不明'
WHEN '5' THEN 'エラー(解消済)'
WHEN '6' THEN 'ロット不明'
WHEN '7' THEN '除外'
WHEN '8' THEN '除外'
WHEN 'Z' THEN '除外'
END
END AS data_kbn,
CASE bio.slip_org_kbn
WHEN 'J' THEN 'JD-NET'
WHEN 'N' THEN 'NHI'
WHEN 'H' THEN '手入力'
END AS data_kind,
CASE conv.conv_cd
WHEN 1 THEN
CASE bio.err_flg11
WHEN '0' THEN '正常'
WHEN '1' THEN 'ロットエラー'
WHEN '2' THEN '日付エラー'
WHEN '3' THEN 'ロットエラー(解消済)'
WHEN '4' THEN 'ロットエラー(調査不能)'
WHEN '5' THEN '日付エラー(解消済)'
WHEN '6' THEN '日付エラー(調査不能)'
WHEN '7' THEN '除外(卸都合)'
WHEN '8' THEN '除外(再送信)'
WHEN 'Z' THEN '過去データ'
END
WHEN 2 THEN
CASE bio.err_flg12
WHEN '0' THEN '正常'
WHEN '1' THEN 'ロットエラー'
WHEN '2' THEN '日付エラー'
WHEN '3' THEN 'ロットエラー(解消済)'
WHEN '4' THEN 'ロットエラー(調査不能)'
WHEN '5' THEN '日付エラー(解消済)'
WHEN '6' THEN '日付エラー(調査不能)'
WHEN '7' THEN '除外(卸都合)'
WHEN '8' THEN '除外(再送信)'
WHEN 'Z' THEN '過去データ'
END
WHEN 3 THEN
CASE bio.err_flg13
WHEN '0' THEN '正常'
WHEN '1' THEN 'ロットエラー'
WHEN '2' THEN '日付エラー'
WHEN '3' THEN 'ロットエラー(解消済)'
WHEN '4' THEN 'ロットエラー(調査不能)'
WHEN '5' THEN '日付エラー(解消済)'
WHEN '6' THEN '日付エラー(調査不能)'
WHEN '7' THEN '除外(卸都合)'
WHEN '8' THEN '除外(再送信)'
WHEN 'Z' THEN '過去データ'
END
END AS err_dtl_kind,
NULL AS expr_dt
FROM
src05.bio_sales bio
-- 生物由来変換マスタ
CROSS JOIN src05.bio_conv conv
WHERE
bio.err_flg1 = '0'
AND bio.err_flg2 = '0'
AND bio.err_flg3 = '0'
AND bio.err_flg4 = '0'
AND bio.err_flg5 = '0'
AND bio.err_flg6 = '0'
AND bio.err_flg7 = '0'
AND bio.err_flg8 = '0'
AND bio.err_flg9 = '0'
AND bio.err_flg10 = '0'
AND bio.rec_sts_kbn <> '99'
AND DATE(bio.dwh_upd_dt) >= src05.get_syor_date()
"""
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'Query OK, {res.rowcount} rows affected ({elapsed_time.of})')
except Exception as e:
logger.debug('生物由来ロット分解データの作成に失敗')
raise e
logger.debug('生物由来ロット分解データの作成に成功')
def _delete_empty_lot_record(db: Database):
logger.debug('生物由来ロット分解データの製造番号が空のレコードを削除開始')
try:
elapsed_time = ElapsedTime()
sql = """
DELETE FROM src05.bio_sales_lot lot
WHERE
-- 空白15桁のデータはロット情報が空とみなして削除する
lot.rec_lot_num = REPEAT(' ', 15) OR lot.rec_lot_num IS NULL
"""
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'Query OK, {res.rowcount} rows affected ({elapsed_time.of})')
except Exception as e:
logger.debug('生物由来ロット分解データの製造番号が空のレコードを削除に失敗')
raise e
logger.debug('生物由来ロット分解データの製造番号が空のレコードを削除に成功')
def _set_inst_info_from_mdb_or_mst_inst(db: Database):
logger.debug('MDB変換マスタビュー生物由来ロット分解処理用、メルク施設マスタから施設情報を生物由来ロット分解データにセット開始')
try:
elapsed_time = ElapsedTime()
sql = """
UPDATE
src05.bio_sales_lot bio
LEFT OUTER JOIN internal05.view_mdb_cnv_mst mdb
ON bio.v_inst_cd = mdb.hco_vid_v
LEFT OUTER JOIN src05.mst_inst inst
ON bio.v_inst_cd = inst.inst_cd
SET
-- 施設コード
bio.inst_cd = (
CASE
WHEN mdb.mdb_cd IS NOT NULL THEN mdb.mdb_cd
ELSE bio.v_inst_cd
END
),
-- 正式施設名漢字
bio.inst_name_form = (
CASE
WHEN mdb.mdb_cd IS NOT NULL THEN mdb.inst_name_form
ELSE inst.inst_name_form
END
),
-- 施設住所
bio.address = (
CASE
WHEN mdb.mdb_cd IS NOT NULL THEN mdb.address
ELSE inst.address
END
),
-- 施設電話番号
bio.tel_num = (
CASE
WHEN mdb.mdb_cd IS NOT NULL THEN mdb.tel_num
ELSE inst.tel_num
END
)
"""
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'Query OK, {res.rowcount} rows affected ({elapsed_time.of})')
except Exception as e:
logger.debug('MDB変換マスタビュー生物由来ロット分解処理用、メルク施設マスタから施設情報を生物由来ロット分解データにセットに失敗')
raise e
logger.debug('MDB変換マスタビュー生物由来ロット分解処理用、メルク施設マスタから施設情報を生物由来ロット分解データにセットに成功')
def _set_prd_info_from_v_prd_mst(db: Database):
logger.debug('V製品マスタから製品情報を生物由来ロット分解データにセット開始')
try:
elapsed_time = ElapsedTime()
sql = """
UPDATE
src05.bio_sales_lot bio
LEFT OUTER JOIN src05.phm_prd_mst_v prd
ON bio.comm_cd = prd.prd_cd
AND STR_TO_DATE(bio.rev_hsdnymd_srk,'%Y%m%d') BETWEEN prd.start_date AND prd.end_date
AND prd.rec_sts_kbn <> '9'
SET
bio.mkr_inf_1 = prd.mkr_inf_1,
bio.mkr_cd = prd.mkr_cd
"""
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'Query OK, {res.rowcount} rows affected ({elapsed_time.of})')
except Exception as e:
logger.debug('V製品マスタから製品情報を生物由来ロット分解データにセットに失敗')
raise e
logger.debug('V製品マスタから製品情報を生物由来ロット分解データにセットに成功')
def _set_expr_dt_from_lot_num_mst(db: Database):
# 製造ロット管理番号マスタから有効期限をセット
logger.debug('製造ロット管理番号マスタから有効期限をセット開始')
try:
elapsed_time = ElapsedTime()
sql = """
UPDATE
src05.bio_sales_lot bio
LEFT OUTER JOIN src05.lot_num_mst lot
ON bio.mkr_cd = lot.ser_num
AND bio.rec_lot_num = lot.lot_num
SET
bio.expr_dt = lot.expr_dt
"""
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'Query OK, {res.rowcount} rows affected ({elapsed_time.of})')
except Exception as e:
logger.debug('製造ロット管理番号マスタから有効期限をセットに失敗')
raise e
logger.debug('製造ロット管理番号マスタから有効期限をセットに成功')

View File

@ -0,0 +1,48 @@
class BatchContext:
__instance = None
__syor_date: str # 処理日(yyyy/mm/dd形式)
__is_not_business_day: bool # 日次バッチ起動日フラグ
__is_ultmarc_imported: bool # アルトマーク取込実施済フラグ
__is_vjsk_stock_import_day: bool # 卸在庫データ取込対象フラグ
def __init__(self) -> None:
self.__is_not_business_day = False
self.__is_ultmarc_imported = False
@classmethod
def get_instance(cls):
if cls.__instance is None:
cls.__instance = cls()
return cls.__instance
@property
def syor_date(self):
return self.__syor_date
@syor_date.setter
def syor_date(self, syor_date_str: str):
self.__syor_date = syor_date_str
@property
def is_not_business_day(self):
return self.__is_not_business_day
@is_not_business_day.setter
def is_not_business_day(self, flag: bool):
self.__is_not_business_day = flag
@property
def is_ultmarc_imported(self):
return self.__is_ultmarc_imported
@is_ultmarc_imported.setter
def is_ultmarc_imported(self, flag: bool):
self.__is_ultmarc_imported = flag
@property
def is_vjsk_stock_import_day(self):
return self.__is_vjsk_stock_import_day
@is_vjsk_stock_import_day.setter
def is_vjsk_stock_import_day(self, flag: bool):
self.__is_vjsk_stock_import_day = flag

View File

@ -0,0 +1,32 @@
from src.system_var import constants
class CalendarFile:
"""カレンダーファイル"""
__calendar_file_lines: list[str]
def __init__(self, calendar_file_path):
with open(calendar_file_path) as f:
self.__calendar_file_lines: list[str] = f.readlines()
def compare_date(self, date_str: str) -> bool:
"""与えられた日付がカレンダーファイル内に含まれているかどうか
カレンダーファイル内の日付はyyyy/mm/ddで書かれている前提
コメント#)が含まれている行は無視される
Args:
date_str (str): yyyy/mm/dd文字列
Returns:
bool: 含まれていればTrue
"""
for calendar_date in self.__calendar_file_lines:
# コメント行が含まれている場合はスキップ
if constants.CALENDAR_COMMENT_SYMBOL in calendar_date:
continue
if date_str in calendar_date:
return True
return False

View File

@ -0,0 +1,167 @@
from src.batch.batch_functions import logging_sql
from src.batch.common.batch_context import BatchContext
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger
from src.time.elapsed_time import ElapsedTime
batch_context = BatchContext.get_instance()
logger = get_logger('DCF施設統合マスタ作成')
def exec():
"""DCF施設統合マスタ作成"""
# アルトマーク取込が行われていない場合は処理をスキップする
if not batch_context.is_ultmarc_imported:
logger.info('アルトマーク取込が行われていないため、DCF施設統合マスタ作成処理をスキップします。')
return
db = Database.get_instance()
try:
db.connect()
db.to_jst()
db.begin()
logger.debug('DCF施設統合マスタ作成処理開始')
# COM施設からDCF施設統合マスタに登録
(is_add_dcf_inst_merge, duplication_inst_records) = _insert_dcf_inst_merge_from_com_inst(db)
db.commit()
# DCF施設統合マスタ追加のログを出力する
if is_add_dcf_inst_merge:
logger.info('[NOTICE]DCF施設統合マスタが追加されました。')
_output_add_dcf_inst_merge_log(duplication_inst_records)
logger.debug('DCF施設統合マスタ作成処理終了')
except Exception as e:
db.rollback()
raise BatchOperationException(e)
finally:
db.disconnect()
def _insert_dcf_inst_merge_from_com_inst(db: Database) -> tuple[bool, list[dict]]:
# com_instからdcf_inst_mergeにinsert
# 重複コードがあるデータを取得する(処理日~システム日付を対象)
try:
sql = """
SELECT
ci.dcf_dsf_inst_cd AS dcf_dsf_inst_cd,
ci.form_inst_name_kanji AS form_inst_name_kanji,
ci.dup_opp_cd AS dup_opp_cd,
(
SELECT
dupci.form_inst_name_kanji
FROM
src05.com_inst AS dupci
WHERE
dupci.dcf_dsf_inst_cd = ci.dup_opp_cd
) AS dup_inst_name_kanji,
DATE_FORMAT((src05.get_syor_date() + INTERVAL 1 MONTH), '%Y%m') AS sys_update_date
FROM
src05.com_inst AS ci
WHERE
CHAR_LENGTH(ci.dup_opp_cd) > 0
AND ci.delete_sche_reason_cd = 'D'
AND ci.abolish_ymd IS NULL
AND ci.sys_update_date BETWEEN src05.get_syor_date() AND SYSDATE()
AND NOT EXISTS (
SELECT
dim.dcf_inst_cd
FROM
src05.dcf_inst_merge AS dim
WHERE
dim.dcf_inst_cd = ci.dcf_dsf_inst_cd
)
AND (
EXISTS (
SELECT
eci.inst_cd
FROM
src05.emp_chg_inst AS eci
WHERE
eci.inst_cd = ci.dcf_dsf_inst_cd
)
OR (
SELECT
sl.inst_cd
FROM
src05.sales_lau AS sl
WHERE
sl.inst_cd = ci.dcf_dsf_inst_cd
)
)
"""
duplication_inst_records = db.execute_select(sql)
logging_sql(logger, sql)
logger.info('施設統合対象データの取得に成功')
except Exception as e:
logger.debug('施設統合対象データの取得に失敗')
raise e
if len(duplication_inst_records) == 0:
logger.info('施設統合対象データはありません')
return (False, None)
# DCF施設統合マスタ追加
values_clauses = []
params = {}
for clauses_no, row in enumerate(duplication_inst_records, start=1):
dcf_inst_cd_arr = f'dcf_inst_cd{clauses_no}'
dup_opp_cd_arr = f'dup_opp_cd{clauses_no}'
tekiyo_month_arr = f'tekiyo_month{clauses_no}'
values_clause = f'(:{dcf_inst_cd_arr}, :{dup_opp_cd_arr}, :{tekiyo_month_arr}, "Y", CURRENT_USER(), SYSDATE(), CURRENT_USER(), SYSDATE())'
values_clauses.append(values_clause)
params[dcf_inst_cd_arr] = row['dcf_dsf_inst_cd']
params[dup_opp_cd_arr] = row['dup_opp_cd']
params[tekiyo_month_arr] = row['sys_update_date']
insert_sql = f"""
INSERT INTO
src05.dcf_inst_merge (
dcf_inst_cd,
dup_opp_cd,
tekiyo_month,
enabled_flg,
creater,
create_date,
updater,
update_date
) VALUES
{','.join(values_clauses)}
"""
try:
elapsed_time = ElapsedTime()
res = db.execute(insert_sql, params)
logging_sql(logger, insert_sql)
logger.info(f'COM施設からDCF施設統合マスタに登録成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug('COM施設からDCF施設統合マスタの登録に失敗')
raise e
return (True, duplication_inst_records)
def _output_add_dcf_inst_merge_log(duplication_inst_records: list[dict]):
sys_update_date = duplication_inst_records[0]['sys_update_date']
set_year_month = '{set_year}{set_month}'.format(
set_year=sys_update_date[0:4],
set_month=sys_update_date[-2:]
)
add_dct_inst_merge = 'DCF施設コード {dcf_dsf_inst_cd} {form_inst_name_kanji},  重複時相手先コード {dup_opp_cd} {dup_inst_name_kanji}'
add_dct_inst_merge_list = []
for row in duplication_inst_records:
add_dct_inst_merge_list.append(add_dct_inst_merge.format(**row))
add_dct_inst_merge_list = '\n'.join(add_dct_inst_merge_list)
# 顧客報告用にログ出力
logger.info(
f"""DCF施設統合マスタが追加されました。
**********************************************************
適用月度 {set_year_month}
**********************************************************
{add_dct_inst_merge_list}
**********************************************************
合計 {len(duplication_inst_records)}"""
)
return

View File

@ -0,0 +1,648 @@
from datetime import datetime, timedelta
from src.batch.batch_functions import logging_sql
from src.batch.common.batch_context import BatchContext
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger
from src.time.elapsed_time import ElapsedTime
batch_context = BatchContext.get_instance()
logger = get_logger('DCF施設統合マスタ日次更新')
def exec():
db = Database.get_instance()
try:
db.connect()
db.to_jst()
db.begin()
logger.debug('DCF施設統合マスタ日次更新処理開始')
# DCF施設統合マスタ移行先コードのセット(無効フラグが『0(有効)』)
enabled_dst_inst_merge_records = _set_enabled_dct_inst_merge(db)
# DCF施設統合マスタ移行先コードのセット(無効フラグが『1(無効)』)
_set_disabled_dct_inst_merge(db)
# DCF施設統合マスタに無効フラグが『0(有効)』データが存在する場合
if len(enabled_dst_inst_merge_records) > 0:
_add_emp_chg_inst(db, enabled_dst_inst_merge_records)
_add_ult_ident_presc(db, enabled_dst_inst_merge_records)
db.commit()
logger.debug('DCF施設統合マスタ日次更新処理終了')
except Exception as e:
db.rollback()
raise BatchOperationException(e)
finally:
db.disconnect()
def _set_enabled_dct_inst_merge(db: Database) -> list[dict]:
# データ取得無効フラグが『0(有効)』)
enabled_dst_inst_merge_records = _select_dct_inst_merge(db, 0)
# 移行先DCF施設コードの更新無効フラグが『0(有効)』)
if _update_dcf_inst_merge(db, 0) > 0:
# DCF施設統合マスタの過去分の洗い替え
for row in enabled_dst_inst_merge_records:
_update_dcf_inst_cd_new(db, row['dup_opp_cd'], row['dcf_inst_cd'], '')
return enabled_dst_inst_merge_records
def _set_disabled_dct_inst_merge(db: Database):
# データ取得無効フラグが『1(無効)』)
disabled_dst_inst_merge_records = _select_dct_inst_merge(db, 1)
# 移行先DCF施設コードの更新無効フラグが『1(無効)』)
if _update_dcf_inst_merge(db, 1) > 0:
# DCF施設統合マスタの過去分の洗い替え
for row in disabled_dst_inst_merge_records:
_update_dcf_inst_cd_new(db, row['dcf_inst_cd'], row['dup_opp_cd'], '戻し')
def _select_ult_ident_presc_dcf_inst_cd(db: Database, dcf_inst_cd: str) -> list[dict]:
# 納入先処方元マスタから、DCF施設コードに対応したレコードの取得
try:
sql = """
SELECT
ta_cd,
ult_ident_cd,
ratio
FROM
src05.ult_ident_presc
WHERE
presc_cd = :dcf_inst_cd
AND (SELECT ht.syor_date FROM src05.hdke_tbl AS ht) < end_date
"""
params = {'dcf_inst_cd': dcf_inst_cd}
ult_ident_presc_ta_cd_records = db.execute_select(sql, params)
logging_sql(logger, sql)
logger.info('納入先処方元マスタからDCF施設コードに対応したレコードの取得に成功')
except Exception as e:
logger.debug('納入先処方元マスタからDCF施設コードに対応したレコードの取得に失敗')
raise e
return ult_ident_presc_ta_cd_records
def _add_ult_ident_presc(db: Database, enabled_dst_inst_merge_records: list[dict]):
# 納入先処方元マスタの追加
logger.info('納入先処方元マスタの登録 開始')
for data_inst_cnt, enabled_merge_record in enumerate(enabled_dst_inst_merge_records, start=1):
tekiyo_month_first_day = _get_first_day_of_month(enabled_merge_record['tekiyo_month'])
ult_ident_presc_source_records = _select_ult_ident_presc_dcf_inst_cd(db, enabled_merge_record['dcf_inst_cd'])
for ult_ident_presc_source_record in ult_ident_presc_source_records:
ult_ident_presc_records = _select_ult_ident_presc(db,
enabled_merge_record['dcf_inst_cd'],
enabled_merge_record['dup_opp_cd'],
ult_ident_presc_source_record)
for data_cnt, ult_ident_presc_row in enumerate(ult_ident_presc_records, start=1):
logger.info(f'{data_inst_cnt}件目の移行施設の{data_cnt}レコード目処理 開始')
# 処方元コード=重複時相手先コードが発生した場合
if ult_ident_presc_row['opp_count'] > 0:
continue
start_date = _str_to_date_time(ult_ident_presc_row['start_date'])
set_start_date = start_date \
if start_date > tekiyo_month_first_day else tekiyo_month_first_day
set_start_date = _date_time_to_str(set_start_date)
is_exists_duplicate_key = False
if _count_duplicate_ult_ident_presc(db, set_start_date, ult_ident_presc_row) > 0:
_delete_ult_ident_presc(db, set_start_date, ult_ident_presc_row,
'納入先処方元マスタの重複予定データの削除')
is_exists_duplicate_key = True
else:
logger.info('納入先処方元マスタの重複予定データなし')
_insert_ult_ident_presc(db, set_start_date, enabled_merge_record['dup_opp_cd'], ult_ident_presc_row)
# 重複予定データが存在しない、且つ、適用終了日 ≧ 適用開始日の場合
if not is_exists_duplicate_key and _str_to_date_time(ult_ident_presc_row['end_date']) >= start_date:
last_end_date = tekiyo_month_first_day - timedelta(days=1)
# 適用終了日を、DCF施設統合マスタの適用月度の前月末日で更新
_update_ult_ident_presc_end_date(db, _date_time_to_str(last_end_date), ult_ident_presc_row)
logger.info('納入先処方元マスタの登録 終了')
def _select_emp_chg_inst_ta_cd(db: Database, dcf_inst_cd: str) -> list[dict]:
# 従業員担当施設マスタから、DCF施設コードに対応した領域コードの取得
try:
sql = """
SELECT
ta_cd
FROM
src05.emp_chg_inst
WHERE
inst_cd = :dcf_inst_cd
AND enabled_flg = 'Y'
AND (SELECT ht.syor_date FROM src05.hdke_tbl AS ht) < end_date
"""
params = {'dcf_inst_cd': dcf_inst_cd}
emp_chg_inst_ta_cd_records = db.execute_select(sql, params)
logging_sql(logger, sql)
logger.info('従業員担当施設マスタから領域コードの取得に成功')
except Exception as e:
logger.debug('従業員担当施設マスタから領域コードの取得に失敗')
raise e
return emp_chg_inst_ta_cd_records
def _add_emp_chg_inst(db: Database, enabled_dst_inst_merge_records: list[dict]):
# 従業員担当施設マスタの登録
logger.info('従業員担当施設マスタの登録 開始')
for enabled_merge_record in enabled_dst_inst_merge_records:
tekiyo_month_first_day = _get_first_day_of_month(enabled_merge_record['tekiyo_month'])
emp_chg_inst_ta_cd_records = _select_emp_chg_inst_ta_cd(db, enabled_merge_record['dcf_inst_cd'])
for emp_chg_inst_ta_cd_record in emp_chg_inst_ta_cd_records:
emp_chg_inst_records = _select_emp_chg_inst(db, enabled_merge_record['dcf_inst_cd'], enabled_merge_record['dup_opp_cd'],
emp_chg_inst_ta_cd_record['ta_cd'])
for emp_chg_inst_row in emp_chg_inst_records:
# 重複時相手先コードが存在したかのチェック
if emp_chg_inst_row['opp_count'] > 0:
continue
start_date = _str_to_date_time(emp_chg_inst_row['start_date'])
set_start_date = start_date \
if start_date > tekiyo_month_first_day else tekiyo_month_first_day
_insert_emp_chg_inst(db, enabled_merge_record['dup_opp_cd'], _date_time_to_str(set_start_date),
emp_chg_inst_row)
# 適用開始日 < DCF施設統合マスタの適用月度の1日の場合
if start_date < tekiyo_month_first_day:
# DCF施設統合マスタの適用月度の前月末日で、適用終了日を更新する
last_end_date = tekiyo_month_first_day - timedelta(days=1)
_update_emp_chg_inst_end_date(db, enabled_merge_record['dcf_inst_cd'], _date_time_to_str(last_end_date),
emp_chg_inst_row)
continue
# 適用開始日 ≧ DCF施設統合マスタの適用月度の1日の場合、N(論理削除レコード)に設定する
_update_emp_chg_inst_disabled(db, enabled_merge_record['dcf_inst_cd'], emp_chg_inst_row['ta_cd'],
emp_chg_inst_row['start_date'])
logger.info('従業員担当施設マスタの登録 終了')
def _delete_ult_ident_presc(db: Database, start_date: str, ult_ident_presc_row: dict,
log_message: str):
# ult_ident_prescのDelete
try:
elapsed_time = ElapsedTime()
sql = """
DELETE FROM
src05.ult_ident_presc
WHERE
ta_cd = :ta_cd
AND ult_ident_cd = :ult_ident_cd
AND ratio = :ratio
AND start_date = :start_date
"""
params = {
'ta_cd': ult_ident_presc_row['ta_cd'],
'ult_ident_cd': ult_ident_presc_row['ult_ident_cd'],
'ratio': ult_ident_presc_row['ratio'],
'start_date': start_date
}
res = db.execute(sql, params)
logging_sql(logger, sql)
logger.info(f'{log_message} 成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug(f'{log_message} 失敗')
raise e
def _update_emp_chg_inst_disabled(db: Database, dcf_inst_cd: str, ta_cd: str, start_date: str):
# emp_chg_instをUPDATE
try:
elapsed_time = ElapsedTime()
sql = """
UPDATE
src05.emp_chg_inst
SET
enabled_flg = 'N',
updater = CURRENT_USER(),
update_date = SYSDATE()
WHERE
inst_cd = :dcf_inst_cd
AND ta_cd = :ta_cd
AND start_date = :start_date
"""
params = {'dcf_inst_cd': dcf_inst_cd, 'ta_cd': ta_cd, 'start_date': start_date}
res = db.execute(sql, params)
logging_sql(logger, sql)
logger.info(f'従業員担当施設マスタのYorNフラグ更新に成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug('従業員担当施設マスタのYorNフラグ更新に失敗')
raise e
def _update_emp_chg_inst_end_date(db: Database, dcf_inst_cd: str, last_end_date: str,
emp_chg_inst_row: dict):
# emp_chg_instをUPDATE
try:
elapsed_time = ElapsedTime()
sql = """
UPDATE
src05.emp_chg_inst
SET end_date = :end_date,
updater = CURRENT_USER(),
update_date = SYSDATE()
WHERE
inst_cd = :dcf_inst_cd
AND ta_cd = :ta_cd
AND emp_cd = :emp_cd
AND bu_cd = :bu_cd
AND start_date = :start_date
"""
params = {
'end_date': last_end_date,
'dcf_inst_cd': dcf_inst_cd,
'ta_cd': emp_chg_inst_row['ta_cd'],
'emp_cd': emp_chg_inst_row['emp_cd'],
'bu_cd': emp_chg_inst_row['bu_cd'],
'start_date': emp_chg_inst_row['start_date']
}
res = db.execute(sql, params)
logging_sql(logger, sql)
logger.info(f'従業員担当施設マスタの適用終了日更新 成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug('従業員担当施設マスタの適用終了日更新 失敗')
raise e
def _insert_emp_chg_inst(db: Database, dup_opp_cd: str, set_start_date: str,
emp_chg_inst_row: dict):
# emp_chg_instにINSERT
try:
elapsed_time = ElapsedTime()
sql = """
INSERT INTO
src05.emp_chg_inst(
inst_cd,
ta_cd,
emp_cd,
bu_cd,
start_date,
end_date,
main_chg_flg,
enabled_flg,
creater,
create_date,
updater,
update_date
)
VALUES(
:dup_opp_cd,
:ta_cd,
:emp_cd,
:bu_cd,
:start_date,
:end_date,
:main_chg_flg,
'Y',
CURRENT_USER(),
SYSDATE(),
CURRENT_USER(),
SYSDATE()
)
"""
params = {
'dup_opp_cd': dup_opp_cd,
'ta_cd': emp_chg_inst_row['ta_cd'],
'emp_cd': emp_chg_inst_row['emp_cd'],
'bu_cd': emp_chg_inst_row['bu_cd'],
'start_date': set_start_date,
'end_date': emp_chg_inst_row['end_date'],
'main_chg_flg': None
if emp_chg_inst_row['main_chg_flg'] is None else emp_chg_inst_row['main_chg_flg']
}
res = db.execute(sql, params)
logging_sql(logger, sql)
logger.info(f'従業員担当施設マスタの追加に成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug('従業員担当施設マスタの追加に失敗')
raise e
def _select_dct_inst_merge(db: Database, muko_flg: int) -> list[dict]:
# dcf_inst_mergeからSELECT
# 無効フラグがOFFのときは、移行先DCF施設コードが設定されてないデータを抽出する。
# ONのときは、移行先DCF施設コードが設定されているデータを抽出する。
try:
sql = """
SELECT
dim.dcf_inst_cd,
dim.dup_opp_cd,
dim.tekiyo_month
FROM
src05.dcf_inst_merge AS dim
INNER JOIN
src05.hdke_tbl AS ht
ON dim.tekiyo_month = DATE_FORMAT(ht.syor_date, '%Y%m')
WHERE
dim.muko_flg = :muko_flg
AND dim.enabled_flg = 'Y'
AND dim.dcf_inst_cd_new IS {not_null}NULL
""".format(
not_null='' if muko_flg == 0 else 'NOT '
)
params = {
'muko_flg': muko_flg
}
dst_inst_merge_records = db.execute_select(sql, params)
logging_sql(logger, sql)
logger.info('DCF施設統合マスタの取得に成功')
except Exception as e:
logger.debug('DCF施設統合マスタの取得に失敗')
raise e
return dst_inst_merge_records
def _update_dcf_inst_merge(db: Database, muko_flg: int) -> int:
# dcf_inst_mergeをUPDATE
# 無効フラグがOFFのときは、
# 移行先DCF施設コードが設定されていないデータを抽出し、移行先DCF施設コードに重複時相手先コードを上書きする
# 無効フラグがONのときは、
# 移行先DCF施設コードが設定されているデータを抽出し、移行先DCF施設コードにNULLを上書きする。
try:
elapsed_time = ElapsedTime()
log_message = '更新しました' if muko_flg == 0 else '無効データに戻しました'
sql = """
UPDATE
src05.dcf_inst_merge AS updim
INNER JOIN(
SELECT
dim.dcf_inst_cd AS base_dcf_inst_cd,
dim.dup_opp_cd AS base_dup_opp_cd,
dim.tekiyo_month AS base_tekiyo_month,
dim.muko_flg AS base_muko_flg,
dim.enabled_flg AS base_enabled_flg
FROM
src05.dcf_inst_merge AS dim
INNER JOIN
src05.hdke_tbl AS ht
ON dim.tekiyo_month = DATE_FORMAT(ht.syor_date, '%Y%m')
WHERE
dim.muko_flg = :muko_flg
AND dim.enabled_flg ='Y'
AND dim.dcf_inst_cd_new IS {not_null}NULL
) AS bf_dim
SET
updim.dcf_inst_cd_new = {column},
updim.updater = CURRENT_USER(),
updim.update_date = SYSDATE()
WHERE
updim.dcf_inst_cd = base_dcf_inst_cd
AND updim.dup_opp_cd = base_dup_opp_cd
AND updim.tekiyo_month = base_tekiyo_month
AND updim.muko_flg = base_muko_flg
AND updim.enabled_flg = base_enabled_flg
""".format(
not_null='' if muko_flg == 0 else 'NOT ',
column='base_dup_opp_cd' if muko_flg == 0 else 'NULL'
)
params = {
'muko_flg': muko_flg
}
res = db.execute(sql, params)
logging_sql(logger, sql)
logger.info(f'DCF施設統合マスタの有効データを{log_message} 成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug(f'DCF施設統合マスタの有効データを{log_message} 失敗')
raise e
return res.rowcount
def _update_dcf_inst_cd_new(db: Database, dcf_inst_cd_new_after: str, dcf_inst_cd_new_before: str, log_message: str):
# dcf_inst_mergeをUPDATE
try:
elapsed_time = ElapsedTime()
sql = """
UPDATE
src05.dcf_inst_merge
SET
dcf_inst_cd_new = :dcf_inst_cd_new_after,
updater = CURRENT_USER(),
update_date = SYSDATE()
WHERE
dcf_inst_cd_new = :dcf_inst_cd_new_before
AND enabled_flg = 'Y'
AND muko_flg = 0
"""
params = {
'dcf_inst_cd_new_after': dcf_inst_cd_new_after,
'dcf_inst_cd_new_before': dcf_inst_cd_new_before
}
res = db.execute(sql, params)
logging_sql(logger, sql)
logger.info(f'移行先DCF施設コードの{log_message}更新に成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug(f'移行先DCF施設コードの{log_message}更新に失敗')
raise e
def _update_ult_ident_presc_end_date(db: Database, last_end_date: str, ult_ident_presc_row: dict):
# ult_ident_presc_endをUPDATE
try:
elapsed_time = ElapsedTime()
sql = """
UPDATE
src05.ult_ident_presc
SET end_date = :end_date,
updater = CURRENT_USER(),
update_date = SYSDATE()
WHERE
ta_cd = :ta_cd
AND ult_ident_cd = :ult_ident_cd
AND ratio = :ratio
AND start_date = :start_date
"""
params = {
'end_date': last_end_date,
'ta_cd': ult_ident_presc_row['ta_cd'],
'ult_ident_cd': ult_ident_presc_row['ult_ident_cd'],
'ratio': ult_ident_presc_row['ratio'],
'start_date': ult_ident_presc_row['start_date']
}
res = db.execute(sql, params)
logging_sql(logger, sql)
logger.info(f'終了日 > 開始月のため適用終了日を更新 成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug('終了日 > 開始月のため適用終了日を更新 失敗')
raise e
def _insert_ult_ident_presc(db: Database, set_Start_Date: str, dup_opp_cd: str,
ult_ident_presc_row: dict):
# ult_ident_prescにINSERT
try:
elapsed_time = ElapsedTime()
sql = """
INSERT INTO
src05.ult_ident_presc(
ta_cd,
ult_ident_cd,
ratio,
start_date,
presc_cd,
end_date,
creater,
create_date,
update_date,
updater
)
VALUES(
:ta_cd,
:ult_ident_cd,
:ratio,
:start_date,
:presc_cd,
:end_date,
CURRENT_USER(),
SYSDATE(),
SYSDATE(),
CURRENT_USER()
)
"""
params = {
'ta_cd': ult_ident_presc_row['ta_cd'],
'ult_ident_cd': ult_ident_presc_row['ult_ident_cd'],
'ratio': ult_ident_presc_row['ratio'],
'start_date': set_Start_Date,
'presc_cd': dup_opp_cd,
'end_date': ult_ident_presc_row['end_date']
}
res = db.execute(sql, params)
logging_sql(logger, sql)
logger.info(f'納入先処方元マスタに追加 成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug('納入先処方元マスタに追加 失敗')
raise e
def _select_emp_chg_inst(db: Database, dcf_inst_cd: str, dup_opp_cd: str, ta_cd: str) -> list[dict]:
# emp_chg_instからSELECT
try:
sql = """
SELECT
eci.inst_cd,
eci.ta_cd,
eci.emp_cd,
eci.bu_cd,
eci.start_date,
eci.end_date,
eci.main_chg_flg,
eci.enabled_flg,
(
SELECT
COUNT(eciopp.inst_cd)
FROM
src05.emp_chg_inst AS eciopp
WHERE
eciopp.inst_cd = :dup_opp_cd
AND eciopp.ta_cd = :ta_cd
) AS opp_count
FROM
src05.emp_chg_inst AS eci
WHERE
eci.inst_cd = :dcf_inst_cd
AND eci.ta_cd = :ta_cd
AND eci.enabled_flg = 'Y'
AND (SELECT ht.syor_date FROM src05.hdke_tbl AS ht) < eci.end_date
"""
params = {'dcf_inst_cd': dcf_inst_cd, 'dup_opp_cd': dup_opp_cd, 'ta_cd': ta_cd}
emp_chg_inst_records = db.execute_select(sql, params)
logging_sql(logger, sql)
logger.info('従業員担当施設マスタの取得 成功')
except Exception as e:
logger.debug('従業員担当施設マスタの取得 失敗')
raise e
return emp_chg_inst_records
def _select_ult_ident_presc(db: Database, dcf_inst_cd: str, dup_opp_cd: str,
ult_ident_presc_row: dict) -> list[dict]:
# ult_ident_prescからSELECT
try:
sql = """
SELECT
uip.ta_cd,
uip.ult_ident_cd,
uip.ratio,
uip.start_date,
uip.end_date,
(
SELECT
COUNT(uipopp.ta_cd)
FROM
src05.ult_ident_presc AS uipopp
WHERE
uipopp.presc_cd = :dup_opp_cd
AND uipopp.ta_cd = :ta_cd
AND uipopp.ult_ident_cd = :ult_ident_cd
AND uipopp.ratio = :ratio
) AS opp_count
FROM
src05.ult_ident_presc AS uip
WHERE
uip.presc_cd = :dcf_inst_cd
AND uip.ta_cd = :ta_cd
AND (SELECT ht.syor_date FROM src05.hdke_tbl AS ht) < uip.end_date
"""
params = {
'dcf_inst_cd': dcf_inst_cd,
'dup_opp_cd': dup_opp_cd,
'ta_cd': ult_ident_presc_row['ta_cd'],
'ult_ident_cd': ult_ident_presc_row['ult_ident_cd'],
'ratio': ult_ident_presc_row['ratio']
}
ult_ident_presc_records = db.execute_select(sql, params)
logging_sql(logger, sql)
logger.info('納入先処方元マスタの取得 成功')
except Exception as e:
logger.debug('納入先処方元マスタの取得 失敗')
raise e
return ult_ident_presc_records
def _count_duplicate_ult_ident_presc(db: Database, set_start_date: str,
ult_ident_presc_row: dict) -> int:
# ult_ident_prescの重複時相手先コードの件数取得
try:
sql = """
SELECT
COUNT(ta_cd) AS cnt
FROM
src05.ult_ident_presc
WHERE
ta_cd = :ta_cd
AND ult_ident_cd = :ult_ident_cd
AND ratio = :ratio
AND start_date = :start_date
"""
params = {
'ta_cd': ult_ident_presc_row['ta_cd'],
'ult_ident_cd': ult_ident_presc_row['ult_ident_cd'],
'ratio': ult_ident_presc_row['ratio'],
'start_date': set_start_date
}
result = db.execute_select(sql, params)
logging_sql(logger, sql)
logger.info('納入先処方元マスタの重複予定データの存在チェック 成功')
except Exception as e:
logger.debug('納入先処方元マスタの重複予定データの存在チェック 失敗')
raise e
return result[0]['cnt']
def _get_first_day_of_month(year_month: str) -> datetime:
# year_monthの初日の日付を日付型に変換し返却する
return datetime.strptime(year_month + '01', '%Y%m%d')
def _str_to_date_time(str_date_time: str) -> datetime:
# str_date_timeを日付型に変換して返却する
return datetime.strptime(str_date_time, '%Y%m%d')
def _date_time_to_str(date_time: datetime) -> str:
# date_timeをYmd型に変換して返却する
return date_time.strftime('%Y%m%d')

View File

@ -0,0 +1,32 @@
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger
logger = get_logger('洗替用マスタ作成')
def exec():
db = Database.get_instance()
try:
db.connect()
logger.debug('処理開始')
db.to_jst()
call_v_inst_merge_t_create(db)
call_inst_merge_t_create(db)
logger.debug('処理終了')
except Exception as e:
raise BatchOperationException(e)
finally:
db.disconnect()
def call_v_inst_merge_t_create(db: Database):
db.execute('CALL src05.v_inst_merge_t_create()')
return
def call_inst_merge_t_create(db: Database):
db.execute('CALL src05.inst_merge_t_create()')
return

View File

@ -0,0 +1,178 @@
from src.batch.batch_functions import logging_sql
from src.batch.common.batch_context import BatchContext
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger
from src.time.elapsed_time import ElapsedTime
logger = get_logger('施設担当者マスタ洗替')
batch_context = BatchContext.get_instance()
def exec():
db = Database.get_instance()
try:
db.connect()
db.to_jst()
logger.debug('##########################')
logger.debug('START Changing Employee in charge of institution PGM.')
# `emp_chg_inst_lau`をTruncate
_truncate_emp_chg_inst_lau(db)
# emp_chg_inst から、`emp_chg_inst_lau`へInsert
_insert_into_emp_chg_inst_lau_from_emp_chg_inst(db)
# v_inst_merge_tから、emp_chg_inst_lauをUpdate
_update_emp_chg_inst_lau_from_v_inst_merge_t(db)
# inst_merge_tから、emp_chg_inst_lauをUpdate
_update_emp_chg_inst_lau_from_inst_merge_t(db)
logger.debug('##########################')
logger.debug('End All Processing PGM.')
except Exception as e:
raise BatchOperationException(e)
finally:
db.disconnect()
def _truncate_emp_chg_inst_lau(db: Database):
logger.debug("##########################")
try:
db.execute("TRUNCATE TABLE src05.emp_chg_inst_lau")
except Exception as e:
logger.debug("Error! Truncate Table `emp_chg_inst_lau` is Failed!!!")
raise e
logger.debug("Table `emp_chg_inst_lau` was truncated!")
return
def _insert_into_emp_chg_inst_lau_from_emp_chg_inst(db: Database):
logger.debug("##########################")
try:
elapsed_time = ElapsedTime()
sql = """
INSERT INTO
src05.emp_chg_inst_lau
SELECT
inst_cd,
ta_cd,emp_cd,
bu_cd,
start_date,
end_date,
main_chg_flg,
enabled_flg,
creater,
create_date,
updater,
update_date,
NULL -- lua_ope_dt
FROM
src05.emp_chg_inst
WHERE
enabled_flg = 'Y'
"""
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'Query OK, {res.rowcount} rows affected ({elapsed_time.of})')
except Exception as e:
logger.debug("Error! Insert into `emp_chg_inst_lau` from `emp_chg_inst` was failed!!!")
raise e
logger.debug("Success! Insert into `emp_chg_inst_lau` from `emp_chg_inst` was inserted!")
return
def _update_emp_chg_inst_lau_from_v_inst_merge_t(db: Database):
# v_inst_merge_tの元となるvop_hco_merge_vはデータが作られないため、この洗い替え処理は基本空振りする
logger.debug("##########################")
try:
select_result = db.execute_select(
"""
SELECT
COUNT(v_inst_cd) AS row_count
FROM
internal05.v_inst_merge_t
"""
)
except Exception as e:
logger.debug("Error! `v_inst_merge_t` Table count error!")
raise e
count = [row for row in select_result][0]['row_count']
if count == 0:
logger.info('v_inst_merge_t Table Data is not exists!')
return
logger.info('v_inst_merge_t Table Data is exists!')
# v_inst_merge_t から、emp_chg_inst_lauをUpdateします
logger.debug("##########################")
logger.debug("#### UPDATE DATA #########")
logger.debug("##########################")
try:
elapsed_time = ElapsedTime()
update_sql = """
UPDATE
src05.emp_chg_inst_lau el, internal05.v_inst_merge_t vimt
SET
el.inst_cd = vimt.v_inst_cd_merge,
el.lua_ope_dt = SYSDATE()
WHERE
el.inst_cd = vimt.v_inst_cd
"""
res = db.execute(update_sql)
logging_sql(logger, update_sql)
logger.info(f'Query OK, {res.rowcount} rows affected ({elapsed_time.of})')
except Exception as e:
logger.debug("emp_chg_inst_lau.v_inst_cd could not set!")
raise e
logger.debug("emp_chg_inst_lau.v_inst_cd was set!")
return
def _update_emp_chg_inst_lau_from_inst_merge_t(db: Database):
# inst_merge_tから、emp_chg_inst_lauをUpdate
# Get count from INST_MERGE_T
logger.debug("##########################")
try:
select_result = db.execute_select(
"""
SELECT
COUNT(dcf_dsf_inst_cd) AS row_count
FROM
internal05.inst_merge_t
"""
)
except Exception as e:
logger.debug("Error! Getting Count of internal05.inst_merge_t was failed!")
raise e
count = [row for row in select_result][0]['row_count']
if count == 0:
logger.info('inst_merge_t Table Data is not exists!')
return
logger.debug('inst_merge_t Table Data is exists!')
# inst_merge_tから、emp_chg_inst_lauをUpdate
logger.debug("##########################")
logger.debug("#### UPDATE DATA #########")
logger.debug("##########################")
try:
elapsed_time = ElapsedTime()
update_sql = """
UPDATE
src05.emp_chg_inst_lau el, internal05.inst_merge_t imt
SET
el.inst_cd = imt.dup_opp_cd,
el.lua_ope_dt = SYSDATE()
WHERE
el.inst_cd = imt.dcf_dsf_inst_cd
"""
res = db.execute(update_sql)
logging_sql(logger, update_sql)
logger.info(f'Query OK, {res.rowcount} rows affected ({elapsed_time.of})')
except Exception as e:
logger.debug("emp_chg_inst_lau.v_inst_cd could not set!")
raise e
logger.debug("emp_chg_inst_lau.v_inst_cd was set!")
return

View File

@ -0,0 +1,269 @@
from src.batch.batch_functions import logging_sql
from src.batch.common.batch_context import BatchContext
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger
from src.time.elapsed_time import ElapsedTime
batch_context = BatchContext.get_instance()
logger = get_logger('メルク施設マスタ作成')
def exec():
"""メルク施設マスタ作成"""
# 営業日ではないかつ、アルトマーク取込が行われていない場合は処理をスキップする
if batch_context.is_not_business_day is True and batch_context.is_ultmarc_imported is not True:
logger.info('営業日ではない、かつ、アルトマーク取込が行われていないため、メルク施設マスタ作成処理をスキップします。')
return
db = Database.get_instance()
try:
db.connect()
logger.debug('メルク施設マスタ作成処理開始')
# mst_instをTruncate
_truncate_mst_inst(db)
# fcl_mst_vから、mst_instへInsert
_insert_mst_inst_from_fcl_mst_v(db)
# com_instから、mst_instへInsert
_insert_mst_inst_from_com_inst(db)
logger.debug('メルク施設マスタ作成処理終了')
except Exception as e:
raise BatchOperationException(e)
finally:
db.disconnect()
def _truncate_mst_inst(db: Database):
try:
db.execute("TRUNCATE TABLE src05.mst_inst")
except Exception as e:
logger.debug("メルク施設マスタの全件削除に失敗")
raise e
logger.debug("メルク施設マスタの全件削除に成功")
return
def _insert_mst_inst_from_fcl_mst_v(db: Database):
# fcl_mst_vから、mst_instへInsert
try:
elapsed_time = ElapsedTime()
sql = """
INSERT INTO
src05.mst_inst (
inst_cd,
inst_clas_cd,
inst_name_form,
inst_name,
pref_cd,
city_cd,
pref_name,
city_name,
address,
postal_cd,
tel_num,
delete_date,
v_inst_cd,
create_date,
update_date
)
SELECT
fmv1.v_inst_cd,
CASE
WHEN fmv1.fcl_type IN ('A1', 'A0') THEN '3'
WHEN fmv1.fcl_type BETWEEN '20' AND '29' THEN '2'
END AS inst_clas_cd,
fmv1.fcl_name,
fmv1.fcl_abb_name,
fmv1.prft_cd,
RIGHT(fmv1.admin_kbn, 3),
mp.prefc_name,
LEFT(mc.city_name, 40),
CASE
WHEN fmv1.fcl_type IN ('A1', 'A0') THEN LEFT(fmv1.fmt_addr, 200)
WHEN fmv1.fcl_type BETWEEN '20' AND '29' THEN CONCAT(fmv1.prft_name,fmv1.city_name,fmv1.addr_line_1)
END AS address,
fmv1.postal_cd,
fmv1.tel_num,
CASE
WHEN
fmv1.fcl_type BETWEEN '20' AND '29' THEN LEFT(fmv1.closed_dt, 10)
WHEN
fmv1.fcl_type IN ('A1', 'A0') AND fmv1.end_date != '9999-12-31' THEN DATE_FORMAT(fmv1.end_date, "%Y-%m-%d")
ELSE
null
END AS delete_date,
fmv1.v_inst_cd,
fmv1.ins_dt,
fmv1.upd_dt
FROM
src05.fcl_mst_v AS fmv1
INNER JOIN (
SELECT
v_inst_cd,
MAX(sub_num) AS sno
FROM
src05.fcl_mst_v
GROUP BY
v_inst_cd
) fmv2
ON fmv1.v_inst_cd = fmv2.v_inst_cd
AND fmv1.sub_num = fmv2.sno
LEFT OUTER JOIN src05.mst_prefc AS mp
ON fmv1.prft_cd = mp.prefc_cd
LEFT OUTER JOIN src05.mst_city AS mc
ON LEFT(fmv1.admin_kbn, 2) = mc.prefc_cd
AND RIGHT(fmv1.admin_kbn, 3) = mc.city_cd
WHERE
((fmv1.fcl_type IN ('A1', 'A0')) OR fmv1.fcl_type BETWEEN '20' AND '29')
AND fmv1.rec_sts_kbn != '9'
"""
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'V施設マスタからメルク施設マスタに登録成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug("V施設マスタからメルク施設マスタに登録失敗")
raise e
return
def _insert_mst_inst_from_com_inst(db: Database):
# オプティマイザのderived_mergeフラグをoffにする
try:
sql = """
SET SESSION optimizer_switch = 'derived_merge=off'
"""
db.execute(sql)
logger.debug("オプティマイザのderived_mergeフラグ = Off")
except Exception as e:
logger.debug("オプティマイザのderived_mergeフラグの値変更に失敗")
raise e
# com_instから、mst_instへInsert
try:
elapsed_time = ElapsedTime()
sql = """
INSERT INTO
src05.mst_inst (
inst_cd,
inst_clas_cd,
inst_name_form,
inst_name,
pref_cd,
city_cd,
pref_name,
city_name,
address,
postal_cd,
tel_num,
bed_num,
manage_cd,
manage_name,
delete_date,
inst_div_cd,
inst_div_name,
v_inst_cd,
creater,
create_date,
updater,
update_date
)
SELECT
ci.dcf_dsf_inst_cd,
'1',
ci.form_inst_name_kanji,
ci.inst_name_kanji,
ci.prefc_cd,
ci.city_cd,
mp.prefc_name,
LEFT(mc.city_name, 40),
ci.inst_addr,
ci.postal_number,
ci.inst_phone_number,
ci.bed_num,
ci.manage_cd,
LEFT(cm.manage_name, 40),
ci.abolish_ymd,
ci.inst_div_cd,
LEFT(cid.inst_div_name, 40),
mcmv.hco_vid_v,
ci.create_user,
ci.regist_date,
ci.update_user,
ci.update_date
FROM
src05.com_inst AS ci
LEFT OUTER JOIN src05.mst_prefc AS mp
ON ci.prefc_cd = mp.prefc_cd
LEFT OUTER JOIN src05.mst_city AS mc
ON ci.prefc_cd = mc.prefc_cd
AND ci.city_cd = mc.city_cd
LEFT OUTER JOIN src05.com_manage AS cm
ON ci.manage_cd = cm.manage_cd
LEFT OUTER JOIN src05.com_inst_div AS cid
ON ci.inst_div_cd = cid.inst_div_cd
LEFT OUTER JOIN ( -- MDBコード変換表を使用してV施設コードを取得するためのテーブル結合
SELECT
mcmv4.*
FROM (
SELECT
mcmv1.*
FROM
src05.mdb_cnv_mst_v AS mcmv1
INNER JOIN (
SELECT
mcmv2.hco_vid_v,
MAX(mcmv2.sub_num) AS sno -- MDBコード変換データの枝番MAX
FROM
src05.mdb_cnv_mst_v mcmv2
WHERE
mcmv2.rec_sts_kbn != '9' -- 状態区分9削除以外 and 適用開始日付テーブル.処理日
AND src05.get_syor_date() >= mcmv2.start_date
GROUP BY
mcmv2.hco_vid_v
) AS mcmv3
ON mcmv1.hco_vid_v = mcmv3.hco_vid_v
AND mcmv1.sub_num = mcmv3.sno
) AS mcmv4
INNER JOIN (
SELECT
MIN(mcmv8.hco_vid_v) AS hvv, -- 1つのMDBコードに対し複数のV施設コードが割り当てられている場合最小のV施設コードを選択する
mcmv8.mdb_cd
FROM (
SELECT
mcmv5.*
FROM
src05.mdb_cnv_mst_v AS mcmv5
INNER JOIN (
SELECT
mcmv6.hco_vid_v,
MAX(mcmv6.sub_num) AS sno -- MDBコード変換データの枝番MAX
FROM
src05.mdb_cnv_mst_v AS mcmv6
WHERE
mcmv6.rec_sts_kbn != '9' -- 状態区分9削除以外 and 適用開始日付テーブル.処理日
AND src05.get_syor_date() >= mcmv6.start_date
GROUP BY
mcmv6.hco_vid_v
) AS mcmv7
ON mcmv5.hco_vid_v = mcmv7.hco_vid_v
AND mcmv5.sub_num = mcmv7.sno
) AS mcmv8
GROUP BY
mcmv8.mdb_cd
) AS mcmv9
ON mcmv4.mdb_cd = mcmv9.mdb_cd
AND mcmv4.hco_vid_v = mcmv9.hvv
) AS mcmv
ON ci.dcf_dsf_inst_cd = mcmv.mdb_cd
"""
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'COM施設からメルク施設マスタに登録成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug("COM施設からメルク施設マスタに登録失敗")
raise e
return

View File

@ -0,0 +1,31 @@
from src.batch.common.batch_context import BatchContext
from src.batch.laundering import (
create_inst_merge_for_laundering, emp_chg_inst_laundering,
ult_ident_presc_laundering, sales_results_laundering)
from src.batch.dcf_inst_merge import integrate_dcf_inst_merge
from src.logging.get_logger import get_logger
batch_context = BatchContext.get_instance()
logger = get_logger('実績洗替')
def exec():
"""実績洗替処理"""
logger.info('実績更新:起動')
# 営業日ではない場合、実績洗替処理は実行しない
if batch_context.is_not_business_day:
logger.info('営業日ではないため、実績洗替処理をスキップします。')
return
# DCF施設統合マスタ日次更新
integrate_dcf_inst_merge.exec()
# 洗替用マスタ作成
create_inst_merge_for_laundering.exec()
# 施設担当者洗替
emp_chg_inst_laundering.exec()
# 納入先処方元マスタ洗替
ult_ident_presc_laundering.exec()
# 卸販売洗替
sales_results_laundering.exec()
logger.info('実績更新:終了')

View File

@ -0,0 +1,169 @@
from src.batch.batch_functions import logging_sql
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger
from src.system_var import environment
logger = get_logger('卸販売洗替')
def exec():
db = Database.get_instance(autocommit=True)
try:
db.connect()
db.to_jst()
logger.debug('処理開始')
# 卸販売実績テーブル(洗替後)過去データ削除
_call_sales_lau_delete(db)
# 卸販売実績テーブル(洗替後)作成
_call_sales_lau_upsert(db)
# 1:卸組織洗替
_call_whs_org_laundering(db)
# HCO施設コードの洗替
_update_sales_lau_from_vop_hco_merge_v(db)
# 4:メルク施設コードの洗替
_update_mst_inst_laundering(db)
logger.debug('処理終了')
except Exception as e:
raise BatchOperationException(e)
finally:
db.disconnect()
def _call_sales_lau_delete(db: Database):
# 卸販売実績テーブル(洗替後)過去データ削除
logger.info('sales_lau_delete(プロシージャ―) 開始')
db.execute(f"""
CALL src05.sales_lau_delete(
'{environment.SALES_LAUNDERING_TARGET_TABLE_NAME}',
{environment.SALES_LAUNDERING_TARGET_YEAR_OFFSET}
)
""")
logger.info('sales_lau_delete(プロシージャ―) 終了')
return
def _call_sales_lau_upsert(db: Database):
# 卸販売実績テーブル(洗替後)作成
# sales_lau_upsertの第3引数は、NEWDWH2021-1230の対応の結果、未使用の引数となっています
logger.info('sales_lau_upsert(プロシージャ―) 開始')
db.execute(f"""
CALL src05.sales_lau_upsert(
'{environment.SALES_LAUNDERING_TARGET_TABLE_NAME}',
(src05.get_syor_date() - interval {environment.SALES_LAUNDERING_EXTRACT_DATE_PERIOD} day),
src05.get_syor_date()
)
""")
logger.info('sales_lau_upsert(プロシージャ―) 終了')
return
def _call_whs_org_laundering(db: Database):
# 卸組織洗替
logger.info('whs_org_laundering(プロシージャ―) 開始')
db.execute(f"""
CALL src05.whs_org_laundering(
'{environment.SALES_LAUNDERING_TARGET_TABLE_NAME}'
)
""")
logger.info('whs_org_laundering(プロシージャ―) 終了')
return
def _update_sales_lau_from_vop_hco_merge_v(db: Database):
# HCO施設コードの洗替
if _count_v_inst_merge_t(db) == 0:
logger.info('V施設統合マスタ(洗替処理一時テーブル)にデータは存在しません')
return
_call_v_inst_merge_laundering(db)
return
def _count_v_inst_merge_t(db: Database) -> int:
# V施設統合マスタ(洗替処理一時テーブル)のデータ件数の取得
try:
sql = """
SELECT
COUNT(v_inst_cd) AS cnt
FROM
internal05.v_inst_merge_t
"""
result = db.execute_select(sql)
logging_sql(logger, sql)
logger.info('V施設統合マスタ(洗替処理一時テーブル)のデータ件数の取得 成功')
except Exception as e:
logger.debug('V施設統合マスタ(洗替処理一時テーブル)のデータ件数の取得 失敗')
raise e
return result[0]['cnt']
def _call_v_inst_merge_laundering(db: Database):
# HCO施設コードの洗替(プロシージャ―の呼び出し)
logger.info('v_inst_merge_laundering(プロシージャ―) 開始')
db.execute(f"""
CALL src05.v_inst_merge_laundering(
'{environment.SALES_LAUNDERING_TARGET_TABLE_NAME}'
)
""")
logger.info('v_inst_merge_laundering(プロシージャ―) 終了')
return
def _update_mst_inst_laundering(db: Database):
# メルク施設コードの洗替
_call_hco_to_mdb_laundering(db)
_update_sales_lau_from_dcf_inst_merge(db)
def _call_hco_to_mdb_laundering(db: Database):
# A:医療機関のデータはMDB変換表からHCO⇒DCFへ変換
logger.info('hco_to_mdb_laundering(プロシージャ―) 開始')
db.execute(f"""
CALL src05.hco_to_mdb_laundering(
'{environment.SALES_LAUNDERING_TARGET_TABLE_NAME}'
)
""")
logger.info('hco_to_mdb_laundering(プロシージャ―) 終了')
return
def _update_sales_lau_from_dcf_inst_merge(db: Database):
# B:DCF施設統合マスタがある場合は、コードを変換し、住所等をSETする
if _count_inst_merge_t(db) == 0:
logger.info('アルトマーク施設統合マスタ(洗替処理一時テーブル)にデータは存在しません')
return
_call_inst_merge_laundering(db)
return
def _count_inst_merge_t(db: Database) -> int:
# アルトマーク施設統合マスタ(洗替処理一時テーブル)のデータ件数の取得
try:
sql = """
SELECT
COUNT(dcf_dsf_inst_cd) AS cnt
FROM
internal05.inst_merge_t
"""
result = db.execute_select(sql)
logging_sql(logger, sql)
logger.info('アルトマーク施設統合マスタ(洗替処理一時テーブル)のデータ件数の取得 成功')
except Exception as e:
logger.debug('アルトマーク施設統合マスタ(洗替処理一時テーブル)のデータ件数の取得 失敗')
raise e
return result[0]['cnt']
def _call_inst_merge_laundering(db: Database):
# B:DCF施設統合マスタがある場合は、コードを変換し、住所等をSETする(プロシージャ―の呼び出し)
logger.info('inst_merge_laundering(プロシージャ―) 開始')
db.execute(f"""
CALL src05.inst_merge_laundering(
'{environment.SALES_LAUNDERING_TARGET_TABLE_NAME}'
)
""")
logger.info('inst_merge_laundering(プロシージャ―) 終了')
return

View File

@ -0,0 +1,140 @@
from src.batch.batch_functions import logging_sql
from src.batch.common.batch_context import BatchContext
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger
from src.time.elapsed_time import ElapsedTime
logger = get_logger('納入先処方元マスタ洗替')
batch_context = BatchContext.get_instance()
def exec():
db = Database.get_instance()
try:
db.connect()
db.to_jst()
logger.debug('納入先処方元マスタの洗替処理開始')
# ult_ident_presc_lauをTruncate
_truncate_ult_ident_presc_lau(db)
# ult_ident_presc から、ult_ident_presc_lauへInsert
_insert_into_ult_ident_presc_lau_from_ult_ident_presc(db)
# v_inst_merge_tから、ult_ident_presc_lauをUpdate
_update_ult_ident_presc_lau_from_v_inst_merge_t(db)
# inst_merge_tから、ult_ident_presc_lauをUpdate
_update_ult_ident_presc_lau_from_inst_merge_t(db)
logger.debug('納入先処方元マスタの洗替処理製造終了')
except Exception as e:
raise BatchOperationException(e)
finally:
db.disconnect()
def _truncate_ult_ident_presc_lau(db: Database):
try:
db.execute("TRUNCATE TABLE src05.ult_ident_presc_lau")
except Exception as e:
logger.debug("納入先処方元マスタ(洗替後)の全件削除に失敗")
raise e
logger.debug("納入先処方元マスタ(洗替後)の全件削除に成功")
return
def _insert_into_ult_ident_presc_lau_from_ult_ident_presc(db: Database):
try:
elapsed_time = ElapsedTime()
sql = "INSERT INTO src05.ult_ident_presc_lau SELECT *, NULL FROM src05.ult_ident_presc"
res = db.execute(sql)
logging_sql(logger, sql)
logger.info(f'処方元マスタから処方元マスタ(洗替後)に全件コピーに成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug("処方元マスタから処方元マスタ(洗替後)に全件コピーに失敗")
raise e
return
def _update_ult_ident_presc_lau_from_v_inst_merge_t(db: Database):
# v_inst_merge_tの元となるvop_hco_merge_vはデータが作られないため、この洗い替え処理は基本空振りする
try:
select_result = db.execute_select(
"""
SELECT
COUNT(v_inst_cd) AS row_count
FROM
internal05.v_inst_merge_t
"""
)
except Exception as e:
logger.debug("V施設統合マスタ(洗替処理一時テーブル)からの移行対象施設件数取得失敗")
raise e
count = [row for row in select_result][0]['row_count']
if count == 0:
logger.info('V施設統合マスタ(洗替処理一時テーブル)からの移行対象施設が存在しません')
return
logger.info(f'V施設統合マスタ(洗替処理一時テーブル)からの移行対象施設件数取得成功 移行対象施設件数={count}')
try:
elapsed_time = ElapsedTime()
update_sql = """
UPDATE
src05.ult_ident_presc_lau uipl, internal05.v_inst_merge_t vimt
SET
uipl.ult_ident_cd = vimt.v_inst_cd_merge,
uipl.lau_ope_dt = SYSDATE()
WHERE
uipl.ult_ident_cd = vimt.v_inst_cd
"""
res = db.execute(update_sql)
logging_sql(logger, update_sql)
logger.info(f'納入先処方元マスタの納入先コードを施設コード移行先に更新成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug("納入先処方元マスタの納入先コードを施設コード移行先に更新失敗")
raise e
return
def _update_ult_ident_presc_lau_from_inst_merge_t(db: Database):
# inst_merge_tから、ult_ident_presc_lauをUpdate
try:
select_result = db.execute_select(
"""
SELECT
COUNT(dcf_dsf_inst_cd) AS row_count
FROM
internal05.inst_merge_t
"""
)
except Exception as e:
logger.debug("施設統合マスタ(洗替処理一時テーブル)からの移行対象施設件数取得失敗")
raise e
count = [row for row in select_result][0]['row_count']
if count == 0:
logger.info('施設統合マスタ(洗替処理一時テーブル)からの移行対象施設が存在しません')
return
logger.info(f'施設統合マスタ(洗替処理一時テーブル)からの移行対象施設件数取得成功 移行対象施設件数={count}')
# inst_merge_tから、ult_ident_presc_lauをUpdate
try:
elapsed_time = ElapsedTime()
update_sql = """
UPDATE
src05.ult_ident_presc_lau uipl, internal05.inst_merge_t imt
SET
uipl.presc_cd = imt.dup_opp_cd,
uipl.lau_ope_dt = SYSDATE()
WHERE
uipl.presc_cd = imt.dcf_dsf_inst_cd
"""
res = db.execute(update_sql)
logging_sql(logger, update_sql)
logger.info(f'納入先処方元マスタの処方元コードを施設コード移行先に更新成功, {res.rowcount} 行更新 ({elapsed_time.of})')
except Exception as e:
logger.debug("納入先処方元マスタの処方元コードを施設コード移行先に更新失敗")
raise e
return

View File

@ -0,0 +1,32 @@
"""並列処理"""
import concurrent.futures
from src.batch.bio_sales import create_bio_sales_lot
from src.batch.laundering import sales_laundering
from src.error.exceptions import BatchOperationException
def exec():
# 並列処理を開始
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
# 実績更新
future_sales_laundering = executor.submit(sales_laundering.exec)
# 生物由来ロット分解
future_create_bio_sales_lot = executor.submit(create_bio_sales_lot.exec)
# 両方の処理が完了するまで待つ
concurrent.futures.wait([future_sales_laundering, future_create_bio_sales_lot])
# エラーがあれば呼び出し元でキャッチする
sales_laundering_exc = future_sales_laundering.exception()
create_bio_sales_lot_exc = future_create_bio_sales_lot.exception()
# いずれかにエラーが発生していれば、1つのエラーとして返す。
if sales_laundering_exc is not None or create_bio_sales_lot_exc is not None:
sales_laundering_exc_message = str(sales_laundering_exc) if sales_laundering_exc is not None else ''
create_bio_sales_lot_exc_message = str(create_bio_sales_lot_exc) if create_bio_sales_lot_exc is not None else ''
raise BatchOperationException(f'並列処理中にエラーが発生しました。実績更新="{sales_laundering_exc_message}", 生物由来ロット分解={create_bio_sales_lot_exc_message}')
return

View File

@ -0,0 +1,60 @@
import csv
from io import TextIOWrapper
class DatFileLine:
layout_class: str
records: list[str]
def __init__(self, dat_line: list[str]) -> None:
self.layout_class = dat_line[0]
self.records = dat_line
class DatFile:
"""アルトマークデータファイル"""
lines: list[DatFileLine]
success_count: int = 0
error_count: int = 0
total_count: int = 0
__i: int = 0
def __iter__(self):
return self
def __next__(self) -> DatFileLine:
if self.__i == len(self.lines):
raise StopIteration()
line = self.lines[self.__i]
self.__i += 1
return line
def __init__(self, file: TextIOWrapper) -> None:
reader = csv.reader(file)
csv_rows = [DatFileLine(row) for row in reader]
self.lines = csv_rows
self.total_count = len(csv_rows)
def count_up_success(self):
self.success_count += 1
def count_up_error(self):
self.error_count += 1
@classmethod
def from_path(cls, local_file_path: str):
"""アルトマークデータファイルを読み込み、新しいインスタンスを作成する
Args:
local_file_path (str): ローカルのファイルパス
Returns:
DatFile: このクラスのインスタンス
"""
# cp932(Shift-JIS Windows拡張)でファイルを読み込む
file = open(local_file_path, encoding='cp932')
instance = cls(file)
file.close()
return instance

View File

@ -0,0 +1,271 @@
"""output_vjsk_inst_pharm_data"""
from src.aws.s3 import VjskSendBucket
from src.batch.common.batch_context import BatchContext
from src.db.database import Database
from src.logging.get_logger import get_logger
import tempfile
import os.path as path
import csv
logger = get_logger('V実消化用施設データ作成処理')
sql_err_msg = "SQL実行エラーです。"
def exec():
vjsk_csv_file_name = 'ComInst.csv'
# バッチ共通設定を取得
batch_context = BatchContext.get_instance()
if not batch_context.is_ultmarc_imported:
logger.info('アルトマーク取込が行われていないため、V実消化用施設データ作成処理をスキップします。')
return
db = Database.get_instance()
try:
logger.info('処理開始')
try:
# DB接続
db.connect()
except Exception as e:
logger.info('DB接続エラーです。')
raise e
# CSVファイルの作成用のSQL実行(施設)
record_inst = select_inst_record(db)
# CSVファイルの作成用のSQL実行(薬局)
record_pharm = select_pharm_record(db)
# CSVファイル作成
csv_file_path = make_csv_data(record_inst, record_pharm, vjsk_csv_file_name)
vjsk_bucket = VjskSendBucket()
try:
# s3へデータ移動
vjsk_bucket.upload_inst_pharm_csv_file(vjsk_csv_file_name, csv_file_path)
except Exception as e:
logger.info('S3バケットにCSVデータを作成できませんでした。')
raise e
try:
# 処理後ファイルをバックアップ
vjsk_bucket.backup_inst_pharm_csv_file(vjsk_csv_file_name, batch_context.syor_date)
except Exception as e:
logger.info('バックアップバケットへCSVデータをコピーできませんでした。')
raise e
csv_count = len(record_inst) + len(record_pharm)
logger.info(f'CSV出力件数: {csv_count}')
logger.info('正常終了')
except Exception as e:
raise e
finally:
db.disconnect()
return
def select_inst_record(db):
# CSVファイル作成用のSQL実行(施設)
try:
# 施設テーブル検索SQL
sql = """\
SELECT dcf_dsf_inst_cd,
inst_div_cd,
addr_unknown_reason_cd,
form_inst_name_kana,
inst_name_kana,
form_inst_name_kanji,
inst_name_kanji,
rltd_univ_prnt_cd,
bed_num,
close_flg,
estab_sche_flg,
close_start_ym,
estab_sche_ym,
ward_abolish_flg,
inst_repre_cd,
inst_repre_kana,
inst_repre,
phone_number_non_flg,
unconf_flg,
inst_phone_number,
inst_addr_kana,
inst_addr,
postal_number,
village_cd,
prefc_cd,
city_cd,
addr_display_number,
addr_cnt_kana,
addr_cnt,
manage_cd,
delete_sche_reason_cd,
hp_assrt_cd,
dup_opp_cd,
insp_item_micrb,
insp_item_serum,
insp_item_blood,
insp_item_patho,
insp_item_paras,
insp_item_biochem,
insp_item_ri,
re_exam_cd,
prmit_bed_num_other,
prmit_bed_num_mental,
prmit_bed_num_tuber,
prmit_bed_num_infection,
prmit_bed_num_sum,
prmit_bed_num_gen,
prmit_bed_num_rcup,
prmit_bed_maint_ymd,
inst_pharm_div,
abolish_ymd,
delete_flg,
filler_1,
filler_2,
filler_3,
filler_4,
filler_5,
regist_date,
create_user,
update_date,
update_user,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
FROM src05.com_inst ORDER BY dcf_dsf_inst_cd
"""
return db.execute_select(sql)
except Exception as e:
logger.debug(sql_err_msg)
raise e
def select_pharm_record(db):
# CSVファイル作成用のSQL実行(薬局)
try:
# 薬局テーブル検索SQL
sql = """\
SELECT dcf_dsf_inst_cd,
inst_div_cd,
addr_unknown_reason_cd,
form_inst_name_kana,
inst_name_kana,
form_inst_name_kanji,
inst_name_kanji,
'' AS rltd_univ_prnt_cd,
'' AS bed_num,
close_flg,
estab_sche_flg,
close_start_ym,
estab_sche_ym,
'' AS ward_abolish_flg,
'' AS inst_repre_cd,
inst_repre_kana,
inst_repre,
phone_number_non_flg,
unconf_flg,
inst_phone_number,
inst_addr_kana,
inst_addr,
postal_number,
village_cd,
prefc_cd,
city_cd,
addr_display_number,
addr_cnt_kana,
addr_cnt,
manage_cd,
delete_sche_reason_cd,
'' AS hp_assrt_cd,
dup_opp_cd,
'' AS insp_item_micrb,
'' AS insp_item_serum,
'' AS insp_item_blood,
'' AS insp_item_patho,
'' AS insp_item_paras,
'' AS insp_item_biochem,
'' AS insp_item_ri,
'' AS re_exam_cd,
'' AS prmit_bed_num_other,
'' AS prmit_bed_num_mental,
'' AS prmit_bed_num_tuber,
'' AS prmit_bed_num_infection,
'' AS prmit_bed_num_sum,
'' AS prmit_bed_num_gen,
'' AS prmit_bed_num_rcup,
'' AS prmit_bed_maint_ymd,
inst_pharm_div,
abolish_ymd,
delete_flg,
filler_1,
filler_2,
filler_3,
filler_4,
filler_5,
regist_date,
create_user,
update_date,
update_user,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
FROM src05.com_pharm ORDER BY dcf_dsf_inst_cd
"""
return db.execute_select(sql)
except Exception as e:
logger.debug(sql_err_msg)
raise e
def make_csv_data(record_inst: list, record_pharm: list, vjsk_csv_file_name: str):
# 一時ファイルとして保存する(CSVファイル)
try:
temporary_dir = tempfile.mkdtemp()
csv_file_path = path.join(temporary_dir, vjsk_csv_file_name)
head_str = ['DCF_DSF_INST_CD', 'INST_DIV_CD', 'ADDR_UNKNOWN_REASON_CD', 'FORM_INST_NAME_KANA', 'INST_NAME_KANA',
'FORM_INST_NAME_KANJI', 'INST_NAME_KANJI', 'RLTD_UNIV_PRNT_CD', 'BED_NUM', 'CLOSE_FLG', 'ESTAB_SCHE_FLG',
'CLOSE_START_YM', 'ESTAB_SCHE_YM', 'WARD_ABOLISH_FLG', 'INST_REPRE_CD', 'INST_REPRE_KANA', 'INST_REPRE',
'PHONE_NUMBER_NON_FLG', 'UNCONF_FLG', 'INST_PHONE_NUMBER', 'INST_ADDR_KANA', 'INST_ADDR', 'POSTAL_NUMBER',
'VILLAGE_CD', 'PREFC_CD', 'CITY_CD', 'ADDR_DISPLAY_NUMBER', 'ADDR_CNT_KANA', 'ADDR_CNT', 'MANAGE_CD',
'DELETE_SCHE_REASON_CD', 'HP_ASSRT_CD', 'DUP_OPP_CD', 'INSP_ITEM_MICRB', 'INSP_ITEM_SERUM', 'INSP_ITEM_BLOOD',
'INSP_ITEM_PATHO', 'INSP_ITEM_PARAS', 'INSP_ITEM_BIOCHEM', 'INSP_ITEM_RI', 'RE_EXAM_CD', 'PRMIT_BED_NUM_OTHER',
'PRMIT_BED_NUM_MENTAL', 'PRMIT_BED_NUM_TUBER', 'PRMIT_BED_NUM_INFECTION', 'PRMIT_BED_NUM_SUM', 'PRMIT_BED_NUM_GEN',
'PRMIT_BED_NUM_RCUP', 'PRMIT_BED_MAINT_YMD', 'INST_PHARM_DIV', 'ABOLISH_YMD', 'DELETE_FLG', 'FILLER_1', 'FILLER_2',
'FILLER_3', 'FILLER_4', 'FILLER_5', 'REGIST_DATE', 'CREATE_USER', 'UPDATE_DATE', 'UPDATE_USER', 'SYS_REGIST_DATE',
'REGIST_PRGM_ID', 'SYS_UPDATE_DATE', 'UPDATE_PRGM_ID']
with open(csv_file_path, mode='w', encoding='UTF-8') as csv_file:
# ヘッダ行書き込みくくり文字をつけない為にwriterowではなく、writeを使用しています
csv_file.write(f"{','.join(head_str)}\n")
# Shift-JIS、CRLF、価囲いありで書き込む
writer = csv.writer(csv_file, delimiter=',', lineterminator='\n',
quotechar='"', doublequote=True, quoting=csv.QUOTE_ALL,
strict=True
)
# データ部分書き込み(施設)
for record_inst_data in record_inst:
record_inst_value = list(record_inst_data.values())
csv_data = ['' if n is None else n for n in record_inst_value]
writer.writerow(csv_data)
# データ部分書き込み(薬局)
for record_pharm_data in record_pharm:
record_pharm_value = list(record_pharm_data.values())
csv_data = ['' if n is None else n for n in record_pharm_value]
writer.writerow(csv_data)
except Exception as e:
logger.info('CSVデータの作成に失敗しました。')
raise e
return csv_file_path

View File

@ -0,0 +1,156 @@
"""アルトマークデータ処理"""
import json
from datetime import datetime
from src.aws.s3 import ConfigBucket, UltmarcBucket
from src.batch.common.batch_context import BatchContext
from src.batch.ultmarc.datfile import DatFile
from src.batch.ultmarc.utmp_tables.ultmarc_table_mapper_factory import \
UltmarcTableMapperFactory
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger
from src.system_var import constants
logger = get_logger('アルトマークデータ処理')
ultmarc_bucket = UltmarcBucket()
config_bucket = ConfigBucket()
batch_context = BatchContext.get_instance()
def exec_import():
"""アルトマーク取り込み処理"""
try:
logger.info('アルトマーク取込処理: 開始')
# datファイルをS3から取得する
dat_file_list = ultmarc_bucket.list_dat_file()
# ファイルがない場合は処理せず、正常終了とする
if len(dat_file_list) == 0:
logger.info('取込ファイルがないため、アルトマーク取込処理をスキップします')
# アルトマークデータ受信予定曜日である月曜日は、保守ユーザーに通知する
if datetime.strptime(batch_context.syor_date, '%Y/%m/%d').weekday() == constants.WEEKDAY_MONDAY:
logger.info('[NOTICE]アルトマークデータの受信がありませんでした')
return
# ファイルが複数ある場合はエラーとする
if len(dat_file_list) > 1:
raise BatchOperationException(f'複数の取込ファイルがあるため、異常終了 ファイル一覧:{dat_file_list}')
# ファイルの件数は必ず1件になる
dat_file_info = dat_file_list[0]
# 0Byteの場合、
if dat_file_info['size'] == 0:
logger.info(f'0Byteファイルのため、処理をスキップします。ファイル名={dat_file_info["filename"]}')
return
dat_file_name = dat_file_info['filename']
logger.info(f"{dat_file_name}を取り込みます")
# datファイルをダウンロード
local_file_path = ultmarc_bucket.download_dat_file(dat_file_name)
# 文字コード変換設定ファイルをダウンロード
convert_config_file_path = config_bucket.download_ultmarc_hex_convert_config()
# 文字コードを変換
converted_file_path = _convert_character_hex(local_file_path, convert_config_file_path)
dat_file = DatFile.from_path(converted_file_path)
# アルトマーク取り込み実行
_import_to_ultmarc_table(dat_file)
# 処理後ファイルをバックアップ
ultmarc_bucket.backup_dat_file(dat_file_name, batch_context.syor_date)
logger.info(f'取り込み処理が完了したため、datファイルをバックアップ。ファイル名={dat_file_name}')
# アルトマーク取込済をマーク
batch_context.is_ultmarc_imported = True
logger.info('アルトマーク取込処理: 終了')
except Exception as e:
raise BatchOperationException(e)
def _convert_character_hex(dat_file_path: str, config_file_path: str) -> str:
"""_summary_
アルトマークデータファイルを読みバイトレベルで文字コードを変換して新しいファイルに書き出す
Args:
dat_file_path (str): 変換前のアルトマークデータファイル
config_file_path (str): 変換設定
Returns:
str: 文字コード変換後のアルトマークデータファイルのパス
"""
logger.debug('文字コード変換開始')
# 変換設定ファイルを読み込む
with open(config_file_path, 'r', encoding='utf-8') as f:
hex_convert_config_dict: dict = json.load(f)
with open(dat_file_path, 'rb') as org_file, open(f'{dat_file_path}.converted', 'wb') as dest_file:
while True:
first_byte = org_file.read(1)
# ファイルの末尾まで読んだらループ終了
if not first_byte:
break
# 読みだした1バイトが、Shift_JIS の日本語範囲(2バイト文字)に含まれる場合、もう1バイト読む
if 0x81 <= first_byte[0] <= 0x9F or 0xE0 <= first_byte[0] <= 0xFC:
second_byte = org_file.read(1)
# ファイルの末尾まで読んだらループ終了
if not second_byte:
break
# 2バイトを結合して、HEXの4桁(大文字)に変換
japanese_bytes = first_byte + second_byte
japanese_hex = ''.join([hex(b)[2:].zfill(2).upper() for b in japanese_bytes])
# 変換対象の文字を変換設定に基づいて変換する
processed_hex = hex_convert_config_dict.get(japanese_hex)
if processed_hex is None:
processed_hex = japanese_hex
# HEXの4桁をバイト列に戻して書き出す
processed_bytes = bytes.fromhex(processed_hex)
dest_file.write(processed_bytes)
else:
# 1バイト文字はそのまま書き出す
dest_file.write(first_byte)
logger.debug('文字コード変換終了')
return f'{dat_file_path}.converted'
def _import_to_ultmarc_table(dat_file: DatFile):
db = Database.get_instance()
try:
# DB接続
db.connect()
# ファイル単位でトランザクションを行う
db.begin()
mapper_factory = UltmarcTableMapperFactory()
# datファイルを1行ずつ処理し、各テーブルへ登録
for line in dat_file:
try:
# 書き込み先のテーブルを特定
mapper_class = mapper_factory.create(
line.layout_class,
line.records,
db
)
mapper_class.make_query()
mapper_class.execute_queries()
dat_file.count_up_success()
except Exception as e:
logger.info(e)
record = line.records
log_message = ','.join([f'"{r}"' for r in record])
logger.info(f'ERROR_LINE: {log_message}')
dat_file.count_up_error()
# 処理結果をログに出力する
logger.info(f'ultmarc import process RESULT')
logger.info(f'SUCCESS_COUNT={dat_file.success_count}')
logger.info(f'ERROR_COUNT={dat_file.error_count}')
logger.info(f'ALL_COUNT={dat_file.total_count}')
# 1件でもエラーがあれば、通知用にログに出力する
if dat_file.error_count > 0:
logger.warning('取り込みに失敗した行があります。詳細は`ERROR_LINE:`の行を確認してください。')
finally:
# 終了時に必ずコミットする
db.commit()
db.disconnect()
return

View File

@ -0,0 +1,107 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_alma_depart_disc import ComAlmaDepartDisc
class ComAlmaDepartDiscMapper(UltmarcTableMapper):
"""レイアウト区分003: COM_出身校学部識別 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_alma_depart_disc
WHERE
alma_cd = :alma_cd
AND
depart_disc_cd = :depart_disc_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_alma_depart_disc
(
alma_cd,
depart_disc_cd,
estab_e,
estab_y,
alma_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:alma_cd,
:depart_disc_cd,
:estab_e,
:estab_y,
:alma_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_alma_depart_disc
SET
estab_e = :estab_e,
estab_y = :estab_y,
alma_name = :alma_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
alma_cd = :alma_cd
AND
depart_disc_cd = :depart_disc_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_alma_depart_disc
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
alma_cd = :alma_cd
AND
depart_disc_cd = :depart_disc_cd
"""
record: ComAlmaDepartDisc
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComAlmaDepartDisc)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,92 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_alma import ComAlma
class ComAlmaMapper(UltmarcTableMapper):
"""レイアウト区分004: COM_出身校 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_alma
WHERE
alma_cd = :alma_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_alma
(
alma_cd,
alma,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:alma_cd,
:alma,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
UPDATE_QUERY = """\
UPDATE
src05.com_alma
SET
alma = :alma,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
alma_cd = :alma_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_alma
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
alma_cd = :alma_cd
"""
record: ComAlma
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComAlma)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,105 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_blng_sec import ComBlngSec
class ComBlngSecMapper(UltmarcTableMapper):
"""レイアウト区分008: COM_所属部科 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_blng_sec
WHERE
blng_sec_cd = :blng_sec_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_blng_sec
(
blng_sec_cd,
blng_sec_kana,
blng_sec_name,
inst_category,
trt_category,
category_sort,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:blng_sec_cd,
:blng_sec_kana,
:blng_sec_name,
:inst_category,
:trt_category,
:category_sort,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_blng_sec
SET
blng_sec_kana = :blng_sec_kana,
blng_sec_name = :blng_sec_name,
inst_category = :inst_category,
trt_category = :trt_category,
category_sort = :category_sort,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
blng_sec_cd = :blng_sec_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_blng_sec
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
blng_sec_cd = :blng_sec_cd
"""
record: ComBlngSec
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComBlngSec)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,108 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_cop_hp import ComCopHp
class ComCopHpMapper(UltmarcTableMapper):
"""レイアウト区分112: COM_臨床研修病院 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_cop_hp
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
cophp_cd = :cophp_code
AND
openyear = :open_year
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_cop_hp
(
dcf_dsf_inst_cd,
cophp_cd,
openyear,
sortkey,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_code,
:cophp_code,
:open_year,
:sort_key,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_cop_hp
SET
sortkey = :sort_key,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
cophp_cd = :cophp_code
AND
openyear = :open_year
"""
# 削除『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_cop_hp
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
cophp_cd = :cophp_code
AND
openyear = :open_year
"""
record: ComCopHp
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComCopHp)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 削除『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
if self.record.maint_flag == 'B' and self.record.adddel_div == '1':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
# 更新する値が空の場合は、更新処理を行わない
if self.record.sort_key == '':
return None
else:
return self.UPDATE_QUERY

View File

@ -0,0 +1,480 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_dr import ComDr
class ComDrMapper(UltmarcTableMapper):
"""レイアウト区分501: COM_医師 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_dr
WHERE
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_dr
(
dcf_pcf_dr_cd,
dr_name_kana,
dr_name,
birthday_era,
birthday_year,
birthday_month,
birthday_day,
birthday,
hometown_cd,
estab_era,
estab_year,
estab_y,
home_phone_number,
home_addr_kana,
home_addr,
home_postal_number,
addr_village_cd,
prefc_cd,
city_cd,
addr_display_number,
addr_cnt_kana,
addr_cnt,
dr_circle_cd,
estab_div_cd,
sex_cd,
delete_sche_reason_cd,
addr_unknown_reason_cd,
alma_cd,
depart_disc_cd,
grad_era,
grad_year,
grad_y,
lump_regist_flg,
opp_dup_cd,
dr_ph_div,
use_stop_div,
use_stop_reason_cd,
use_stop_regist_ymd,
use_stop_cancel_ymd,
drday_era,
drday_year,
drday_y,
abolish_ymd,
delete_flg,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcf_pcf_dr_cd,
:dr_name_kana,
:dr_name,
:birthday_era,
:birthday_year,
:birthday_month,
:birthday_day,
:birth_day,
:home_town_code,
:pract_yearera,
:pract_year,
:estab_y,
:dr_tel,
:dr_addr_kana,
:dr_addr,
:dr_zip_code,
:addr_village_cd,
:pref_code,
:city_code,
:dr_addr_num,
:addr_cnt_kana,
:addr_cnt,
:medassoci_code,
:pract_class_code,
:sex_code,
:drdel_code,
:dr_addr_lost_code,
:graduniv_code,
:graduniv_dept_code,
:grad_yearera,
:grad_year,
:grad_y,
:bskregst_flag,
:opp_dup_code,
1,
:use_stop_flag,
:use_stopc_ode,
:cre_stop_date,
:release_date,
:drda_yera,
:drday_year,
:drday_y,
NULL,
0,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
UPDATE_QUERY = """\
UPDATE
src05.com_dr
SET
{update_columns}
dr_ph_div = 1,
abolish_ymd = NULL,
delete_flg = 0,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_dr
SET
abolish_ymd = :maint_date,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
"""
# 西暦取得SQL
YEAR_GET_QUERY = """\
SELECT
year AS year
FROM
src05.com_era
WHERE
era_cd = :era_cd
"""
# COM_医師診療科目の物理削除SQL
PHYSICAL_DELETE_QUERY_TRT = """\
DELETE FROM
src05.com_dr_trt_course
WHERE
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
"""
# COM_医師診療科目の登録用SQL
INSERT_QUERY_TRT = """\
INSERT INTO src05.com_dr_trt_course
(
trt_course_cd,
sequence,
dcf_pcf_dr_cd,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
{trt_course_code},
{trt_sequence},
:dcf_pcf_dr_cd,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
record: ComDr
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComDr)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.__make_upsert_query()
return
def __make_upsert_query(self):
# 西暦の取得
self.__set_era()
# 「@」による項目クリアを設定
self.__set_clearing_item()
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
self.queries.append(self.INSERT_QUERY)
# COM_医師診療科目 削除登録
self.__make_delete_insert_trt_query()
return
# 存在する場合ではUpdate
# COM_医師診療科目 削除登録
self.__make_delete_insert_trt_query()
update_columns = ','.join(self.__make_update_query())
# 何も更新項目が無い場合はNoneとする更新処理は行わない
if len(update_columns) == 0:
self.queries.append(None)
return
else:
# 末尾にカンマを付けてSET句を完成させる
update_columns += ','
update_query = self.UPDATE_QUERY.format(
update_columns=update_columns
)
self.queries.append(update_query)
return
def __make_update_query(self):
set_clauses = [] # 設定項目
# 削除予定理由
if len(self.record.drdel_code) > 0:
set_clauses.append('delete_sche_reason_cd = :drdel_code')
# 重複時相手先コード
if len(self.record.opp_dup_code) > 0:
set_clauses.append('opp_dup_cd = :opp_dup_code')
# 医師名(漢字)
if len(self.record.dr_name) > 0:
set_clauses.append('dr_name = :dr_name')
# 医師名(カナ)
if len(self.record.dr_name_kana) > 0:
set_clauses.append('dr_name_kana = :dr_name_kana')
# 性別
if len(self.record.sex_code) > 0:
set_clauses.append('sex_cd = :sex_code')
# 生年月日
if len(self.record.birthday_era + self.record.birthday_year + self.record.birthday_month + self.record.birthday_day) > 0:
set_clauses.append('birthday_era = :birthday_era')
set_clauses.append('birthday_year = :birthday_year')
set_clauses.append('birthday_month = :birthday_month')
set_clauses.append('birthday_day = :birthday_day')
set_clauses.append('birthday = :birth_day')
# 出身都道府県コード
if len(self.record.home_town_code) > 0:
set_clauses.append('hometown_cd = :home_town_code')
# 医師会コード
if len(self.record.medassoci_code) > 0:
set_clauses.append('dr_circle_cd = :medassoci_code')
# 卒年
if len(self.record.grad_yearera + self.record.grad_year) > 0:
set_clauses.append('grad_year = :grad_year')
set_clauses.append('grad_era = :grad_yearera')
set_clauses.append('grad_y = :grad_y')
# 出身校コード
if len(self.record.graduniv_code) > 0:
set_clauses.append('alma_cd = :graduniv_code')
# 出身校学部識別コード
if len(self.record.graduniv_dept_code) > 0:
set_clauses.append('depart_disc_cd = :graduniv_dept_code')
# 登録年
if len(self.record.drda_yera + self.record.drday_year) > 0:
set_clauses.append('drday_era = :drda_yera')
set_clauses.append('drday_year = :drday_year')
set_clauses.append('drday_y = :drday_y')
# 住所不明
if len(self.record.dr_addr_lost_code) > 0:
set_clauses.append('addr_unknown_reason_cd = :dr_addr_lost_code')
# 住所
# 集合項目のいずれかに入力がある場合に更新
if sum(len(item) for item in self.record.address_aggregation_items) > 0:
set_clauses.append('home_addr_kana = :dr_addr_kana')
set_clauses.append('home_addr = :dr_addr')
set_clauses.append('home_postal_number = :dr_zip_code')
set_clauses.append('addr_village_cd = :addr_village_cd')
set_clauses.append('prefc_cd = :pref_code')
set_clauses.append('city_cd = :city_code')
set_clauses.append('addr_display_number = :dr_addr_num')
set_clauses.append('addr_cnt_kana = :addr_cnt_kana')
set_clauses.append('addr_cnt = :addr_cnt')
# 自宅電話番号
if len(self.record.dr_tel) > 0:
set_clauses.append('home_phone_number = :dr_tel')
# 利用停止区分
if len(self.record.use_stop_flag) > 0:
set_clauses.append('use_stop_div = :use_stop_flag')
# 利用停止理由
if len(self.record.use_stopc_ode) > 0:
set_clauses.append('use_stop_reason_cd = :use_stopc_ode')
# 利用停止登録年月日
if len(self.record.cre_stop_date) > 0:
set_clauses.append('use_stop_regist_ymd = :cre_stop_date')
# 利用停止解除年月日
if len(self.record.release_date) > 0:
set_clauses.append('use_stop_cancel_ymd = :release_date')
# 開勤区分
if len(self.record.pract_class_code) > 0:
set_clauses.append('estab_div_cd = :pract_class_code')
# 開業年
if len(self.record.pract_yearera + self.record.pract_year) > 0:
set_clauses.append('estab_era = :pract_yearera')
set_clauses.append('estab_year = :pract_year')
set_clauses.append('estab_y = :estab_y')
# 一括登録フラグ
if len(self.record.bskregst_flag) > 0:
set_clauses.append('lump_regist_flg = :bskregst_flag')
return set_clauses
def __make_delete_insert_trt_query(self):
# 診療科目(集合項目)のいずれかに入力がある場合
if sum(len(item) for item in self.record.medsbj_code_items) == 0:
return
# 削除
self.queries.append(self.PHYSICAL_DELETE_QUERY_TRT)
# @マークの場合は、登録しない
if self.record.medsbj_code1 == '@':
return
# 登録
for num, m_code in enumerate(self.record.medsbj_code_items, start=1):
if len(m_code) > 0:
insert_trt_query = self.INSERT_QUERY_TRT.format(
trt_course_code=f':medsbj_code{num}',
trt_sequence=num
)
self.queries.append(insert_trt_query)
return
def __set_era(self):
# 西暦の取得
# 生年月日(西暦)
western_year: str = ""
if self.record.birthday_era != "@" and len(self.record.birthday_era) > 0 and len(self.record.birthday_year) > 0:
self.query_parameter['era_cd'] = self.record.birthday_era
record_year = self.db.execute_select(self.YEAR_GET_QUERY, self.query_parameter)
western_year = str(int(record_year[0]['year']) + int(self.record.birthday_year))
# 生年月日の設定
self.query_parameter['birth_day'] = self.__set_birthday(western_year)
# 開業年(西暦)
if self.record.pract_yearera != "@" and len(self.record.pract_yearera) > 0 and len(self.record.pract_year) > 0:
self.query_parameter['era_cd'] = self.record.pract_yearera
record_year = self.db.execute_select(self.YEAR_GET_QUERY, self.query_parameter)
self.query_parameter['estab_y'] = str(int(record_year[0]['year']) + int(self.record.pract_year))
# 卒業年(西暦)
if self.record.grad_yearera != "@" and len(self.record.grad_yearera) > 0 and len(self.record.grad_year) > 0:
self.query_parameter['era_cd'] = self.record.grad_yearera
record_year = self.db.execute_select(self.YEAR_GET_QUERY, self.query_parameter)
self.query_parameter['grad_y'] = str(int(record_year[0]['year']) + int(self.record.grad_year))
# 登録年(西暦)
if self.record.drda_yera != "@" and len(self.record.drda_yera) > 0 and len(self.record.drday_year) > 0:
self.query_parameter['era_cd'] = self.record.drda_yera
record_year = self.db.execute_select(self.YEAR_GET_QUERY, self.query_parameter)
self.query_parameter['drday_y'] = str(int(record_year[0]['year']) + int(self.record.drday_year))
return
def __set_birthday(self, western_year: str = ""):
# 生年月日を設定
# 年(西暦)、月、日を連結
birth_day = ''.join([western_year, self.record.birthday_month, self.record.birthday_day])
# 年(西暦)、月、日が全て揃っている場合
if len(birth_day) == 8:
return birth_day
# 西暦が空の場合、先頭に半角スペース4つで連結
if len(western_year) == 0 and len(self.record.birthday_month) > 0 and len(self.record.birthday_day) > 0:
return f' {self.record.birthday_month}{self.record.birthday_day}'
# 月日も空の場合、生年月日は空
return ''
def __set_clearing_item(self):
# 削除予定理由
if self.record.drdel_code == '@':
self.query_parameter['drdel_code'] = None
# 重複時相手先コード
if self.record.reptdr_id == '@':
self.query_parameter['opp_dup_code'] = None
# 生年月日
if self.record.birthday_era == '@':
self.query_parameter['birthday_era'] = None
self.query_parameter['birthday_year'] = None
self.query_parameter['birthday_month'] = None
self.query_parameter['birthday_day'] = None
self.query_parameter['birth_day'] = None
# 卒年
if self.record.grad_yearera == '@':
self.query_parameter['grad_yearera'] = None
self.query_parameter['grad_year'] = None
self.query_parameter['grad_y'] = None
# 登録年
if self.record.drda_yera == '@':
self.query_parameter['drda_yera'] = None
self.query_parameter['drday_year'] = None
self.query_parameter['drday_y'] = None
# 住所不明
if self.record.dr_addr_lost_code == '@':
self.query_parameter['dr_addr_lost_code'] = None
# 自宅電話番号
if self.record.dr_tel == '@':
self.query_parameter['dr_tel'] = None
# 利用停止区分
if self.record.use_stop_flag == '@':
self.query_parameter['use_stop_flag'] = None
# 利用停止理由
if self.record.use_stopc_ode == '@':
self.query_parameter['use_stopc_ode'] = None
# 利用停止登録年月日
if self.record.cre_stop_date == '@':
self.query_parameter['cre_stop_date'] = None
# 利用停止解除年月日
if self.record.release_date == '@':
self.query_parameter['release_date'] = None
# 開業年
if self.record.pract_yearera == '@':
self.query_parameter['pract_yearera'] = None
self.query_parameter['pract_year'] = None
self.query_parameter['estab_y'] = None
return

View File

@ -0,0 +1,97 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_dr_sosiety import ComDrSosiety
class ComDrSosietyMapper(UltmarcTableMapper):
"""レイアウト区分521: COM_所属学会 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_dr_sosiety
WHERE
sosiety_cd = :sosiety_cd
AND
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_dr_sosiety
(
sosiety_cd,
dcf_pcf_dr_cd,
sosiety_f,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:sosiety_cd,
:dcf_pcf_dr_cd,
:sosiety_f,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
UPDATE_QUERY = """\
UPDATE
src05.com_dr_sosiety
SET
sosiety_f = :sosiety_f,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
sosiety_cd = :sosiety_cd
AND
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
"""
# 削除『修正区分がB(修正)かつ追加削除区分が1(退職)』SQL
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_dr_sosiety
WHERE
sosiety_cd = :sosiety_cd
AND
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
"""
record: ComDrSosiety
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComDrSosiety)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 削除『修正区分がB(修正)かつ追加削除区分が1(退職)』
if self.record.maint_flag == 'B' and self.record.cont_flag == '1':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,281 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_dr_wrkplace import ComDrWrkplace
class ComDrWrkplaceMapper(UltmarcTableMapper):
"""レイアウト区分502:COM_医師勤務先 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_dr_wrkplace
WHERE
dcf_pcf_dr_cd = :full_dcfdr_code
AND dcf_dsf_inst_cd = :full_hp_code
"""
# COM_所属部課から所属部科カナと所属部科名を取得するSQL
GET_NAME_FROM_COM_BLNG_SEC_QUERY = """\
SELECT
blng_sec_kana,
blng_sec_name
FROM
src05.com_blng_sec
WHERE
blng_sec_cd = :sectcode
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_dr_wrkplace (
dcf_dsf_inst_cd,
dcf_pcf_dr_cd,
blng_sec_cd,
post_cd,
identity_cd,
aply_start_ymd,
blng_sec_name_kana,
blng_sec_name,
notdm_flg,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:full_hp_code,
:full_dcfdr_code,
:sectcode,
:postcode,
:identitycode,
:syor_date,
:sectname_kana,
:sectname,
:notdm_flg,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE src05.com_dr_wrkplace
SET
{update_columns}
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
DCF_PCF_DR_CD= :full_dcfdr_code
AND DCF_DSF_INST_CD = :full_hp_code
"""
# 更新・削除の場合に該当のデータを履歴テーブルに退避するSQL
TO_HISTORY_QUERY = """\
INSERT INTO src05.com_dr_wrkplace_his (
dcf_dsf_inst_cd,
dcf_pcf_dr_cd,
blng_sec_cd,
post_cd,
identity_cd,
aply_start_ymd,
blng_sec_name_kana,
blng_sec_name,
notdm_flg,
aply_end_ymd,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
SELECT
dcf_dsf_inst_cd,
dcf_pcf_dr_cd,
(
CASE
WHEN blng_sec_cd = '' THEN '9999'
ELSE blng_sec_cd
END
) AS blng_sec_cd,
(
CASE
WHEN post_cd = '' THEN '999'
ELSE post_cd
END
) AS post_cd,
(
CASE
WHEN identity_cd = '' THEN '999'
ELSE identity_cd
END
) AS identity_cd,
DATE_FORMAT(aply_start_ymd, '%Y%m%d'),
blng_sec_name_kana,
blng_sec_name,
notdm_flg,
(
CASE
WHEN DATE_FORMAT(aply_start_ymd, '%Y%m%d') > DATE_FORMAT(DATE_SUB(:syor_date, INTERVAL 1 DAY), '%Y%m%d') THEN DATE_FORMAT(aply_start_ymd, '%Y%m%d')
ELSE DATE_FORMAT(DATE_SUB(:syor_date, INTERVAL 1 DAY), '%Y%m%d')
END
) AS aply_end_ymd,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
FROM
src05.com_dr_wrkplace
WHERE
dcf_pcf_dr_cd = :full_dcfdr_code
AND dcf_dsf_inst_cd = :full_hp_code
AND aply_start_ymd < :syor_date
"""
# 退職レコードの場合、物理削除するSQL
PHYSICAL_DELETE_FOR_RETIREMENT_QUERY = """\
DELETE FROM src05.com_dr_wrkplace
WHERE
dcf_pcf_dr_cd = :full_dcfdr_code
AND dcf_dsf_inst_cd = :full_hp_code
"""
record: ComDrWrkplace
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComDrWrkplace)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 所属部科を取得し、所属部科カナと所属部科名を設定する
self.__set_sect_name_and_sect_name_kana()
# 追加/更新クエリを生成
# 履歴へのレコード登録もあるため、リストで返る
self.queries = self.__make_queries()
return
def __set_sect_name_and_sect_name_kana(self):
# 所属部科を取得し、所属部科カナと所属部科名を設定する
com_blng_sec_records = self.db.execute_select(self.GET_NAME_FROM_COM_BLNG_SEC_QUERY, self.query_parameter)
if len(com_blng_sec_records) == 0:
return
# 必ず1件
com_blng_sec_record = com_blng_sec_records[0]
if com_blng_sec_record['blng_sec_kana'] != '' and com_blng_sec_record['blng_sec_kana'] is not None:
self.record.sectname_kana = com_blng_sec_record['blng_sec_kana']
self.query_parameter['sectname_kana'] = com_blng_sec_record['blng_sec_kana']
if com_blng_sec_record['blng_sec_name'] != '' and com_blng_sec_record['blng_sec_name'] is not None:
self.record.sectname = com_blng_sec_record['blng_sec_name']
self.query_parameter['sectname'] = com_blng_sec_record['blng_sec_name']
def __make_queries(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 「@」による項目クリアを設定
self.__set_clearing_item()
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return [self.INSERT_QUERY]
# 存在する場合はUpdate
# 予備/退職異動区分が1(削除:退職)の場合、退職済みのレコードとして処理
if self.record.drretflag == '1':
# 履歴への移動と、退職レコードの物理削除
return [self.TO_HISTORY_QUERY, self.PHYSICAL_DELETE_FOR_RETIREMENT_QUERY]
# 履歴への移動+更新クエリ生成
return self.__make_update_query()
def __make_update_query(self):
make_history_query = None
# 履歴レコード作成判断となる、UPDATE SET句を作成
set_clauses_with_historical = self.__make_update_columns_with_historical()
# 履歴レコード作成判断とならない、UPDATE SET句
set_clause_without_historical = []
# DM不可フラグの値をセット
# 履歴レコード作成判断とならないため、後から設定
if self.record.notdm_flg != '':
set_clause_without_historical.append(f'notdm_flg = :notdm_flg')
# 何かしら更新がある場合、履歴作成クエリを作成
if len(set_clauses_with_historical) != 0:
make_history_query = self.TO_HISTORY_QUERY
update_columns = ','.join(set_clauses_with_historical + set_clause_without_historical)
if len(update_columns) == 0:
update_query = None
else:
# 何かしら更新がある場合、末尾にカンマを付けてSET句を完成させる
update_columns += ','
update_query = self.UPDATE_QUERY.format(
update_columns=update_columns
)
return [make_history_query, update_query]
def __make_update_columns_with_historical(self):
# 履歴レコードの作成有無を判断するカラムの更新設定
# DM不可フラグは、履歴レコードの作成判断に使わないため、この関数の外で判定する
set_clauses = []
# 役職コード
if self.record.postcode != '':
set_clauses.append('post_cd = :postcode')
# 大学順位
if self.record.identitycode != '':
set_clauses.append('identity_cd = :identitycode')
# 所属部科(集合項目)
if self.record.sectcode != '':
# 所属部科コード
set_clauses.append('blng_sec_cd = :sectcode')
# 所属部科(カナ)
set_clauses.append(f'blng_sec_name_kana = :sectname_kana')
# 所属部科(漢字)
set_clauses.append(f'blng_sec_name = :sectname')
# 何かしら更新がある場合、適用開始日をセットする
if len(set_clauses) != 0:
# 処理日はパラメータに設定済み
set_clauses.append("aply_start_ymd = DATE_FORMAT(:syor_date, '%Y%m%d')")
return set_clauses
def __set_clearing_item(self):
# 役職コード
if self.record.postcode == '@':
# NOT NULLのため、空文字をセット
self.query_parameter['postcode'] = ''
# 大学順位
if self.record.identitycode == '@':
# NOT NULLのため、空文字をセット
self.query_parameter['identitycode'] = ''
# DM不可フラグ
if self.record.notdm_flg == '@':
self.query_parameter['notdm_flg'] = None
# 以下、実際には項目のクリアが発生しない為不要なロジックだが、現行踏襲の為残しておく
# 所属部科コード
if self.record.sectcode == '@':
# NOT NULLのため、固定の所属部科コードをセット
self.query_parameter['sectcode'] = '9999'
# 所属部科(カナ)
if self.record.sectname_kana == '@':
self.query_parameter['sectname_kana'] = None
# 所属部科(漢字)
# 全角の項目なので、修正項目値として全角@が連携される
if self.record.sectname == '':
self.query_parameter['sectname'] = None
return

View File

@ -0,0 +1,95 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_forfront_med_equip import ComForfrontMedEquip
class ComForfrontMedEquipMapper(UltmarcTableMapper):
"""レイアウト区分022: COM_先端医療機器 登録処理 """
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_forfront_med_equip
WHERE
forfront_med_equip_cd = :hi_medicmach_code
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_forfront_med_equip
(
forfront_med_equip_cd,
forfront_med_equip_name,
regist_ymd,
delete_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:hi_medicmach_code,
:hi_medicmach_name,
:execute_date_str_ymd,
NULL,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE src05.com_forfront_med_equip
SET
forfront_med_equip_name = :hi_medicmach_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
forfront_med_equip_cd = :hi_medicmach_code
"""
# 修正区分が「C(削除)」の場合の更新SQL
# 削除年月日 ← システム日付
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_forfront_med_equip
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
forfront_med_equip_cd = :hi_medicmach_code
"""
record: ComForfrontMedEquip
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComForfrontMedEquip)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,96 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_hamtec import ComHamtec
class ComHamtecMapper(UltmarcTableMapper):
"""レイアウト区分021: COM_高度先進医療 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_hamtec
WHERE
hamtec_cd = :hamtec_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_hamtec
(
hamtec_cd,
hamtec_div,
hamtec_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:hamtec_cd,
:hamtec_div,
:hamtec_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_hamtec
SET
hamtec_div = :hamtec_div,
hamtec_name = :hamtec_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
hamtec_cd = :hamtec_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_hamtec
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
hamtec_cd = :hamtec_cd
"""
record: ComHamtec
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComHamtec)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,93 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_hp_assrt import ComHpAssrt
class ComHpAssrtMapper(UltmarcTableMapper):
"""レイアウト区分002: COM_病院種別 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_hp_assrt
WHERE
hp_assrt_cd = :hp_assrt_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_hp_assrt
(
hp_assrt_cd,
hp_assrt_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:hp_assrt_cd,
:hp_assrt_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_hp_assrt
SET
hp_assrt_name = :hp_assrt_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
hp_assrt_cd = :hp_assrt_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_hp_assrt
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
hp_assrt_cd = :hp_assrt_cd
"""
record: ComHpAssrt
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComHpAssrt)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,93 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_inst_div import ComInstDiv
class ComInstDivMapper(UltmarcTableMapper):
"""レイアウト区分011: COM_施設区分 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_inst_div
WHERE
inst_div_cd = :inst_div_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_inst_div
(
inst_div_cd,
inst_div_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:inst_div_cd,
:inst_div_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_inst_div
SET
inst_div_name = :inst_div_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
inst_div_cd = :inst_div_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_inst_div
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
inst_div_cd = :inst_div_cd
"""
record: ComInstDiv
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComInstDiv)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,563 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_inst import ComInst
class ComInstMapper(UltmarcTableMapper):
"""レイアウト区分101: COM_施設 登録処理"""
# レコード存在確認SQL(COM_施設)
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_inst
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
"""
# データ登録用SQL(COM_施設)
INSERT_INST_QUERY = """\
INSERT INTO src05.com_inst
(
dcf_dsf_inst_cd,
inst_div_cd,
addr_unknown_reason_cd,
form_inst_name_kana,
inst_name_kana,
form_inst_name_kanji,
inst_name_kanji,
rltd_univ_prnt_cd,
bed_num,
close_flg,
estab_sche_flg,
close_start_ym,
estab_sche_ym,
ward_abolish_flg,
inst_repre_cd,
inst_repre_kana,
inst_repre,
phone_number_non_flg,
unconf_flg,
inst_phone_number,
inst_addr_kana,
inst_addr,
postal_number,
village_cd,
prefc_cd,
city_cd,
addr_display_number,
addr_cnt_kana,
addr_cnt,
manage_cd,
delete_sche_reason_cd,
hp_assrt_cd,
dup_opp_cd,
insp_item_micrb,
insp_item_serum,
insp_item_blood,
insp_item_patho,
insp_item_paras,
insp_item_biochem,
insp_item_ri,
re_exam_cd,
prmit_bed_num_other,
prmit_bed_num_mental,
prmit_bed_num_tuber,
prmit_bed_num_infection,
prmit_bed_num_sum,
prmit_bed_num_gen,
prmit_bed_num_rcup,
prmit_bed_maint_ymd,
inst_pharm_div,
abolish_ymd,
delete_flg,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_code,
:hp_class_code,
:hp_addr_lost_code,
:hp_name_kana,
:hp_ryaku_name_kana,
:hp_name,
:hp_ryaku_name,
:univ_prnt_code,
:bed_num,
:close_flag,
:open_flag,
:close_year_month,
:open_year_month,
:close_flag2,
:inst_repre_code,
:president_kana,
:president,
:tel_nothing_flag,
:uncheck_flag,
:hp_tel,
:hp_addr_kana,
:hp_addr,
:hp_zip_code,
:village_code,
:pref_code,
:city_code,
:hp_addr_number,
:addr_cnt_kana,
:addr_cnt,
:mgt_class_code,
:hpdel_code,
:hp_kind_code,
:dup_opp_code,
:inspect_code1,
:inspect_code2,
:inspect_code3,
:inspect_code4,
:inspect_code5,
:inspect_code6,
:inspect_code7,
:reexam_flag,
:bed_num_gen,
:bed_num_psy,
:bed_num_tub,
:bed_num_epi,
:bed_num_sum,
:bed_num_gen2,
:bed_num_rest,
:bed_class_maint_date,
1,
NULL,
0,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ更新用SQL(COM_施設)
UPDATE_INST_QUERY = """\
UPDATE
src05.com_inst
SET
{update_columns}
abolish_ymd = NULL,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_inst
SET
abolish_ymd = :maint_date,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
"""
# データ登録用SQL(COM_施設診療科目)
INSERT_INST_TRT_QUERY = """\
INSERT INTO src05.com_inst_trt_course
(
dcf_dsf_inst_cd,
trt_course_cd,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_code,
{trt_course_cd},
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ削除用SQL(COM_施設診療科目)
PHYSICAL_DELETE_TRT_QUERY = """\
DELETE FROM
src05.com_inst_trt_course
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
"""
# レコード存在確認SQL(COM_特養医務室)
RECORD_EXISTS_SPCARE_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_spcare_med_office_dat
WHERE
dcf_chld_inst_cd = :dcfdsf_inst_code
"""
# データ更新用SQL(COM_特養医務室)
UPDATE_SPCARE_QUERY = """\
UPDATE
src05.com_spcare_med_office_dat
SET
dcf_prnt_inst_cd = :dcf_prnt_inst_code,
update_ymd = :execute_date_str_ymd,
delete_ymd = :delete_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_chld_inst_cd = :dcfdsf_inst_code
"""
# データ登録用SQL(COM_特養医務室)
INSERT_SPCARE_QUERY = """\
INSERT INTO src05.com_spcare_med_office_dat
(
dcf_chld_inst_cd,
dcf_prnt_inst_cd,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_code,
:dcf_prnt_inst_code,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
record: ComInst
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComInst)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.__make_upsert_query()
return
def __make_upsert_query(self):
# レコードの存在確認(施設)
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 「@」による項目クリアを設定
self.__set_clearing_item()
if record_count[0]['count_num'] == 0:
# 存在しない場合はInsert
self.queries.append(self.INSERT_INST_QUERY)
else:
# 存在する場合はUpdate(施設)
self.__make_update_query()
# 施設診療科目の削除挿入
self.__make_delete_insert_trt_query()
# 特養医務室の追加更新
self.__make_upsert_spcare_query()
return
def __make_update_query(self):
# 存在する場合はUpdate(施設)
set_clauses = [] # 設定項目
# 施設区分コード
if len(self.record.hp_class_code) > 0:
set_clauses.append('inst_div_cd = :hp_class_code')
# 正式施設名(カナ)
if len(self.record.hp_name_kana) > 0:
set_clauses.append('form_inst_name_kana = :hp_name_kana')
# 略式施設名(カナ)
if len(self.record.hp_ryaku_name_kana) > 0:
set_clauses.append('inst_name_kana = :hp_ryaku_name_kana')
# 正式施設名(漢字)
if len(self.record.hp_name) > 0:
set_clauses.append('form_inst_name_kanji = :hp_name')
# 略式施設名(漢字)
if len(self.record.hp_ryaku_name) > 0:
set_clauses.append('inst_name_kanji = :hp_ryaku_name')
# 施設住所カナ
if len(self.record.hp_addr_kana) > 0:
set_clauses.append('inst_addr_kana = :hp_addr_kana')
# 施設住所
if len(self.record.hp_addr) > 0:
set_clauses.append('inst_addr = :hp_addr')
# 郵便番号
if len(self.record.hp_zip_code) > 0:
set_clauses.append('postal_number = :hp_zip_code')
# 町字コード・都道府県コード・市区町村コード
if len(self.record.village_code + self.record.pref_code + self.record.city_code) > 0:
set_clauses.append('village_cd = :village_code')
set_clauses.append('prefc_cd = :pref_code')
set_clauses.append('city_cd = :city_code')
# 住所カウント
if len(self.record.pref_code) > 0:
set_clauses.append('addr_cnt_kana = :addr_cnt_kana')
set_clauses.append('addr_cnt = :addr_cnt')
# 住所表示番号
if len(self.record.hp_addr_number) > 0:
set_clauses.append('addr_display_number = :hp_addr_number')
# 経営体コード
if len(self.record.mgt_class_code) > 0:
set_clauses.append('manage_cd = :mgt_class_code')
# 病院種別
if len(self.record.hp_kind_code) > 0:
set_clauses.append('hp_assrt_cd = :hp_kind_code')
# 再審査コード
if len(self.record.reexam_flag) > 0:
set_clauses.append('re_exam_cd = :reexam_flag')
# 未確認フラグ
if len(self.record.uncheck_flag) > 0:
set_clauses.append('unconf_flg = :uncheck_flag')
# 削除予定理由コード
if len(self.record.hpdel_code) > 0:
set_clauses.append('delete_sche_reason_cd = :hpdel_code')
# 重複時相手先コード
if len(self.record.dup_opp_code) > 0:
set_clauses.append('dup_opp_cd = :dup_opp_code')
# 住所不明理由コード
if len(self.record.hp_addr_lost_code) > 0:
set_clauses.append('addr_unknown_reason_cd = :hp_addr_lost_code')
# 電話番号なしフラグ
if len(self.record.tel_nothing_flag) > 0:
set_clauses.append('phone_number_non_flg = :tel_nothing_flag')
# 電話番号
if len(self.record.hp_tel) > 0:
set_clauses.append('inst_phone_number = :hp_tel')
# 施設代表者コード
if len(self.record.inst_repre_code) > 0:
set_clauses.append('inst_repre_cd = :inst_repre_code')
# 代表者(カナ)
if len(self.record.president_kana) > 0:
set_clauses.append('inst_repre_kana = :president_kana')
# 代表者(漢字) ※「@」が大文字
if len(self.record.president) > 0:
set_clauses.append('inst_repre = :president')
# 開業予定フラグ・開業予定年月
if len(self.record.open_flag + self.record.open_year_month) > 0:
set_clauses.append('estab_sche_flg = :open_flag')
set_clauses.append('estab_sche_ym = :open_year_month')
# 休院フラグ・休院開始年月
if len(self.record.close_flag + self.record.close_year_month) > 0:
set_clauses.append('close_flg = :close_flag')
set_clauses.append('close_start_ym = :close_year_month')
# 関連大学親コード
if len(self.record.univ_prnt_code) > 0:
set_clauses.append('rltd_univ_prnt_cd = :univ_prnt_code')
# 病棟閉鎖フラグ
if len(self.record.close_flag2) > 0:
set_clauses.append('ward_abolish_flg = :close_flag2')
# 病床数(定員)
if self.record.bed_num is not None:
set_clauses.append('bed_num = :bed_num')
# 許可病床メンテ日付
if len(self.record.bed_class_maint_date) > 0:
set_clauses.append('prmit_bed_maint_ymd = :bed_class_maint_date')
# 許可ベッド数(合計、精神、結核、感染、その他、一般病床、療養病床)
if not self.record.prmit_bed.count(None) == len(self.record.prmit_bed):
set_clauses.append('prmit_bed_num_sum = :bed_num_sum')
set_clauses.append('prmit_bed_num_mental = :bed_num_psy')
set_clauses.append('prmit_bed_num_tuber = :bed_num_tub')
set_clauses.append('prmit_bed_num_infection = :bed_num_epi')
set_clauses.append('prmit_bed_num_other = :bed_num_gen')
set_clauses.append('prmit_bed_num_gen = :bed_num_gen2')
set_clauses.append('prmit_bed_num_rcup = :bed_num_rest')
# 検査項目(微生物、血清、血液、病理、寄生虫、生化、RI)
if sum(len(item) for item in self.record.insp_item) > 0:
set_clauses.append('insp_item_micrb = :inspect_code1')
set_clauses.append('insp_item_serum = :inspect_code2')
set_clauses.append('insp_item_blood = :inspect_code3')
set_clauses.append('insp_item_patho = :inspect_code4')
set_clauses.append('insp_item_paras = :inspect_code5')
set_clauses.append('insp_item_biochem = :inspect_code6')
set_clauses.append('insp_item_ri = :inspect_code7')
update_columns = ','.join(set_clauses)
# 何も更新項目が無い場合は更新処理は行わない
if len(update_columns) == 0:
self.queries.append(None)
return
else:
update_columns += ','
update_query = self.UPDATE_INST_QUERY.format(
update_columns=update_columns
)
self.queries.append(update_query)
return
def __make_delete_insert_trt_query(self):
# 施設診療科目の削除挿入
# 診療科目(集合項目)のいずれも入力がない場合、何もしない
if sum(len(item) for item in self.record.medsbj_code) == 0:
return
# 削除
self.queries.append(self.PHYSICAL_DELETE_TRT_QUERY)
# 診療科目の1つ目の値がクリアマーク@)の場合、診療科目を登録しない
if self.record.medsbj_code[0] == "@":
return
# 診療科目160
for medsbj_code in self.record.medsbj_code:
if len(medsbj_code) > 0:
insert_query = self.INSERT_INST_TRT_QUERY.format(
trt_course_cd=f"'{medsbj_code}'"
)
self.queries.append(insert_query)
return
def __make_upsert_spcare_query(self):
# 特養医務室の追加、更新
# DCF親施設コードがない場合、何もしない
if (self.record.dcf_prnt_inst_code == "00" or len(self.record.dcf_prnt_inst_code) == 0):
return
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_SPCARE_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
# 特養医務室の集合項目の値がクリアマーク(@)の場合、登録しない
if self.record.dcfhp_92id == "@":
return
self.queries.append(self.INSERT_SPCARE_QUERY)
return
# 存在する場合はUpdate
# 特養医務室の集合項目の値がクリアマーク(@)の場合、DCF親施設コードをクリアし、削除日を設定
if self.record.dcfhp_92id == "@":
self.query_parameter['dcf_prnt_inst_code'] = None
self.query_parameter['delete_ymd'] = self.query_parameter['execute_date_str_ymd']
else:
self.query_parameter['delete_ymd'] = None
self.queries.append(self.UPDATE_SPCARE_QUERY)
return
def __set_clearing_item(self):
# 未確認フラグ
if self.record.uncheck_flag == '@':
self.query_parameter['uncheck_flag'] = None
# 削除予定理由コード
if self.record.hpdel_code == '@':
self.query_parameter['hpdel_code'] = None
# 重複時相手先コード
if self.record.duphp_id == '@':
self.query_parameter['dup_opp_code'] = None
# 住所不明理由コード
if self.record.hp_addr_lost_code == '@':
self.query_parameter['hp_addr_lost_code'] = None
# 電話番号なしフラグ
if self.record.tel_nothing_flag == '@':
self.query_parameter['tel_nothing_flag'] = None
# 電話番号
if self.record.hp_tel == '@':
self.query_parameter['hp_tel'] = None
# 施設代表者コード
if self.record.president_id == '@':
self.query_parameter['inst_repre_code'] = None
# 代表者(カナ)
if self.record.president_kana == '@':
self.query_parameter['president_kana'] = None
# 代表者(漢字) ※全角文字のため、クリアマーク「@」が大文字
if self.record.president == '':
self.query_parameter['president'] = None
# 開業予定フラグ・開業予定年月
if self.record.open_flag == '@':
self.query_parameter['open_flag'] = None
self.query_parameter['open_year_month'] = None
# 休院フラグ・休院開始年月
if self.record.close_flag == '@':
self.query_parameter['close_flag'] = None
self.query_parameter['close_year_month'] = None
# 関連大学親コード
if self.record.assoc_parrent_id == '@':
self.query_parameter['univ_prnt_code'] = None
# 病棟閉鎖フラグ
if self.record.close_flag2 == '@':
self.query_parameter['close_flag2'] = None
# 病床数(定員)
if self.record.bed_num == '@':
self.query_parameter['bed_num'] = None
# 許可病床メンテ日付
if self.record.bed_class_maint_date == '@':
self.query_parameter['bed_class_maint_date'] = None
# 許可ベッド数(合計、精神、結核、感染、その他、一般病床、療養病床)
if self.record.bed_num_sum == '@':
self.query_parameter['bed_num_sum'] = None
self.query_parameter['bed_num_psy'] = None
self.query_parameter['bed_num_tub'] = None
self.query_parameter['bed_num_epi'] = None
self.query_parameter['bed_num_gen'] = None
self.query_parameter['bed_num_gen2'] = None
self.query_parameter['bed_num_rest'] = None
# 検査項目(微生物、血清、血液、病理、寄生虫、生化、RI)
if self.record.inspect_code1 == '@':
self.query_parameter['inspect_code1'] = None
self.query_parameter['inspect_code2'] = None
self.query_parameter['inspect_code3'] = None
self.query_parameter['inspect_code4'] = None
self.query_parameter['inspect_code5'] = None
self.query_parameter['inspect_code6'] = None
self.query_parameter['inspect_code7'] = None
return

View File

@ -0,0 +1,94 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_manage import ComManage
class ComManageMapper(UltmarcTableMapper):
"""レイアウト区分007: COM_経営体 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_manage
WHERE
manage_cd = :manage_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_manage
(
manage_cd,
manage_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:manage_cd,
:manage_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_manage
SET
manage_name = :manage_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
manage_cd = :manage_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_manage
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
manage_cd = :manage_cd
"""
record: ComManage
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComManage)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,117 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_med_area_city import ComMedAreaCity
class ComMedAreaCityMapper(UltmarcTableMapper):
"""レイアウト区分124: COM_医療圏都道府県市町村対応表 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_med_area_city
WHERE
prefc_cd = :prefc_cd
AND
med_sphe_cd = :med_sphe_cd
AND
jis_prefc_cd = :jis_prefc_cd
AND
jis_city_cd = :jis_city_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_med_area_city
(
prefc_cd,
med_sphe_cd,
jis_prefc_cd,
jis_city_cd,
zen_prefcode,
zen_medareacode,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:prefc_cd,
:med_sphe_cd,
:jis_prefc_cd,
:jis_city_cd,
:zen_prefcode,
:zen_medareacode,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_med_area_city
SET
zen_prefcode = :zen_prefcode,
zen_medareacode = :zen_medareacode,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
prefc_cd = :prefc_cd
AND
med_sphe_cd = :med_sphe_cd
AND
jis_prefc_cd = :jis_prefc_cd
AND
jis_city_cd = :jis_city_cd
"""
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_med_area_city
WHERE
prefc_cd = :prefc_cd
AND
med_sphe_cd = :med_sphe_cd
AND
jis_prefc_cd = :jis_prefc_cd
AND
jis_city_cd = :jis_city_cd
"""
record: ComMedAreaCity
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComMedAreaCity)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
if self.record.maintflag == 'B' and self.record.addDelDiv == '1':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,97 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_med_func_valuation import ComMedFuncValuation
class ComMedFuncValuationMapper(UltmarcTableMapper):
"""レイアウト区分024: COM_医療機器評価 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_med_func_valuation
WHERE
med_func_valuation_cd = :med_func_valuation_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_med_func_valuation
(
med_func_valuation_cd,
med_func_valuation_name,
regist_ymd,
update_ymd,
delete_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:med_func_valuation_cd,
:med_func_valuation_name,
:execute_date_str_ymd,
:execute_date_str_ymd,
NULL,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
UPDATE_QUERY = """\
UPDATE
src05.com_med_func_valuation
SET
med_func_valuation_name = :med_func_valuation_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
med_func_valuation_cd = :med_func_valuation_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_med_func_valuation
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
med_func_valuation_cd = :med_func_valuation_cd
"""
record: ComMedFuncValuation
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComMedFuncValuation)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,129 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_med_prefc import ComMedPrefc
class ComMedPrefcMapper(UltmarcTableMapper):
"""レイアウト区分121: COM_医療圏都道府県 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_med_prefc
WHERE
pref_code = :pref_code
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_med_prefc
(
pref_code,
rev_date,
post_date,
psy_bednumtg,
psy_bednumgen,
psy_bednumdate,
psy_eqbednum,
tb_bednumtg,
tb_bednumgen,
tb_bednumdate,
tb_eqbednum,
inf_bednumtg,
inf_bednumgen,
inf_bednumdate,
inf_eqbednum,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:pref_code,
:rev_date,
:post_date,
:psy_bednum_tg,
:psy_bednum_gen,
:psy_bednum_date,
:psy_eqbed_num,
:tb_bednum_tg,
:tb_bednum_gen,
:tb_bednum_date,
:tb_eqbed_num,
:inf_bednum_tg,
:inf_bednum_gen,
:inf_bednum_date,
:inf_eqbed_num,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_med_prefc
SET
rev_date = :rev_date,
post_date = :post_date,
psy_bednumtg = :psy_bednum_tg,
psy_bednumgen = :psy_bednum_gen,
psy_bednumdate = :psy_bednum_date,
psy_eqbednum = :psy_eqbed_num,
tb_bednumtg = :tb_bednum_tg,
tb_bednumgen = :tb_bednum_gen,
tb_bednumdate = :tb_bednum_date,
tb_eqbednum = :tb_eqbed_num,
inf_bednumtg = :inf_bednum_tg,
inf_bednumgen = :inf_bednum_gen,
inf_bednumdate = :inf_bednum_date,
inf_eqbednum = :inf_eqbed_num,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
pref_code = :pref_code
"""
# 修正区分が「C(削除)」の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_med_prefc
WHERE
pref_code = :pref_code
"""
record: ComMedPrefc
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComMedPrefc)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、物理削除
if self.record.maintflag == 'C':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,95 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_nurse_assrt import ComNurseAssrt
class ComNurseAssrtMapper(UltmarcTableMapper):
"""レイアウト区分023: COM_看護種別 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_nurse_assrt
WHERE
nurse_assrt_cd = :nurse_assrt_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_nurse_assrt
(
nurse_assrt_cd,
nurse_assrt_name,
regist_ymd,
delete_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:nurse_assrt_cd,
:nurse_assrt_name,
:execute_date_str_ymd,
NULL,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_nurse_assrt
SET
nurse_assrt_name = :nurse_assrt_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
nurse_assrt_cd = :nurse_assrt_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_nurse_assrt
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
nurse_assrt_cd = :nurse_assrt_cd
"""
record: ComNurseAssrt
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComNurseAssrt)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はupdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,327 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_pharm import ComPharm
class ComPharmMapper(UltmarcTableMapper):
"""レイアウト区分102: COM_薬局 登録処理 """
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_pharm
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_pharm
(
dcf_dsf_inst_cd,
inst_div_cd,
addr_unknown_reason_cd,
form_inst_name_kana,
inst_name_kana,
form_inst_name_kanji,
inst_name_kanji,
close_flg,
estab_sche_flg,
close_start_ym,
estab_sche_ym,
inst_repre_kana,
inst_repre,
phone_number_non_flg,
unconf_flg,
inst_phone_number,
inst_addr_kana,
inst_addr,
postal_number,
village_cd,
prefc_cd,
city_cd,
addr_display_number,
addr_cnt_kana,
addr_cnt,
manage_cd,
delete_sche_reason_cd,
dup_opp_cd,
supervising_pharmacist,
supervising_pharmacist_kana,
franchise_hq_cd,
inst_pharm_div,
abolish_ymd,
delete_flg,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_cd,
:hpclass_code,
:hp_addr_lost_code,
:hp_name_kana,
:hp_ryaku_name_kana,
:hp_name,
:hp_ryaku_name,
:close_flg,
:open_flag,
:close_yearmonth,
:open_yearmonth,
:president_Kana,
:president,
:tel_nothing_flag,
:unconf_flg,
:tel_number,
:addr_kana,
:addr,
:zip_code,
:village_code,
:prefc_cd,
:city_cd,
:addr_number,
:addr_count_kana,
:addr_count,
:mgtclass_code,
:del_cd,
:dup_opp_cd,
:pharmacist,
:pharmacist_kana,
:franchise_hq_cd,
2,
NULL,
0,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE src05.com_pharm
SET
{update_columns}
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
# 廃業年月日 ← メンテナンス年月日
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_pharm
SET
abolish_ymd = :maintdate,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_cd
"""
record: ComPharm
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComPharm)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# 「@」による項目クリアを設定
self.__set_clearing_item()
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 更新の場合
update_columns = ','.join(self.__make_update_query())
# 何も更新項目が無い場合はNoneとする更新処理は行わない
if len(update_columns) == 0:
return None
else:
# 末尾にカンマを付けてSET句を完成させる
update_columns += ','
update_query = self.UPDATE_QUERY.format(
update_columns=update_columns
)
return update_query
def __make_update_query(self):
set_clauses = [] # 設定項目
# 設定項目の判定
# DCFDSF施設コード(主キーなので更新対象外)
# 施設区分コード
if self.record.hpclass_code != '':
set_clauses.append('inst_div_cd = :hpclass_code')
# 住所不明理由コード
if self.record.hp_addr_lost_code != '':
set_clauses.append('addr_unknown_reason_cd = :hp_addr_lost_code')
# 正式施設名カナ
if self.record.hp_name_kana != '':
set_clauses.append('form_inst_name_kana = :hp_name_kana')
# 施設名カナ
if self.record.hp_ryaku_name_kana != '':
set_clauses.append('inst_name_kana = :hp_ryaku_name_kana')
# 正式施設名(漢字)
if self.record.hp_name != '':
set_clauses.append('form_inst_name_kanji = :hp_name')
# 施設名(漢字)
if self.record.hp_ryaku_name != '':
set_clauses.append('inst_name_kanji = :hp_ryaku_name')
# 休院フラグ、休院予定年月
if len(self.record.close_flg + self.record.close_yearmonth) > 0:
set_clauses.append('close_flg = :close_flg')
set_clauses.append('close_start_ym = :close_yearmonth')
# 開業予定フラグ、開業予定年月
if len(self.record.open_flag + self.record.open_yearmonth) > 0:
set_clauses.append('estab_sche_flg = :open_flag')
set_clauses.append('estab_sche_ym = :open_yearmonth')
# 施設代表者カナ
if self.record.president_Kana != '':
set_clauses.append('inst_repre_kana = :president_Kana')
# 施設代表者
if self.record.president != '':
set_clauses.append('inst_repre = :president')
# 電話番号なしフラグ
if self.record.tel_nothing_flag != '':
set_clauses.append('phone_number_non_flg = :tel_nothing_flag')
# 未確認フラグ
if self.record.unconf_flg != '':
set_clauses.append('unconf_flg = :unconf_flg')
# 施設電話番号
if self.record.tel_number != '':
set_clauses.append('inst_phone_number = :tel_number')
# 施設住所カナ
if self.record.addr_kana != '':
set_clauses.append('inst_addr_kana = :addr_kana')
# 施設住所
if self.record.addr != '':
set_clauses.append('inst_addr = :addr')
# 郵便番号
if self.record.zip_code != '':
set_clauses.append('postal_number = :zip_code')
# 町字コード(住所コード)
if len(self.record.village_code) > 0:
set_clauses.append('village_cd = :village_code') # 住所コード
set_clauses.append('prefc_cd = :prefc_cd') # 都道府県コード
set_clauses.append('city_cd = :city_cd') # 市区町村コード
# 住所表示番号
if self.record.addr_number != '':
set_clauses.append('addr_display_number = :addr_number')
# 住所カウント(集合項目である県コードが入っていればカウントをセットする)
if len(self.record.prefc_cd) > 0:
set_clauses.append('addr_cnt = :addr_count') # 住所カウント
set_clauses.append('addr_cnt_kana = :addr_count_kana') # 住所カウントカナ
# 経営体コード
if self.record.mgtclass_code != '':
set_clauses.append('manage_cd = :mgtclass_code')
# 削除予定理由コード
if self.record.del_cd != '':
set_clauses.append('delete_sche_reason_cd = :del_cd')
# 重複時相手先コード
if self.record.dup_opp_cd != '':
set_clauses.append('dup_opp_cd = :dup_opp_cd')
# 管理薬剤師名(漢字)※@が大文字
if self.record.pharmacist != '':
set_clauses.append('supervising_pharmacist = :pharmacist')
# 管理薬剤師名(カナ)
if self.record.pharmacist_kana != '':
set_clauses.append('supervising_pharmacist_kana = :pharmacist_kana')
# チェーン店本部コード
if self.record.franchise_hq_id != '':
set_clauses.append('franchise_hq_cd = :franchise_hq_cd')
return set_clauses
def __set_clearing_item(self):
# 住所不明理由コード
if self.record.hp_addr_lost_code == '@':
self.query_parameter['hp_addr_lost_code'] = None
# 休院フラグ、休院予定年月
if self.record.close_flg == '@':
self.query_parameter['close_flg'] = None
self.query_parameter['close_yearmonth'] = None
# 開業予定フラグ、開業予定年月
if self.record.open_flag == '@':
self.query_parameter['open_flag'] = None
self.query_parameter['open_yearmonth'] = None
# 施設代表者カナ
if self.record.president_Kana == '@':
self.query_parameter['president_Kana'] = None
# 施設代表者 ※@が大文字
if self.record.president == '':
self.query_parameter['president'] = None
# 電話番号なしフラグ
if self.record.tel_nothing_flag == '@':
self.query_parameter['tel_nothing_flag'] = None
# 未確認フラグ
if self.record.unconf_flg == '@':
self.query_parameter['unconf_flg'] = None
# 施設電話番号
if self.record.tel_number == '@':
self.query_parameter['tel_number'] = None
# 住所表示番号
if self.record.addr_number == '@':
self.query_parameter['addr_number'] = None
# 削除予定理由コード
if self.record.del_cd == '@':
self.query_parameter['del_cd'] = None
# 重複時相手先コード
if self.record.duphp_id == '@':
self.query_parameter['dup_opp_cd'] = None
# 管理薬剤師名(漢字) ※@が大文字
if self.record.pharmacist == '':
self.query_parameter['pharmacist'] = None
# 管理薬剤師名(カナ)
if self.record.pharmacist_kana == '@':
self.query_parameter['pharmacist_kana'] = None
# チェーン店本部コード
if self.record.franchise_hq_id == '@':
self.query_parameter['franchise_hq_cd'] = None
return

View File

@ -0,0 +1,96 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_policy_med import ComPolicyMed
class ComPolicyMedMapper(UltmarcTableMapper):
"""レイアウト区分028: COM_政策医療 登録処理 """
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_policy_med
WHERE
policy_med_cd = :policy_med_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_policy_med
(
policy_med_cd,
field_name,
regist_ymd,
delete_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:policy_med_cd,
:field_name,
:execute_date_str_ymd,
NULL,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE src05.com_policy_med
SET
field_name = :field_name,
update_ymd = :execute_date_str_ymd,
delete_ymd = NULL,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
policy_med_cd = :policy_med_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
# 削除年月日 ← システム日付
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_policy_med
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
policy_med_cd = :policy_med_cd
"""
record: ComPolicyMed
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComPolicyMed)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,92 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_post import ComPost
class ComPostMapper(UltmarcTableMapper):
"""レイアウト区分005: COM_役職 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_post
WHERE
post_cd = :post_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_post
(
post_cd,
form_post_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:post_cd,
:form_post_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
UPDATE_QUERY = """\
UPDATE
src05.com_post
SET
form_post_name = :form_post_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
post_cd = :post_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_post
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
post_cd = :post_cd
"""
record: ComPost
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComPost)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,489 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_prefc_med_base import \
ComPrefcMedBase
class ComPrefcMedBaseMapper(UltmarcTableMapper):
"""レイアウト区分132: COM_都道府県医療機能情報(基本)"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_prefc_med_base
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_prefc_med_base
(
dcf_dsf_inst_cd,
info_date,
home_page,
hppre_flg,
expre_flg,
trial_flg,
trial_contcount,
trialwhet_from,
trialwhet_to,
equipment_flg,
cos_disease_flg,
cos_surgery,
specialclinic_flg,
establishment_flg,
critical_flg,
cop_system,
sys_exists_flg,
sys_inspection,
sys_prescription,
sys_reserv,
icduse_flg,
echart_flg,
fulltime_flg,
fulltime_count,
ge_patient_avg,
mt_patient_avg,
mc_patient_avg,
ca_patient_avg,
pys_patient_avg,
tub_patient_avg,
inf_patient_avg,
patient_avg_sum,
patient_avg_from,
patient_avg_to,
cl_patient_avg,
cl_patient_avg_from,
cl_patient_avg_to,
hm_patient_avg,
hm_patient_avg_from,
hm_patient_avg_to,
ge_patient_ex,
mt_patient_ex,
mc_patient_ex,
ca_patient_ex,
pys_patient_ex,
tub_patient_ex,
inf_patient_ex,
patient_ex_sum,
patient_ex_from,
patient_ex_to,
cl_patient_ex,
cl_patient_ex_from,
cl_patient_ex_to,
hm_patient_ex,
hm_patient_ex_from,
hm_patient_ex_to,
ge_stay_avg,
mt_stay_avg,
mc_stay_avg,
ca_stay_avg,
pys_stay_avg,
tub_stay_avg,
inf_stay_avg,
stay_avg_sum,
stay_avg_from,
stay_avg_to,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_code,
:info_date,
:home_page,
:hppre_flg,
:expre_flg,
:trial_flg,
:trial_cont_count,
:trial_whet_from,
:trial_whet_to,
:equipment_flg,
:cos_disease_flg,
:cos_surgery,
:specialclinic_flg,
:establishment_flg,
:critical_flg,
:cop_system,
:sys_exists_flg,
:sys_inspection,
:sys_prescription,
:sys_reserv,
:icduse_flg,
:echart_flg,
:fulltime_flg,
:fulltime_count,
:ge_patient_avg,
:mt_patient_avg,
:mc_patient_avg,
:ca_patient_avg,
:pys_patient_avg,
:tub_patient_avg,
:inf_patient_avg,
:patient_avg_sum,
:patient_avg_from,
:patient_avg_to,
:cl_patient_avg,
:cl_patient_avg_from,
:cl_patient_avg_to,
:hm_patient_avg,
:hm_patient_avg_from,
:hm_patient_avg_to,
:ge_patient_ex,
:mt_patient_ex,
:mc_patient_ex,
:ca_patient_ex,
:pys_patient_ex,
:tub_patient_ex,
:inf_patient_ex,
:patient_ex_sum,
:patient_ex_from,
:patient_ex_to,
:cl_patient_ex,
:cl_patient_ex_from,
:cl_patient_ex_to,
:hm_patient_ex,
:hm_patient_ex_from,
:hm_patient_ex_to,
:ge_stay_avg,
:mt_stay_avg,
:mc_stay_avg,
:ca_stay_avg,
:pys_stay_avg,
:tub_stay_avg,
:inf_stay_avg,
:stay_avg_sum,
:stay_avg_from,
:stay_avg_to,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_prefc_med_base
SET
{update_columns}
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
"""
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_prefc_med_base
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
"""
record: ComPrefcMedBase
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComPrefcMedBase)
program_name = __name__.split(".")[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter["program_name"] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {
**self.query_parameter,
**self.record.to_sql_parameter(),
}
def make_query(self):
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
if self.record.maint_flag == "B" and self.record.adddel_div == "1":
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# 「@」による項目クリアを設定
self.__set_clearing_item()
# レコードの存在確認
record_count = self.db.execute_select(
self.RECORD_EXISTS_QUERY, self.query_parameter
)
# 存在しない場合はInsert
if record_count[0]["count_num"] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.__make_update_query()
def __make_update_query(self):
set_clauses = [] # 設定項目
# 情報年月日
if len(self.record.info_date) > 0:
set_clauses.append("info_date = :info_date")
# 案内用ホームページアドレス
if len(self.record.home_page) > 0:
set_clauses.append("home_page = :home_page")
# 院内処方フラグ
if len(self.record.hppre_flg) > 0:
set_clauses.append("hppre_flg = :hppre_flg")
# 院外処方フラグ
if len(self.record.expre_flg) > 0:
set_clauses.append("expre_flg = :expre_flg")
# 治験の実施
if len(self.record.trial_flg) > 0:
set_clauses.append("trial_flg = :trial_flg")
set_clauses.append("trial_contcount = :trial_cont_count")
set_clauses.append("trialwhet_from = :trial_whet_from")
set_clauses.append("trialwhet_to = :trial_whet_to")
# 保有する施設設備フラグ
if len(self.record.equipment_flg) > 0:
set_clauses.append("equipment_flg = :equipment_flg")
# 対応することができる疾患・治療の内容フラグ
if len(self.record.cos_disease_flg) > 0:
set_clauses.append("cos_disease_flg = :cos_disease_flg")
# 対応することができる短期滞在手術フラグ
if len(self.record.cos_surgery) > 0:
set_clauses.append("cos_surgery = :cos_surgery")
# 専門外来フラグ
if len(self.record.specialclinic_flg) > 0:
set_clauses.append("specialclinic_flg = :specialclinic_flg")
# 地域医療連携体制_窓口設置フラグ
if len(self.record.establishment_flg) > 0:
set_clauses.append("establishment_flg = :establishment_flg")
# 地域医療連携体制_地域連携パスフラグ
if len(self.record.critical_flg) > 0:
set_clauses.append("critical_flg = :critical_flg")
# 入院診療計画策定時における院内の連携体制
if len(self.record.cop_system) > 0:
set_clauses.append("cop_system = :cop_system")
# オーダリングシステム
if len(self.record.sys_exists_flg) > 0:
set_clauses.append("sys_exists_flg = :sys_exists_flg")
set_clauses.append("sys_inspection = :sys_inspection")
set_clauses.append("sys_prescription = :sys_prescription")
set_clauses.append("sys_reserv = :sys_reserv")
# ICDコード利用フラグ
if len(self.record.icduse_flg) > 0:
set_clauses.append("icduse_flg = :icduse_flg")
# 電子カルテフラク
if len(self.record.echart_flg) > 0:
set_clauses.append("echart_flg = :echart_flg")
# 専任従事者
if len(self.record.fulltime_flg) > 0:
set_clauses.append("fulltime_flg = :fulltime_flg")
set_clauses.append("fulltime_count = :fulltime_count")
# 病床患者数平均
if len(self.record.ge_patient_avg) > 0:
set_clauses.append("ge_patient_avg = :ge_patient_avg")
set_clauses.append("mt_patient_avg = :mt_patient_avg")
set_clauses.append("mc_patient_avg = :mc_patient_avg")
set_clauses.append("ca_patient_avg = :ca_patient_avg")
set_clauses.append("pys_patient_avg = :pys_patient_avg")
set_clauses.append("tub_patient_avg = :inf_patient_avg")
set_clauses.append("inf_patient_avg = :tub_patient_avg")
set_clauses.append("patient_avg_sum = :patient_avg_sum")
set_clauses.append("patient_avg_from = :patient_avg_from")
set_clauses.append("patient_avg_to = :patient_avg_to")
# 患者数平均
if len(self.record.cl_patient_avg) > 0:
set_clauses.append("cl_patient_avg = :cl_patient_avg")
set_clauses.append("cl_patient_avg_from = :cl_patient_avg_from")
set_clauses.append("cl_patient_avg_to = :cl_patient_avg_to")
# 患者数平均
if len(self.record.hm_patient_avg) > 0:
set_clauses.append("hm_patient_avg = :hm_patient_avg")
set_clauses.append("hm_patient_avg_from = :hm_patient_avg_from")
set_clauses.append("hm_patient_avg_to = :hm_patient_avg_to")
# 患者数延数
if len(self.record.ge_patient_ex) > 0:
set_clauses.append("ge_patient_ex = :ge_patient_ex")
set_clauses.append("mt_patient_ex = :mt_patient_ex")
set_clauses.append("mc_patient_ex = :mc_patient_ex")
set_clauses.append("ca_patient_ex = :ca_patient_ex")
set_clauses.append("pys_patient_ex = :pys_patient_ex")
set_clauses.append("tub_patient_ex = :tub_patient_ex")
set_clauses.append("inf_patient_ex = :inf_patient_ex")
set_clauses.append("patient_ex_sum = :patient_ex_sum")
set_clauses.append("patient_ex_from = :patient_ex_from")
set_clauses.append("patient_ex_to = :patient_ex_to")
# 患者数延数
if len(self.record.cl_patient_ex) > 0:
set_clauses.append("cl_patient_ex = :cl_patient_ex")
set_clauses.append("cl_patient_ex_from = :cl_patient_ex_from")
set_clauses.append("cl_patient_ex_to = :cl_patient_ex_to")
# 患者数延数
if len(self.record.hm_patient_ex) > 0:
set_clauses.append("hm_patient_ex = :hm_patient_ex")
set_clauses.append("hm_patient_ex_from = :hm_patient_ex_from")
set_clauses.append("hm_patient_ex_to = :hm_patient_ex_to")
# 平均在院日数
if len(self.record.ge_stay_avg) > 0:
set_clauses.append("ge_stay_avg = :ge_stay_avg")
set_clauses.append("mt_stay_avg = :mt_stay_avg")
set_clauses.append("mc_stay_avg = :mc_stay_avg")
set_clauses.append("ca_stay_avg = :ca_stay_avg")
set_clauses.append("pys_stay_avg = :pys_stay_avg")
set_clauses.append("tub_stay_avg = :tub_stay_avg")
set_clauses.append("inf_stay_avg = :inf_stay_avg")
set_clauses.append("stay_avg_sum = :stay_avg_sum")
set_clauses.append("stay_avg_from = :stay_avg_from")
set_clauses.append("stay_avg_to = :stay_avg_to")
update_columns = ",".join(set_clauses)
# 何も更新項目が無い場合はNoneとする更新処理は行わない
if len(update_columns) == 0:
return None
else:
# 末尾にカンマを付けてSET句を完成させる
update_columns += ","
update_query = self.UPDATE_QUERY.format(update_columns=update_columns)
return update_query
def __set_clearing_item(self):
# 情報年月日
if self.record.info_date == "@":
self.query_parameter["info_date"] = None
# 案内用ホームページアドレス
if self.record.home_page == "@":
self.query_parameter["home_page"] = None
# 院内処方フラグ
if self.record.hppre_flg == "@":
self.query_parameter["hppre_flg"] = None
# 院外処方フラグ
if self.record.expre_flg == "@":
self.query_parameter["expre_flg"] = None
# 治験の実施
if self.record.trial_flg == "@":
self.query_parameter["trial_flg"] = None
self.query_parameter["trial_cont_count"] = None
self.query_parameter["trial_whet_from"] = None
self.query_parameter["trial_whet_to"] = None
# 保有する施設設備フラグ
if self.record.equipment_flg == "@":
self.query_parameter["equipment_flg"] = None
# 対応することができる疾患・治療の内容フラグ
if self.record.cos_disease_flg == "@":
self.query_parameter["cos_disease_flg"] = None
# 対応することができる短期滞在手術フラグ
if self.record.cos_surgery == "@":
self.query_parameter["cos_surgery"] = None
# 専門外来フラグ
if self.record.specialclinic_flg == "@":
self.query_parameter["specialclinic_flg"] = None
# 地域医療連携体制_窓口設置フラグ
if self.record.establishment_flg == "@":
self.query_parameter["establishment_flg"] = None
# 地域医療連携体制_地域連携パスフラグ
if self.record.critical_flg == "@":
self.query_parameter["critical_flg"] = None
# 入院診療計画策定時における院内の連携体制
if self.record.cop_system == "@":
self.query_parameter["cop_system"] = None
# オーダリングシステム
if self.record.sys_exists_flg == "@":
self.query_parameter["sys_exists_flg"] = None
self.query_parameter["sys_inspection"] = None
self.query_parameter["sys_prescription"] = None
self.query_parameter["sys_reserv"] = None
# ICDコード利用フラグ
if self.record.icduse_flg == "@":
self.query_parameter["icduse_flg"] = None
# 電子カルテフラク
if self.record.echart_flg == "@":
self.query_parameter["echart_flg"] = None
# 専任従事者
if self.record.fulltime_flg == "@":
self.query_parameter["fulltime_flg"] = None
self.query_parameter["fulltime_count"] = None
# 病床患者数平均
if self.record.ge_patient_avg == "@":
self.query_parameter["ge_patient_avg"] = None
self.query_parameter["mt_patient_avg"] = None
self.query_parameter["mc_patient_avg"] = None
self.query_parameter["ca_patient_avg"] = None
self.query_parameter["pys_patient_avg"] = None
self.query_parameter["inf_patient_avg"] = None
self.query_parameter["tub_patient_avg"] = None
self.query_parameter["patient_avg_sum"] = None
self.query_parameter["patient_avg_from"] = None
self.query_parameter["patient_avg_to"] = None
# 患者数平均
if self.record.cl_patient_avg == "@":
self.query_parameter["cl_patient_avg"] = None
self.query_parameter["cl_patient_avg_from"] = None
self.query_parameter["cl_patient_avg_to"] = None
# 患者数平均
if self.record.hm_patient_avg == "@":
self.query_parameter["hm_patient_avg"] = None
self.query_parameter["hm_patient_avg_from"] = None
self.query_parameter["hm_patient_avg_to"] = None
# 患者数延数
if self.record.ge_patient_ex == "@":
self.query_parameter["ge_patient_ex"] = None
self.query_parameter["mt_patient_ex"] = None
self.query_parameter["mc_patient_ex"] = None
self.query_parameter["ca_patient_ex"] = None
self.query_parameter["pys_patient_ex"] = None
self.query_parameter["tub_patient_ex"] = None
self.query_parameter["inf_patient_ex"] = None
self.query_parameter["patient_ex_sum"] = None
self.query_parameter["patient_ex_from"] = None
self.query_parameter["patient_ex_to"] = None
# 患者数延数
if self.record.cl_patient_ex == "@":
self.query_parameter["cl_patient_ex"] = None
self.query_parameter["cl_patient_ex_from"] = None
self.query_parameter["cl_patient_ex_to"] = None
# 患者数延数
if self.record.hm_patient_ex == "@":
self.query_parameter["hm_patient_ex"] = None
self.query_parameter["hm_patient_ex_from"] = None
self.query_parameter["hm_patient_ex_to"] = None
# 平均在院日数
if self.record.ge_stay_avg == "@":
self.query_parameter["ge_stay_avg"] = None
self.query_parameter["mt_stay_avg"] = None
self.query_parameter["mc_stay_avg"] = None
self.query_parameter["ca_stay_avg"] = None
self.query_parameter["pys_stay_avg"] = None
self.query_parameter["tub_stay_avg"] = None
self.query_parameter["inf_stay_avg"] = None
self.query_parameter["stay_avg_sum"] = None
self.query_parameter["stay_avg_from"] = None
self.query_parameter["stay_avg_to"] = None
return

View File

@ -0,0 +1,106 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_prefc_med_dis_treat import \
ComPrefcMedDisTreat
class ComPrefcMedDisTreatMapper(UltmarcTableMapper):
"""レイアウト区分134: COM_都道府県医療機能情報(疾患治療)"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_prefc_med_dis_treat
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
treatment_code = :treatment_code
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_prefc_med_dis_treat
(
dcf_dsf_inst_cd,
treatment_code,
pre_num,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_code,
:treatment_code,
:pre_num,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_prefc_med_dis_treat
SET
pre_num = :pre_num,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
treatment_code = :treatment_code
"""
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_prefc_med_dis_treat
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
treatment_code = :treatment_code
"""
record: ComPrefcMedDisTreat
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComPrefcMedDisTreat)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
if self.record.maint_flag == 'B' and self.record.adddel_div == '1':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# 修正のレコードだった場合はNULLに変換する
if self.record.pre_num == '@':
self.query_parameter['pre_num'] = None
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
if self.record.is_pre_num_not_empty:
return self.UPDATE_QUERY
else:
return None

View File

@ -0,0 +1,107 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_prefc_med_equpment import \
ComPrefcMedEqupment
class ComPrefcMedEqupmentMapper(UltmarcTableMapper):
"""レイアウト区分133: COM_都道府県医療機能情報(施設設備)"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_prefc_med_equpment
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
equipment_code = :equipment_code
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_prefc_med_equpment
(
dcf_dsf_inst_cd,
equipment_code,
bednum,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_code,
:equipment_code,
:bed_num,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_prefc_med_equpment
SET
bednum = :bed_num,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
equipment_code = :equipment_code
"""
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_prefc_med_equpment
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
equipment_code = :equipment_code
"""
record: ComPrefcMedEqupment
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComPrefcMedEqupment)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
if self.record.maint_flag == 'B' and self.record.adddel_div == '1':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# 修正のレコードだった場合はNULLに変換する
if self.record.bed_num == '@':
self.query_parameter['bed_num'] = None
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
# 病床数が空の場合は更新しない
if self.record.is_bed_num_not_empty:
return self.UPDATE_QUERY
else:
return None

View File

@ -0,0 +1,95 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_prefc_med_sojourn_ope import ComPrefcMedSojournOpe
class ComPrefcMedSojournOpeMapper(UltmarcTableMapper):
"""レイアウト区分135: COM_都道府県医療機能情報(短期滞在手術)"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_prefc_med_sojourn_ope
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
cos_surgery_code = :cos_surgery_code
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_prefc_med_sojourn_ope
(
dcf_dsf_inst_cd,
cos_surgery_code,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_code,
:cos_surgery_code,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_prefc_med_sojourn_ope
SET
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
cos_surgery_code = :cos_surgery_code
"""
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_prefc_med_sojourn_ope
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
cos_surgery_code = :cos_surgery_code
"""
record: ComPrefcMedSojournOpe
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComPrefcMedSojournOpe)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
if self.record.maint_flag == 'B' and self.record.adddel_div == '1':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,133 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import (
UltmarcTableMapper,
)
from src.batch.ultmarc.utmp_tables.tables.com_prefc_med_sp_outpat import (
ComPrefcMedSpOutpat,
)
class ComPrefcMedSpOutpatMapper(UltmarcTableMapper):
"""レイアウト区分136: COM_都道府県医療機能情報(専門外来)"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_prefc_med_sp_outpat
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
specialclinic_name = :specialclinic_name
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_prefc_med_sp_outpat
(
dcf_dsf_inst_cd,
specialclinic_name,
sort_key,
sectsub_cd,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcfdsf_inst_code,
:specialclinic_name,
:sort_key,
:sectsub_cd,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_prefc_med_sp_outpat
SET
{update_columns}
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
specialclinic_name = :specialclinic_name
"""
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_prefc_med_sp_outpat
WHERE
dcf_dsf_inst_cd = :dcfdsf_inst_code
AND
specialclinic_name = :specialclinic_name
"""
record: ComPrefcMedSpOutpat
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComPrefcMedSpOutpat)
program_name = __name__.split(".")[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter["program_name"] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {
**self.query_parameter,
**self.record.to_sql_parameter(),
}
def make_query(self):
# 『修正区分がB(修正)かつ追加削除区分が1(退職)』の場合、物理削除
if self.record.maint_flag == "B" and self.record.adddel_div == "1":
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# 修正のレコードだった場合はNULLに変換する
if self.record.sectsub_cd == "@":
self.query_parameter["sectsub_cd"] = None
# レコードの存在確認
record_count = self.db.execute_select(
self.RECORD_EXISTS_QUERY, self.query_parameter
)
# 存在しない場合はInsert
if record_count[0]["count_num"] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.__make_update_query()
def __make_update_query(self):
set_clauses = [] # 設定項目
# ソートキー
if len(self.record.sort_key) > 0:
set_clauses.append("sort_key = :sort_key")
# 分類補助コード
if len(self.record.sectsub_cd) > 0:
set_clauses.append("sectsub_cd = :sectsub_cd")
update_columns = ",".join(set_clauses)
# 何も更新項目が無い場合はNoneとする更新処理は行わない
if len(update_columns) == 0:
return None
else:
# 末尾にカンマを付けてSET句を完成させる
update_columns += ","
update_query = self.UPDATE_QUERY.format(update_columns=update_columns)
return update_query

View File

@ -0,0 +1,95 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_regn_critic_pass import ComRegnCriticPass
class ComRegnCriticPassMapper(UltmarcTableMapper):
"""レイアウト区分026: COM_地域クリティカルパス 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_regn_critic_pass
WHERE
regn_co_critic_pass_cd = :regn_co_critic_pass_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_regn_critic_pass
(
regn_co_critic_pass_cd,
disease_name_kanji,
regist_ymd,
delete_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:regn_co_critic_pass_cd,
:disease_name_kanji,
:execute_date_str_ymd,
NULL,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_regn_critic_pass
SET
disease_name_kanji = :disease_name_kanji,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
regn_co_critic_pass_cd = :regn_co_critic_pass_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_regn_critic_pass
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
regn_co_critic_pass_cd = :regn_co_critic_pass_cd
"""
record: ComRegnCriticPass
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComRegnCriticPass)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,94 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_rehabili import ComRehabili
class ComRehabiliMapper(UltmarcTableMapper):
"""レイアウト区分027: COM_疾患別リハビリテーション科 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_rehabili
WHERE
rehabili_cd = :rehabili_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_rehabili
(
rehabili_cd,
rehabili_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:rehabili_cd,
:rehabili_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_rehabili
SET
rehabili_name = :rehabili_name,
update_ymd = :execute_date_str_ymd,
delete_ymd = NULL,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
rehabili_cd = :rehabili_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_rehabili
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
rehabili_cd = :rehabili_cd
"""
record: ComRehabili
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComRehabili)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,113 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_snd_med_sphe import ComSndMedSphe
class ComSndMedSpheMapper(UltmarcTableMapper):
"""レイアウト区分123: COM_医療圏二次医療圏 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_snd_med_sphe
WHERE
prefc_cd = :prefc_cd
AND
med_sphe_cd = :med_sphe_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_snd_med_sphe
(
prefc_cd,
med_sphe_cd,
thrd_cd,
snd_med_sphe_name,
requd_bed_or_equip_target,
exist_bed_num,
exist_bed_num_regist_ymd,
plsmns_bed_num,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:prefc_cd,
:med_sphe_cd,
:thrd_cd,
:snd_med_sphe_name,
:requd_bed_or_equip_target,
:exist_bed_num,
:exist_bed_num_regist_ymd,
:plsmns_bed_num,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_snd_med_sphe
SET
thrd_cd = :thrd_cd,
snd_med_sphe_name = :snd_med_sphe_name,
requd_bed_or_equip_target = :requd_bed_or_equip_target,
exist_bed_num = :exist_bed_num,
exist_bed_num_regist_ymd = :exist_bed_num_regist_ymd,
plsmns_bed_num = :plsmns_bed_num,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
prefc_cd = :prefc_cd
AND
med_sphe_cd = :med_sphe_cd
"""
# 修正区分が「C(削除)」の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_snd_med_sphe
WHERE
prefc_cd = :prefc_cd
AND
med_sphe_cd = :med_sphe_cd
"""
record: ComSndMedSphe
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComSndMedSphe)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、物理削除
if self.record.maintflag == 'C':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,93 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_sosiety import ComSosiety
class ComSosietyMapper(UltmarcTableMapper):
"""レイアウト区分009: COM_学会 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_sosiety
WHERE
sosiety_cd = :sosiety_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_sosiety
(
sosiety_cd,
sosiety_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:sosiety_cd,
:sosiety_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_sosiety
SET
sosiety_name = :sosiety_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
sosiety_cd = :sosiety_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_sosiety
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
sosiety_cd = :sosiety_cd
"""
record: ComSosiety
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComSosiety)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,158 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_sp_field import ComSpField
class ComSpFieldMapper(UltmarcTableMapper):
"""レイアウト区分511: COM_専門分野 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_sp_field
WHERE
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
and
specialist_cd = :specialist_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_sp_field
(
dcf_pcf_dr_cd,
specialist_cd,
specialist_maint_div,
specialist_flg,
specialist_publsh_ymd,
ackn_med_flg,
ackn_med_publsh_ymd,
guide_med_flg,
guide_med_publsh_ymd,
regist_ymd,
delete_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:dcf_pcf_dr_cd,
:specialist_cd,
:specialist_maint_div,
:specialist_flg,
:specialist_publsh_ymd,
:ackn_med_flg,
:ackn_med_publsh_ymd,
:guide_med_flg,
:guide_med_publsh_ymd,
:execute_date_str_ymd,
NULL,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
UPDATE_QUERY = """\
UPDATE
src05.com_sp_field
SET
{update_columns}
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
and
specialist_cd = :specialist_cd
"""
# 『修正区分がB(修正)かつ専門医メンテナンス区分が1(退職)』の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_sp_field
WHERE
dcf_pcf_dr_cd = :dcf_pcf_dr_cd
and
specialist_cd = :specialist_cd
"""
record: ComSpField
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComSpField)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 『修正区分がB(修正)かつ専門医メンテナンス区分が1(退職)』の場合、物理削除
if self.record.maint_flag == 'B' and self.record.specialist_maint_div == '1':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
set_clauses = [] # 設定項目
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 「@」による項目クリアを設定
self.__set_clearing_item()
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
if len(self.record.specialist_maint_div) > 0:
set_clauses.append('specialist_maint_div = :specialist_maint_div')
if len(self.record.specialist_flg) > 0:
set_clauses.append('specialist_flg = :specialist_flg')
set_clauses.append('specialist_publsh_ymd = :specialist_publsh_ymd')
if len(self.record.ackn_med_flg) > 0:
set_clauses.append('ackn_med_flg = :ackn_med_flg')
set_clauses.append('ackn_med_publsh_ymd = :ackn_med_publsh_ymd')
if len(self.record.guide_med_flg) > 0:
set_clauses.append('guide_med_flg = :guide_med_flg')
set_clauses.append('guide_med_publsh_ymd = :guide_med_publsh_ymd')
update_columns = ','.join(set_clauses)
# 何も更新項目が無い場合はNoneとする更新処理は行わない
if len(update_columns) == 0:
return None
else:
# 末尾にカンマを付けてSET句を完成させる
update_columns += ','
update_query = self.UPDATE_QUERY.format(
update_columns=update_columns
)
return update_query
def __set_clearing_item(self):
# 専門医の集合項目クリア
if self.record.specialist_flg == '@':
self.query_parameter['specialist_flg'] = None
self.query_parameter['specialist_publsh_ymd'] = None
# 認定医の集合項目クリア
if self.record.ackn_med_flg == '@':
self.query_parameter['ackn_med_flg'] = None
self.query_parameter['ackn_med_publsh_ymd'] = None
# 指導医の集合項目クリア
if self.record.guide_med_flg == '@':
self.query_parameter['guide_med_flg'] = None
self.query_parameter['guide_med_publsh_ymd'] = None
return

View File

@ -0,0 +1,93 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_specialist_license import ComSpecialistLicense
class ComSpecialistLicenseMapper(UltmarcTableMapper):
"""レイアウト区分010: COM_専門医資格 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_specialist_license
WHERE
specialist_cd = :specialist_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_specialist_license
(
specialist_cd,
specialist_license_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:specialist_cd,
:specialist_license_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 変更用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_specialist_license
SET
specialist_license_name = :specialist_license_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
specialist_cd = :specialist_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_specialist_license
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
specialist_cd = :specialist_cd
"""
record: ComSpecialistLicense
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComSpecialistLicense)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,98 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_thrd_med import ComThrdMed
class ComThrdMedMapper(UltmarcTableMapper):
"""レイアウト区分122: COM_医療圏3次マスタ 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_thrd_med
WHERE
prefcode = :pref_code
AND
thrd_cd = :thrd_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_thrd_med
(
prefcode,
thrd_cd,
thrd_med_sphe_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:pref_code,
:thrd_cd,
:thrd_med_sphe_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# データ更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_thrd_med
SET
thrd_med_sphe_name = :thrd_med_sphe_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
prefcode = :pref_code
AND
thrd_cd = :thrd_cd
"""
# 修正区分が「C(削除)」の場合、物理削除
PHYSICAL_DELETE_QUERY = """\
DELETE FROM
src05.com_thrd_med
WHERE
prefcode = :pref_code
AND
thrd_cd = :thrd_cd
"""
record: ComThrdMed
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComThrdMed)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、物理削除
if self.record.maintflag == 'C':
self.queries.append(self.PHYSICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合はUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,99 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.com_trt_course import ComTrtCourse
class ComTrtCourseMapper(UltmarcTableMapper):
"""レイアウト区分001: COM_診療科目 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.com_trt_course
WHERE
trt_course_cd = :trt_course_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.com_trt_course
(
trt_course_cd,
trt_course_name_kana,
trt_course_name_abb,
trt_course_name,
regist_ymd,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:trt_course_cd,
:trt_course_name_kana,
:trt_course_name_abb,
:trt_course_name,
:execute_date_str_ymd,
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.com_trt_course
SET
trt_course_name_kana = :trt_course_name_kana,
trt_course_name_abb = :trt_course_name_abb,
trt_course_name = :trt_course_name,
update_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
trt_course_cd = :trt_course_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.com_trt_course
SET
delete_ymd = :execute_date_str_ymd,
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
trt_course_cd = :trt_course_cd
"""
record: ComTrtCourse
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, ComTrtCourse)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,96 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
from src.batch.ultmarc.utmp_tables.tables.mst_prefc import MstPrefc
class MstPrefcMapper(UltmarcTableMapper):
"""レイアウト区分006: 都道府県マスタ 登録処理"""
# レコード存在確認SQL
RECORD_EXISTS_QUERY = """\
SELECT
COUNT(*) AS count_num
FROM
src05.mst_prefc
WHERE
prefc_cd = :prefc_cd
"""
# データ登録用SQL
INSERT_QUERY = """\
INSERT INTO src05.mst_prefc
(
prefc_cd,
prefc_name,
prefc_name_kana,
delete_flg,
sys_regist_date,
regist_prgm_id,
sys_update_date,
update_prgm_id
)
VALUES (
:prefc_cd,
:prefc_name,
'',
'0',
:execute_datetime,
:program_name,
:execute_datetime,
:program_name
)
"""
# 更新用SQL
UPDATE_QUERY = """\
UPDATE
src05.mst_prefc
SET
prefc_name = :prefc_name,
delete_flg = '0',
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
prefc_cd = :prefc_cd
"""
# 修正区分が「C(削除)」の場合の更新SQL
LOGICAL_DELETE_QUERY = """\
UPDATE
src05.mst_prefc
SET
delete_flg = '1',
sys_update_date = :execute_datetime,
update_prgm_id = :program_name
WHERE
prefc_cd = :prefc_cd
"""
record: MstPrefc
def __init__(self, record: list[str], db) -> None:
super().__init__(record, db, MstPrefc)
program_name = __name__.split('.')[-1] # 当モジュール名(現行から変わっている)
# モジュール名をクエリパラメータに設定
self.query_parameter['program_name'] = program_name
# 読み込んだレコード値もクエリパラメータに追加
self.query_parameter = {**self.query_parameter, **self.record.to_sql_parameter()}
def make_query(self):
# 修正区分がC(削除)の場合、論理削除
if self.record.maint_flag == 'C':
self.queries.append(self.LOGICAL_DELETE_QUERY)
return
# 追加、更新の場合
self.queries.append(self.__make_upsert_query())
return
def __make_upsert_query(self):
# レコードの存在確認
record_count = self.db.execute_select(self.RECORD_EXISTS_QUERY, self.query_parameter)
# 存在しない場合はInsert
if record_count[0]['count_num'] == 0:
return self.INSERT_QUERY
# 存在する場合ではUpdate
return self.UPDATE_QUERY

View File

@ -0,0 +1,8 @@
from src.batch.ultmarc.utmp_tables.table_mapper.ultmarc_table_mapper import \
UltmarcTableMapper
class NullMapper(UltmarcTableMapper):
def make_query(self):
return super().make_query()

View File

@ -0,0 +1,50 @@
from abc import ABCMeta, abstractmethod
from datetime import datetime
from src.batch.common.batch_context import BatchContext
from src.batch.ultmarc.utmp_tables.tables.ultmarc_table import UltmarcTable
from src.db.database import Database
# 処理日を使用するために、configを使用
batch_context = BatchContext.get_instance()
class UltmarcTableMapper(metaclass=ABCMeta):
"""アルトマークテーブルへの登録処理の抽象クラス"""
record: UltmarcTable
db: Database
queries: list[str]
query_parameter: dict
def __init__(self, record: list[str], db: Database, table_class: type[UltmarcTable]) -> None:
self.record = table_class(record)
self.db = db
# 実行年月日(文字列)、実行年月日時分秒を設定
now = datetime.now()
execute_date_str_ymd = now.strftime('%Y%m%d')
execute_datetime = now.strftime('%Y/%m/%d %H:%M:%S')
# クエリリストを初期化
self.queries = []
# 共通クエリパラメータを設定
self.query_parameter = {
'execute_date_str_ymd': execute_date_str_ymd,
'execute_datetime': execute_datetime,
# バッチ共通設定から処理日を取得
'syor_date': batch_context.syor_date
}
@abstractmethod
def make_query(self):
pass
def execute_queries(self):
if len(self.queries) == 0:
raise Exception('make_queryを呼び出してから実行してください')
for query in self.queries:
if query is None:
continue
self.db.execute(query, self.query_parameter)

Some files were not shown because too many files have changed in this diff Show More