clean_zammad_quotes.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #!/usr/bin/env python3
  2. import re
  3. import sqlite3
  4. DB = "data/zammad_analysis.sqlite3"
  5. db = sqlite3.connect(DB)
  6. db.row_factory = sqlite3.Row
  7. cols = {r[1] for r in db.execute("PRAGMA table_info(articles)")}
  8. if "analysis_body" not in cols:
  9. db.execute("""
  10. ALTER TABLE articles
  11. ADD COLUMN analysis_body TEXT
  12. """)
  13. db.commit()
  14. def clean_quotes(text):
  15. if not text:
  16. return ""
  17. text = text.replace("\r\n", "\n").replace("\r", "\n")
  18. # Klassische Antwortmarker.
  19. markers = [
  20. r"^\s*Am\s+.+?\bschrieb\s+.+?:\s*$",
  21. r"^\s*On\s+.+?\bwrote:\s*$",
  22. r"^\s*-----Original[- ]Nachricht-----\s*$",
  23. r"^\s*-----Ursprüngliche Nachricht-----\s*$",
  24. r"^\s*-----Original Message-----\s*$",
  25. r"^\s*Von:\s+.+$",
  26. r"^\s*From:\s+.+$",
  27. r"^\s*Gesendet:\s+.+$",
  28. r"^\s*Sent:\s+.+$",
  29. r"^\s*Datum:\s+.+$",
  30. r"^\s*Date:\s+.+$",
  31. ]
  32. lines = text.splitlines()
  33. cut = None
  34. for i, line in enumerate(lines):
  35. stripped = line.strip()
  36. for pattern in markers:
  37. if re.match(pattern, stripped, re.I):
  38. cut = i
  39. break
  40. if cut is not None:
  41. break
  42. if cut is not None:
  43. lines = lines[:cut]
  44. # Klassische >-Zitatblöcke entfernen.
  45. result = []
  46. for line in lines:
  47. if re.match(r"^\s*>", line):
  48. continue
  49. result.append(line)
  50. text = "\n".join(result)
  51. # Automatisch zitierte Bestellformulare entfernen.
  52. # Diese enthalten keinen neuen Kunden-/Agenteninhalt.
  53. order_markers = [
  54. r"\bwir haben nachfolgende bestellung soeben erhalten\b",
  55. r"\bguten tag\s*,?\s*wir haben nachfolgende bestellung",
  56. ]
  57. for marker in order_markers:
  58. m = re.search(marker, text, re.I)
  59. if m:
  60. text = text[:m.start()].rstrip()
  61. break
  62. # Signaturen nur am Ende.
  63. text = re.split(
  64. r"\n\s*(Mit freundlichen Grüßen|"
  65. r"Viele Grüße|"
  66. r"Beste Grüße|"
  67. r"Freundliche Grüße|"
  68. r"Kind regards|"
  69. r"Best regards)\b",
  70. text,
  71. maxsplit=1,
  72. flags=re.I,
  73. )[0]
  74. text = re.sub(r"[ \t]+", " ", text)
  75. text = re.sub(r"\n{3,}", "\n\n", text)
  76. return text.strip()
  77. rows = db.execute("""
  78. SELECT id, ticket_id, role, clean_body
  79. FROM articles
  80. WHERE clean_body IS NOT NULL
  81. """).fetchall()
  82. changed = 0
  83. empty = 0
  84. for row in rows:
  85. original = row["clean_body"] or ""
  86. cleaned = clean_quotes(original)
  87. db.execute("""
  88. UPDATE articles
  89. SET analysis_body = ?
  90. WHERE id = ?
  91. """, (
  92. cleaned,
  93. row["id"],
  94. ))
  95. if cleaned != original:
  96. changed += 1
  97. if not cleaned:
  98. empty += 1
  99. db.commit()
  100. print("=" * 72)
  101. print("ZAMMAD ZITATBEREINIGUNG")
  102. print("=" * 72)
  103. print(f"Artikel: {len(rows)}")
  104. print(f"Verändert: {changed}")
  105. print(f"Leer nach Bereinigung: {empty}")
  106. print()
  107. print("BEISPIELE")
  108. print("-" * 72)
  109. examples = db.execute("""
  110. SELECT
  111. ticket_id,
  112. role,
  113. clean_body,
  114. analysis_body
  115. FROM articles
  116. WHERE clean_body != analysis_body
  117. ORDER BY id
  118. LIMIT 20
  119. """).fetchall()
  120. for row in examples:
  121. print(f"\nTicket #{row['ticket_id']} [{row['role']}]")
  122. print("VORHER:")
  123. print((row["clean_body"] or "")[:500])
  124. print("NACHHER:")
  125. print((row["analysis_body"] or "")[:500])
  126. db.close()