💡 Full working example available on GitHub:
document-version-metadata-diff-python

What You’ll Build

Dalam panduan ini Anda akan membandingkan setiap properti metadata antara dua versi dokumen dan mencetak secara tepat apa yang ditambahkan, dihapus, atau diubah. Perbandingan versi metadata adalah perbandingan pada tingkat properti dari dua revisi satu file, dan ia menangkap sinyal yang tidak pernah terlihat oleh perbandingan teks: Creator baru, RevisionNumber yang naik, sesi pengeditan yang tercatat setelah review ditutup. Pada akhir panduan Anda akan memiliki solusi yang berfungsi plus dua detektor terfokus dan dua format ekspor, semuanya diambil dari repositori yang dapat dijalankan dengan pasangan revisi contoh.

Tingkat keahlian: pengembang Python menengah
Apa yang Anda butuhkan: Python 3, pip, dan dua revisi satu dokumen

Percobaan pertama saya dengan skrip ini menandai perubahan nilai Company yang tidak diingat oleh siapa pun di tim; satu baris itu membayar biaya penyiapan. Semua di bawah ini siap disalin‑tempel dan totalnya jauh di bawah seratus baris.

Pipeline sengaja dibuat sederhana: dua pembukaan file, tiga pemahaman dict, satu loop print. Kesederhanaan adalah tujuannya. Perselisihan versi diputuskan berdasarkan apakah metode dapat dijelaskan dan diulang, dan skrip sekecil ini dapat dibaca seluruhnya oleh siapa pun yang menantang temuan.


1. Install

pip install groupdocs-metadata-net==26.5

companion repository mengunci versi ini dan menyertakan document-v1.docx serta document-v2.docx sehingga kode di bawah ini dapat dijalankan apa adanya. Kunci versi yang Anda gunakan dalam audit; reproduktibilitas adalah bagian dari bukti.


2. The Core Code

Baca kedua pohon properti, lalu klasifikasikan delta dengan logika set. Inilah seluruh 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}")

Itulah minimum yang Anda perlukan. Harapkan hitungan kecil pada pasangan revisi yang sah; delta berjumlah puluhan biasanya berarti file tersebut melewati perubahan templat atau migrasi penyimpanan di tengah jalan. Bagian selanjutnya menjelaskan pemanggilan kunci dan menunjukkan kustomisasi yang biasanya ditambahkan tim pertama kali.


3. How It Works

  • Metadata: manajer konteks yang membuka file dan melepaskannya saat keluar; satu instance per revisi.
  • find_properties: menelusuri bidang bawaan, properti khusus, dan XMP dalam satu kali jalan, mengembalikan semua yang diterima predikat.
  • interpreted_value: bentuk yang dapat dibaca manusia dari sebuah properti; mengutamakan ini berarti tanggal dan enumerasi dibandingkan sebagai string yang dapat dicetak dalam laporan.
  • Qualified names as keys: bidang bawaan dan khusus tidak dapat berbenturan dalam dict, sehingga logika set tetap aman.

Tidak ada yang di sini mem-parsing struktur DOCX. product documentation mencantumkan lebih dari 170 format di balik pemanggilan yang sama, jadi skrip identik dapat membandingkan pasangan PDF atau XLSX.

Satu lagi sifat desain yang patut disebutkan: batas API berakhir pada dua pemanggilan read_props. Semua setelahnya adalah Python standar, sehingga unit test, ambang batas, dan aturan peringatan tidak pernah menyentuh lapisan dokumen. Tim yang membungkus ini dalam layanan biasanya menyimpan dict yang diekstrak per revisi dan membiarkan setiap pemeriksaan hilir menggunakannya kembali, menjaga IO file pada satu pembukaan per versi tidak peduli berapa banyak pertanyaan yang diajukan.


4. Common Customizations

Detect ownership changes only

Ketika pertanyaannya adalah “siapa yang menyentuh file ini”, saring pada saat pembacaan dengan predikat tag alih‑alih memfilter diff lengkap setelahnya:

# 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

Jalankan loop delta yang sama atas dua dict ini, gunakan <missing> sebagai nilai default sehingga bidang yang menghilang tetap muncul. Nama predikat tidak menyebutkan bidang apa pun, yang memungkinkan satu detektor melayani semua format yang dibaca perpustakaan.

Track the editing timeline

Ganti predikat menjadi Tags.time plus aturan nama penghitung dan detektor akan melaporkan pergerakan RevisionNumber, TotalEditingTime, dan 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)))

