# -*- coding: utf-8 -*-
"""第7回 PDF「確認事項（第7回）」の自己点検。

原稿 (docs/handoff/TeamWorks確認事項_第7回_20260910.md) の文言が
すべて PDF に含まれているか、フォントが日本語フォントで埋め込まれているかを見る。
PDF のパスは第1引数で渡せる。省略時はこのスクリプトと同じディレクトリ。

check_teamworks_qa6_pdf.py（第6回用）と同じ構成。

★2026-09-10 の改訂に追随済み★
  1. 旧第4部（ダンプ元サーバが MariaDB）は削除された。旧5→4、旧6→5。
     MariaDB 関連は第1部 1-4（謝意と当方の対応状況）だけを検査する。
  2. 第3部の依頼は A-2 / A-3b / B-2 / B-3b の4件のみ。
     B-1（外部キー／索引）と A-1（主キー）の取り下げは 1-5 で検査する。
  3. 質問は3件。サマリ表・おわりにの優先順表も3件に追随している。

★must に「ページを跨ぐ語」を入れないこと★
抽出テキストにフッター（D-Watch / DFA Robotics とページ番号）が挟まり、
空白を詰めても一致しません。第3回で bigint auto_increment が実際に引っかかりました。

★4 の検査があっても、表の列幅崩れの目視確認は省かないこと★
4 は「識別子が行の途中で割れていないか」を抽出テキストで見るだけで、
枠のはみ出し・改ページ位置・重なりは見られません（pdf/README.md）。
"""
import os
import re
import sys

from pypdf import PdfReader

PDF = (sys.argv[1] if len(sys.argv) > 1 else
       os.path.join(os.path.dirname(os.path.abspath(__file__)),
                    "DWatch_確認事項_第7回_20260910.pdf"))
if not os.path.exists(PDF):
    raise SystemExit("PDF が見つからない: %s\n"
                     "先に make_teamworks_qa7_pdf.py を実行するか、"
                     "PDF のパスを第1引数で渡すこと。" % PDF)

raw = open(PDF, "rb").read()
reader = PdfReader(PDF)
pages = [p.extract_text() for p in reader.pages]
text = "\n".join(pages)
flat = re.sub(r"\s+", "", text)

print("== 1. ページ数とサイズ ==")
print("ページ数:", len(reader.pages), " サイズ:", len(raw), "bytes",
      "(%.1f KB)" % (len(raw) / 1024))
print("抽出文字数:", len(text))

print("\n== 2. フォント埋め込み ==")
print("/FontFile2 出現回数:", raw.count(b"/FontFile2"))
biz = sorted(set(re.findall(rb"/([A-Z]{6}\+[A-Za-z0-9\-]*BIZ[A-Za-z0-9\-]*)", raw)))
print("BIZUDGothic を含むフォント名:", biz)
if not biz:
    print("  ★NG★ BIZ UDGothic が埋め込まれていない")
# 中国語向けフォントが混ざっていないこと（CLAUDE.md §0-3）
CN = [b"SimSun", b"SimHei", b"MicrosoftYaHei", b"MSYaHei",
      b"NotoSansSC", b"NotoSerifSC", b"SourceHanSansSC", b"SourceHanSerifSC"]
hit = [n.decode() for n in CN if n in raw]
print("中国語向けフォント:", hit if hit else "混入なし")
if hit:
    print("  ★NG★ 中国語向けフォントが混ざっている")

