import pymysql
import pandas as pd
from datetime import datetime 
import re
from conversion_map import conversion_map
from excluded_columns import excluded_columns
from toshima_excluded_columns import toshima_excluded_columns
from schema_mismatch_columns import schema_mismatch_columns

def to_cycle_str(tsuiseki: str) -> str:
    """
    '3ヶ月後' → '01', '1年後' → '02', '2年後' → '03', ..., '50年後' → '51'
    """
    tsuiseki = tsuiseki.strip()
    if tsuiseki == "3ヶ月後":
        return "01"
    elif "年後" in tsuiseki:
        try:
            year = int(tsuiseki.replace("年後", "").strip())
            return f"{year + 1:02}"  # +1 することで3ヶ月後→01, 1年後→02, 2年後→03になる
        except:
            return "99"
    return "99"  # 未知の値

def get_kensa_metadata(column_name):
    mapping = {
        # ドナー用
        'T.Bil[検査値と合併症・再手術・再入院]': ('T.Bil', 'mg/dl', 'float', 1.2),
        'D.Bil[検査値と合併症・再手術・再入院]': ('D.Bil', 'mg/dl', 'float', 0.4),
        'AST[検査値と合併症・再手術・再入院]':   ('AST', 'U/L', 'int', 35),
        'ALT[検査値と合併症・再手術・再入院]':   ('ALT', 'U/L', 'int', 35),
        'INR[検査値と合併症・再手術・再入院]':   ('PT-INR', None, 'float', 1.1),
        '疾患の有無[検査値と合併症・再手術・再入院]': ('ワーファリンの使用', None, 'str', '無'),

        # レシピエント用（追跡時）
        'T.Bil[追跡時の検査値]': ('T.Bil', 'mg/dl', 'float', 1.2),
        'D.Bil[追跡時の検査値]': ('D.Bil', 'mg/dl', 'float', 0.4),
        'AST[追跡時の検査値]':   ('AST', 'U/L', 'int', 35),
        'ALT[追跡時の検査値]':   ('ALT', 'U/L', 'int', 35),
        'INR[追跡時の検査値]':   ('PT-INR', None, 'float', 1.1),
        'ワーファリンの使用[追跡時の検査値]': ('ワーファリンの使用', None, 'str', '無'),
    }
    return mapping.get(column_name, (None, None, None, None))

def evaluate_kensa_code(value, dtype, threshold):
    if value in [None, '']:
        return 3
    try:
        if dtype == 'float':
            return 2 if float(value) >= threshold else 1
        elif dtype == 'int':
            return 2 if int(value) >= threshold else 1
        elif dtype == 'str':
            # "無"なら2, "有"なら1, それ以外は3
            if str(value).strip() == '無':
                return 2
            elif str(value).strip() == '有':
                return 1
            else:
                return 3
    except (ValueError, TypeError):
        return 3



def get_sisetu_cd(facility_name):
    match = institution_df[institution_df["institution_name"] == facility_name]
    if not match.empty:
        return match.iloc[0]["SISETU_CD"]
    match_like = institution_df[institution_df["institution_name"].str.contains(facility_name, na=False)]
    if not match_like.empty:
        return match_like.iloc[0]["SISETU_CD"]
    #print(f'施設名がない病院:{facility_name}')
    return "700000"

def reset_tables(cursor, table_list):
    # 外部キー依存のあるテーブルを、子 → 親の順に並べる
    ordered_tables = [
        "T_TRACER_IKO",
        "T_ISHOKU_KIHON_LIVER_LIV",
        "T_GAPPEI_D_LIV",
        "T_KENSA_D_LIV",
        "T_KENSA_R_LIV",
        "T_IJI_MENEKI_YOKUSEI_R_LIV",
        "T_REJECTION_R_LIV",
        "T_GAPPEI_R_LIV",
        "T_LIVING_R_LIV",
        "T_DONOR_LIVER_LIV",
        "T_DONOR_LIV",
        "T_NYURYOKUJOKYO_LIV",
        "T_ISHOKU_KIHON_LIV"  # 最後に親テーブル
    ]

    for table in ordered_tables:
        cursor.execute(f"DELETE FROM {table};")
        cursor.execute(f"ALTER TABLE {table} AUTO_INCREMENT = 1;")
    print("✅ 外部キー制約を考慮したテーブル初期化完了")