Export an audit report

Temuan yang hanya muncul di konsol akan mati di sana. Empat kolom mencakup spreadsheet dan kasus 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])

Repositori juga menyertakan ekspor JSON dengan skema tiga‑peta yang stabil untuk dasbor dan API manajemen kasus.


Where This Runs in Practice

Tiga pola penerapan terus muncul. Pipeline intake membandingkan setiap dokumen yang masuk dengan salinan yang sudah tercatat dan mengarantina pasangan dengan perubahan identitas. Pekerjaan kepatuhan menjalankan diff secara terjadwal dan mengarsipkan CSV per pasangan, membangun garis waktu properti yang tidak perlu direkonstruksi kemudian. Dan alat sengketa menjalankan kedua detektor atas permintaan, karena ketika klaim muncul pertanyaan pembuka selalu siapa yang menyentuh file dan kapan, bukan apa yang berubah di paragraf keempat.

Pola keempat, membandingkan file dengan snapshot terakhir yang diketahui baik, menggunakan kode yang sama dengan dict yang disimpan di satu sisi. Dalam semua pola, file ekspor adalah deliverable; output konsol hanyalah kebisingan progres. Pola kode keluar mengikuti main.py di repositori, sehingga penjadwal dan CI memperlakukan assert yang gagal sebagai run yang gagal tanpa wiring tambahan. Tidak ada yang memerlukan kode di luar apa yang ditunjukkan halaman ini.


What counts as a change worth flagging?

Apa pun yang diklasifikasikan diff ditambah konteks yang Anda tambahkan. Properti yang ditambahkan dan dihapus selalu patut diperiksa karena berarti struktur berubah, bukan hanya nilai. Untuk entri yang diubah, kebanyakan tim memberi peringatan pada grup identitas dan revisi terlebih dahulu dan memperlakukan sisanya sebagai informasi. Detektor ada agar pass pertama hanya memerlukan satu pemanggilan fungsi.


5. Quick Reference: Key Calls

Pemanggilan Fungsinya
Metadata(path) Membuka file; manajer konteks menangani pelepasan
find_properties(predicate) Mengembalikan setiap properti yang diterima predikat, melintasi semua lapisan
p.interpreted_value Nilai yang dapat dibaca manusia; fallback ke p.value
Tags.person.* / Tags.corporate.company Klasifikasi identitas, independen format
Tags.time.* Klasifikasi timestamp untuk detektor revisi

Lihat complete API reference untuk pencarian dan permukaan tagging lengkap. Kosakata tag lebih luas daripada baris tabel ini; grup asal, konten, dan legal mengikuti tes keanggotaan yang sama.


6. Common Issues & Fixes

Diff terlalu besar dan terlihat seperti noise
→ Kemungkinan dua jalur bukan revisi dari satu dokumen. Perbaiki: validasi asal sebelum melakukan diff; file yang tidak terkait menghasilkan delta yang tidak berarti.

Field penulis yang dikenal tidak muncul di detektor kepemilikan
→ Beberapa produsen menyimpan identitas di bidang khusus yang tidak ditandai. Perbaiki: jalankan diff lengkap sekali, temukan nama bidang sebenarnya, dan tambahkan aturan nama pada predikat.

Console menampilkan peringatan mode evaluasi
→ Tidak ada file lisensi yang ditemukan. Perbaiki: arahkan LICENSE_PATH di main.py ke file .lic Anda, atau tetap gunakan mode evaluasi untuk pengembangan; logikanya tetap sama.

Tanggal tercetak sebagai angka serial mentah
p.value mentah masuk ke pembaca di suatu tempat. Perbaiki: pertahankan pola interpreted_value‑first dari read_props; itulah alasan laporan tetap dapat dibaca.


What’s Next?

Anda kini memiliki diff metadata yang berfungsi. Berikut langkah selanjutnya:

  • Batch it: loop skrip atas pasangan dokumen dan simpan CSV per pasangan; biaya per pasangan hanya dua pembukaan file, dan CSV dapat digabungkan bersih untuk tampilan perpustakaan secara keseluruhan.
  • Schedule it: main.py di repositori melakukan assert pada setiap langkah dan mengembalikan kode keluar yang tepat, yang dapat langsung dimasukkan ke CI atau penjadwal.
  • Walk the tutorial version: use case guide membangun pipeline yang sama dalam tiga tutorial bertahap.
  • See the whole project: document-version-metadata-diff-python dengan pasangan revisi yang sudah disediakan.

Resources