print("\n== 3. 原稿の全項目が含まれるか ==")
must = [
    # 表題・前文
    "確認事項（第7回）", "TeamWorks（DB開発側）",
    "作成日:2026-09-10", "DFARobotics",
    "最新のDDL（clean_watch_20260909.sql）をお送りいただき",
    "ありがとうございました",
    "2026-09-09受領。第6回でお願いしていた項目です",
    "率のカラムを小数第一位まで持つ形（double(4,1)）に変更いただき",
    "当方からお願いしていた内容が、そのまま反映されておりました",
    "これで当方の判定処理が正しく行えるようになります（詳細は第1部）",
    "本書は次の5部構成です",
    "今回は、お伺いすることを3件に絞りました",
    "接続先/DDLの未反映4件/precheckの値域",
    "お伺いする件数が少ないほど、早くお返事をいただけると考えたためです",
    "第1部と第5部は当方からの共有ですので、回答は不要です",
    # 前文の表（5部構成）
    "1.照合結果のご共有", "いただいたDDLを当日中に照合しました",
    "MariaDBの件と、依頼2件の取り下げもこちらです", "回答不要",
    "2.接続先", "（ホスト名・ポート）",
    "第6回からの継続。あとはこれだけです", "最優先",
    "3.DDLの未反映項目", "（4件）",
    "採番・桁数・DEFAULT・索引の食い違いです", "お手すきの折に",
    "4.precheckに入る", "値の範囲",
    "反映時期と既存データの扱い", "中",
    "5.面積の型変更について", "共有のみ。質問はありません",
    # 1. 照合結果
    "1.照合結果のご共有（回答不要です）",
    "第6回で", "受領しましたら、その日のうちに照合してご報告します",
    "とお約束していた件です",
    "2026-09-09に受領し、当日中に照合を完了しました",
    # 1-1
    "1-1.照合方法",
    "ローカルのMySQLに読み込み、09-07版・09-02版と",
    "information_schemaで機械的に差分を取りました",
    "目視ではなく、列名・型の両方を双方向で突き合わせています",
    # 1-2
    "1-2.率のカラムが小数第一位になっていました（13列）",
    "変更前", "変更後",
    "tinyintunsigned（整数）", "double(4,1)", "（小数第一位）",
    "対象は", "13列", "です",
    "テーブル", "列",
    "customer_summaries/", "facility_summaries",
    "operation_ratio/launch_ratio/", "clean_ratio/error_ratio（各4列）",
    "clean_status_histories/", "clean_statuses/", "clean_task_reports/",
    "robots/", "section_robot_statuses", "clean_ratio（各1列）",
    "これで当方の判定が正しく行えるようになりました",
    "当方は清掃完了率から4段階（◎○△×）の判定を行っており",
    "その境界値は90.0/90.1/70.0/70.1/10.0/9.9と、すべて小数第一位までです",
    "整数で格納されていると",
    "例えば89.6%が90に丸められて格納され",
    "本来「○」であるべきものが「◎」になってしまう",
    "という問題がありました",
    "今回の変更で、この問題は解消しました",
    # 1-3
    "1-3.テーブル・列の増減はありませんでした",
    "CREATETABLEは39で、09-07版・09-02版と同数です",
    "テーブル名の集合も完全に一致しました",
    "列の増減もありません",
    "今回の変更は型（と一部のDEFAULT）だけでした",
    "なお、面積のカラムもdecimalからdoubleに変わっておりましたので",
    "この点だけ第5部で申し添えます",
    "当方に実害はありません",
    # 1-4 MariaDB（質問ではなく謝意と対応状況）
    "1-4.本番がMariaDBであることをお教えいただき",
    "本書をお送りする前に、本番サーバがMariaDBであることを",
    "お教えいただきました。ありがとうございます",
    "09-09版のダンプヘッダが5.5.5-10.11.13-MariaDB-logに",
    "なっており、09-07版までのMySQL8.0.41と異なっておりましたので",
    "お伺いしようとしていた件です",
    "先にご回答いただけましたので、本書からは質問を取り下げました",
    "当方の対応状況を申し添えます",
    "アプリの実装に生のSQLは1件もありません",
    "すべてPrismaのクエリビルダ経由ですので",
    "現時点で対応が必要な箇所はありません",
    "DBクライアントのドライバも、すでにMariaDB用のもの",
    "（@prisma/adapter-mariadb）を使っております",
    "どうしてもMariaDB特有のDMLを使いたい場合は",
    "その箇所をお知らせください」とのご指示、承知しました",
    "その必要が生じた際にご連絡します",
    "なお、", "当方のローカルの検証環境はMySQLです", "ので",
    "今後もし生のSQLを書く場合は、",
    "MySQLとMariaDBの双方で通る書き方に注意いたします",
    # 1-5 B-1 / A-1 の取り下げ
    "1-5.これまでお願いしていた2件（外部キー／索引・主キー）を、",
    "今回の依頼から外します",
    "第5回・第6回でお願いしておりました",
    "B-1（clean_timetable_idの外部キー／索引）",
    "A-1（robot_error_reportsの主キー）",
    "当方の実装が固まり、いずれも当方側で支障が無いことが",
    "はっきりしました",
    "今回の依頼から外させてください",
    "B-1（clean_timetable_idの外部キー・索引）",
    "当方は、これらのテーブルを全件取得し、",
    "アプリ側で突き合わせる実装にしました",
    "clean_timetables/clean_status_histories/",
    "robot_error_reportsを、それぞれ",
    "WHERE句なしの全件取得", "で読み出しています",
    "読み出したあと、clean_timetable_idをキーにした対応表を",
    "メモリ上に作り、そこで結合しています",
    "つまり、clean_timetable_idで「検索」をしておりません",
    "索引はWHERE句で絞り込むときに効くものですので、",
    "この実装では使われません",
    "この実装はすでに完成しており、外部キーが無いまま",
    "清掃ログの画面が動いております",
    "どの索引が必要かは、実際のクエリの形と、",
    "実データの量・分布で決まります",
    "当方はまだ実データを1行も見ておらず、",
    "絞り込みのクエリも書いておりません",
    "この状態で特定の索引を名指しでお願いするのは、",
    "当方の根拠が薄いと考え直しました",
    "したがって、現時点で支障はありません",
    "将来クエリの形が変わって索引が必要になりましたら、",
    "その時点で具体的なクエリを添えて、",
    "あらためてご相談します",
    "過去の確認書では外部キーの追加をお願いしておりましたが",
    "当方の実装方針が定まった結果、当方の見立てが変わりました",
    "お手数をおかけしました",
    "A-1（robot_error_reportsの主キー）",
    "error_idを識別子として使う方針は第4回のご回答でいただいており",
    "当方はその前提で実装しております",
    "主キーが無くても当方の実装は動いております",
    "error_idにUNIQUEがありますので、一意性は担保されています",
    "当方の実装で支障が無い以上、",
    "DDLの変更をお願いする根拠がありませんので",
    "こちらも依頼から外します",
    # 2. 接続先
    "をお教えいただけますでしょうか—最優先",
    "第6回でもお願いした件で、恐れ入りますが繰り返しになります",
    "参照用アカウントは2026-09-08にいただいておりますので",
    "あとは接続先だけです",
    "接続先のホスト名（またはIPアドレス）と、ポート番号",
    "をお教えいただければ接続できます",
    "あわせて確認させてください",
    "接続元IPの制限はありますか",
    "ある場合、", "当方の接続元をお伝えします", "のでお知らせください",
    "SSL/TLS接続は必須でしょうか",
    "証明書の受け渡し方法", "もお教えください",
    "接続先はステージング環境でしょうか、本番環境でしょうか",
    "当方の扱い（参照のみ/負荷のかけ方）を合わせたいためです",
    "なぜ急いでお願いしているか",
    "9月中にPPIH様向けにデモを実施する可能性があります",
    "実データでお見せできるかどうかが、この接続情報で決まります",
    # 2. 準備状況
    "当方の準備状況",
    "接続情報さえいただければ、すぐに使える状態になっております",
    "済んでいること",
    "いただいたDDLからローカルにDBを作成し、実DDLと列レベルで完全一致させました",
    "差分0件を機械的に確認済み",
    "prismadbpullでスキーマを取り込み済みです",
    "今回の23列の型変更も反映済み",
    "施設マスタ・スケジュール・D-Secログ・D-Secアラート・"
    "清掃詳細ログの5画面が、",
    "そのローカルDB経由で動くところまで実装済みです",
    "接続先をいただければ、接続文字列を差し替えるだけです",
    "また第6回でお伝えしたとおり、接続でき次第",
    "error_typeの値の分布・robots.statusの実出現値",
    "週次サマリのweekの値域など",
    "当方で数えられるものは当方で確認します",
    "その分だけ貴社への照会が減ります",
    # 3. 未反映項目（4件）
    "3.DDLの未反映項目（4件）を、",
    "お手すきの折にご対応いただけますでしょうか",
    "お忙しいところ恐縮です。3回目のお願いになります",
    "第5回・第6回でお願いしていた項目のうち",
    "4項目が09-09版でも未反映でした",
    "今回のDDLは率と面積の型変更が主眼だったかと存じますので",
    "これらは単に今回のスコープに入っていなかっただけかと思っております",
    "状況だけ共有させてください",
    "お願いしていた内容", "09-09版の実測",
    "A-2", "履歴テーブルのidを", "AUTO_INCREMENTで採番",
    "clean_status_histories.id/", "clean_timetable_histories.idとも",
    "bigint(20)unsignedNOTNULL。", "AUTO_INCREMENT無し",
    "A-3b", "facility_contacts.emailを", "varchar(100)に",
    "varchar(255)DEFAULTNULLのまま",
    "（contact_typeはtinyint(3)unsigned", "で反映済みです）",
    "B-2", "robot_task_versions.clean_schedule_id", "にインデックス",
    "KEYは", "robot_task_versions_robot_id_foreign", "（robot_id）のみ",
    "clean_schedule_idのインデックスは0件",
    "B-3b", "security_status_histories.reported_at", "にDEFAULT",
    "timestampNOTNULL、", "DEFAULTなしのまま",
    # 3-1
    "3-1.この4件は、当方の設計上のお願いではなく、具体的なズレです",
    "1-5で取り下げた2件（外部キー／索引・主キー）は",
    "「DBの設計をどうするか」という判断を伴うお願い", "でした",
    "当方の実装方針が固まり、必要がなくなりましたので",
    "取り下げました",
    "一方、この4件は性質が違います",
    "いずれも、採番・桁数・DEFAULT・索引の有無という、",
    "具体的で機械的な食い違いです",
    "食い違いの中身", "補足",
    "採番の仕組み", "idにAUTO_INCREMENTが無く、",
    "誰が採番するのかが決まっていない状態", "です",
    "桁数", "「emailはvarchar(100)とします」と",
    "ご回答いただいた桁数が、", "varchar(255)のままです",
    "索引の有無", "定義書でfkと記載されている列",
    "ですが、", "実DDLには外部キーも索引もありません",
    "DEFAULTの有無", "timestampNOTNULLに", "DEFAULT句がありません",
    "いずれも「どちらが正なのか」が当方には判断できない箇所です",
    "意図的なものでしたら、",
    "その旨だけお知らせいただければ結構です",
    # 3-2
    "3-2.お手すきの折で結構です",
    "いずれも当方の開発を止めておりません",
    "お手すきの折に、まとめてご対応いただければ十分です",
    "急かす意図はありません", "状況が分かれば大丈夫です",
    "次回まとまった段階でDDLをいただければ、"
    "同じように当日中に照合してご報告します",
    # 4. precheck
    "clean_timetables.precheckに入る値の範囲をお教えいただけますでしょうか",
    "4-1.現状の理解",
    "clean_timetables.precheckは現行",
    "tinyintunsigned/既定値0", "です（09-09版でも変化なし）",
    "DFA側で「0=本清掃/60=60分前/30=30分前」のように",
    "分前の数値をそのまま入れる方向でご検討いただいている",
    "と伺っております",
    "設定値によっては", "20なら20分前", "のように",
    "任意の分数が入りうるとも伺っております",
    "4-2.お伺いしたいこと",
    "この理解で合っていますでしょうか",
    "いつ頃反映される見込みでしょうか",
    "既存のデータはどうなりますでしょうか",
    "1/2のまま残るのか、60/30に更新されるのか",
    "新旧の値が混在する期間はありますでしょうか",
    "0以外に予約値はありますでしょうか",
    "特に4番をお伺いしたいです",
    "混在すると、当方は1を「1分前」と読み違えてしまうためです",
    "値だけを見て新旧を判別する方法がありませんので",
    "期間が分かれば当方で切り替え時期を合わせられます",
    "4-3.なぜお伺いするか",
    "アラート画面で「60分前の異常」「30分前の異常」を出し分けるのに",
    "この値を使います",
    "値の意味を取り違えると、現場への連絡タイミングの判断を誤ります",
    "「30分前のチェックで異常が出た」のか「60分前で出た」のかによって",
    "現場に間に合うかどうかが変わるためです",
    "DDLからは値の意味が読めませんでした",
    "カラムコメントが空のため",
    "型はtinyintunsigned（0〜255）ですので",
    "60も30も20も、型の変更なしで入ります",
    "つまり", "値の意味の変更はDDLには現れません", "ので、お伺いする次第です",
    # 5. 面積（共有のみ）
    "5.面積のカラムの型変更について（共有のみ。回答は不要です）",
    "これは要望でも質問でもなく、事実の共有だけです",
    "5-1.実測",
    "09-09版で、面積のカラムも型が変わっておりました（", "10列", "）。",
    "decimal(12,6)unsigned",
    "double(12,6)", "（customer_summaries/", "facility_summariesはdouble(14,6)）",
    "task_area/clean_area",
    "5-2.当方に実害はありません",
    "この変更で当方が困ることはありません",
    "理由は2つです", "理由",
    "viewerに面積を表示する箇所は1つもありません",
    "画面に出るのは%だけです",
    "面積は完了率の計算材料としてのみ使う想定でしたが、"
    "率を小数でいただけるようになったため、"
    "当方で計算し直す必要も無くなりました",
    "したがって、お戻しいただく必要はありません",
    "5-3.意図の確認は今回は見送ります",
    "当初は「率の小数化に伴う変更でしょうか」と",
    "お伺いしようと考えておりましたが",
    "上記のとおり当方に実害がありませんので、",
    "今回は質問といたしません",
    "お伺いする件数を絞り、",
    "早くお返事をいただきたいためです",
    "もし何か意図がおありでしたら、",
    "次回ついでの折にお聞かせいただければ十分です",
    # おわりに
    "おわりに",
    "優先度をつけるとすれば次の順です",
    "順", "項目",
    "アカウントはいただいておりますので、これだけで接続できます",
    "9月中のデモを実データで行えるかがここで決まります",
    "precheckの値の範囲と", "反映時期",
    "新旧混在の期間があるかどうかを特に知りたいです",
    "DDLの未反映4件", "（A-2/A-3b/B-2/B-3b）",
    "お手すきの折で結構です。", "当方の開発は止まっていません",
    "1（接続先）が最優先です",
    "これだけ先にいただければ、当方で確認できることが一気に増え、"
    "その分だけ貴社への照会が減ります",
    "なお、これまでお願いしておりました外部キー／索引（B-1）と",
    "主キー（A-1）は、",
    "当方の実装方針が固まり必要がなくなりましたので、",
    "今回の依頼から外しました", "（1-5に経緯を記しました）",
    "率のカラムの小数化、そして本番がMariaDBであることのご教示、",
    "あらためてありがとうございました",
    "引き続きよろしくお願いします",
]