def apply_conversion_rules_to_row(table_name, mapping_row, all_row, recipient_rows, donor_rows, conversion_map, toshima_excluded_columns):
    result = []
    for source_col, config in conversion_map.items():
        source_col = source_col.strip()
        # 対象のテーブルでなければスキップ
        if config.get("target_table") != table_name:
            continue
        toshima_excluded_columns = [col.strip() for col in toshima_excluded_columns]
        # 戸嶋さんに相談しないといけないカラムはスキップ
        #if source_col.strip() in toshima_excluded_columns:
            #print(f"{source_col}############################ 戸嶋さんのところとっているよね？")
            #continue
        # 違う箇所で変換処理をしているので、変換処理をする必要がないカラムのため、処理をスキップする
        # 全部完了後に一応、戸嶋さんに確認する
        if source_col.strip() in [
            "倫理的問題(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]", 
            "倫理的問題-その他（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]",
            "倫理的問題-血縁関係（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]"
        ]:
            #print(f"{source_col}####### スキップ対象カラム（倫理的問題）")
            continue

        matching_rows = mapping_row[mapping_row["アルトマーク　項目名"] == source_col.strip()]
        if not matching_rows.empty:
            target_column = matching_rows.iloc[0]['項目']
            if target_column == '項目名（移植時）':
                if source_col.strip() not in all_row:
                    continue
                raw_value = all_row[source_col]
            elif target_column == '項目名（レシピエントフォロー）' and recipient_rows:
                col_index = [col.strip() for col in recipient_rows[0][1].index]
                if source_col.strip() in col_index:
                    ## 最初の1件を取得している。空でも空でなくても
                    raw_value = recipient_rows[0][1][source_col.strip()]
                else:
                    continue
            elif target_column == '項目名（ドナーフォロー）' and donor_rows:
                col_index = [col.strip() for col in donor_rows[0][1].index]
                if source_col.strip() in col_index:
                    ## 最初の1件を取得している。空でも空でなくても
                    raw_value = donor_rows[0][1][source_col.strip()]
                else:
                    continue
                #if source_col == 'AST[検査値と合併症・再手術・再入院]':
        conv_type = config.get("type")
        if 'raw_value' not in locals():
            #print(f"☑ UPDATEの条件に当てはまらないケースがある{target_column} {source_col} ")
            continue
        if conv_type == "multi_flag":
            for keyword, target_col in config["rules"].items():
                value = 1 if keyword in str(raw_value) else 0
                result.append((target_col, value, source_col))


        elif conv_type == "multi_flag_split":
            # 候補を収集（all_row → recipient → donor）
            candidate_values = [str(raw_value).strip()] if raw_value else []

            for row_set in [recipient_rows, donor_rows]:
                for row in row_set:
                    df = row[1] if isinstance(row, tuple) else row
                    if isinstance(df, pd.Series):
                        df = df.to_frame().T
                    if source_col in df.columns:
                        val = str(df[source_col].values[0]).strip()
                        if val:
                            candidate_values.append(val)

            # 最後の非空値を採用し、カンマで分割
            final_value = next((v for v in reversed(candidate_values) if v), "")
            parts = [p.strip() for p in final_value.split(',') if p.strip()]

            # 各キーワードが含まれているか判定
            for keyword, target_col in config["rules"].items():
                value = 1 if keyword in parts else 0
                result.append((target_col, value, source_col))

        elif conv_type == "single_map":
            rules = config["rules"]
            candidate_values = [str(raw_value).strip()] if raw_value else []

            for row_set in [recipient_rows, donor_rows]:
                for row in row_set:
                    df = row[1] if isinstance(row, tuple) else row
                    if isinstance(df, pd.Series):
                        df = df.to_frame().T
                    if source_col in df.columns:
                        val = str(df[source_col].values[0]).strip()
                        if val:
                            candidate_values.append(val)

            final_value = next((v for v in reversed(candidate_values) if v), "")
            mapped_value = next((v for k, v in rules.items() if k != "default" and k in final_value), rules.get("default"))
            result.append((config["target_col"], mapped_value, source_col))

            # オプション: extra_outputs
            if "extra_outputs" in config:
                for item in config["extra_outputs"]:
                    if item["condition"] in final_value:
                        result.append((item["target_col"], item["value"], source_col))

        elif conv_type == "date_format":
            # raw_value に加え、recipient_rows・donor_rows も確認
            candidate_values = [str(raw_value).strip()] if raw_value else []

            for row_set in [recipient_rows, donor_rows]:
                for row in row_set:
                    df = row[1] if isinstance(row, tuple) else row
                    if isinstance(df, pd.Series):
                        df = df.to_frame().T
                    if source_col in df.columns:
                        val = str(df[source_col].values[0]).strip()
                        if val:
                            candidate_values.append(val)

            # 最後に見つかった非空の値を使う
            final_value = next((v for v in reversed(candidate_values) if v), None)

            try:
                dt = pd.to_datetime(final_value, errors='coerce')
                formatted = dt.strftime(config.get("format", "%Y%m%d")) if not pd.isna(dt) else None
                result.append((config.get("target_col", source_col), formatted, source_col))
            except Exception as e:
                #print(f"❌ date_format error: {source_col} → {e}")
                result.append((config.get("target_col", source_col), None, source_col))

        elif conv_type == "exist_flag":
            value_if_exists = config.get("value_if_exists", 1)
            value_if_not_exists = config.get("value_if_not_exists", 0)
            keywords = config.get("keywords")

            raw_str = str(raw_value).strip()

            if keywords:
                # キーワードのいずれかが含まれていれば存在と見なす
                exists = any(k in raw_str for k in keywords)
                value = value_if_exists if exists else value_if_not_exists
            else:
                value = value_if_exists if raw_str else value_if_not_exists

            result.append((config["target_col"], value, source_col))


        elif conv_type == "pass_through":
            result.append((config["target_col"], raw_value, source_col))

        elif conv_type == "split_dates_backfill":
            target_cols = config["target_cols"]
            max_split = config.get("max_split", len(target_cols))
            parts = [d.strip() for d in str(raw_value).split(",") if d.strip()]
            parts = parts[:max_split]  # 先頭から max 件取得（古いデータ優先）
            padded = parts + [None] * (len(target_cols) - len(parts))  # 後ろに None を埋める

            for col, val in zip(target_cols, padded):
                if val:
                    try:
                        date_obj = pd.to_datetime(val, errors='coerce')
                        val = date_obj.strftime("%Y%m%d") if not pd.isna(date_obj) else None
                    except Exception:
                        val = None
                result.append((col, val, source_col))

        elif conv_type == "split_text_backfill":
            parts = [d.strip() for d in re.split(r"[、,]", str(raw_value)) if d.strip()]
            max_split = config.get("max_split", len(config["target_cols"]))
            parts = parts[:max_split]  # 先頭から max 件取得
            padded = parts + [None] * (len(config["target_cols"]) - len(parts))  # 後ろに None

            result.extend([(col, val, source_col) for col, val in zip(config["target_cols"], padded)])


        elif conv_type == "multi_output_map":
            value_str = str(raw_value).strip()
            is_null = value_str == "" or pd.isna(raw_value)

            for target_col, rule_map in config["rules"].items():
                result_value = None

                if is_null:
                    result_value = rule_map.get("default", None)
                elif value_str.isdigit():
                    num = int(value_str)
                    if str(num) in rule_map:
                        result_value = rule_map[str(num)]
                    elif num >= 1 and "1+" in rule_map:
                        result_value = rule_map["1+"]
                    else:
                        result_value = rule_map.get("default", None)
                else:
                    result_value = rule_map.get("default", None)

                if result_value == "value":
                    result_value = raw_value  # 元の値をそのまま使う

                result.append((target_col, result_value, source_col))
        elif conv_type == "aggregate_sum_flagged":
            value_col = config["value_col"]
            sum_target_col = config["sum_target_col"]
            flag_target_col = config["flag_target_col"]
            rules = config.get("rules", {})

            total_sum = 0

            # 処理対象セット（例: recipient_rows, donor_rows）
            for row_set in [recipient_rows, donor_rows]:
                for row in row_set:
                    # rowがtupleの場合（(key, DataFrame)）
                    if isinstance(row, tuple):
                        df = row[1]
                    else:
                        df = row  # rowがそのままDataFrameまたはSeries

                    # SeriesをDataFrameに変換
                    if isinstance(df, pd.Series):
                        df = df.to_frame().T

                    if value_col in df.columns:
                        col_values = pd.to_numeric(df[value_col], errors='coerce').fillna(0)
                        total_sum += col_values.sum()

            result.append((sum_target_col, int(total_sum), source_col))

            if total_sum == 0:
                flag_value = rules.get("if_sum_equals_0", None)
            else:
                flag_value = rules.get("if_sum_greater_than_0", None)

            result.append((flag_target_col, flag_value, source_col))

        elif conv_type == "paired_split_backfill":
            paired_date_col = source_col.strip()
            paired_text_col = config.get("paired_text_col", "").strip()
            max_split = config.get("max_split", 5)

            combined_pairs = []

            # recipient_rows, donor_rows 両方からデータを取得
            for row_set in [recipient_rows, donor_rows]:
                for row in row_set:
                    # rowがtupleならDataFrameを取り出す
                    df = row[1] if isinstance(row, tuple) else row

                    # Series を DataFrame に変換
                    if isinstance(df, pd.Series):
                        df = df.to_frame().T
                    
                    if paired_date_col in df.columns:
                        date_values = str(df[paired_date_col].values[0])
                        text_values = str(df[paired_text_col].values[0]) if paired_text_col in df.columns else ""

                        date_list = [d.strip() for d in date_values.split(',') if d.strip()]
                        text_list = [t.strip() for t in text_values.split(',') if t.strip()]

                        date_objs = [pd.to_datetime(d, errors='coerce') for d in date_list]
                        valid_pairs = [(d, t) for d, t in zip(date_objs, text_list) if not pd.isna(d)]

                        combined_pairs.extend(valid_pairs)

            # 最新順に並べ替えて、max_split件だけ取得
            sorted_pairs = sorted(combined_pairs, key=lambda x: x[0], reverse=True)[:max_split]

            # 日付とテキストを分解
            sorted_dates = [d.strftime("%Y%m%d") for d, _ in sorted_pairs]
            sorted_texts = [t for _, t in sorted_pairs]

            # 必要数だけNone埋め
            filled_dates = sorted_dates + [None] * (len(config["target_cols_date"]) - len(sorted_dates))
            filled_texts = sorted_texts + [None] * (len(config["target_cols_text"]) - len(sorted_texts))

            # 結果に追加（DATE）
            for col, val in zip(config["target_cols_date"], filled_dates):
                result.append((col, val, paired_date_col))

            # 結果に追加（TEXT）
            for col, val in zip(config["target_cols_text"], filled_texts):
                result.append((col, val, paired_text_col))

        elif conv_type == "latest_exist_and_detail_flag":
            flag_col = config["flag_target_col"]
            detail_col = config["detail_target_col"]
            latest_value = None

            # ① all_row を優先的に確認
            if source_col in all_row and str(all_row[source_col]).strip():
                latest_value = str(all_row[source_col]).strip()

            # ② all_row になければ recipient_rows / donor_rows を順に確認
            if not latest_value:
                for row_set in [recipient_rows, donor_rows]:
                    for row in row_set:
                        df = row[1] if isinstance(row, tuple) else row
                        if isinstance(df, pd.Series):
                            df = df.to_frame().T

                        if source_col in df.columns:
                            val = str(df[source_col].values[0]).strip()
                            if val:
                                latest_value = val  # 上書きしていくので最後にある値が採用される

            # ③ 結果格納
            if latest_value:
                result.append((flag_col, 1, source_col))
                result.append((detail_col, latest_value, source_col))
            else:
                result.append((flag_col, 0, source_col))
                result.append((detail_col, None, source_col))

        elif conv_type == "disease_code_mapping_regular":
            # 通常の疾患コード（管理者以外）の処理
            # すでにresultに存在するGENSIKKAN{n}_Hフィールドを確認し、使用済みインデックスを取得            
            disease_mappings = [
                # パターン①: 胆汁鬱滞性疾患
                {
                    "group_code_field": "胆汁鬱滞性疾患-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "disease_code_field": "胆汁鬱滞性疾患-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "01",
                    "l_mappings": {
                        "C1": "101", "BA": "101", "C2": "102", "PBC": "102", "C3": "103", 
                        "PSC": "103", "C4": "104", "Alagille": "104", "C5": "105", "C5'": "106", 
                        "C8": "107", "C6": "108", "Caroli": "108", "肝内結石": "109"
                    },
                    "default_l": "111"
                },
                # パターン②: 腫瘍性疾患
                {
                    "group_code_field": "腫瘍性疾患-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "disease_code_field": "腫瘍性疾患-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "02",
                    "l_mappings": {
                        "N1": "0201", "HCC": "0201", "N5": "0202", "CCC": "0203", "N2": "0204",
                        "肝芽腫": "0204", "Hemangioma": "0205", "Epithelioid hemangioendothelioma": "0206",
                        "N4": "0208", "転移性": "0208"
                    },
                    "default_l": "0207"
                },
                # パターン③: 肝細胞性疾患
                {
                    "group_code_field": "肝細胞性疾患（肝硬変）-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "disease_code_field": "肝細胞性疾患（肝硬変）-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "03",
                    "l_mappings": {
                        "H1": "0301", "HBV": "0301", "H2": "0302", "HCV": "0302", "H4": "0303",
                        "アルコール性": "0303", "H3": "0304", "AIH": "0304", "H8": "0305", "NASH": "0305",
                        "Infantile hepatitis": "0306", "Neonatal hepatitis": "0307", "原因不明の肝硬変": "0308",
                        "H9": "0309", "その他": "0309"
                    },
                    "default_l": "0309"
                },
                # パターン④: 急性肝不全
                {
                    "group_code_field": "急性肝不全-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "disease_code_field": "急性肝不全-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "04",
                    "l_mappings": {
                        "HAV": "0401", "F1": "0401", "HBV": "0402", "AIH": "0404", "F4": "0405",
                        "F2": "0406", "薬剤性": "0406", "F3": "0407", "F6": "0408", "原因不明": "0408",
                        "F9": "0409", "その他": "0409"
                    },
                    "default_l": "0409"
                },
                # パターン⑤: 代謝性疾患
                {
                    "group_code_field": "代謝性疾患-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "disease_code_field": "代謝性疾患-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "05",
                    "l_mappings": {
                        "M1": "501", "Willson": "501", "M2": "502", "FAP": "502", "M3": "503",
                        "Citrullinemia": "503", "M4": "504", "OTC deficiency": "504", "M6": "506",
                        "Glycogen Strage Disease": "506", "M8": "507", "Methylmalonic acidemia": "507",
                        "M5": "509", "Tyrosinemia": "509", "M7": "510", "Primary hyperoxaluria": "510"
                    },
                    "default_l": "511"
                },
                # パターン⑥: 血管性疾患
                {
                    "group_code_field": "血管性疾患-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "disease_code_field": "血管性疾患-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "06",
                    "l_mappings": {
                        "V1": "0601", "Budd-Chian症候群": "0601", "V2": "0602", "先天性門脈欠損症": "0602"
                    },
                    "default_l": "0603"
                },
                {
                    "group_code_field": "肝細胞性疾患（肝硬変）-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "disease_code_field": "肝細胞性疾患（肝硬変）-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "03",
                    "l_mappings": {
                        "H1": "0301", "HBV": "0301",
                        "H2": "0302", "HCV": "0302",
                        "H4": "0303", "アルコール性": "0303",
                        "H3": "0304", "AIH": "0304",
                        "H8": "0305", "NASH": "0305",
                        "Infantile hepatitis": "0306",
                        "Neonatal hepatitis": "0307",
                        "原因不明の肝硬変": "0308",
                        "H9": "0309", "その他": "0309"
                    },
                    "default_l": "0309"
                }
            ]
            filed_count = 1
            # 各疾患タイプを1回だけ処理するよう反復処理
            for mapping in disease_mappings:
                group_code_field = mapping["group_code_field"]
                disease_code_field = mapping["disease_code_field"]
                group_code_value = all_row[group_code_field]
                disease_code_value = all_row[disease_code_field]
                ## 群コード/疾患コードが入っていたら、default値を設定
                if group_code_value and disease_code_value:
                    code_list = str(disease_code_value).split(",")  # カンマで分割
                    ## 群コードdefault値を設定                    
                    code_list = [c.strip() for c in code_list if c.strip()]  # 空白除去
                    if filed_count >= 6:
                        continue    
                    for code in code_list:
                        group_code_value = mapping["h_value"]
                        disease_code_value = mapping["l_mappings"].get(code, mapping["default_l"])
                        result.append((f"GENSIKKAN{filed_count}_H", group_code_value, group_code_field))
                        result.append((f"GENSIKKAN{filed_count}_L", disease_code_value, disease_code_field))
                        filed_count = filed_count + 1
                else:
                    ## 群コードが入っていなかったら次のカラムを参照する
                    continue

        elif conv_type == "disease_code_mapping_admin":
            admin_mappings = [
                # パターン①: Primary non-function
                {
                    "field": "Primary non-function(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "08",
                    "l_value": "801",
                    "cmnt_field": None
                },
                # パターン②: 慢性拒絶反応
                {
                    "field": "慢性拒絶反応(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "08",
                    "l_value": "802",
                    "cmnt_field": None
                },
                # パターン③: 原疾患の再発
                {
                    "field": "原疾患の再発(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "08",
                    "l_value": "803",
                    "cmnt_field": "原疾患の再発（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]"
                },
                # パターン④: 血管系合併症
                {
                    "field": "血管系合併症(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "08",
                    "l_value": "804",
                    "cmnt_field": "血管系合併症（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]"
                },
                # パターン⑤: その他
                {
                    "field": "その他(R)[初回調査用紙（レシピエント／ドナー情報）]",
                    "h_value": "08",
                    "l_value": "805",
                    "cmnt_field": "その他（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]"
                }
            ]
            filed_count = 1
            # 各疾患タイプを1回だけ処理するよう反復処理
            for mapping in admin_mappings:
                if filed_count >= 6:
                    continue
                field = mapping["field"]
                cmnt_field = mapping["cmnt_field"]
                value = all_row[field]
                cmnt_value = all_row.get(cmnt_field) if cmnt_field in all_row else None
                if value == 'TRUE':
                    h_value = mapping["h_value"]
                    l_value = mapping["l_value"]
                    l_value = mapping["l_value"]
                    result.append((f"GENSIKKAN_KANRISHA{filed_count}_H", h_value, field))
                    result.append((f"GENSIKKAN_KANRISHA{filed_count}_L", l_value, field))
                    if cmnt_value:
                        result.append((f"GENSIKKAN_KANRISHA{filed_count}_CMNT", cmnt_value, cmnt_field))
                    filed_count = filed_count + 1
                else:
                    ## 群コードが入っていなかったら次のカラムを参照する
                    continue
        elif conv_type == "multi_flag_split_contains":
            # 改行・カンマ・全角カンマ・読点を考慮して分割
            parts = re.split(r"[,\n、，]", str(raw_value)) if raw_value else []
            parts = [p.strip() for p in parts if p.strip()]
            
            for keyword, target_col in config["rules"].items():
                value = 1 if any(keyword in p for p in parts) else 0
                result.append((target_col, value, source_col))

        elif conv_type == "conditional_flag_with_detail":
            main_flag_col = config["main_flag_col"]
            value_if_true = config["value_if_true"]
            value_if_false = config["value_if_false"]
            details_cfg = config["details"]
            details_col = details_cfg["source_col"]
            
            # まず main_flag を判断（all_row, recipient_rows, donor_rows）
            def get_value(col):
                if col in all_row and str(all_row[col]).strip():
                    return str(all_row[col]).strip()
                for row_set in [recipient_rows, donor_rows]:
                    for row in row_set:
                        df = row[1] if isinstance(row, tuple) else row
                        if isinstance(df, pd.Series):
                            df = df.to_frame().T
                        if col in df.columns:
                            val = str(df[col].values[0]).strip()
                            if val:
                                return val
                return ""

            main_value = get_value(source_col)
            detail_value = get_value(details_col)

            # フラグの判定
            if main_value == value_if_true:
                result.append((main_flag_col, 1, source_col))
                
                for keyword, mapping in details_cfg["mappings"].items():
                    if keyword in detail_value:
                        result.append((mapping["target_col"], 1, details_col))
                        # コメントが必要な場合
                        if "comment_col" in mapping and "comment_source_col" in mapping:
                            comment_val = get_value(mapping["comment_source_col"])
                            result.append((mapping["comment_col"], comment_val, mapping["comment_source_col"]))
                    else:
                        result.append((mapping["target_col"], 0, details_col))
                        if "comment_col" in mapping:
                            result.append((mapping["comment_col"], None, mapping["comment_source_col"]))
            elif main_value == value_if_false:
                result.append((main_flag_col, 0, source_col))
            else:
                result.append((main_flag_col, None, source_col))  # 値が不明な場合

        elif conv_type == "latest_date_from_split":
            candidate_values = [str(raw_value).strip()] if raw_value else []

            for row_set in [recipient_rows, donor_rows]:
                for row in row_set:
                    df = row[1] if isinstance(row, tuple) else row
                    if isinstance(df, pd.Series):
                        df = df.to_frame().T
                    if source_col in df.columns:
                        val = str(df[source_col].values[0]).strip()
                        if val:
                            candidate_values.append(val)

            # 最後に見つかった非空値
            final_value = next((v for v in reversed(candidate_values) if v), "")

            # カンマ区切り → 日付に変換 → 最新値を抽出
            date_parts = [pd.to_datetime(d.strip(), errors="coerce") for d in final_value.split(",")]
            valid_dates = [d for d in date_parts if not pd.isna(d)]

            if valid_dates:
                latest_date = max(valid_dates)
                formatted = latest_date.strftime(config.get("format", "%Y%m%d"))
            else:
                formatted = None
            result.append((config.get("target_col", source_col), formatted, source_col))

        elif conv_type == "minutes_to_hour_minute":
            candidate_values = [str(raw_value).strip()] if raw_value else []

            for row_set in [recipient_rows, donor_rows]:
                for row in row_set:
                    df = row[1] if isinstance(row, tuple) else row
                    if isinstance(df, pd.Series):
                        df = df.to_frame().T
                    if source_col in df.columns:
                        val = str(df[source_col].values[0]).strip()
                        if val:
                            candidate_values.append(val)

            final_value = next((v for v in reversed(candidate_values) if v), "")
            try:
                total_minutes = int(final_value)
                hours = total_minutes // 60
                minutes = total_minutes % 60
            except Exception as e:
                #print(f"❌ minutes_to_hour_minute parse error: {source_col} → {e}")
                hours = None
                minutes = None

            result.append((config["target_cols"]["hour"], hours, source_col))
            result.append((config["target_cols"]["minute"], minutes, source_col))

        elif conv_type == "donor_jutsumae_fukki":
            status_value = str(all_row.get(source_col, "")).strip()

            fields = config.get("fields", {})
            is_kenzon = status_value == "健存"
            is_byotai = status_value in ["死亡", "病悩", "病脳"]

            if is_kenzon:
                fukki_field = fields.get("復帰_健存_可否")
                katsudo_field = fields.get("復帰_健存_活動")
                riyu_field = fields.get("復帰_健存_理由区分")
                cmnt_field = fields.get("復帰_健存_理由詳細")

                fukki_value = all_row.get(fukki_field, "").strip()
                result.append(("JUTSUMAE_FUKKI", 1 if fukki_value == "可" else 0, fukki_field))

                katsudo_value = all_row.get(katsudo_field, "").strip()
                result.append(("KATSUDO_JOKYO", katsudo_value, katsudo_field))

                riyu_value = all_row.get(riyu_field, "").strip()
                riyu_mapped = 1 if riyu_value == "医学的" else 2 if riyu_value == "社会的" else None
                result.append(("JUTSUMAE_FUKKI_NOT", riyu_mapped, riyu_field))

                cmnt_value = all_row.get(cmnt_field, "").strip()
                result.append(("JUTSUMAE_FUKKI_NOT_CMNT", cmnt_value, cmnt_field))

            elif is_byotai:
                fukki_field = fields.get("復帰_病態_可否")
                katsudo_field = fields.get("復帰_病態_活動")
                riyu_field = fields.get("復帰_健存_理由区分")  # 同じ項目名
                period_field = fields.get("復帰_病態_期間")
                byonou_riyuu_field = fields.get("復帰_病態_理由詳細")

                fukki_value = all_row.get(fukki_field, "").strip()
                result.append(("JUTSUMAE_FUKKI", 1 if fukki_value == "可" else 0, fukki_field))

                katsudo_value = all_row.get(katsudo_field, "").strip()
                result.append(("KATSUDO_JOKYO", katsudo_value, katsudo_field))

                riyu_value = all_row.get(riyu_field, "").strip()
                riyu_mapped = 1 if riyu_value == "医学的" else 2 if riyu_value == "社会的" else None
                result.append(("JUTSUMAE_FUKKI_NOT", riyu_mapped, riyu_field))

                period = all_row.get(period_field, "").strip()
                reason = all_row.get(byonou_riyuu_field, "").strip()
                cmnt_value = f"{period} {reason}".strip() if reason else period
                result.append(("JUTSUMAE_FUKKI_NOT_CMNT", cmnt_value, byonou_riyuu_field))


        elif conv_type == "keyword_map_with_default":
            candidate_values = [str(raw_value).strip()] if raw_value else []

            # recipient_rows → donor_rows からも追加で値を取得
            for row_set in [recipient_rows, donor_rows]:
                for row in row_set:
                    df = row[1] if isinstance(row, tuple) else row
                    if isinstance(df, pd.Series):
                        df = df.to_frame().T
                    if source_col in df.columns:
                        val = str(df[source_col].values[0]).strip()
                        if val:
                            candidate_values.append(val)

            # 最後に出現した非空値を使用
            final_value = next((v for v in reversed(candidate_values) if v), "")

            matched = False
            rules = config.get("rules", {})
            for keyword, rule in rules.items():
                if keyword != "default" and keyword in final_value:
                    result.append((rule["target_col"], rule["value"], source_col))
                    matched = True
                    break

            if not matched and final_value:
                default_rule = rules.get("default", {})
                if "target_col" in default_rule:
                    result.append((default_rule["target_col"], default_rule.get("value"), source_col))
                if "comment_col" in default_rule:
                    result.append((default_rule["comment_col"], final_value, source_col))


        elif conv_type == "split_minutes_to_hour_minute":
            try:
                minutes = int(float(raw_value)) if raw_value else 0
                hours = minutes // 60
                remainder = minutes % 60
                result.append((config["hour_target_col"], hours, source_col))
                result.append((config["minute_target_col"], remainder, source_col))
            except (ValueError, TypeError):
                result.append((config["hour_target_col"], None, source_col))
                result.append((config["minute_target_col"], None, source_col))

        elif conv_type == "multi_flag_map_with_etc":
            raw_items = [s.strip() for s in str(raw_value).split(config.get("delimiter", ",")) if s.strip()]
            matched_keys = set(config["map"].keys())

            used = set()
            for item in raw_items:
                if item in config["map"]:
                    target_col = config["map"][item]
                    result.append((target_col, 1, source_col))
                    used.add(item)

            # 未マッチがあったら etc フラグとコメント
            unmatched = [item for item in raw_items if item not in used]
            if unmatched:
                result.append((config["etc_flag_field"], 1, source_col))
                result.append((config["etc_comment_field"], raw_value, source_col))
            else:
                result.append((config["etc_flag_field"], 0, source_col))
                result.append((config["etc_comment_field"], None, source_col))

        else:
            print(f"DEBUG: conv_type = {repr(conv_type)}")
            print(f"⚠️ 未対応の変換タイプ: {conv_type} ({source_col})")

    return result


