#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 聲請人歸屬標註 第 1 步:建工作檔 ================================= 把每一件釋憲案做成一張標註卡:官網原文(解釋文/主文+理由書/理由) 加上該案所有待歸屬的確定終局裁判案號。 python _pipeline\\聲請人_建工作檔.py 輸出於 50_聲請人標註/: 工作檔.json 一件釋憲案一筆,含原文全文與待歸屬案號清單 工作檔清單.csv 一列一筆待歸屬裁判,供進度追蹤與併檔 卡片/<釋憲案>.txt 逐件的可讀卡片(標註時實際閱讀的東西) 鐵律(見 00_索引/碼表_聲請人歸屬.md): * 標註依據是判決全文,不是關鍵詞窗格。 * 每筆必須附逐字證據句,且證據句必須能在原文中以子字串還原。 * 一次看一整件釋憲案的全部待歸屬裁判,不得一次看一筆。 """ import csv, json, re, sys, collections from pathlib import Path ROOT = Path(__file__).resolve().parent.parent PIPE = ROOT / "_pipeline" SRC = ROOT / "40_案件歷程" OUT = ROOT / "50_聲請人標註" CARD = OUT / "卡片" csv.field_size_limit(10 ** 8) # 原文中屬於「本文」的段落標籤。判決摘要與意見書不列入標註依據。 BODY = ("解釋文", "主文", "理由書", "理由") def main(): try: sys.stdout.reconfigure(encoding="utf-8") except Exception: pass dbp = PIPE / "資料庫.json" fjp = SRC / "確定終局裁判.csv" for p in (dbp, fjp): if not p.exists(): print("找不到", p) sys.exit(1) recs = {r["k"]: r for r in json.load(open(dbp, encoding="utf-8"))} fj = list(csv.DictReader(open(fjp, encoding="utf-8-sig"))) tgt = collections.OrderedDict() nohit = collections.Counter() for r in fj: k = r["釋憲案"] if not r.get("法院"): nohit[k] += 1 continue tgt.setdefault(k, []).append({ "案號": f'{r["法院"]} {r["年"]}年度{r["字別"]}字第{r["號"]}號', "法院": r["法院"], "年": r["年"], "字別": r["字別"], "號": r["號"], "官網原文法院": r.get("官網原文法院", ""), }) OUT.mkdir(exist_ok=True) CARD.mkdir(exist_ok=True) work, rows, nochar = [], [], [] for k, cases in tgt.items(): rec = recs.get(k) if not rec: nochar.append(k) continue secs = [(lab, "\n".join((f"({n})" if n else "") + t for n, t in ps)) for lab, ps in rec["s"] if lab in BODY] text = "\n\n".join(f"【{lab}】\n{body}" for lab, body in secs) work.append({"k": k, "z": rec.get("z", k), "n": rec.get("n", ""), "u": rec.get("u", ""), "ty": rec.get("ty", ""), "cases": cases, "text": text, "chars": len(text)}) for c in cases: rows.append([k, rec.get("z", k), c["案號"], len(text), "", "", "", "", ""]) head = (f"釋憲案 {k} {rec.get('z','')}" + (f"【{rec.get('n')}】" if rec.get("n") else "") + f"\n官網原文 {rec.get('u','')}\n" + f"待歸屬之確定終局裁判 {len(cases)} 筆:\n" + "".join(f" [{i+1:02d}] {c['案號']}\n" for i, c in enumerate(cases)) + "=" * 78 + "\n") (CARD / f"{k}.txt").write_text(head + text + "\n", encoding="utf-8") json.dump(work, open(OUT / "工作檔.json", "w", encoding="utf-8"), ensure_ascii=False) with open(OUT / "工作檔清單.csv", "w", encoding="utf-8-sig", newline="") as fh: w = csv.writer(fh) w.writerow(["釋憲案", "釋憲字號", "確定終局裁判", "原文字數", "P碼", "聲請人序號", "聲請人稱謂原文", "證據句", "註記"]) w.writerows(rows) ch = [x["chars"] for x in work] ch.sort() print(f"待標註釋憲案 {len(work)} 件") print(f"待歸屬裁判  {len(rows)} 筆") print(f"原文字數 中位 {ch[len(ch)//2]:,}/最長 {ch[-1]:,}/最短 {ch[0]:,}") print(f"合計字數  {sum(ch):,}") print(f"每件待歸屬裁判數 {collections.Counter(len(x['cases']) for x in work).most_common()}") if nochar: print("!有裁判但找不到原文:", nochar) print(f"另有 {sum(nohit.values())} 筆未取得裁判字號(分屬 {len(nohit)} 件),不列入標註母體") print("卡片在", CARD) if __name__ == "__main__": main()