miss = [m for m in must if re.sub(r"\s+", "", m) not in flat]
print("検査項目:", len(must), " 欠落:", len(miss))
for m in miss:
    print("  ★欠落★", m)

print("\n== 4. 識別子の語中改行 ==")
# wordWrap="CJK" は ASCII の語中でも折り返す。折り返されると抽出テキストに
# 改行が入るため、空白を詰めない生の抽出テキストで原形のまま探す。
IDENT = [
    "clean_watch_20260909.sql",
    "double(4,1)", "double(12,6)", "double(14,6)",
    "decimal(12,6) unsigned", "tinyint unsigned", "tinyint(3) unsigned",
    "information_schema", "CREATE TABLE",
    "customer_summaries", "facility_summaries",
    "operation_ratio", "launch_ratio", "clean_ratio", "error_ratio",
    "clean_status_histories", "clean_statuses", "clean_task_reports",
    "section_robot_statuses",
    "task_area", "clean_area",
    "prisma db pull", "error_type", "robots.status",
    "robot_error_reports", "error_id",
    "clean_status_histories.id", "clean_timetable_histories.id",
    "bigint(20) unsigned NOT NULL", "AUTO_INCREMENT",
    "facility_contacts.email", "varchar(100)", "varchar(255) DEFAULT NULL",
    "contact_type",
    "robot_task_versions.clean_schedule_id",
    "robot_task_versions_robot_id_foreign",
    "clean_schedule_id",
    "security_status_histories.reported_at", "timestamp NOT NULL",
    "clean_timetable_id", "clean_timetables",
    "@prisma/adapter-mariadb",
    # PRIMARY KEY / CONSTRAINT は A-1・B-1 を落としたので本文から消えている
    "SSL/TLS",
    "MySQL 8.0.41", "5.5.5-10.11.13-MariaDB-log",
    "clean_timetables.precheck",
]
broken = [i for i in IDENT if i not in text]
print("検査した識別子:", len(IDENT), " 語中改行:", len(broken))
for b in broken:
    print("  ★語中で割れている★", b)

print("\n== 5. 認証情報が載っていないこと ==")
# 参照用アカウントのユーザー名・パスワードは PDF に書かない（原稿にも無い）
SECRET = ["password", "PASSWORD", "IDENTIFIED BY", "passwd", "pwd="]
leak = [s for s in SECRET if s in text]
print("認証情報らしき語:", leak if leak else "なし")

print("\n== 6. 判定 ==")
ok = (not miss) and bool(biz) and (not hit) and (not broken) and (not leak)
print("結果:", "OK" if ok else "NG")
sys.exit(0 if ok else 1)