def update_table(cursor, connection, table_name, column_series, data_row, id_columns, id_values, ishoku_toroku_id, sisetsu_name, recipient_rows=None, donor_rows=None):
    if not isinstance(id_columns, (list, tuple)):
        id_columns = [id_columns]
    if not isinstance(id_values, (list, tuple)):
        id_values = [id_values]

    where_clause = " AND ".join([f"`{col}` = %s" for col in id_columns])
    # 1. conversion_map による更新
    conversion_results = apply_conversion_rules_to_row(table_name, column_series, data_row, recipient_rows, donor_rows, conversion_map, toshima_excluded_columns)
    for db_field_name, value, source_col in conversion_results:
        if value is None or value == "":
            continue
        try:
            sql = f"UPDATE {table_name} SET `{db_field_name}` = %s WHERE {where_clause}"
            ## デバック用 Start　片岡
            #if db_field_name in ["ISYOKUGO_NINSINREKI","ISYOKUGO_NINSIN_CNT"]:
            #if any(substr in db_field_name for substr in ["AKUSEISIKKAN_ETC"]):
            #if any(substr in db_field_name for substr in ["GAPPEI", "TEKISHUTSU_LI_TYPE"]):
            #    params = [value] + list(id_values)
                # 生SQL風に再現
            #    formatted_sql = sql
            #    for p in params:
            #        val = f"'{p}'" if isinstance(p, str) else str(p)
            #        formatted_sql = formatted_sql.replace("%s", val, 1)
            #    print(f"▶️ 実行SQL: {formatted_sql}")
            ## デバック用 End
            cursor.execute(sql, [value] + list(id_values))
        except Exception as e:
            #if source_col in [col.strip() for col in schema_mismatch_columns]:
            #    pass
            #else:
            error_logs.append({
                "テーブル名": table_name,
                "移植登録ID": ishoku_toroku_id,
                "施設名": sisetsu_name, 
                "カラム名": db_field_name,
                "データ": value,
                "エラー内容": str(e)
            })

    # 2. 通常カラムの更新（conversion_mapに含まれないもの）
    for _, row_update in column_series.iterrows():
        key = row_update['アルトマーク　項目名']
        db_field_name = row_update['物理名']
        target_column = row_update['項目']

        key_normalized = key.strip()
        #print(f"aaaa{key_normalized}")

        # 変換マップで処理済ならスキップ
        if key_normalized in conversion_map:
            continue

        # 移行しないデータはスキップする
        excluded_columns_strip = [col.strip() for col in excluded_columns]
        if key_normalized in excluded_columns_strip:
            continue

        value = None

        #if key_normalized in toshima_excluded_columns:
        #    continue
        #print(f"DBカラム: {db_field_name} 対象カラム: {key_normalized}")
        try:
            if target_column == '項目名（移植時）':
                matching_column = next((col for col in data_row.index if col.strip() == key_normalized), None)
                if matching_column:
                    value = data_row.get(matching_column)

            elif target_column == '項目名（レシピエントフォロー）' and recipient_rows:
                for recipient_row in recipient_rows:
                    if recipient_row[1][key_normalized]:
                        value = recipient_row[1][key_normalized]

            elif target_column == '項目名（ドナーフォロー）' and donor_rows:
                for donor_row in donor_rows:
                    if donor_row[1][key_normalized]:
                        value = donor_row[1][key_normalized]

            if value is not None and value != "":
                sql = f"UPDATE {table_name} SET `{db_field_name}` = %s WHERE {where_clause}"
                cursor.execute(sql, [value] + list(id_values))

        except Exception as e:
            #if key_normalized in [col.strip() for col in schema_mismatch_columns]:
            #    pass
                #print(f"❌ 通常項目UPDATE失敗: {db_field_name}, 移植ID: {ishoku_toroku_id}, データ: {value} CSVカラム:{key_normalized}, エラー: {e}")
            #else:
            error_logs.append({
                "テーブル名": table_name,
                "移植登録ID": ishoku_toroku_id,
                "施設名": sisetsu_name, 
                "カラム名": db_field_name,
                "データ": value,
                "エラー内容": str(e)
            })


# DB接続情報
#db_config = {
#    "host": "mysql-db",
#    "user": "root",
#    "password": "admin",
#    "database": "tracer",
#    "charset": "utf8mb4",
#    "cursorclass": pymysql.cursors.DictCursor
#}

### 片岡
db_config = {
    "host": "db",  # コンテナ名
    "user": "root",
    "password": "123456",
    "database": "dev_tracer_db4",
    "charset": "utf8mb4",
        "cursorclass": pymysql.cursors.DictCursor
}


"""該当テーブル
T_ISHOKU_KIHON_LIV
T_ISHOKU_KIHON_LIVER_LIV
T_DONOR_LIV
T_DONOR_LIVER_LIV
T_LIVING_R_LIV
T_GAPPEI_R_LIV
T_REJECTION_R_LIV
T_IJI_MENEKI_YOKUSEI_R_LIV
T_KENSA_R_LIV
T_KENSA_D_LIV
T_GAPPEI_D_LIV
"""


# 空データ挿入対象テーブルと設定値
table_names = [
    "T_ISHOKU_KIHON_LIV",         ## 移植基本情報(共通-生体)             / １レコード　　　　※問題あり
    "T_TRACER_IKO",               ## TRACER移行紐づけ                  /  ※複数行だけど、１レコード
    "T_ISHOKU_KIHON_LIVER_LIV",   ## 移植基本情報(肝-生体)              /  1レコード　　　　※問題あり
    "T_DONOR_LIV",                ## ドナー情報(共通-生体)              /  1レコード　　　　※問題あり
    "T_DONOR_LIVER_LIV",          ## ドナー情報(肝-生体)                /  1レコード　　　　※問題あり
    "T_GAPPEI_R_LIV",             ## 合併症R(生体-レシピエント)          /  複数レコード　　　※問題あり
    "T_GAPPEI_D_LIV",              ## 合併症D(生体-ドナー)               /  複数レコード　　　※問題あり
    "T_LIVING_R_LIV",             ## 生活状況R(生体-レシピエント)        /  複数レコード　　※一旦完了　戸嶋さんに確認中
    "T_REJECTION_R_LIV",          ## 拒絶反応R(生体-レシピエント)        /  複数レコード   ※一旦完了　戸嶋さんに確認中
    "T_IJI_MENEKI_YOKUSEI_R_LIV", ## 維持期免疫抑制薬R(生体-レシピエント) /  複数レコード   ※一旦完了　戸嶋さんに確認中   問題あり
    "T_KENSA_R_LIV",              ## 検査項目R(生体-レシピエント)        /  複数レコード    ※完了
    "T_KENSA_D_LIV",              ## 検査項目D(生体-ドナー)             /  複数レコード    ※完了
]


# TRACERデータ（仮にCSVなどで読み込み済とする）
# 実際には元データや整備済DataFrameを使ってください
#tracer_df = pd.read_csv("/csv/liver/LITREJ_肝臓_移植時_250217.csv", encoding='cp932', dtype=str).fillna("")
tracer_df = pd.read_csv("/csv/liver/all.csv", encoding='cp932', dtype=str).fillna("")
# 数値的にIDが189以上の行を抽出（astypeでintに変換）　片岡
#tracer_df = tracer_df[tracer_df['移植登録ID'].astype(int) == 511]
#tracer_df = tracer_df[tracer_df['移植登録ID'].astype(int).between(1, 100)]
# 除外リスト（肝臓移植ID）
#exclude_ids = [196, 362, 365, 433, 501, 502, 504, 506, 507, 508, 509, 513, 514, 515, 518, 519]
exclude_ids=[]
# デバック用 IDが"189"の行を抽出（文字列で一致） 
#tracer_df = tracer_df[tracer_df['移植登録ID'] == '196']
# 件数を出力して確認
#print(f"抽出された件数: {len(filtered_df)} 件")
# 先頭1件だけ表示
#print(filtered_df.head(1))

#donor_df = pd.read_csv("/csv/liver/LITREJ_肝臓_移植後経過_ドナー_250217.csv", encoding='cp932', dtype=str).fillna("")
#recipient_df = pd.read_csv("/csv/liver/LITREJ_肝臓_移植後経過_レシピエント_250217.csv", encoding='cp932', dtype=str).fillna("")
#institution_df = pd.read_csv("/csv/liver/施設.csv", sep="\t", dtype=str).fillna("")
#mapping_dict = pd.read_excel("/csv/liver/移行データ対応表.xlsx", sheet_name=None)
# 手動でソート順を定義
followup_order = ["3ヶ月後"] + [f"{i}年後" for i in range(1, 51)]

donor_df = pd.read_csv("/csv/liver/donor.csv", encoding='cp932', dtype=str).fillna("")
# カテゴリ型として順序を指定
donor_df["追跡調査の種類[ドナー追跡調査]"] = pd.Categorical(
    donor_df["追跡調査の種類[ドナー追跡調査]"],
    categories=followup_order,
    ordered=True
)

# ソート実行
donor_df.sort_values(
    by=["肝臓移植ID[ドナー追跡調査]", "追跡調査の種類[ドナー追跡調査]"],
    ascending=True,
    inplace=True
)

recipient_df = pd.read_csv("/csv/liver/recipient.csv", encoding='cp932', dtype=str).fillna("")
# カテゴリ型として順序を指定
recipient_df["追跡調査の種類[レシピエント追跡調査]"] = pd.Categorical(
    recipient_df["追跡調査の種類[レシピエント追跡調査]"],
    categories=followup_order,
    ordered=True
)

# ソート実行
recipient_df.sort_values(
    by=["肝臓移植ID[レシピエント追跡調査]", "追跡調査の種類[レシピエント追跡調査]"],
    ascending=True,
    inplace=True
)

institution_df = pd.read_csv("/csv/liver/shisetsu.csv", sep="\t", dtype=str).fillna("")
mapping_dict = pd.read_excel("/csv/liver/map.xlsx", sheet_name=None, keep_default_na=False)

change_list_df = pd.read_csv("/csv/liver/map_table_list.csv", sep="\t", dtype=str).fillna("")
error_logs = []  # エラー情報をここにためるリスト

df_mapping = mapping_dict['肝臓_生体']

