💡 Plně funkční příklad dostupný na GitHubu:
document-version-metadata-diff-python
Co vytvoříte
V tomto průvodci porovnáte (diff) každou vlastnost metadat mezi dvěma verzemi dokumentu a přesně vytisknete, co bylo přidáno, odebráno nebo změněno. Diff verzí metadat je porovnání na úrovni vlastností dvou revizí jednoho souboru a zachytí signály, které textové porovnání nikdy nevidí: nový Tvůrce, zvýšené číslo Revize, relace úprav zaznamenaná po uzavření revize. Na konci budete mít funkční řešení plus dva zaměřené detektory a dva exportní formáty, vše pocházející z spustitelného repozitáře naplněného ukázkovým párem revizí.
Úroveň dovedností: středně pokročilý vývojář Pythonu
Co potřebujete: Python 3, pip a dvě revize jednoho dokumentu
Moje první spuštění tohoto skriptu označilo změnu hodnoty Company, na kterou si nikdo v týmu nepamatoval; ten jeden řádek zaplatil za nastavení. Vše níže je připravené ke kopírování a vložení a má méně než sto řádků.
Pipeline je záměrně nudná: dva otevření souboru, tři slovníkové comprehensions, smyčka výpisu. Nudnost je záměrem. Rozpory o verzi se rozhodují na základě toho, zda lze metodu vysvětlit a zopakovat, a skript tak malý může plně přečíst kdokoli, kdo výsledek zpochybňuje.
1. Instalace
pip install groupdocs-metadata-net==26.5
The companion repository pins this version and ships document-v1.docx and document-v2.docx so the code below runs as-is. Pin the version your audit ran with; reproducibility is part of the evidence.
2. Hlavní kód
Přečtěte oba stromy vlastností a poté klasifikujte delta pomocí logiky množin. Toto je celý diff:
# Flatten a file's complete property tree into a dict
def read_props(path):
props = {}
with Metadata(path) as metadata:
for p in metadata.find_properties(lambda p: p.name is not None):
props[p.name] = (str(p.interpreted_value) if p.interpreted_value is not None
else (str(p.value) if p.value is not None else ""))
return props
v1 = read_props("resources/document-v1.docx")
v2 = read_props("resources/document-v2.docx")
# Classify every key; changed entries keep both values
added = {k: v for k, v in v2.items() if k not in v1}
removed = {k: v for k, v in v1.items() if k not in v2}
changed = {k: (v1[k], v2[k]) for k in v1 if k in v2 and v1[k] != v2[k]}
print(f"added={len(added)} removed={len(removed)} changed={len(changed)}")
for k, (old_v, new_v) in changed.items():
print(f" {k}: {old_v} -> {new_v}")
Toto je minimum, které potřebujete. Očekávejte malé počty u skutečných párů revizí; delta v desítkách obvykle znamená, že soubor prošel změnou šablony nebo migrací úložiště. Následující sekce vysvětlují klíčová volání a ukazují úpravy, které většina týmů přidává jako první.
3. Jak to funguje
Metadata: kontextový manažer, který otevře soubor a uvolní jej při ukončení; jedna instance na revizi.find_properties: prochází vestavěná pole, vlastní vlastnosti a XMP v jednom průchodu a vrací vše, co predikát akceptuje.interpreted_value: lidsky čitelná forma vlastnosti; upřednostnění této hodnoty znamená, že data a výčty se porovnávají jako řetězce, které můžete vytisknout v reportu.- Kvalifikovaná jména jako klíče: vestavěná a vlastní pole se nemohou v dictu kolidovat, takže logika množin zůstává bezpečná.
Zde se neparsují struktury DOCX. Produktová dokumentace uvádí více než 170 formátů za stejným voláním, takže stejný skript porovnává i páry PDF nebo XLSX.
Ještě jedna vlastnost designu stojí za zmínku: hranice API končí u dvou volání read_props. Všechno po nich je standardní knihovna Pythonu, takže jednotkové testy, prahové hodnoty a pravidla upozornění se nikdy nedotýkají vrstvy dokumentu. Týmy, které toto zabalí do služby, obvykle kešují extrahované dicty podle revize a umožní každé následné kontrole je znovu použít, přičemž souborové I/O zůstane na jednom otevření na verzi bez ohledu na počet dotazů.
4. Běžné úpravy
Detekovat pouze změny vlastnictví
Když je otázka „kdo se dotkl tohoto souboru“, filtrujte při čtení pomocí predikátů tagů místo následného filtrování celého diffu:
# Identity fields only, whatever the format calls them
def read_ownership(path):
result = {}
with Metadata(path) as metadata:
props = metadata.find_properties(lambda p:
Tags.person.creator in list(p.tags)
or Tags.person.editor in list(p.tags)
or Tags.person.manager in list(p.tags)
or Tags.corporate.company in list(p.tags))
for prop in props:
result[prop.name] = (str(prop.interpreted_value)
if prop.interpreted_value is not None
else (str(prop.value) if prop.value is not None else ""))
return result
Spusťte stejnou smyčku delta nad dvěma těmito dicty, použijte <missing> jako výchozí hodnotu, aby pole, které zmizelo, stále bylo viditelné. Predikát neuvádí žádné konkrétní pole, což umožňuje jednomu detektoru obsluhovat každý formát, který knihovna čte.
Sledovat časovou osu úprav
Vyměňte predikát za Tags.time plus pravidla pro názvy čítačů a detektor hlásí pohyby RevisionNumber, TotalEditingTime a LastPrinted:
props = metadata.find_properties(lambda p:
Tags.time.modified in list(p.tags)
or Tags.time.created in list(p.tags)
or Tags.time.printed in list(p.tags)
or (p.name is not None and ("Revision" in p.name
or "EditTime" in p.name or "EditingTime" in p.name)))
Exportovat auditní zprávu
Naleznete, které zůstávají v konzoli, tam také končí. Čtyři sloupce pokrývají tabulku a případ v SIEM:
with open("output/diff.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["change_type", "property", "old_value", "new_value"])
for k, v in added.items():
writer.writerow(["added", k, "", v])
for k, v in removed.items():
writer.writerow(["removed", k, v, ""])
for k, (old_v, new_v) in changed.items():
writer.writerow(["changed", k, old_v, new_v])
Repozitář také obsahuje JSON exportér se stabilním schématem tří map pro dashboardy a API pro správu případů.
Kde se to používá v praxi
Tři nasazení se stále objevují. Příjmové pipeline porovnávají každý přicházející dokument s kopií již zaznamenanou a karanténují páry se změnami identity. Úkoly compliance spouštějí diff podle plánu a archivují CSV pro každý pár, vytvářejí časovou osu vlastností, kterou nikdo později nemusí rekonstruovat. A nástroje pro spory spouštějí oba detektory na vyžádání, protože když se objeví nárok, úvodní otázka je vždy, kdo se souboru dotkl a kdy, ne co se změnilo ve čtvrtém odstavci.
Čtvrtý vzor, porovnání souboru s jeho vlastním posledním známým dobrým snapshotem, znovu používá stejný kód s uloženým dictem na jedné straně. Ve všech případech je exportní soubor výstupem; výstup do konzole je jen šum. Vzor návratového kódu skriptu následuje main.py v repozitáři, takže plánovače a CI považují selhání aserce za neúspěšný běh bez dalšího zapojení. Žádný z nich nepotřeboval kód nad rámec toho, co tato stránka ukazuje.
Co se počítá jako změna, kterou stojí za označení?
Cokoliv, co diff klasifikuje, plus kontext, který přidáte. Přidané a odebrané vlastnosti jsou vždy stojí za pohled, protože znamenají změnu struktury, nikoli hodnoty. U změněných položek většina týmů nejprve upozorňuje na skupiny identity a revize a zbytek považuje za informativní. Detektory existují proto, aby první průchod stál jen jeden volání funkce.
5. Rychlý přehled: Klíčová volání
| Volání | Co dělá |
|---|---|
Metadata(path) |
Otevře soubor; kontextový manažer se postará o uvolnění |
find_properties(predicate) |
Vrací každou vlastnost, kterou predikát akceptuje, napříč všemi vrstvami |
p.interpreted_value |
Lidsky čitelná hodnota; v případě potřeby se vrátí k p.value |
Tags.person.* / Tags.corporate.company |
Klasifikace identity, nezávislá na formátu |
Tags.time.* |
Klasifikace časových razítek pro detektor revizí |
Viz kompletní referenční API pro úplné vyhledávání a tagování. Slovník tagů je větší než tyto řádky; skupiny tagů origin, content a legal používají stejný test příslušnosti.
6. Běžné problémy a opravy
Diff je obrovský a čte se jako šum
→ Pravděpodobně nejde o revize jednoho dokumentu. Oprava: ověřte původ před porovnáním; nesouvisející soubory produkují nesmyslné delty.
Pole známého autora se nikdy neukáže v detektoru vlastnictví
→ Někteří výrobci ukládají identitu do neoznačených vlastních polí. Oprava: spusťte jednou celý diff, najděte skutečný název pole a rozšiřte predikát o pravidlo názvu.
Konzole zobrazuje varování režimu hodnocení
→ Nebyl nalezen licenční soubor. Oprava: nasměrujte LICENSE_PATH v main.py na váš soubor .lic, nebo ponechte režim hodnocení pro vývoj; logika je stejná.
Data se tisknou jako surová sériová čísla
→ Surové p.value se někde dostalo do čtečky. Oprava: zachovejte vzor nejprve interpreted_value z read_props; je to důvod, proč jsou reporty čitelné.
Co dál?
Máte funkční diff metadat. Zde je, kam dál:
- Batch it: spustěte skript v cyklu nad páry dokumentů a uložte CSV pro každý pár; náklad na pár jsou dvě otevření souboru a CSV se čistě spojí pro celkový přehled knihovny.
- Schedule it:
main.pyv repozitáři kontroluje každý krok a vrací správný návratový kód, který se přímo vloží do CI nebo plánovače. - Projít tutoriálovou verzi: průvodce použitím vytváří stejnou pipeline ve třech stupních tutoriálů.
- Prohlédněte si celý projekt: document-version-metadata-diff-python se vzorovým párem revizí.