@piecemaker-legal/piecemaker 2.0.11 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{FindReplaceDialog-KU5TEIBJ-BucOHLdm.js → FindReplaceDialog-KU5TEIBJ-DiS1F6O4.js} +1 -1
- package/dist/assets/{FootnotePropertiesDialog-W3YHM4BV-D5UqbelV.js → FootnotePropertiesDialog-W3YHM4BV-BMRWztwh.js} +1 -1
- package/dist/assets/{HyperlinkDialog-ZYH7ZHRF-8yEhLigd.js → HyperlinkDialog-ZYH7ZHRF-D5r9CZf2.js} +4 -4
- package/dist/assets/{ImagePositionDialog-HEWYR6Q3-wGIBYxCm.js → ImagePositionDialog-HEWYR6Q3-EeK_GFbX.js} +1 -1
- package/dist/assets/{ImagePropertiesDialog-RAB47C6M-D86PckZ2.js → ImagePropertiesDialog-RAB47C6M-BgrZEZLp.js} +1 -1
- package/dist/assets/{PageSetupDialog-AIGIXO3E-DLETP-7v.js → PageSetupDialog-AIGIXO3E-DdFpkcLH.js} +1 -1
- package/dist/assets/{SplitCellDialog-IFBKYCD6-CwdQBXeb.js → SplitCellDialog-IFBKYCD6-B2J0yPH6.js} +1 -1
- package/dist/assets/{TablePropertiesDialog-TMLKGOOW-j2nA7KJs.js → TablePropertiesDialog-TMLKGOOW-iTyD5Vwt.js} +1 -1
- package/dist/assets/{WatermarkDialog-KRL7WMIS-XFJ9gTkC.js → WatermarkDialog-KRL7WMIS-CdwIWeEi.js} +1 -1
- package/dist/assets/{abnfDiagram-VCTEODGH-DiCrND_Y.js → abnfDiagram-VCTEODGH-CCQgv7yc.js} +1 -1
- package/dist/assets/{arc-DOjgbMYz.js → arc-B-Y10mRQ.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-brzIFVhl.js → architectureDiagram-5GKGNRK7-C-mYZ4DW.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-C8JhTjQQ.js → blockDiagram-I7D4REHJ-Bby14xNO.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-jfYxccyk.js → c4Diagram-7LVT6UL2-CoqTzQi7.js} +1 -1
- package/dist/assets/channel-BxbXkYrr.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-CrlEShp9.js → chunk-2Q5K7J3B-CYlmti0k.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DzPlUHGq.js → chunk-5VM5RSS4-W35KZUQj.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-Cp9cpeWU.js → chunk-F27PBJKO-BVIu5FJA.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-BifUldEN.js → chunk-IMKFNOWR-DRf1mnVb.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-ggPEIkWu.js → chunk-JWPE2WC7-CUJ1N2G9.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-3h_nayRX.js → chunk-POPQ4Y6H-p6IVobio.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-CH29aG6J.js → chunk-SVP7TREG-DOM_pdOM.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-PluJUM5k.js → chunk-TICWLB2K-VqAu84r8.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-DXU39Vwg.js → chunk-XXDRQBXY-vaUsyH7g.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-COu_XNpX.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-COu_XNpX.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-Dz6roYrN.js → cose-bilkent-JH36ORCC-B_sTVfyq.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-BLzrpSit.js → cynefin-OW5HDTMX-DvnWaxw0.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-kfxnERDZ.js → cynefinDiagram-5FMLGOSQ-DriBDRYQ.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-DP57bp-N.js → dagre-GXQ25YYZ-BAwYXMGG.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-DAlsrQVW.js → diagram-S7CK7UJ4-DX03OYpD.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-By5yrMer.js → diagram-UQ7AKVKN-rdUs_hF8.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-Dx89kryN.js → diagram-VSXAHHWV-CobmLXkM.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-B9fARitZ.js → diagram-VX7I27RA-B18D7USE.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-DC15CEH1.js → diagram-Z3DM3KII-CRrNHL6r.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-BKQzghGF.js → ebnfDiagram-PWID7BFC-HXzGHTn6.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-CPY6RM7G.js → erDiagram-RLTQ6QDP-lbfD5sxC.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-4dL4GJcf.js → flowDiagram-HODETNUW-Dd0lb1Fm.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-D4-cVcfb.js → ganttDiagram-EL5Y4UJY-CKBO9TRh.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-BMeVIEiX.js → gitGraphDiagram-WWUBYQGX-Ds_AlKrJ.js} +1 -1
- package/dist/assets/index-C5slQNvE.css +1 -0
- package/dist/assets/{index-DNtFDOni.js → index-C9XXF765.js} +146 -146
- package/dist/assets/{infoDiagram-27XIBGKW-D5qMSUEU.js → infoDiagram-27XIBGKW-BbNgbn6W.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-BDnyKRHm.js → ishikawaDiagram-5VMMS53U-B9FZGWPM.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-dGz5ZGzq.js → journeyDiagram-3NMN7TZE-9z26Mgkz.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-D7UAz7o_.js → kanban-definition-UXKFOSKX-dQoIQkFu.js} +1 -1
- package/dist/assets/{layout-DKrmEGrz.js → layout-C03dZv1L.js} +1 -1
- package/dist/assets/{linear-CrTdMY-p.js → linear-BAgwn459.js} +1 -1
- package/dist/assets/{mermaid.core-CXQOT5G6.js → mermaid.core-Bb-zdGYl.js} +6 -6
- package/dist/assets/{mindmap-definition-YA3MSWOX-VgEiYfay.js → mindmap-definition-YA3MSWOX-BZ77-g1t.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-9IqT8gGx.js → pegDiagram-XKGWAZYB-vlBCwMpy.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-D98pRE-t.js → pieDiagram-E7YTZNPT-DGaLzhN2.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-b9PwkYzL.js → quadrantDiagram-AXDQQJYC-J517wJ3s.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-Dz87hsPE.js → railroadDiagram-O6MQD6OU-MiMqTB2q.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-CkcV73dK.js → requirementDiagram-BXWQKSXE-ClFWrfsX.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-UBN31Yrs.js → sankeyDiagram-P5KCCOFB-BQjzvuzZ.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-CpQxd5Za.js → sequenceDiagram-WJ2MYXX4-BVI9Mtfy.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-mCsFmyzw.js → sizeCapture-INFHLROL-npWe2anw.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-B0MTBN-3.js → stateDiagram-D77RDMKH-BE4pwhgm.js} +1 -1
- package/dist/assets/{stateDiagram-v2-MP3YSRHH-DzBPN39W.js → stateDiagram-v2-MP3YSRHH-PdR5r-x_.js} +1 -1
- package/dist/assets/{swimlanes-42K2YHIH-Dzydjm0e.js → swimlanes-42K2YHIH-DJMuv4Fv.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-Cxv8b9_h.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-Dz0toOLu.js → timeline-definition-24CTP7MA-BLHKdXUW.js} +1 -1
- package/dist/assets/{vennDiagram-4TSXK5OY-DOWp7dkf.js → vennDiagram-4TSXK5OY-vpWJbaKf.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-CUxsztUB.js → wardleyDiagram-VM6X3IG4-BC1q3_Z5.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-DS96vNED.js → xychartDiagram-S5SC5T6Z--yDqjI6l.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js +61 -1
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js.map +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js +0 -48
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js.map +1 -1
- package/dist-server/server/piecemaker/desktop-update/service.js +2 -1
- package/dist-server/server/piecemaker/desktop-update/service.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/pipeline.js +25 -29
- package/dist-server/server/piecemaker/knowledge/pipeline.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/service.js +1 -16
- package/dist-server/server/piecemaker/knowledge/service.js.map +1 -1
- package/dist-server/server/piecemaker/project-registry.js +5 -5
- package/dist-server/server/piecemaker/project-registry.js.map +1 -1
- package/package.json +1 -1
- package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +1 -1
- package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +2 -1
- package/server/piecemaker/anonymizer/rewrite.cjs +2 -2
- package/server/piecemaker/anonymizer/service.cjs +3 -2
- package/server/piecemaker/anonymizer/sqlite-dictionary.cjs +3 -31
- package/server/piecemaker/desktop-update/service.ts +2 -1
- package/server/piecemaker/index.ts +1 -1
- package/server/piecemaker/knowledge/pipeline.ts +24 -31
- package/server/piecemaker/knowledge/service.ts +1 -14
- package/server/piecemaker/project-registry.ts +6 -6
- package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +9 -162
- package/server/piecemaker/vendor/installer/lib/commandes.mjs +1 -2
- package/server/piecemaker/vendor/installer/lib/conversion-client.mjs +62 -3
- package/server/piecemaker/vendor/installer/steps/06-hooks.mjs +23 -69
- package/server/piecemaker/vendor/installer/steps/12-mcp-piecemaker.mjs +3 -3
- package/server/piecemaker/vendor/mcp/README.md +1 -1
- package/server/piecemaker/vendor/mcp/piecemaker/server.mjs +12 -75
- package/server/piecemaker/vendor/piecemaker-plugin/hooks/hooks.json +0 -40
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/compile-recherche.mjs +2 -2
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folders.cjs +26 -7
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/commits.cjs +0 -28
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/hook-io.mjs +0 -13
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/institutional-terms.cjs +3 -3
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/mapping.cjs +10 -143
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/protection.cjs +1 -1
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/session-timing.cjs +0 -65
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/protect-originals.mjs +11 -2
- package/server/piecemaker/vendor/piecemaker-plugin/skills/conversion-md/SKILL.md +9 -5
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +2 -150
- package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +3 -0
- package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +23 -372
- package/server/piecemaker/vendor/websocket-server/renamed-originals.cjs +136 -0
- package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +115 -96
- package/dist/assets/channel-Dajzu2W1.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-dDEP_giv.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-dDEP_giv.js +0 -1
- package/dist/assets/index-Dc9wNuS4.css +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-BXQpbUMD.js +0 -8
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/billing-track.mjs +0 -194
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/commit-track.mjs +0 -71
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/session-commit-worker.mjs +0 -260
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/session-commit.mjs +0 -83
|
@@ -6,10 +6,11 @@ This script chains smart_converter.py (conversion to Markdown) and
|
|
|
6
6
|
scanner_worker.py (PII scanning) for batch processing of multiple documents.
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
|
-
python3 convert_and_scan_pipeline.py <file1> [file2 ...] -o <output_dir> [options]
|
|
9
|
+
python3 convert_and_scan_pipeline.py <file1> [file2 ...] -o <output_dir> --database <auth.db> [options] < case-mapping
|
|
10
10
|
|
|
11
11
|
Options:
|
|
12
12
|
-o, --output DIR Output directory for all generated files
|
|
13
|
+
--database PATH PieceMaker database; codes held by every case are reserved
|
|
13
14
|
--engine ENGINE Conversion engine: auto|markitdown|mineru (default: auto)
|
|
14
15
|
--mode MODE MinerU mode: pipeline|hybrid|vlm (default: pipeline)
|
|
15
16
|
--lang CODE OCR language code for MinerU (default: auto)
|
|
@@ -17,17 +18,17 @@ Options:
|
|
|
17
18
|
Progress Format:
|
|
18
19
|
PROGRESS:CONVERT:percentage:current:total (Phase 1: Converting documents)
|
|
19
20
|
PROGRESS:SCAN:percentage:current:total (Phase 2: Scanning for PII)
|
|
21
|
+
MAPPING:<json> (merged case mapping, written to the database by PieceMaker)
|
|
20
22
|
|
|
21
23
|
Persistent Output Structure:
|
|
22
24
|
{output_dir}/
|
|
23
25
|
├── document1.md
|
|
24
26
|
├── document2.md
|
|
25
|
-
├── mapping_default.json
|
|
26
27
|
└── .piecemaker/anonymization-state.json
|
|
27
28
|
|
|
28
29
|
The per-document sensitive maps are transient scanner payloads. They are
|
|
29
|
-
created in a private temporary directory, merged into
|
|
30
|
-
then removed automatically.
|
|
30
|
+
created in a private temporary directory, merged into the case mapping sent
|
|
31
|
+
back on stdout, then removed automatically. No mapping file is written.
|
|
31
32
|
|
|
32
33
|
Exit Codes:
|
|
33
34
|
0: Success (at least one file fully processed)
|
|
@@ -41,9 +42,11 @@ import argparse
|
|
|
41
42
|
import subprocess
|
|
42
43
|
import json
|
|
43
44
|
import hashlib
|
|
45
|
+
import sqlite3
|
|
44
46
|
import tempfile
|
|
45
47
|
import unicodedata
|
|
46
48
|
import re
|
|
49
|
+
from contextlib import closing
|
|
47
50
|
from pathlib import Path
|
|
48
51
|
from typing import List, Optional, Tuple, Dict, Set
|
|
49
52
|
|
|
@@ -393,30 +396,30 @@ def convert_file(
|
|
|
393
396
|
return False, None
|
|
394
397
|
|
|
395
398
|
|
|
396
|
-
def
|
|
397
|
-
"""
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
"""
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
399
|
+
def read_mapping_seed() -> Dict:
|
|
400
|
+
"""Mapping of the case being scanned, sent by PieceMaker on stdin."""
|
|
401
|
+
if sys.stdin.isatty():
|
|
402
|
+
return {}
|
|
403
|
+
raw = sys.stdin.read().strip()
|
|
404
|
+
seed = json.loads(raw) if raw else {}
|
|
405
|
+
print(f"✅ Loaded case mapping: {len(seed.get('mapping', {}))} entries")
|
|
406
|
+
return seed
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def load_reserved_codes(database_path: str) -> Set[str]:
|
|
410
|
+
"""Every code already held by any case: a new code must never reuse one."""
|
|
411
|
+
database = Path(database_path).resolve()
|
|
412
|
+
if not database.exists():
|
|
413
|
+
raise FileNotFoundError(f"PieceMaker database not found: {database}")
|
|
414
|
+
with closing(sqlite3.connect(f"{database.as_uri()}?mode=ro", uri=True)) as connection:
|
|
415
|
+
tables = {row[0] for row in connection.execute("SELECT name FROM sqlite_master WHERE type='table'")}
|
|
416
|
+
codes: Set[str] = set()
|
|
417
|
+
if "piecemaker_mappings" in tables:
|
|
418
|
+
codes |= {row[0] for row in connection.execute("SELECT masked_value FROM piecemaker_mappings")}
|
|
419
|
+
if "piecemaker_nodes" in tables:
|
|
420
|
+
codes |= {row[0][len("entity:"):] for row in connection.execute("SELECT id FROM piecemaker_nodes WHERE id LIKE 'entity:%'")}
|
|
421
|
+
print(f"✅ Codes reserved by every case: {len(codes)}")
|
|
422
|
+
return codes
|
|
420
423
|
|
|
421
424
|
|
|
422
425
|
def read_individual_mappings(json_files: List[str]) -> Dict:
|
|
@@ -633,6 +636,48 @@ def consolidate_duplicate_entities(entities: List[Dict]) -> List[Dict]:
|
|
|
633
636
|
return consolidated
|
|
634
637
|
|
|
635
638
|
|
|
639
|
+
CIVILITY_TITLE = (
|
|
640
|
+
r"(?:monsieur|madame|mademoiselle|messieurs|mesdames|ma[iî]tre|docteure|doctoresse|docteur|professeure|professeur"
|
|
641
|
+
r"|mmes|mlles|mme|mlle|mm|m|me|mrs|mr|ms|miss|dr|pr|prof)\.?\s+"
|
|
642
|
+
)
|
|
643
|
+
CIVILITY_PREFIX = re.compile(
|
|
644
|
+
rf"^(?:{CIVILITY_TITLE}(?:(?:le|la|les)\s+(?={CIVILITY_TITLE}))?)+",
|
|
645
|
+
re.IGNORECASE,
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def strip_civility(text: str) -> str:
|
|
650
|
+
"""« Monsieur Jean Dupont » → « Jean Dupont » ; « Maître Prunelle » → « Prunelle »."""
|
|
651
|
+
bare = CIVILITY_PREFIX.sub("", text.strip()).strip()
|
|
652
|
+
if re.match(r"(?:le|la|les)\s|l['’]", bare, re.IGNORECASE):
|
|
653
|
+
return ""
|
|
654
|
+
return bare if re.search(r"[^\W\d_]{2,}", bare) else ""
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def _is_person_code(code: str, person_codes: Set[str]) -> bool:
|
|
658
|
+
return code in person_codes or "PERSONNE_PHYSIQUE_" in code or code.startswith(("DIRIGEANT_", "AVOCAT_"))
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
def add_bare_person_variants(merged_mapping: Dict, merged_reverse: Dict,
|
|
662
|
+
merged_extracted: Dict, ignored_lower: Set[str]) -> None:
|
|
663
|
+
"""Every titled name also gets its title-less variant, under the same code."""
|
|
664
|
+
person_entries = merged_extracted.setdefault("personnes_physiques", {})
|
|
665
|
+
person_codes = set(person_entries)
|
|
666
|
+
known_lower = {text.lower() for text in merged_mapping}
|
|
667
|
+
for text, code in list(merged_mapping.items()):
|
|
668
|
+
if not _is_person_code(str(code), person_codes):
|
|
669
|
+
continue
|
|
670
|
+
bare = strip_civility(text)
|
|
671
|
+
if not bare or bare == text or bare.lower() in known_lower or bare.lower() in ignored_lower:
|
|
672
|
+
continue
|
|
673
|
+
merged_mapping[bare] = code
|
|
674
|
+
known_lower.add(bare.lower())
|
|
675
|
+
merged_reverse[code] = list(dict.fromkeys([*(merged_reverse.get(code) or [text]), bare]))
|
|
676
|
+
entry = person_entries.get(code)
|
|
677
|
+
if entry is not None:
|
|
678
|
+
entry["variants"] = list(dict.fromkeys([*(entry.get("variants") or []), bare]))
|
|
679
|
+
|
|
680
|
+
|
|
636
681
|
def _person_entry_texts(code: str, entry: Dict, reverse_mapping: Dict,
|
|
637
682
|
mapping_texts: Optional[List[str]] = None) -> List[str]:
|
|
638
683
|
values = [entry.get("original", ""), *(entry.get("variants") or [])]
|
|
@@ -1227,18 +1272,18 @@ def convert_to_anonymization_format(consolidated_entities: Dict) -> Dict:
|
|
|
1227
1272
|
}
|
|
1228
1273
|
|
|
1229
1274
|
|
|
1230
|
-
def merge_with_existing_mapping(new_mapping: Dict, existing_mapping: Optional[Dict]) -> Dict:
|
|
1275
|
+
def merge_with_existing_mapping(new_mapping: Dict, existing_mapping: Optional[Dict], reserved_codes: Set[str] = frozenset()) -> Dict:
|
|
1231
1276
|
"""Merge new mapping with existing mapping, avoiding duplicates.
|
|
1232
1277
|
|
|
1233
1278
|
Args:
|
|
1234
1279
|
new_mapping: Newly generated mapping from current batch
|
|
1235
1280
|
existing_mapping: Existing mapping data (or None)
|
|
1281
|
+
reserved_codes: Codes held by any case, never handed out again
|
|
1236
1282
|
|
|
1237
1283
|
Returns:
|
|
1238
1284
|
Merged mapping data
|
|
1239
1285
|
"""
|
|
1240
|
-
|
|
1241
|
-
return new_mapping
|
|
1286
|
+
existing_mapping = existing_mapping or {}
|
|
1242
1287
|
|
|
1243
1288
|
# `ignored` is written by the admin editor (originals-pipeline.cjs): entities
|
|
1244
1289
|
# the lawyer removed by hand are false positives and must not come back.
|
|
@@ -1285,7 +1330,10 @@ def merge_with_existing_mapping(new_mapping: Dict, existing_mapping: Optional[Di
|
|
|
1285
1330
|
# URGOT SA), là où un test par sous-chaîne ne reconnaîtrait pas SA_3.
|
|
1286
1331
|
societe_counters: Dict[str, int] = {}
|
|
1287
1332
|
|
|
1288
|
-
|
|
1333
|
+
used_codes = set(merged_reverse) | set(merged_mapping.values()) | set(reserved_codes)
|
|
1334
|
+
for code in used_codes:
|
|
1335
|
+
if not re.search(r"_\d+$", code):
|
|
1336
|
+
continue
|
|
1289
1337
|
if "PERSONNE_PHYSIQUE_" in code or code.startswith("DIRIGEANT_"):
|
|
1290
1338
|
num = int(code.split("_")[-1])
|
|
1291
1339
|
code_counters["personnes_physiques"] = max(code_counters["personnes_physiques"], num + 1)
|
|
@@ -1296,7 +1344,7 @@ def merge_with_existing_mapping(new_mapping: Dict, existing_mapping: Optional[Di
|
|
|
1296
1344
|
num = int(code.split("_")[-1])
|
|
1297
1345
|
code_counters["siren"] = max(code_counters["siren"], num + 1)
|
|
1298
1346
|
|
|
1299
|
-
for code in merged_extracted.get("societes", {}).keys():
|
|
1347
|
+
for code in set(merged_extracted.get("societes", {}).keys()) | set(reserved_codes):
|
|
1300
1348
|
match = re.search(r"_(\d+)$", code)
|
|
1301
1349
|
if not match:
|
|
1302
1350
|
continue
|
|
@@ -1353,27 +1401,27 @@ def merge_with_existing_mapping(new_mapping: Dict, existing_mapping: Optional[Di
|
|
|
1353
1401
|
continue
|
|
1354
1402
|
|
|
1355
1403
|
# Generate new code with incremented counter
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
new_code = f"SIREN_{code_counters[category]:02d}"
|
|
1371
|
-
code_counters[category] += 1
|
|
1372
|
-
else:
|
|
1373
|
-
# Extract entity type from new code
|
|
1374
|
-
entity_type = code.split("_")[0] if "_" in code else "AUTRE"
|
|
1375
|
-
new_code = f"{entity_type}_{code_counters[category]:02d}"
|
|
1404
|
+
def next_code() -> str:
|
|
1405
|
+
if category == "societes":
|
|
1406
|
+
# Le sigle du code d'origine est préservé : SA_3 → SA_<n>, legacy
|
|
1407
|
+
# URGOT SA → SA_<n>, sinon PERS_MORALE_<n>. Compteur par sigle.
|
|
1408
|
+
key = _societe_counter_key_of_code(code)
|
|
1409
|
+
n = societe_counters.get(key, 0) + 1
|
|
1410
|
+
societe_counters[key] = n
|
|
1411
|
+
return f"{key}_{n}"
|
|
1412
|
+
prefix = {
|
|
1413
|
+
"personnes_physiques": "PERSONNE_PHYSIQUE",
|
|
1414
|
+
"adresses": "ADRESSE",
|
|
1415
|
+
"siren": "SIREN",
|
|
1416
|
+
}.get(category) or (code.split("_")[0] if "_" in code else "AUTRE")
|
|
1417
|
+
candidate = f"{prefix}_{code_counters[category]:02d}"
|
|
1376
1418
|
code_counters[category] += 1
|
|
1419
|
+
return candidate
|
|
1420
|
+
|
|
1421
|
+
new_code = next_code()
|
|
1422
|
+
while new_code in used_codes:
|
|
1423
|
+
new_code = next_code()
|
|
1424
|
+
used_codes.add(new_code)
|
|
1377
1425
|
|
|
1378
1426
|
# Add all variants to merged structures (no lowercase duplicates)
|
|
1379
1427
|
accepted_variants = [variant for variant in variants if variant.lower() not in ignored_lower]
|
|
@@ -1430,28 +1478,17 @@ def merge_with_existing_mapping(new_mapping: Dict, existing_mapping: Optional[Di
|
|
|
1430
1478
|
merged_ignored = list(dict.fromkeys(
|
|
1431
1479
|
ignored + [str(text).strip() for text in new_mapping.get('ignored', []) if str(text).strip()] + left_visible
|
|
1432
1480
|
))
|
|
1481
|
+
add_bare_person_variants(merged_mapping, merged_reverse, merged_extracted, ignored_lower)
|
|
1433
1482
|
return {
|
|
1434
1483
|
"mapping": merged_mapping,
|
|
1435
1484
|
"reverse_mapping": merged_reverse,
|
|
1436
1485
|
"extracted_data": merged_extracted,
|
|
1437
|
-
"informations_dossier": existing_mapping.get("informations_dossier", {}),
|
|
1438
1486
|
**({"ignored": merged_ignored} if merged_ignored else {}),
|
|
1439
1487
|
}
|
|
1440
1488
|
|
|
1441
1489
|
|
|
1442
|
-
def
|
|
1443
|
-
"""
|
|
1444
|
-
|
|
1445
|
-
Uses the SAME file as server.cjs (line 2146), ensuring the validated
|
|
1446
|
-
mapping can be read by GET /api/anonymize/mapping/:documentId.
|
|
1447
|
-
|
|
1448
|
-
Args:
|
|
1449
|
-
mapping_path: Full path to the mapping file to write
|
|
1450
|
-
mapping_data: Complete mapping data structure
|
|
1451
|
-
|
|
1452
|
-
Returns:
|
|
1453
|
-
Path to saved file
|
|
1454
|
-
"""
|
|
1490
|
+
def emit_mapping(mapping_data: Dict) -> None:
|
|
1491
|
+
"""Send the merged mapping to PieceMaker, which writes it to its database."""
|
|
1455
1492
|
payload = {
|
|
1456
1493
|
**mapping_data,
|
|
1457
1494
|
"mapping": dict(sorted(
|
|
@@ -1459,15 +1496,7 @@ def save_mapping(mapping_path: Path, mapping_data: Dict) -> Path:
|
|
|
1459
1496
|
key=lambda item: (-len(item[0]), item[0].casefold()),
|
|
1460
1497
|
)),
|
|
1461
1498
|
}
|
|
1462
|
-
|
|
1463
|
-
temporary = mapping_path.with_name(f"{mapping_path.name}.piecemaker-{os.getpid()}.tmp")
|
|
1464
|
-
with open(temporary, 'w', encoding='utf-8') as f:
|
|
1465
|
-
json.dump(payload, f, indent=2, ensure_ascii=False)
|
|
1466
|
-
f.write("\n")
|
|
1467
|
-
os.chmod(temporary, 0o600)
|
|
1468
|
-
os.replace(temporary, mapping_path)
|
|
1469
|
-
|
|
1470
|
-
return mapping_path
|
|
1499
|
+
print("MAPPING:" + json.dumps(payload, ensure_ascii=False, separators=(",", ":")), flush=True)
|
|
1471
1500
|
|
|
1472
1501
|
|
|
1473
1502
|
def load_anonymization_state(state_path: Path) -> Dict:
|
|
@@ -1757,7 +1786,7 @@ def run_pipeline(resources: PipelineResources):
|
|
|
1757
1786
|
"-o",
|
|
1758
1787
|
"--output",
|
|
1759
1788
|
required=True,
|
|
1760
|
-
help="Output directory for Markdown files
|
|
1789
|
+
help="Output directory for Markdown files",
|
|
1761
1790
|
)
|
|
1762
1791
|
parser.add_argument(
|
|
1763
1792
|
"--engine",
|
|
@@ -1777,14 +1806,9 @@ def run_pipeline(resources: PipelineResources):
|
|
|
1777
1806
|
help=argparse.SUPPRESS,
|
|
1778
1807
|
)
|
|
1779
1808
|
parser.add_argument(
|
|
1780
|
-
"--
|
|
1781
|
-
|
|
1782
|
-
help="
|
|
1783
|
-
)
|
|
1784
|
-
parser.add_argument(
|
|
1785
|
-
"--mapping-file",
|
|
1786
|
-
default=None,
|
|
1787
|
-
help="Exact mapping file to read and rewrite (overrides --mapping-dir)",
|
|
1809
|
+
"--database",
|
|
1810
|
+
required=True,
|
|
1811
|
+
help="PieceMaker database (auth.db): codes held by every case are reserved; the case mapping comes on stdin",
|
|
1788
1812
|
)
|
|
1789
1813
|
parser.add_argument(
|
|
1790
1814
|
"--state-file",
|
|
@@ -1822,11 +1846,8 @@ def run_pipeline(resources: PipelineResources):
|
|
|
1822
1846
|
|
|
1823
1847
|
# Ensure output directory exists
|
|
1824
1848
|
os.makedirs(args.output, exist_ok=True)
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
if args.mapping_file
|
|
1828
|
-
else Path(args.mapping_dir or args.output) / "mapping_default.json"
|
|
1829
|
-
)
|
|
1849
|
+
reserved_codes = load_reserved_codes(args.database)
|
|
1850
|
+
current_mapping = read_mapping_seed()
|
|
1830
1851
|
state_target = Path(args.state_file) if args.state_file else Path(args.output) / ".piecemaker" / "anonymization-state.json"
|
|
1831
1852
|
# Base pour les clés du manifeste : le dossier juridique, découplé de --output
|
|
1832
1853
|
# (désormais un sous-dossier). Les sources vivent sous le dossier, pas sous la
|
|
@@ -1961,7 +1982,6 @@ def run_pipeline(resources: PipelineResources):
|
|
|
1961
1982
|
|
|
1962
1983
|
scan_success_count = 0
|
|
1963
1984
|
final_mapping_data = None
|
|
1964
|
-
mapping_path = None
|
|
1965
1985
|
index_path = state_target.parent / "document-index.json"
|
|
1966
1986
|
# Raw detections contain PII. They live only in a private OS temporary
|
|
1967
1987
|
# directory and are deleted individually as each file is merged.
|
|
@@ -2011,9 +2031,9 @@ def run_pipeline(resources: PipelineResources):
|
|
|
2011
2031
|
source = md_sources[md_file]
|
|
2012
2032
|
consolidated = read_individual_mappings([str(json_path)])
|
|
2013
2033
|
new_mapping_data = convert_to_anonymization_format(consolidated)
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2034
|
+
final_mapping_data = merge_with_existing_mapping(new_mapping_data, current_mapping, reserved_codes)
|
|
2035
|
+
current_mapping = final_mapping_data
|
|
2036
|
+
emit_mapping(final_mapping_data)
|
|
2017
2037
|
update_anonymization_state(state_target, state_case_root, [source])
|
|
2018
2038
|
|
|
2019
2039
|
record = {
|
|
@@ -2046,7 +2066,7 @@ def run_pipeline(resources: PipelineResources):
|
|
|
2046
2066
|
print()
|
|
2047
2067
|
return 1
|
|
2048
2068
|
|
|
2049
|
-
print(
|
|
2069
|
+
print("✅ Mapping sent to PieceMaker")
|
|
2050
2070
|
print(f" • Total entities: {len(final_mapping_data['mapping'])} variants")
|
|
2051
2071
|
print(f" • Unique codes: {len(final_mapping_data['reverse_mapping'])}")
|
|
2052
2072
|
print()
|
|
@@ -2061,7 +2081,6 @@ def run_pipeline(resources: PipelineResources):
|
|
|
2061
2081
|
print(f" • Entities mapped: {len(final_mapping_data['mapping'])} variants")
|
|
2062
2082
|
print(f" • Unique entities: {len(final_mapping_data['reverse_mapping'])} codes")
|
|
2063
2083
|
print(f"📂 Output directory: {args.output}")
|
|
2064
|
-
print(f"📋 Mapping file: {mapping_path}")
|
|
2065
2084
|
print()
|
|
2066
2085
|
|
|
2067
2086
|
# Exit with success if at least one file was fully processed
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{U as a,Q as n}from"./mermaid.core-CXQOT5G6.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-TICWLB2K-PluJUM5k.js";import{_ as i}from"./mermaid.core-CXQOT5G6.js";import"./chunk-5VM5RSS4-DzPlUHGq.js";import"./chunk-XXDRQBXY-DXU39Vwg.js";import"./chunk-POPQ4Y6H-3h_nayRX.js";import"./chunk-F27PBJKO-Cp9cpeWU.js";import"./index-DNtFDOni.js";import"./vendor-codemirror-CN-sJePf.js";import"./vendor-react-B22wqN3J.js";import"./vendor-xterm-BWUgpGtR.js";var c={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{c as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-TICWLB2K-PluJUM5k.js";import{_ as i}from"./mermaid.core-CXQOT5G6.js";import"./chunk-5VM5RSS4-DzPlUHGq.js";import"./chunk-XXDRQBXY-DXU39Vwg.js";import"./chunk-POPQ4Y6H-3h_nayRX.js";import"./chunk-F27PBJKO-Cp9cpeWU.js";import"./index-DNtFDOni.js";import"./vendor-codemirror-CN-sJePf.js";import"./vendor-react-B22wqN3J.js";import"./vendor-xterm-BWUgpGtR.js";var c={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{c as diagram};
|