"""
カラムの項目を表示する
tracer_df_columns = tracer_df.columns.tolist()
print(tracer_df_columns)
['移植登録ID', '作成日', '生体肝／脳死肝の別', '施設名', '診療科', '電話番号', 'FAX番号', '担当医名', '移植肝亜区域', '移植肝亜区域-中肝静脈', '移植肝重量', '移植日', '移植回数', '移植回数（テキスト）', '姓(R)[初回調査用紙（レシピエント／ドナー情報）]', '名(R)[初回調査用紙（レシピエント／ドナー情報）]', '性別(R)[初回調査用紙（レシピエント／ドナー情報）]', '生年月日(R)[初回調査用紙（レシピエント／ドナー情報）]', '年齢(R)[初回調査用紙（レシピエント／ドナー情報）]', '月齢(R)[初回調査用紙（レシピエント／ドナー情報）]', 'カルテ番号／ID(R)[初回調査用紙（レシピエント／ドナー情報）]', '胆汁鬱滞性疾患(R)[初回調査用紙（レシピエント／ドナー情報）]', '胆汁鬱滞性疾患-詳細(R)[初回調査用紙（レシピエント／ドナー情報）]', '胆汁鬱滞性疾患-その他（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '胆汁鬱滞性疾患-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '胆汁鬱滞性疾患-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '腫瘍性疾患(R)[初回調査用紙（レシピエント／ドナー情報）]', '腫瘍性疾患-詳細(R)[初回調査用紙（レシピエント／ドナー情報）]', '腫瘍性疾患-他の原発性（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '腫瘍性疾患-転移性（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '腫瘍性疾患-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '腫瘍性疾患-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '肝細胞性疾患（肝硬変）(R)[初回調査用紙（レシピエント／ドナー情報）]', '肝細胞性疾患（肝硬変）-詳細(R)[初回調査用紙（レシピエント／ドナー情報）]', '肝細胞性疾患（肝硬変）-その他（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '肝細胞性疾患（肝硬変）-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '肝細胞性疾患（肝硬変）-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '急性肝不全(R)[初回調査用紙（レシピエント／ドナー情報）]', '急性肝不全-詳細(R)[初回調査用紙（レシピエント／ドナー情報）]', '急性肝不全-他のviral（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '急性肝不全-薬剤性（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '急性肝不全-その他（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '急性肝不全-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '急性肝不全-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '代謝性疾患(R)[初回調査用紙（レシピエント／ドナー情報）]', '代謝性疾患-詳細(R)[初回調査用紙（レシピエント／ドナー情報）]', '代謝性疾患-Glycogen Storage Disease（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '代謝性疾患-その他（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '代謝性疾患-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '代謝性疾患-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '血管性疾患(R)[初回調査用紙（レシピエント／ドナー情報）]', '血管性疾患-詳細(R)[初回調査用紙（レシピエント／ドナー情報）]', '血管性疾患-その他（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '血管性疾患-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]', '血管性疾患-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]', 'その他の疾患(R)[初回調査用紙（レシピエント／ドナー情報）]', 'その他の疾患-詳細(R)[初回調査用紙（レシピエント／ドナー情報）]', 'その他の疾患-その他（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', 'その他の疾患-群コード(R)[初回調査用紙（レシピエント／ドナー情報）]', 'その他の疾患-疾患コード(R)[初回調査用紙（レシピエント／ドナー情報）]', 'Primary non-function(R)[初回調査用紙（レシピエント／ドナー情報）]', '慢性拒絶反応(R)[初回調査用紙（レシピエント／ドナー情報）]', '原疾患の再発(R)[初回調査用紙（レシピエント／ドナー情報）]', '原疾患の再発（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '血管系合併症(R)[初回調査用紙（レシピエント／ドナー情報）]', '血管系合併症（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', 'その他(R)[初回調査用紙（レシピエント／ドナー情報）]', 'その他（テキスト）(R)[初回調査用紙（レシピエント／ドナー情報）]', '姓(D)[初回調査用紙（レシピエント／ドナー情報）]', '名(D)[初回調査用紙（レシピエント／ドナー情報）]', '性別(D)[初回調査用紙（レシピエント／ドナー情報）]', '生年月日(D)[初回調査用紙（レシピエント／ドナー情報）]', '年齢(D)[初回調査用紙（レシピエント／ドナー情報）]', '月齢(D)[初回調査用紙（レシピエント／ドナー情報）]', '人種(R)[レシピエント情報／ドナー情報]', '人種-その他（テキスト）(R)[レシピエント情報／ドナー情報]', '身長(R)[レシピエント情報／ドナー情報]', '体重(R)[レシピエント情報／ドナー情報]', 'ABO血液型(R)[レシピエント情報／ドナー情報]', 'Rh血液型(R)[レシピエント情報／ドナー情報]', 'HLA Type:A1(R)[レシピエント情報／ドナー情報]', 'HLA Type:A2(R)[レシピエント情報／ドナー情報]', 'HLA Type:B1(R)[レシピエント情報／ドナー情報]', 'HLA Type:B2(R)[レシピエント情報／ドナー情報]', 'HLA Type:C1(R)[レシピエント情報／ドナー情報]', 'HLA Type:C2(R)[レシピエント情報／ドナー情報]', 'HLA Type:DR1(R)[レシピエント情報／ドナー情報]', 'HLA Type:DR2(R)[レシピエント情報／ドナー情報]', '年齢(D)[レシピエント情報／ドナー情報]', '人種(D)[レシピエント情報／ドナー情報]', '人種-その他（テキスト）(D)[レシピエント情報／ドナー情報]', '身長(D)[レシピエント情報／ドナー情報]', '体重(D)[レシピエント情報／ドナー情報]', 'ABO血液型(D)[レシピエント情報／ドナー情報]', 'Rh血液型(D)[レシピエント情報／ドナー情報]', 'HLA Type:A1(D)[レシピエント情報／ドナー情報]', 'HLA Type:A2(D)[レシピエント情報／ドナー情報]', 'HLA Type:B1(D)[レシピエント情報／ドナー情報]', 'HLA Type:B2(D)[レシピエント情報／ドナー情報]', 'HLA Type:C1(D)[レシピエント情報／ドナー情報]', 'HLA Type:C2(D)[レシピエント情報／ドナー情報]', 'HLA Type:DR1(D)[レシピエント情報／ドナー情報]', 'HLA Type:DR2(D)[レシピエント情報／ドナー情報]', '腹水(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '脳症(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '血液浄化法(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '血液浄化法-詳細(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '血液浄化法-詳細-その他（テキスト）(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '肝細胞癌歴(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '透析治療(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '肝肺症候群(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '肺高血圧(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'Quality of Life(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'PS(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'Platelet(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'T.Bil(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'D.Bil(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'Alb(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'Creatinine(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'PT(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'ワーファリンの使用(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'INR(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'Na(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBsAg(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBsAb(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBeAg(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBeAb(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBcAb(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBV-DNA(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HCVAb(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HCV-RNA(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HIV(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'ATLA(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'CMV(IgG)(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'MELDスコア(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'PELDスコア(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'CTPスコア(R)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '死因(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '死因-頭部外傷（その他）（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '死因-脳血管障害（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '死因-脳腫瘍（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '死因-その他（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '経過中の心停止(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '昇圧剤の使用(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '種類(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '使用量(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '種類(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_1', '使用量(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_2', '既往歴(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '既往歴-有（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'アルコール歴(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '種類と一日量(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '年数(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '併存症(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '併存症-詳細(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '併存症-詳細-その他（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '脂肪肝(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '脂肪肝-有（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'AST(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'ALT(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'T.Bil(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'Na(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBsAg(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBsAb(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBeAg(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBeAb(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBcAb(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HCVAb(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HIV(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'ATLA(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'CMV(IgG)(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '血液(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '血液-菌種（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'レシピエントとの関係(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'レシピエントとの関係-その他血縁者（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'レシピエントとの関係-その他血縁者以外（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '既往歴(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_3', '既往歴-有（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_4', '併存症(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_5', '併存症-詳細(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_6', '併存症-詳細-その他（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_7', '脂肪肝(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_8', '脂肪肝-有（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_9', '倫理的問題(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '倫理的問題-詳細(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '倫理的問題-合併症（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '倫理的問題-血縁関係（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', '倫理的問題-その他（テキスト）(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'AST(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_10', 'ALT(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_11', 'T.Bil(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_12', 'D.Bil(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'Alb(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'Creatinine(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'PT(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'ワーファリンの使用(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'INR(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'APTT(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]', 'HBsAg(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_13', 'HBsAb(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_14', 'HBeAg(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_15', 'HBeAb(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_16', 'HBcAb(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_17', 'HCVAb(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_18', 'ATLA(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_19', 'CMV(IgG)(D)[移植直前の状態（レシピエント）／提供直前の状態（ドナー）]_20', '他臓器（組織）の移植の既往(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '他臓器（組織）の移植の既往-有（テキスト）(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '他臓器（組織）の同時移植(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '他臓器（組織）の同時移植-その他（テキスト）(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '手術時間(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '無肝期(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '手術法(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '分割（split）移植(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '分割（split）移植-Yes：他施設（テキスト）(R)[移植手術（レシピエント）／摘出手術（ドナー）]', 'ドミノ移植(R)[移植手術（レシピエント）／摘出手術（ドナー）]', 'ドミノ移植-Yes：他施設（テキスト）(R)[移植手術（レシピエント）／摘出手術（ドナー）]', 'GV/SLV (%)(R)[移植手術（レシピエント）／摘出手術（ドナー）]', 'GRWR(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '門脈血栓(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '特殊な門脈再建(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '特殊な門脈再建-有（テキスト）(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '胆道再建(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '出血量(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '輸血量(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '赤血球液(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '新鮮凍結血漿(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '血小板(R)[移植手術（レシピエント）／摘出手術（ドナー）]', 'その他(R)[移植手術（レシピエント）／摘出手術（ドナー）]', '摘出手術日(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '他の摘出臓器(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '他の摘出臓器-詳細(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '他の摘出臓器-詳細-有（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '肝摘出チーム(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '肝摘出チーム-他施設（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '提供施設(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '手術時間(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '摘出肝(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '摘出肝-その他（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '摘出肝重量(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '残肝率(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '他の摘出臓器・組織(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '他の摘出臓器・組織-詳細(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '他の摘出臓器・組織-血管（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '他の摘出臓器・組織-その他（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '出血量(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '輸血(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '輸血-詳細(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '自己血（術前採血）（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '同種血-詳細(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '同種血-赤血球液（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '同種血-新鮮凍結血漿（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '同種血-血小板（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', '同種血-その他（テキスト）(D)[移植手術（レシピエント）／摘出手術（ドナー）]', 'ステロイド(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', 'カルシニュリン・ インヒビター(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', 'カルシニュリン・ インヒビター-詳細(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', 'mTOR阻害剤(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', 'mTOR阻害剤-詳細(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '核酸合成阻害(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '核酸合成阻害-詳細(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '抗体製剤(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '抗体製剤-詳細(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', 'その他(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', 'その他（テキスト）(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '抗体価(IgG) 抗A抗体処置前(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '抗体価(IgG) 抗A抗体処置後(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '抗体価(IgG) 抗B抗体処置前(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '抗体価(IgG) 抗B抗体処置後(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '抗体処理法(R)[免疫抑制：導入期（移植後1カ月以内）、ABO不適合移植（レシピエント）]', '合併症(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '出血(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '出血-部位（テキスト）(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'Primary non-function(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '免疫学的合併症(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '免疫学的合併症-詳細(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血管系合併症(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血管系合併症-詳細(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血管系合併症-その他（テキスト）(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血栓症(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血栓症-その他（テキスト）(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '狭窄(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '狭窄-その他（テキスト）(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '胆道合併症(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '胆道合併症-詳細(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '胆道合併症-その他（テキスト）(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '感染症(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '感染症（テキスト）(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'その他(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'その他（テキスト）(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '合併症による再手術(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '合併症による再手術-手術日(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '合併症による再手術-術式(R)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血清T. Bil最高値(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血清T. Bil最高値-D.Bil(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血清T. Bil最高値-術後＿日目(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'T.BillND(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'D.BillND(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'T. Bil 5.0以上の持続期間(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'T. Bil 5.0以上の持続期間 ＿日間(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'T. Bil 2.0以上の継続期間(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'T. Bil 2.0以上の継続期間 ＿日間(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血清AST最高値(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血清AST最高値-術後＿日目(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'AST ND(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血清ALT最高値(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血清ALT最高値-術後＿日目(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'ALT ND(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'PT最低値(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'PT最低値-術後＿日目(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'PT ND(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血中アンモニア最高値(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '血中アンモニア最高値-術後＿日目(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'アンモニアND(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '合併症(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '出血(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '出血-部位（テキスト）(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '心血管系(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '心血管系（テキスト）(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '呼吸器系(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '呼吸器系-詳細(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '呼吸器系-その他（テキスト）(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '胆道系(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '胆道系-詳細(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '胆道系-その他（テキスト）(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'その他の消化器系(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'その他の消化器系-詳細(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'その他の消化器系-その他（テキスト）(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '手術部位感染（SSI）(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'それ以外の感染症(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'それ以外の感染症-部位（テキスト）(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'その他(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'その他-詳細(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', 'その他-その他（テキスト）(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '再手術(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '再手術-手術日(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '再手術-術式(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '退院日西暦(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '術後入院日数(D)[手術合併症・再手術・再入院（レシピエント）／術後検査値と手術合併症・再手術（ドナー）]', '治療歴(R)[肝細胞癌歴ありの場合（レシピエント）]', 'viableな肝細胞癌(R)[肝細胞癌歴ありの場合（レシピエント）]', '腫瘍の個数(R)[肝細胞癌歴ありの場合（レシピエント）]', '腫瘍の個数-それ以上（テキスト）(R)[肝細胞癌歴ありの場合（レシピエント）]', '腫瘍の最大径(R)[肝細胞癌歴ありの場合（レシピエント）]', '腫瘍の最大径-5cm以上（テキスト）(R)[肝細胞癌歴ありの場合（レシピエント）]', 'AFP(R)[肝細胞癌歴ありの場合（レシピエント）]', 'AFP-15ng/ml以上99999ng/ml以下（テキスト）(R)[肝細胞癌歴ありの場合（レシピエント）]', 'PIVKA-II(R)[肝細胞癌歴ありの場合（レシピエント）]', 'PIVKA-II-40mAU/ml以上99999mAU/ml以下（テキスト）(R)[肝細胞癌歴ありの場合（レシピエント）]', '初期潅流(D)[保存（ドナー）]', '初期潅流-その他（テキスト）(D)[保存（ドナー）]', '保存液(D)[保存（ドナー）]', '保存液-その他（テキスト）(D)[保存（ドナー）]', 'リンス液(D)[保存（ドナー）]', 'リンス液-詳細(D)[保存（ドナー）]', 'リンス液-その他（テキスト）(D)[保存（ドナー）]', '冷虚血時間(D)[保存（ドナー）]', '温虚血時間(D)[保存（ドナー）]', '全虚血時間(D)[保存（ドナー）]', '登録年[追加情報(管理者用)]', '登録No[追加情報(管理者用)]', 'RecNo[追加情報(管理者用)]', 'CDNo[追加情報(管理者用)]', 'LDNo(D)[追加情報(管理者用)]', '原疾患[追加情報(管理者用)]', '郡コード[追加情報(管理者用)]', '疾患コード1[追加情報(管理者用)]', '疾患コード2[追加情報(管理者用)]', 'ABO適合[追加情報(管理者用)]', '最終確認日(R)[追加情報(管理者用)]', '最終確認日(D)[追加情報(管理者用)]', 'G予後[追加情報(管理者用)]', 'G最終確認日[追加情報(管理者用)]', '死因(R)[追加情報(管理者用)]', 'ドミノ[追加情報(管理者用)]', 'ドミノ相方[追加情報(管理者用)]', 'Split[追加情報(管理者用)]', 'Split相方[追加情報(管理者用)]', 'APOLT[追加情報(管理者用)]', '備考[追加情報(管理者用)]', 'ドナー死因(D)[追加情報(管理者用)]', 'レシピエント最終予後調査日', 'ドナー最終予後調査日']
donor_columns = donor_df.columns.tolist()
recipient_columns = recipient_df.columns.tolist()
print(donor_columns)
['肝臓移植ID[ドナー追跡調査]', '調査日[ドナー追跡調査]', '追跡調査の種類[ドナー追跡調査]', '現状[現状]', '最終生存確認日[現状]', '術前状態への完全復帰[現状]', '否の場合、その理由[現状]', '具体的理由（テキスト）[現状]', '活動状況；術前に比べて（テキスト）[現状]', 'Quality of Life[現状]', 'PS[現状]', '向精神薬（睡眠導入剤を含む）の継続的使用[現状]', '向精神薬（睡眠導入剤を含む）の継続的使用-詳細[現状]', '向精神薬（睡眠導入剤を含む）の継続的使用-その他（テキスト）[現状]', '最終生存確認日[現状].1', '病脳の場合：その理由[現状]', '病脳期間[現状]', '通常生活への復帰[現状]', '活動状況；術前に比べて[現状]', 'Quality of Life[現状].1', 'PS[現状].1', '向精神薬（睡眠導入剤を含む）の継続的使用[現状].1', '向精神薬（睡眠導入剤を含む）の継続的使用-詳細[現状].1', '向精神薬（睡眠導入剤を含む）の継続的使用-その他（テキスト）[現状].1', '死亡：死亡日[現状]', '死因[現状]', '死因-消化器疾患（その他）（テキスト）[現状]', '死因-悪性新生物（テキスト）[現状]', '死因-その他（テキスト）[現状]', 'T.Bil[検査値と合併症・再手術・再入院]', 'D.Bil[検査値と合併症・再手術・再入院]', 'AST[検査値と合併症・再手術・再入院]', 'ALT[検査値と合併症・再手術・再入院]', 'PT[検査値と合併症・再手術・再入院]', 'ワーファリンの使用[検査値と合併症・再手術・再入院]', 'INR[検査値と合併症・再手術・再入院]', '疾患の有無[検査値と合併症・再手術・再入院]', '心血管系[検査値と合併症・再手術・再入院]', '心血管系（テキスト）[検査値と合併症・再手術・再入院]', '呼吸器系[検査値と合併症・再手術・再入院]', '呼吸器系（テキスト）[検査値と合併症・再手術・再入院]', '胆道系[検査値と合併症・再手術・再入院]', '胆道系-詳細[検査値と合併症・再手術・再入院]', '胆道系-その他（テキスト）[検査値と合併症・再手術・再入院]', 'その他の消化器系[検査値と合併症・再手術・再入院]', 'その他の消化器系-詳細[検査値と合併症・再手術・再入院]', 'その他の消化器系-その他（テキスト）[検査値と合併症・再手術・再入院]', '手術部位感染（SSI）[検査値と合併症・再手術・再入院]', 'それ以外の感染症[検査値と合併症・再手術・再入院]', 'それ以外の感染症-部位（テキスト）[検査値と合併症・再手術・再入院]', 'その他[検査値と合併症・再手術・再入院]', 'その他（テキスト）[検査値と合併症・再手術・再入院]', '再手術（前回報告以降のもののみ）[検査値と合併症・再手術・再入院]', '再入院（前回報告以降のもののみ）[検査値と合併症・再手術・再入院]', '再手術（前回報告以降のもののみ）-手術日[検査値と合併症・再手術・再入院]', '再手術（前回報告以降のもののみ）-術式[検査値と合併症・再手術・再入院]', '再入院（前回報告以降のもののみ）-入院年月日[検査値と合併症・再手術・再入院]', '再入院（前回報告以降のもののみ）-理由[検査値と合併症・再手術・再入院]']
print(recipient_columns)
['肝臓移植ID[レシピエント追跡調査]', '調査日[レシピエント追跡調査]', '追跡調査の種類[レシピエント追跡調査]', 'レシピエントの状態[レシピエントの状態]', '最終生存確認日[レシピエントの状態]', 'Quality of Life[レシピエントの状態]', 'PS[レシピエントの状態]', '向精神薬（睡眠導入剤を含む）の継続的使用[レシピエントの状態]', '向精神薬（睡眠導入剤を含む）の継続的使用-詳細[レシピエントの状態]', '向精神薬（睡眠導入剤を含む）の継続的使用-その他（テキスト）[レシピエントの状態]', '禁酒の継続について[レシピエントの状態]', '専門医療機関(精神科)によるフォローアップの有無[レシピエントの状態]', '自助グループへの参加の有無[レシピエントの状態]', '死亡日[レシピエントの状態]', '出血[レシピエントの状態]', '出血-部位（テキスト）[レシピエントの状態]', 'Primary non-function[レシピエントの状態]', '拒絶反応[レシピエントの状態]', '血管系合併症[レシピエントの状態]', '血管系合併症-詳細[レシピエントの状態]', '血管系合併症-その他（テキスト）[レシピエントの状態]', '血栓症[レシピエントの状態]', '血栓症-その他（テキスト）[レシピエントの状態]', '狭窄[レシピエントの状態]', '狭窄-その他（テキスト）[レシピエントの状態]', '胆道合併症[レシピエントの状態]', '胆道合併症-詳細[レシピエントの状態]', '胆道合併症-その他（テキスト）[レシピエントの状態]', '感染症[レシピエントの状態]', '感染症（テキスト）[レシピエントの状態]', '原疾患の再発[レシピエントの状態]', '原疾患の再発（テキスト）[レシピエントの状態]', 'PTLD[レシピエントの状態]', '悪性腫瘍[レシピエントの状態]', '悪性腫瘍（テキスト）[レシピエントの状態]', 'その他[レシピエントの状態]', 'その他（テキスト）[レシピエントの状態]', '最終生存確認日[レシピエントの状態].1', '施設名[レシピエントの状態]', '主治医名[レシピエントの状態]', '郵便番号[レシピエントの状態]', '住所[レシピエントの状態]', 'TEL[レシピエントの状態]', 'FAX[レシピエントの状態]', 'ステロイド[免疫抑制：維持期（現在）]', 'カルシニュリン・ インヒビター[免疫抑制：維持期（現在）]', 'カルシニュリン・ インヒビター-詳細[免疫抑制：維持期（現在）]', 'mTOR阻害剤[免疫抑制：維持期（現在）]', 'mTOR阻害剤 -詳細[免疫抑制：維持期（現在）]', '核酸合成阻害[免疫抑制：維持期（現在）]', '核酸合成阻害-詳細[免疫抑制：維持期（現在）]', 'その他[免疫抑制：維持期（現在）]', 'その他（テキスト）[免疫抑制：維持期（現在）]', '出血[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '出血-部位（テキスト）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', 'Primary non-function[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '免疫学的合併症[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '血管系合併症[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '血管系合併症-詳細[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '血管系合併症-その他（テキスト）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '血栓症[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '血栓症-その他（テキスト）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '狭窄[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '狭窄-その他（テキスト）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '胆道合併症[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '胆道合併症-詳細[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '胆道合併症-その他（テキスト）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '感染症[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '感染症（テキスト）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '原疾患の再発（HCC再発を含む）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '原疾患の再発（HCC再発を含む）（詳細）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '原疾患の再発（HCC再発を含む）（テキスト）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '原疾患の再発（診断年月日）（詳細）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '原疾患の再発（診断年月日）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', 'PTLD[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '悪性腫瘍（HCC再発を除く）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '悪性腫瘍（HCC再発を除く）（詳細）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '悪性腫瘍（HCC再発を除く）（テキスト）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '悪性腫瘍（診断年月日）（詳細）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '悪性腫瘍（診断年月日）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', 'その他[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', 'その他（テキスト）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '合併症による再手術[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '合併症による再手術-手術日[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '合併症による再手術-術式[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '合併症による再入院[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '合併症による再入院-入院年月日[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '合併症による再入院-理由[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '妊娠回数[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', '出産回数[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]', 'AST[追跡時の検査値]', 'ALT[追跡時の検査値]', 'Platelet[追跡時の検査値]', 'T.Bil[追跡時の検査値]', 'D.Bil[追跡時の検査値]', 'Alb[追跡時の検査値]', 'Creatinine[追跡時の検査値]', 'PT[追跡時の検査値]', 'ワーファリンの使用[追跡時の検査値]', 'INR[追跡時の検査値]', 'HBsAg[追跡時の検査値]', 'HBsAb[追跡時の検査値]', 'HBeAg[追跡時の検査値]', 'HBeAb[追跡時の検査値]', 'HBcAb[追跡時の検査値]', 'HBV-DNA[追跡時の検査値]', 'HCVAb[追跡時の検査値]', 'HCV-RNA[追跡時の検査値]', 'HIV[追跡時の検査値]', 'ATLA[追跡時の検査値]', 'CMV(IgG)[追跡時の検査値]', 'AFP[追跡時の検査値]', 'AFP-15ng/ml以上99999ng/ml以下（テキスト）[追跡時の検査値]', 'PIVKA-II[追跡時の検査値]', 'PIVKA-II-40mAU/ml以上99999mAU/ml以下（テキスト）[追跡時の検査値]']
"""

institution_df = pd.read_csv("/csv/liver/shisetsu.csv", sep="\t", dtype=str).fillna("")
# CSVファイルにはないデフォルト値
tracer_df["DEL_FLG"] = 0
tracer_df["INS_USER_ID"] = 1
tracer_df["INS_PROGRAM_ID"] = 1
# 施設名を施設コードに変換する
tracer_df["ISYOKU_ISYOKUSISETU_CD"] = tracer_df["施設名"].apply(get_sisetu_cd)
tracer_df["RECNO"] = tracer_df["RecNo[追加情報(管理者用)]"]


#try:
start=True
if start:
    connection = pymysql.connect(**db_config)
    
    with connection.cursor() as cursor:
        print("✅ DB接続成功")
        # 初期化処理を一括で呼び出し
        # 片岡
        #reset_tables(cursor, table_names)
        print("✅ DB初期化")
        for index, row in tracer_df.iterrows():
            connection.begin()
            #print("カラム一覧:", list(row.index))
            ## ３ヶ月後のデータは除外する
            matched_recipient_df = recipient_df[
                (recipient_df['肝臓移植ID[レシピエント追跡調査]'] == row['移植登録ID']) &
                (recipient_df['追跡調査の種類[レシピエント追跡調査]'] != '3ヶ月後')
            ] 
            matched_donor_df = donor_df[
                (donor_df['肝臓移植ID[ドナー追跡調査]'] == row['移植登録ID']) &
                (donor_df['追跡調査の種類[ドナー追跡調査]'] != '3ヶ月後')
            ]
            recipient_rows = list(matched_recipient_df.iterrows())
            donor_rows = list(matched_donor_df.iterrows())
            ishoku_toroku_id = row["移植登録ID"] if "移植登録ID" in row else "UNKNOWN"
            sisetsu_name = row["施設名"]

            #print(f"#### 移植ID: {ishoku_toroku_id} #####################################################################")
            #print("▶️ ######## 登録処理スタート ############")  
            for table_name in table_names:
                column_series = df_mapping.query(f'テーブル物理名 == "{table_name}"')
                #print(f"🚗{table_name}")
                if table_name == 'T_ISHOKU_KIHON_LIV':                    

                    try:
                        # INSERT対象カラムと値（NOT NULL のみ）
                        insert_columns = ["ISYOKU_ISYOKUSISETU_CD", "ZOKI_CODE", "DEL_FLG", "INS_USER_ID", "INS_PROGRAM_ID"]
                        insert_values = [
                            row["ISYOKU_ISYOKUSISETU_CD"],  # 例：施設コード（上で変換済）
                            3,                              # 臓器コード
                            0,                              # DEL_FLG（削除フラグ）
                            1,                              # INS_USER_ID（登録ユーザ）
                            1                               # INS_PROGRAM_ID（登録プログラム）
                        ]
                        
                        sql = f"""
                            INSERT INTO T_ISHOKU_KIHON_LIV ({','.join(insert_columns)})
                            VALUES ({','.join(['%s'] * len(insert_columns))})
                        """
                        cursor.execute(sql, insert_values)
                        #connection.commit()
                        
                        # 自動採番されたIDを取得
                        seitai_ishoku_id = cursor.lastrowid
                        RECIPIENT_ID = str(seitai_ishoku_id)[-7:].zfill(7)
                        #print(f"✅ 登録完了：SEITAI_ISYOKU_ID = {seitai_ishoku_id}")
                        # TRACER_ID生成
                        organ_code = "3"  # 肝臓
                        transplant_type_code = "1"  # 生体間移植

                        # 移植日 → 年取得（8桁: YYYYMMDD → YYYY）
                        year = row["移植日"][:4] if "移植日" in row and row["移植日"] else "2024"

                        # 同年・同TRACERプレフィックスの最大連番取得
                        prefix = f"{organ_code}{transplant_type_code}{year}"
                        cursor.execute(
                            "SELECT MAX(TRACER_ID) AS max_id FROM T_ISHOKU_KIHON_LIV WHERE TRACER_ID LIKE %s",
                            (f"{prefix}____",)
                        )
                        result = cursor.fetchone()
                        if result["max_id"]:
                            last_seq = int(result["max_id"][-4:])
                            next_seq = last_seq + 1
                        else:
                            next_seq = 1

                        tracer_id = f"{prefix}{next_seq:04d}"

                        # TRACER_ID更新
                        cursor.execute(
                            "UPDATE T_ISHOKU_KIHON_LIV SET TRACER_ID = %s, RECIPIENT_ID=%s WHERE SEITAI_ISYOKU_ID = %s",
                            (tracer_id, RECIPIENT_ID, seitai_ishoku_id)
                        )
                        connection.commit()

                            # connection.commit()

                        #print(f"✅ TRACER_ID 生成＆更新済: {tracer_id}")
                    except Exception as e:
                        print(f"❌ INSERT失敗: T_ISHOKU_KIHON_LIV, エラー: {e}")
                        error_logs.append({
                            "テーブル名": "T_ISHOKU_KIHON_LIV",
                            "移植登録ID": ishoku_toroku_id,
                            "施設名": sisetsu_name, 
                            "SQL": sql,
                            "入力値": insert_values,
                            "エラー内容": str(e)
                        })
                        #connection.rollback()
                        continue  # 処理を次に進める（任意）

                    ## 更新処理
                    # ✅ ここで一気にupdate_tableで更新処理をまとめる！！
                    update_table(
                        cursor=cursor,
                        connection=connection,
                        table_name="T_ISHOKU_KIHON_LIV",
                        column_series=column_series,
                        data_row=row,
                        id_columns="SEITAI_ISYOKU_ID",
                        id_values=seitai_ishoku_id,
                        ishoku_toroku_id=ishoku_toroku_id,
                        sisetsu_name=sisetsu_name,
                        recipient_rows=recipient_rows,
                        donor_rows=donor_rows
                    )

                elif table_name == 'T_TRACER_IKO':

                    # INSERT対象カラムと値（NOT NULL のみ）
                    ishoku_toroku_id = row["移植登録ID"] if "移植登録ID" in row else "UNKNOWN"
                    recno = row["RECNO"] if "RECNO" in row else "0000"

                    tracer_iko_sql = """
                        INSERT INTO T_TRACER_IKO 
                        (TRACER_ID, ZOKI_CODE, SEITAI_ISYOKU_ID, ISHOKU_TOROKU_ID, RECNO) 
                        VALUES (%s, %s, %s, %s, %s)
                    """
                    cursor.execute(tracer_iko_sql, (tracer_id, organ_code, seitai_ishoku_id, ishoku_toroku_id, recno))

                    #print(f"✅ T_TRACER_IKO にも登録完了: TRACER_ID={tracer_id}, 登録ID={ishoku_toroku_id}, RECNO={recno}")
                elif table_name == 'T_ISHOKU_KIHON_LIVER_LIV':

                    # 必須カラムだけでINSERT
                    required_columns = ["SEITAI_ISYOKU_ID", "INS_USER_ID", "INS_PROGRAM_ID"]
                    insert_values = [seitai_ishoku_id, 1, 1]

                    try:
                        sql = f"""
                            INSERT INTO T_ISHOKU_KIHON_LIVER_LIV ({','.join(required_columns)})
                            VALUES ({','.join(['%s'] * len(required_columns))})
                        """
                        cursor.execute(sql, insert_values)
                        #print(f"✅ T_ISHOKU_KIHON_LIVER_LIV に登録完了: SEITAI_ISYOKU_ID={seitai_ishoku_id}")

                    except Exception as e:
                        print(f"❌ INSERT失敗: T_ISHOKU_KIHON_LIVER_LIV, エラー: {e}")
                        error_logs.append({
                            "テーブル名": "T_ISHOKU_KIHON_LIVER_LIV",
                            "移植登録ID": ishoku_toroku_id,
                            "施設名": sisetsu_name, 
                            "SQL": sql,
                            "入力値": insert_values,
                            "エラー内容": str(e)
                        })
                        connection.rollback()
                        continue  # 処理を次に進める（任意）


                    update_table(
                        cursor=cursor,
                        connection=connection,
                        table_name="T_ISHOKU_KIHON_LIVER_LIV",
                        column_series=column_series,
                        data_row=row,
                        id_columns="SEITAI_ISYOKU_ID",
                        id_values=seitai_ishoku_id,
                        ishoku_toroku_id=ishoku_toroku_id,
                        sisetsu_name=sisetsu_name,
                        recipient_rows=recipient_rows,
                        donor_rows=donor_rows
                    )


                elif table_name == 'T_DONOR_LIV':

                    # 仮登録せずに、先に ID を生成する
                    cursor.execute("SELECT MAX(DONOR_ID) AS max_id FROM T_DONOR_LIV")
                    result = cursor.fetchone()
                    next_donor_id = int(result['max_id']) + 1 if result['max_id'] else 1
                    donor_id_str = str(next_donor_id).zfill(7)

                    try:
                        insert_columns = ["DONOR_ID", "SEITAI_ISYOKU_ID", "INS_USER_ID", "INS_PROGRAM_ID"]
                        insert_values = [donor_id_str, seitai_ishoku_id, 1, 1]

                        sql = f"""
                            INSERT INTO T_DONOR_LIV ({','.join(insert_columns)})
                            VALUES ({','.join(['%s'] * len(insert_columns))})
                        """
                        cursor.execute(sql, insert_values)
                        connection.commit()

                        donor_a_id = cursor.lastrowid
                        DONOR_ID = str(donor_a_id)[-7:].zfill(7)
                        cursor.execute(
                            "UPDATE T_DONOR_LIV SET DONOR_ID = %s WHERE DONOR_A_ID = %s",
                            (DONOR_ID, donor_a_id)
                        )
                        connection.commit()
                        
                        #print(f"✅ T_DONOR_LIV 登録完了: DONOR_ID={donor_id_str}, DONOR_A_ID={donor_a_id}")
                    except Exception as e:
                        print(f"❌ INSERT失敗: T_DONOR_LIV, エラー: {e}")
                        error_logs.append({
                            "テーブル名": "T_DONOR_LIV",
                            "移植登録ID": ishoku_toroku_id,
                            "施設名": sisetsu_name, 
                            "SQL": sql,
                            "入力値": insert_values,
                            "エラー内容": str(e)
                        })
                        connection.rollback()
                        continue  # 処理を次に進める（任意）
                    
                    update_table(
                        cursor=cursor,
                        connection=connection,
                        table_name="T_DONOR_LIV",
                        column_series=column_series,
                        data_row=row,
                        id_columns="DONOR_A_ID",
                        id_values=donor_a_id,
                        ishoku_toroku_id=ishoku_toroku_id,
                        sisetsu_name=sisetsu_name,
                        recipient_rows=recipient_rows,
                        donor_rows=donor_rows
                    )

                elif table_name == 'T_DONOR_LIVER_LIV':
                    
                    try:
                        insert_columns = ["SEITAI_ISYOKU_ID", "DONOR_A_ID", "INS_USER_ID", "INS_PROGRAM_ID"]
                        insert_values = [seitai_ishoku_id, donor_a_id, 1, 1]

                        sql = f"""
                            INSERT INTO T_DONOR_LIVER_LIV ({','.join(insert_columns)})
                            VALUES ({','.join(['%s'] * len(insert_columns))})
                        """

                        #print(sql)
                        cursor.execute(sql, insert_values)
                        connection.commit()
                        #print(f"✅ T_DONOR_LIVER_LIV 登録完了: SEITAI_ISYOKU_ID={seitai_ishoku_id}, DONOR_A_ID={donor_a_id}")

                    except Exception as e:
                        print(f"❌ INSERT失敗: T_DONOR_LIVER_LIV, エラー: {e}")
                        error_logs.append({
                            "テーブル名": "T_DONOR_LIVER_LIV",
                            "移植登録ID": ishoku_toroku_id,
                            "施設名": sisetsu_name, 
                            "SQL": sql,
                            "入力値": insert_values,
                            "エラー内容": str(e)
                        })
                        connection.rollback()
                        continue  # 処理を次に進める（任意）
                    
                    # 更新処理も続けて
                    update_table(
                        cursor=cursor,
                        connection=connection,
                        table_name="T_DONOR_LIVER_LIV",
                        column_series=column_series,
                        data_row=row,
                        id_columns=["SEITAI_ISYOKU_ID", "DONOR_A_ID"],
                        id_values=[seitai_ishoku_id, donor_a_id],
                        ishoku_toroku_id=ishoku_toroku_id,
                        sisetsu_name=sisetsu_name,
                        recipient_rows=recipient_rows,
                        donor_rows=donor_rows
                    )                    

                    nyuryoku_rows = [
                        {
                            "KANJA_KBN": "0",  # 1:recipient
                            "KANJA_ID": seitai_ishoku_id,  # 生体移植ID
                            "KIROKU_TIMING": "0",  # 0:新規
                            "NYURYOKUJOKYO": "0",  # 0:未入力
                        },
                        {
                            "KANJA_KBN": "1",  # ドナー:ドナーID.A
                            "KANJA_ID": donor_a_id,
                            "KIROKU_TIMING": "0",  # 1:移植
                            "NYURYOKUJOKYO": "0",  # 1:完了
                        }
                    ]

                    for nyuryoku in nyuryoku_rows:
                        sql = """
                            INSERT INTO T_NYURYOKUJOKYO_LIV (
                                SEITAI_ISYOKU_ID,
                                KANJA_KBN,
                                KANJA_ID,
                                KIROKU_TIMING,
                                NYURYOKUJOKYO,
                                INS_USER_ID,
                                INS_PROGRAM_ID,
                                INS_DATE,
                                UPD_USER_ID,
                                UPD_PROGRAM_ID,
                                UPD_DATE
                            ) VALUES (%s, %s, %s, %s, %s, %s, %s, NOW(), %s, %s, NOW())
                        """
                        values = (
                            seitai_ishoku_id,
                            nyuryoku["KANJA_KBN"],
                            nyuryoku["KANJA_ID"],
                            nyuryoku["KIROKU_TIMING"],
                            nyuryoku["NYURYOKUJOKYO"],
                            "1",      # INS_USER_ID
                            "1",    # INS_PROGRAM_ID
                            "1",      # UPD_USER_ID
                            "1"     # UPD_PROGRAM_ID
                        )
                        cursor.execute(sql, values)

                    ## 更新処理だがupdate_table関数では補えないので個別で行う
                    qol_map = {
                        "入院中（ICU）": 1,
                        "入院中（一般病棟）": 1,
                        "自宅療養、就労・就学不能<br/>（学齢期以前では成長停止）": 2,
                        "パートタイムの就労、常時の就学不能（学齢期以前では成長低下）": 4,
                        "パートタイムの就労、常時の就学不能<br/>（学齢期以前では成長低下）": 4,
                        "常時の就労・就学（学齢期以前では正常な成長）": 5,
                        "常時の就労・就学<br/>（学齢期以前では正常な成長）": 5,
                        "不明": 99
                    }

                    for donor_row in donor_rows:
                        _, row = donor_row

                        genjo = str(row.get("現状[現状]", "")).strip()
                        jutzen_fukki = 0
                        jutzen_fukki_not = None
                        jutzen_fukki_not_cmnt = None
                        katsudo_jokyo = None
                        qol_raw = str(row.get("Quality of Life[現状]", "")).strip()
                        qol = qol_map.get(qol_raw, None)

                        if genjo == "健存":
                            if str(row.get("術前状態への完全復帰[現状]", "")).strip() == "可":
                                jutzen_fukki = 1
                            else:
                                reason = str(row.get("否の場合、その理由[現状]", "")).strip()
                                if reason == "医学的":
                                    jutzen_fukki_not = 1
                                elif reason == "社会的":
                                    jutzen_fukki_not = 2
                                # 健存かつ「否」の場合、コメントも設定
                                jutzen_fukki_not_cmnt = str(row.get("具体的理由（テキスト）[現状]", "")).strip()

                            raw_value = row.get("活動状況；術前に比べて（テキスト）[現状]", "")
                            katsudo_jokyo = None if str(raw_value).strip() == "" else str(raw_value).strip()

                        else:
                            if str(row.get("通常生活への復帰[現状]", "")).strip() == "可":
                                jutzen_fukki = 1
                            else:
                                byonou_reason = str(row.get("病脳の場合：その理由[現状]", "")).strip()
                                byonou_period = str(row.get("病脳期間[現状]", "")).strip()
                                if byonou_reason or byonou_period:
                                    jutzen_fukki_not = 1
                                if genjo == "脳":
                                    jutzen_fukki_not_cmnt = f"{byonou_reason} {byonou_period}".strip()

                            raw_value = row.get("活動状況；術前に比べて[現状]", "")
                            katsudo_jokyo = None if str(raw_value).strip() == "" else str(raw_value).strip()

                        try:
                            update_sql = """
                                UPDATE T_DONOR_LIVER_LIV
                                SET JUTSUMAE_FUKKI = %s,
                                    JUTSUMAE_FUKKI_NOT = %s,
                                    JUTSUMAE_FUKKI_NOT_CMNT = %s,
                                    KATSUDO_JOKYO = %s,
                                    UPD_USER_ID = %s,
                                    UPD_PROGRAM_ID = %s,
                                    UPD_DATE = NOW()
                                WHERE SEITAI_ISYOKU_ID = %s AND DONOR_A_ID = %s
                            """
                            update_params = (
                                jutzen_fukki,
                                jutzen_fukki_not,
                                jutzen_fukki_not_cmnt,
                                katsudo_jokyo,
                                "1",
                                "1",
                                seitai_ishoku_id,
                                donor_a_id
                            )
                            cursor.execute(update_sql, update_params)


                            update_sql = """
                                UPDATE T_DONOR_LIV
                                SET QOL = %s
                                WHERE SEITAI_ISYOKU_ID = %s AND DONOR_A_ID = %s
                            """

                            update_params = (
                                qol,
                                seitai_ishoku_id,
                                donor_a_id
                            )
                            cursor.execute(update_sql, update_params)

                            connection.commit()

                        except Exception as e:
                            print(f"❌ UPDATE失敗: T_DONOR_LIVER_LIV（JUTSUMAE+QOL関連）, DONOR_A_ID={donor_a_id}, エラー: {e}")
                            error_logs.append({
                                "テーブル名": "T_DONOR_LIVER_LIV",
                                "移植登録ID": ishoku_toroku_id,
                                "施設名": sisetsu_name, 
                                "SQL": update_sql,
                                "入力値": update_params,
                                "エラー内容": str(e)
                            })
                            connection.rollback()


                elif table_name == 'T_GAPPEI_D_LIV':

                    for index__, row in donor_rows:
                        row_data = row.to_dict() if isinstance(row, pd.Series) else row
                        original_date = row_data.get("再入院（前回報告以降のもののみ）-入院年月日[検査値と合併症・再手術・再入院]")
                        try:
                            nyuin_date = max([d.strip() for d in original_date.split(',')]) if original_date else ""
                        except Exception:
                            nyuin_date = ""

                        def insert_gappei(gappei, gappei_l, nyuin_date="", cmnt=None):
                            try:
                                insert_columns = ["DONOR_A_ID", "GAPPEI", "GAPPEI_L", "NYUIN_DATE", "INS_USER_ID", "INS_PROGRAM_ID"]
                                insert_values = [donor_a_id, gappei, gappei_l, nyuin_date or "", 1, 1]
                                if cmnt:
                                    insert_columns.append("CMNT")
                                    insert_values.append(cmnt)

                                sql = f"""
                                    INSERT INTO T_GAPPEI_D_LIV ({','.join(insert_columns)})
                                    VALUES ({','.join(['%s'] * len(insert_columns))})
                                """
                                cursor.execute(sql, insert_values)
                                connection.commit()
                            except Exception as e:
                                print(f"❌ INSERT失敗: T_GAPPEI_D_LIV, エラー: {e}")
                                error_logs.append({
                                    "テーブル名": "T_GAPPEI_D_LIV",
                                    "移植登録ID": ishoku_toroku_id,
                                    "施設名": sisetsu_name, 
                                    "SQL": sql,
                                    "入力値": insert_values,
                                    "エラー内容": str(e)
                                })
                                connection.rollback()

                        # 心血管系
                        if row_data.get("心血管系[検査値と合併症・再手術・再入院]") == True:
                            insert_gappei("08", "0801", nyuin_date, row_data.get("心血管系（テキスト）[検査値と合併症・再手術・再入院]"))

                        # 呼吸器系
                        if row_data.get("呼吸器系[検査値と合併症・再手術・再入院]") == True:
                            insert_gappei("99", "9901", nyuin_date, row_data.get("呼吸器系（テキスト）[検査値と合併症・再手術・再入院]"))

                        # 胆道系
                        if row_data.get("胆道系[検査値と合併症・再手術・再入院]") == True:
                            details = str(row_data.get("胆道系-詳細[検査値と合併症・再手術・再入院]", "")).split(',')
                            for d in details:
                                d = d.strip()
                                gappei_l = {"縫合不全": "0601", "狭窄": "0602", "その他": "0603"}.get(d, "0603")
                                insert_gappei("06", gappei_l, nyuin_date, row_data.get("胆道系-その他（テキスト）[検査値と合併症・再手術・再入院]"))

                        # その他の消化器系
                        if row_data.get("その他の消化器系[検査値と合併症・再手術・再入院]") == True:
                            details = str(row_data.get("その他の消化器系-詳細[検査値と合併症・再手術・再入院]", "")).split(',')
                            for d in details:
                                d = d.strip()
                                gappei_l = {
                                    "胃十二指腸潰瘍p": "0804",
                                    "イレウス": "0603",
                                    "その他": "0603"
                                }.get(d, "0603")
                                insert_gappei("99", gappei_l, nyuin_date, row_data.get("感染症-部位（テキスト）[検査値と合併症・再手術・再入院]"))

                        # 再手術
                        if row_data.get("再手術（前回報告以降のもののみ）-術式[検査値と合併症・再手術・再入院]") == True:
                            cmnt = f"{row_data.get('再手術（前回報告以降のもののみ）-手術日[検査値と合併症・再手術・再入院]', '')} {row_data.get('再手術（前回報告以降のもののみ）-術式[検査値と合併症・再手術・再入院]', '')}"
                            insert_gappei("99", "9901", nyuin_date, cmnt)

                        # 再入院
                        if row_data.get("再入院（前回報告以降のもののみ）-理由[検査値と合併症・再手術・再入院]") == True:
                            insert_gappei("99", "9901", nyuin_date, row_data.get("再入院（前回報告以降のもののみ）-理由[検査値と合併症・再手術・再入院]"))


                elif table_name == 'T_GAPPEI_R_LIV':

                    gappei_rules = []

                    # データソースとしてrecipient_rowsの最新1件（または複数）を対象
                    for index__, row in recipient_rows:
                        row_data = row.to_dict() if isinstance(row, pd.Series) else row
                        original_date = row_data.get("合併症による再入院-入院年月日[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]")
                        # 変換処理
                        try:
                            parsed_date = datetime.strptime(original_date, "%Y年%m月%d日")
                            nyuin_date = parsed_date.strftime("%Y%m%d")
                        except (TypeError, ValueError):
                            nyuin_date = None  # または空文字など適宜処理
                        def insert_gappei(gappei, gappei_l, nyuin_date="", cmnt=None):
                            try:
                                insert_columns = [
                                    "SEITAI_ISYOKU_ID", "GAPPEI", "GAPPEI_L", "NYUIN_DATE",
                                    "INS_USER_ID", "INS_PROGRAM_ID"
                                ]
                                insert_values = [
                                    seitai_ishoku_id, gappei, gappei_l, nyuin_date or "", 1, 1
                                ]
                                if cmnt:
                                    insert_columns.append("CMNT")
                                    insert_values.append(cmnt)

                                sql = f"""
                                    INSERT INTO T_GAPPEI_R_LIV ({','.join(insert_columns)})
                                    VALUES ({','.join(['%s'] * len(insert_columns))})
                                """
                                cursor.execute(sql, insert_values)
                                connection.commit()

                            except Exception as e:
                                print(f"❌ INSERT失敗: T_GAPPEI_R_LIV, エラー: {e}")
                                error_logs.append({
                                    "テーブル名": "T_GAPPEI_R_LIV",
                                    "移植登録ID": ishoku_toroku_id,
                                    "施設名": sisetsu_name, 
                                    "SQL": sql,
                                    "入力値": insert_values,
                                    "エラー内容": str(e)
                                })
                                connection.rollback()

                        # 出血
                        if row_data.get("出血[レシピエントの状態]") == True:
                            insert_gappei("01", "0101", nyuin_date, cmnt=row_data.get("出血-部位（テキスト）[レシピエントの状態]"))

                        # Primary non-function
                        if row_data.get("Primary non-function[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]") == True:
                            insert_gappei("99", "9901", nyuin_date)

                        # 血栓症
                        if pd.notna(row_data.get("血栓症[レシピエントの状態]")):
                            thrombo_parts = str(row_data.get("血栓症[レシピエントの状態]"))
                            for part in thrombo_parts.split(','):
                                part = part.strip()
                                gappei_l = {
                                    "門脈": "0402", "動脈": "0403", "肝静脈": "0401", "その他": "0404"
                                }.get(part, "0404")
                                insert_gappei("04", gappei_l, cmnt=row_data.get("血栓症-その他（テキスト）[レシピエントの状態]"))

                        # 狭窄
                        if pd.notna(row_data.get("狭窄[レシピエントの状態]")):
                            stenosis_parts = str(row_data.get("狭窄[レシピエントの状態]"))
                            for part in stenosis_parts.split(','):
                                part = part.strip()
                                gappei_l = {
                                    "肝動脈": "0501", "門脈": "0502", "肝静脈": "0503", "その他": "0504"
                                }.get(part, "0504")
                                insert_gappei("05", gappei_l, nyuin_date, cmnt=row_data.get("狭窄-その他（テキスト）[レシピエントの状態]"))

                        # 胆道合併症
                        if row_data.get("胆道合併症[レシピエントの状態]") == True:
                            detail_parts = str(row_data.get("胆道合併症-詳細[レシピエントの状態]", "")).split(',')
                            for detail in detail_parts:
                                detail = detail.strip()
                                gappei_l = {
                                    "縫合不全": "0601", "狭窄": "0602", "その他": "0603"
                                }.get(detail, "0603")
                                insert_gappei("06", gappei_l, nyuin_date, cmnt=row_data.get("胆道合併症-その他（テキスト）[レシピエントの状態]"))

                        # 感染症
                        if row_data.get("感染症[レシピエントの状態]") == True:
                            insert_gappei("03", "0304", nyuin_date, cmnt=row_data.get("感染症（テキスト）[レシピエントの状態]"))

                        # 血管系合併症-詳細
                        if pd.notna(row_data.get("血管系合併症-詳細[レシピエントの状態]")):
                            parts = str(row_data.get("血管系合併症-詳細[レシピエントの状態]", "")).split(',')
                            cmnt_val = row_data.get("血管系合併症-その他（テキスト）[レシピエントの状態]", "")
                            for p in parts:
                                p = p.strip()
                                gappei = {
                                    "血栓": "04", "狭窄": "05", "その他": "99"
                                }.get(p, "99")
                                gappei_l = {
                                    "門脈盗血": "0402", "outflow obstruction": "0403",
                                    "肝静脈うっ滞": "0403", "脾動脈瘤": "0404", "DIC": "0101",
                                    "肝動脈解離": "0401"
                                }.get(cmnt_val, "9901")
                                insert_gappei(gappei, gappei_l, nyuin_date, cmnt=cmnt_val)

                        # その他
                        if row_data.get("その他[レシピエントの状態]") == True:
                            insert_gappei("99", "9901", nyuin_date, cmnt=row_data.get("その他（テキスト）[レシピエントの状態]"))

                elif table_name == 'T_LIVING_R_LIV':

                    for index_t_living_r_liv, row_t_living_r_liv in recipient_rows:
                        if int(row_t_living_r_liv['肝臓移植ID[レシピエント追跡調査]']) in exclude_ids:
                            continue

                        original_date = row_t_living_r_liv['調査日[レシピエント追跡調査]']
                        if pd.isna(original_date) or original_date == '':
                            original_date = row_t_living_r_liv['最終生存確認日[レシピエントの状態]']
                        if not original_date:
                            continue
                        #input_date = datetime.strptime(original_date, '%Y/%m/%d').strftime('%Y%m%d')
                        input_date = pd.to_datetime(original_date, errors='coerce').strftime('%Y%m%d')


                        facility = row_t_living_r_liv['施設名[レシピエントの状態]']
                        isyoku_isyokusisetu_cd = get_sisetu_cd(facility)
                        try:
                            insert_columns = ["SEITAI_ISYOKU_ID", "INPUT_DATE", "SISETU_CD", "INS_USER_ID", "INS_PROGRAM_ID"]
                            insert_values = [seitai_ishoku_id, input_date, isyoku_isyokusisetu_cd, 1, 1]

                            sql = f"""
                                INSERT INTO T_LIVING_R_LIV ({','.join(insert_columns)})
                                VALUES ({','.join(['%s'] * len(insert_columns))})
                            """
                            cursor.execute(sql, insert_values)

                            # --- 各種フィールドの変換 ---
                            ps_map = {'0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '不明': 6}
                            ps = ps_map.get(str(row_t_living_r_liv.get('PS[レシピエントの状態]')).strip(), None)

                            kousei_flg_raw = row_t_living_r_liv.get('向精神薬（睡眠導入剤を含む）の継続的使用[レシピエントの状態]')
                            kousei_flg = 1 if kousei_flg_raw == 'TRUE' else 0 if kousei_flg_raw == 'FALSE' else None

                            detail = row_t_living_r_liv.get('向精神薬（睡眠導入剤を含む）の継続的使用-詳細[レシピエントの状態]', '')
                            detail_list = [d.strip() for d in detail.split(',') if d.strip()]
                            koufuan = 1 if '抗不安薬（睡眠導入剤を含む）' in detail_list else 0
                            kouutsu = 1 if '抗うつ薬' in detail_list else 0
                            kousei = 1 if '向精神薬' in detail_list else 0
                            etc = 1 if 'その他' in detail_list else 0
                            etc_cmnt = row_t_living_r_liv.get('向精神薬（睡眠導入剤を含む）の継続的使用-その他（テキスト）[レシピエントの状態]')

                            abst = row_t_living_r_liv.get('禁酒の継続について[レシピエントの状態]', '')
                            kankohen_flg = 1 if abst else 0
                            kankohen_kinsyu_flg = 1 if abst == '禁酒が継続できている' else 2 if abst else None

                            followup_raw = row_t_living_r_liv.get('専門医療機関(精神科)によるフォローアップの有無[レシピエントの状態]')
                            followup = 1 if followup_raw == 'TRUE' else 0 if followup_raw == 'FALSE' else None

                            selfhelp = row_t_living_r_liv.get('自助グループへの参加の有無[レシピエントの状態]')
                            selfhelp_flg = 1 if selfhelp == '有' else 0 if selfhelp == '無' else None

                            update_sql = """
                                UPDATE T_LIVING_R_LIV SET
                                    PS = %s,
                                    KOUSEISINYAKU_FLG = %s,
                                    KOUSEISINYAKU_KOUFUANYAKU = %s,
                                    KOUSEISINYAKU_KOUUTUYAKU = %s,
                                    KOUSEISINYAKU_KOUSEISINYAKU = %s,
                                    KOUSEISINYAKU_ETC = %s,
                                    KOUSEISINYAKU_ETC_CMNT = %s,
                                    KANKOHEN_FLG = %s,
                                    KANKOHEN_KINSYU_FLG = %s,
                                    KANKOHEN_FOLLOWUP_FLG = %s,
                                    KANKOHEN_JIJYO_GRP_FLG = %s,
                                    UPD_USER_ID = 1,
                                    UPD_PROGRAM_ID = 1
                                WHERE SEITAI_ISYOKU_ID = %s AND INPUT_DATE = %s
                            """
                            update_values = [
                                ps, kousei_flg, koufuan, kouutsu, kousei, etc, etc_cmnt,
                                kankohen_flg, kankohen_kinsyu_flg, followup, selfhelp_flg,
                                seitai_ishoku_id, input_date
                            ]
                            cursor.execute(update_sql, update_values)
                            connection.commit()

                        except Exception as e:
                            connection.rollback()
                            error_logs.append({
                                "テーブル名": "T_LIVING_R_LIV",
                                "移植登録ID": ishoku_toroku_id,
                                "施設名": sisetsu_name, 
                                "SQL": sql,
                                "入力値": insert_values,
                                "エラー内容": str(e)
                            })


                elif table_name == 'T_REJECTION_R_LIV':

                    ## 下記の診断日でよいのでしょうか。
                    ## 原疾患の再発（診断年月日）（詳細）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]の値がTRUE
                    ## 原疾患の再発（診断年月日）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]
                    ## 悪性腫瘍（診断年月日）（詳細）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]	
                    ## 悪性腫瘍（診断年月日）[合併症・再手術・再入院・妊娠・出産：前回調査以降のもののみ]
                    for index_t_rejection_r_liv, row_t_rejection_r_liv in recipient_rows:
                        id = row_t_living_r_liv['肝臓移植ID[レシピエント追跡調査]']
                        #### 報告: 調査日及び最終生存確認日が不正なデータはスキップする
                        ####  ここで指定しているIDはCSVファイルの肝臓移植ID[レシピエント追跡調査]を指定する
                        if int(id) in exclude_ids:
                            continue  # このIDの場合はスキップ（何もしない）                        
                        ## 梅先生のデータが来たら参照する指定されている最新の日付までのデータを登録している。
                        ## 現在は調査日が設定されていれば全て登録している
                        original_date = row_t_rejection_r_liv['調査日[レシピエント追跡調査]']
                        # 変換処理
                        if original_date:
                            #input_date = datetime.strptime(original_date, '%Y/%m/%d').strftime('%Y%m%d')
                            input_date = pd.to_datetime(original_date, errors='coerce').strftime('%Y%m%d')
                        else:
                            continue
                        rejection_val = row_t_rejection_r_liv.get('拒絶反応[レシピエントの状態]')

                        if rejection_val == "TRUE":
                            rejection_code = 1
                        elif rejection_val == "FALSE":
                            rejection_code = 0
                        elif rejection_val in [None, ""]:
                            rejection_code = None
                        else:
                            rejection_code = None  # その他の予期しない値もNULL扱い


                        try:
                            ### REJECTION_TYPEは「1:急性拒絶反応」を固定
                            insert_columns = ["SEITAI_ISYOKU_ID", "REJECTION_TYPE", "SINDAN_DATE", "REJECTION_TIRYOU_FLG", "INS_USER_ID", "INS_PROGRAM_ID"]
                            insert_values = [seitai_ishoku_id, 1, input_date, rejection_code, 1, 1]

                            sql = f"""
                                INSERT INTO T_REJECTION_R_LIV ({','.join(insert_columns)})
                                VALUES ({','.join(['%s'] * len(insert_columns))})
                            """
                            cursor.execute(sql, insert_values)
                            connection.commit()
                            #print(f"✅ T_REJECTION_R_LIV 登録完了: SEITAI_ISYOKU_ID={seitai_ishoku_id} SINDAN_DATEs={input_date} CSVのID={row_t_rejection_r_liv['肝臓移植ID[レシピエント追跡調査]']}")

                        except Exception as e:
                                print(f"❌ INSERT失敗: T_REJECTION_R_LIV, エラー: {e}")
                                error_logs.append({
                                    "テーブル名": "T_REJECTION_R_LIV",
                                    "移植登録ID": ishoku_toroku_id,
                                    "施設名": sisetsu_name, 
                                    "SQL": sql,
                                    "入力値": insert_values,
                                    "エラー内容": str(e)
                                })
                                connection.rollback()

                elif table_name == 'T_KENSA_R_LIV':

                    target_csv_columns = [
                        'AST[追跡時の検査値]',
                        'ALT[追跡時の検査値]',
                        'T.Bil[追跡時の検査値]',
                        'D.Bil[追跡時の検査値]',
                        'ワーファリンの使用[追跡時の検査値]',
                        'INR[追跡時の検査値]',
                    ]

                    for target_csv_column in target_csv_columns:
                        loop_cnt = 1
                        for index__t_kensa_r_liv, row__t_kensa_r_liv in recipient_rows:
                            try:
                                kensa_value = row__t_kensa_r_liv.get(target_csv_column)
                                cycle_str = f"{loop_cnt:02}"

                                kensa_name, kensa_unit, dtype, threshold = get_kensa_metadata(target_csv_column)
                                kensa_code = evaluate_kensa_code(kensa_value, dtype, threshold)

                                insert_columns = [
                                    "SEITAI_ISYOKU_ID", "KENSA_NAME", "KENSA_VALUE_CODE",
                                    "KENSA_VALUE", "KENSA_UNIT", "CYCLE", "INS_USER_ID", "INS_PROGRAM_ID"
                                ]
                                insert_values = [
                                    seitai_ishoku_id, kensa_name, kensa_code,
                                    kensa_value, kensa_unit, cycle_str, 1, 1
                                ]

                                sql = f"""
                                    INSERT INTO T_KENSA_R_LIV ({','.join(insert_columns)})
                                    VALUES ({','.join(['%s'] * len(insert_columns))})
                                """
                                cursor.execute(sql, insert_values)
                                connection.commit()

                            except Exception as e:
                                print(f"❌ INSERT失敗: T_KENSA_R_LIV, エラー: {e}")
                                error_logs.append({
                                    "テーブル名": "T_KENSA_R_LIV",
                                    "移植登録ID": ishoku_toroku_id,
                                    "施設名": sisetsu_name, 
                                    "SQL": sql,
                                    "入力値": insert_values,
                                    "エラー内容": str(e)
                                })
                                connection.rollback()

                            loop_cnt += 1

                elif table_name == 'T_KENSA_D_LIV':

                    ### このテーブルは更新処理は行わず、そのままデータを登録するだけでおわり。
                    target_csv_columns = [
                        'T.Bil[検査値と合併症・再手術・再入院]',
                        'D.Bil[検査値と合併症・再手術・再入院]',
                        'AST[検査値と合併症・再手術・再入院]',
                        'ALT[検査値と合併症・再手術・再入院]',
                        'INR[検査値と合併症・再手術・再入院]',
                        '疾患の有無[検査値と合併症・再手術・再入院]',
                    ]

                    for target_csv_column in target_csv_columns:
                        loop_cnt = 1
                        for index__t_kensa_d_liv, row__t_kensa_d_liv in donor_rows:
                            try:
                                kensa_value = row__t_kensa_d_liv.get(target_csv_column)
                                cycle_str = to_cycle_str(row__t_kensa_d_liv.get("追跡調査の種類[ドナー追跡調査]"))
                                kensa_name, kensa_unit, dtype, threshold = get_kensa_metadata(target_csv_column)
                                kensa_code = evaluate_kensa_code(kensa_value, dtype, threshold)
                                insert_columns = [
                                    "DONOR_A_ID", "KENSA_NAME", "KENSA_VALUE_CODE",
                                    "KENSA_VALUE", "KENSA_UNIT", "CYCLE", "INS_USER_ID", "INS_PROGRAM_ID"
                                ]
                                insert_values = [
                                    donor_a_id, kensa_name, kensa_code,
                                    kensa_value, kensa_unit, cycle_str, 1, 1
                                ]

                                sql = f"""
                                    INSERT INTO T_KENSA_D_LIV ({','.join(insert_columns)})
                                    VALUES ({','.join(['%s'] * len(insert_columns))})
                                """

                                cursor.execute(sql, insert_values)
                                connection.commit()

                            except Exception as e:
                                print(f"❌ INSERT失敗: T_KENSA_D_LIV, エラー: {e}")
                                error_logs.append({
                                    "テーブル名": "T_KENSA_D_LIV",
                                    "移植登録ID": ishoku_toroku_id,
                                    "施設名": sisetsu_name, 
                                    "SQL": sql,
                                    "入力値": insert_values,
                                    "エラー内容": str(e)
                                })
                                connection.rollback()

                            loop_cnt += 1


                elif table_name == 'T_IJI_MENEKI_YOKUSEI_R_LIV':

                    try:
                        for recipient_row in recipient_rows:
                            row = recipient_row[1]

                            followup_cycle = row.get("追跡調査の種類[レシピエント追跡調査]")
                            if not followup_cycle or followup_cycle.strip() == "":
                                continue
                            cycle_str = followup_cycle.replace("年後", "").zfill(2)

                            # 値の取得と変換
                            steroid_val = row.get('ステロイド[免疫抑制：維持期（現在）]')
                            cs = 1 if steroid_val == 'TRUE' else 2 if steroid_val == 'FALSE' else 3

                            cni_detail = row.get('カルシニュリン・ インヒビター-詳細[免疫抑制：維持期（現在）]', '') or ''
                            tac = 1 if 'FK506' in cni_detail else 0
                            csa = 1 if 'CyA' in cni_detail else 0

                            mtor_detail = row.get('mTOR阻害剤 -詳細[免疫抑制：維持期（現在）]')
                            rap = 1 if mtor_detail else 0

                            nukleic_detail = row.get('核酸合成阻害-詳細[免疫抑制：維持期（現在）]', '') or ''
                            mmf = 1 if 'MMF' in nukleic_detail else 0
                            az = 1 if 'AZP' in nukleic_detail else 0
                            mz = 1 if 'MZR' in nukleic_detail else 0
                            cp = 1 if 'CP' in nukleic_detail else 0

                            meneki_etc = row.get('その他（テキスト）[免疫抑制：維持期（現在）]')

                            # 登録処理（初回 insert）
                            insert_columns = [
                                "SEITAI_ISYOKU_ID", "CYCLE", "INS_USER_ID", "INS_PROGRAM_ID"
                            ]
                            insert_values = [
                                seitai_ishoku_id, cycle_str, 1, 1
                            ]

                            sql = f"""
                                INSERT INTO T_IJI_MENEKI_YOKUSEI_R_LIV ({','.join(insert_columns)})
                                VALUES ({','.join(['%s'] * len(insert_columns))})
                            """
                            cursor.execute(sql, insert_values)

                            # 追加項目を update（キー：SEITAI_ISYOKU_ID + CYCLE）
                            update_sql = """
                                UPDATE T_IJI_MENEKI_YOKUSEI_R_LIV
                                SET CS = %s, TAC = %s, CSA = %s, RAP = %s,
                                    MMF = %s, AZ = %s, MZ = %s, MENEKI_ETC = %s,
                                    CP = %s,
                                    UPD_USER_ID = %s, UPD_PROGRAM_ID = %s
                                WHERE SEITAI_ISYOKU_ID = %s AND CYCLE = %s
                            """
                            update_values = [cs, tac, csa, rap, mmf, az, mz, meneki_etc, cp, 1, 1, seitai_ishoku_id, cycle_str]
                            cursor.execute(update_sql, update_values)

                            connection.commit()

                    except Exception as e:
                        error_logs.append({
                            "テーブル名": "T_IJI_MENEKI_YOKUSEI_R_LIV",
                            "移植登録ID": ishoku_toroku_id,
                            "施設名": sisetsu_name, 
                            "SQL": sql,
                            "入力値": insert_values,
                            "エラー内容": str(e)
                        })
                        connection.rollback()


                elif table_name == 'T_LIVING_D_LIV':

                    for index_living_d, row_living_d in donor_rows:
                        try:
                            # 必須項目処理
                            input_date = datetime.now().strftime("%Y%m%d%H%M%S")
                            sisetu_cd = get_sisetu_cd(row_living_d.get("施設名", ""))  # 要: 施設名列が存在していること

                            # PS変換
                            ps_raw = str(row_living_d.get("PS[現状]", "")).strip()
                            ps_map = {"0": 1, "1": 2, "2": 3, "3": 4, "4": 5, "不明": 6}
                            ps_value = ps_map.get(ps_raw, None)

                            # 向精神薬有無
                            med_raw = str(row_living_d.get("向精神薬（睡眠導入剤を含む）の継続的使用[現状]", "")).strip()
                            med_flag = 1 if med_raw == "有" else 0 if med_raw == "無" else None

                            # 詳細（フラグ）
                            med_detail_raw = str(row_living_d.get("向精神薬（睡眠導入剤を含む）の継続的使用-詳細[現状]", "")).strip()
                            koufuan_flg = 1 if "抗不安薬" in med_detail_raw else 0
                            kouutsu_flg = 1 if "抗うつ薬" in med_detail_raw else 0
                            kousei_flg = 1 if "向精神薬" in med_detail_raw else 0
                            etc_flg     = 1 if "その他" in med_detail_raw else 0

                            # コメント
                            etc_comment = str(row_living_d.get("向精神薬（睡眠導入剤を含む）の継続的使用-その他（テキスト）[現状]", "")).strip()

                            insert_columns = [
                                "DONOR_A_ID", "INPUT_DATE", "SISETU_CD",
                                "PS", "KOUSEISINYAKU_FLG",
                                "KOUSEISINYAKU_KOUFUANYAKU", "KOUSEISINYAKU_KOUUTUYAKU",
                                "KOUSEISINYAKU_KOUSEISINYAKU", "KOUSEISINYAKU_ETC",
                                "KOUSEISINYAKU_ETC_CMNT", "INS_USER_ID", "INS_PROGRAM_ID"
                            ]
                            insert_values = [
                                donor_a_id, input_date, sisetu_cd,
                                ps_value, med_flag,
                                koufuan_flg, kouutsu_flg,
                                kousei_flg, etc_flg,
                                etc_comment, 1, 1
                            ]

                            sql = f"""
                                INSERT INTO T_LIVING_D_LIV ({','.join(insert_columns)})
                                VALUES ({','.join(['%s'] * len(insert_columns))})
                            """
                            cursor.execute(sql, insert_values)
                            connection.commit()

                        except Exception as e:
                            print(f"❌ INSERT失敗: T_LIVING_D_LIV, エラー: {e}")
                            error_logs.append({
                                "テーブル名": "T_LIVING_D_LIV",
                                "移植登録ID": ishoku_toroku_id,
                                "施設名": sisetsu_name, 
                                "SQL": sql,
                                "入力値": insert_values,
                                "エラー内容": str(e)
                            })
                            connection.rollback()

                elif conv_type == "keyword_flag_map":

                    text = str(raw_value)
                    used = set()
                    for keyword, target_col in config["keywords"].items():
                        if keyword in text:
                            result.append((target_col, 1, source_col))
                            used.add(target_col)

                    # 対象の全てのフィールドに対して未使用なら0
                    for target_col in set(config["keywords"].values()):
                        if target_col not in used:
                            result.append((target_col, 0, source_col))

                else:
                    connection.commit()
                    pass
                    #print("@@@@@@@@@@@@@@@@@@@ ")
                    #print(table_name)
                #print(tracer_df["移植登録ID"])
    connection.commit()
#except Exception as e:
#    print(f"❌ エラー: {e}")
#    connection.rollback()
#finally:
#    connection.close()
#    print("✅ DB接続を終了しました。")

if error_logs:  # もしエラーが1件以上あれば
    df_error = pd.DataFrame(error_logs)
    df_error.to_csv('error_logs_kanzou_20250623.csv', index=False, encoding='utf-8-sig')  # 日本語対応のためutf-8-sigで
    print("✅ エラーログを error_logs.csv に出力しました！")
