This is an automated email from the ASF dual-hosted git repository.

humbedooh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ponymail-foal.git


The following commit(s) were added to refs/heads/master by this push:
     new 5fba68f  Add thread info capabilities to the archiver
     new 98bd28e  Merge pull request #43 from sbp/thread-info
5fba68f is described below

commit 5fba68f8c10313059310a30b9aa2f659b59e57ab
Author: Sean B. Palmer <[email protected]>
AuthorDate: Wed Jun 2 13:17:00 2021 +0100

    Add thread info capabilities to the archiver
---
 tools/archiver.py   | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/mappings.yaml |   8 ++++
 tools/rethread.py   |  65 +++++++++++++++++++++++++++++++++
 3 files changed, 176 insertions(+)

diff --git a/tools/archiver.py b/tools/archiver.py
index 71a04e9..580f93a 100755
--- a/tools/archiver.py
+++ b/tools/archiver.py
@@ -258,6 +258,97 @@ class Body:
         return self.string
 
 
+def message_identifiers(header, reverse=False):
+    if "<" not in header:
+        return []
+    parts = header.split("<")
+    identifier_junks = parts[1:]
+    identifiers = []
+    for identifier_junk in identifier_junks:
+        identifier = identifier_junk.split(">").pop(0)
+        identifiers.append("<" + identifier + ">")
+    if reverse is True:
+        identifiers = list(reversed(identifiers))
+    return identifiers
+
+
+def get_parent_identifiers(ojson):
+    identifiers = []
+    for irt in message_identifiers(ojson.get("in-reply-to", ""), reverse=True):
+        identifiers.append(irt)
+    for ref in message_identifiers(ojson.get("references", ""), reverse=True):
+        identifiers.append(ref)
+    return identifiers
+
+
+def get_by_message_id(elastic, msgid):
+    data = elastic.es.search(index=elastic.db_mbox, body={
+        "query": {
+            "bool": {
+                "must": {"term": {"message-id": msgid}}
+            }
+        }
+    })
+    if data["hits"]["total"]["value"] == 1:
+        return data["hits"]["hits"][0]["_source"]
+    return None
+
+
+def get_parent_info(elastic, ojson):
+    parent_identifiers = get_parent_identifiers(ojson)
+    if not parent_identifiers:
+        return None
+    for parent_identifier in parent_identifiers:
+        parent_info = get_by_message_id(elastic, parent_identifier)
+        if parent_info is not None:
+            return parent_info
+    return None
+
+
+def get_previous_mid(elastic, forum, ojson):
+    latest = ojson.get("epoch", 1) - 1
+    data = elastic.es.search(index=elastic.db_mbox, body={
+        "query": {
+            "bool": {
+                "must": [
+                    {"range": {"epoch": {"lte": latest}}},
+                    {"term": {"forum": forum}},
+                    {"term": {"top": True}}
+                ]
+            }
+        },
+        "sort": [{"epoch": "desc"}],
+        "size": 1,
+        "_source": "mid",
+    })
+    for hit in data["hits"]["hits"]:
+        return hit["_source"]["mid"]
+    return None
+
+
+def add_thread_properties(elastic, mid, ojson, size):
+    forum = ojson.get("list", "").strip("<>").replace(".", "@", 1)
+
+    parent_info = get_parent_info(elastic, ojson)
+
+    if parent_info is None:
+        top = True
+        thread = mid
+        previous = get_previous_mid(elastic, forum, ojson)
+    else:
+        top = False
+        thread = parent_info.get("thread")
+        previous = parent_info["mid"]
+
+    ojson["forum"] = forum
+    ojson["previous"] = previous
+    ojson["size"] = size
+    ojson["thread"] = thread
+    ojson["top"] = top
+
+    return ojson
+
+
 class Archiver(object):  # N.B. Also used by import-mbox.py
     """The general archiver class. Compatible with MailMan3 archiver 
classes."""
 
@@ -555,6 +646,18 @@ class Archiver(object):  # N.B. Also used by import-mbox.py
         else:
             elastic = Elastic()
 
+        if config.get("archiver", "threadinfo"):
+            try:
+                ojson = add_thread_properties(elastic, ojson["mid"], ojson, 
len(raw_message))
+            except Exception as err:
+                print("Could not add thread info", err)
+                if logger:
+                    logger.info("Could not add thread info %s" % (err,))
+            else:
+                print("Added thread info successfully", ojson["mid"])
+                if logger:
+                    logger.info("Added thread info successfully %s" % 
(ojson["mid"],))
+
         try:
             if contents:
                 for key in contents:
diff --git a/tools/mappings.yaml b/tools/mappings.yaml
index 49b205a..e80d89c 100644
--- a/tools/mappings.yaml
+++ b/tools/mappings.yaml
@@ -59,6 +59,8 @@ mbox:
       type: keyword
     epoch:
       type: long
+    forum:
+      type: keyword
     from:
       type: text
     from_raw:
@@ -77,6 +79,8 @@ mbox:
       type: keyword
     permalinks:
       type: keyword
+    previous:
+      type: keyword
     private:
       type: boolean
     references:
@@ -84,8 +88,12 @@ mbox:
     subject:
       fielddata: true
       type: text
+    thread:
+      type: keyword
     to:
       type: text
+    top:
+      type: boolean
     _notes:
       type: text
     _archived_at:
diff --git a/tools/rethread.py b/tools/rethread.py
new file mode 100644
index 0000000..c5440cc
--- /dev/null
+++ b/tools/rethread.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+
+import archiver
+from elasticsearch.helpers import scan
+from plugins.elastic import Elastic
+
+
+def first_pass(elastic: Elastic) -> None:
+    hits = scan(
+        client=elastic.es,
+        index=elastic.db_mbox,
+        query={"sort": {"epoch": "asc"}},
+    )
+    for hit in hits:
+        pid = hit["_id"]
+        ojson = hit["_source"]
+        parent_info = archiver.get_parent_info(elastic, ojson)
+        ojson["top"] = parent_info is None
+        ojson["forum"] = ojson.get("list", "").strip("<>").replace(".", "@", 1)
+        source = elastic.es.get(
+            elastic.db_source, ojson["dbid"], _source="source"
+        )["_source"]["source"]
+        ojson["size"] = len(source)
+        ojson["previous"] = ""
+        ojson["thread"] = pid if (parent_info is None) else ""
+        elastic.index(index=elastic.db_mbox, id=pid, body=ojson)
+
+
+def second_pass(elastic: Elastic) -> None:
+    hits = scan(client=elastic.es, index=elastic.db_mbox, query={})
+    for hit in hits:
+        pid = hit["_id"]
+        ojson = hit["_source"]
+        if ojson["thread"] != "":
+            continue
+        if ojson["top"] is True:
+            ojson["previous"] = archiver.get_previous_mid(
+                elastic, ojson["forum"], ojson
+            )
+            ojson["thread"] = pid
+            elastic.index(index=elastic.db_mbox, id=pid, body=ojson)
+        else:
+            tree = []
+            while ojson["thread"] == "":
+                tree.append(ojson)
+                ojson_parent = archiver.get_parent_info(elastic, ojson)
+                if ojson_parent is None:
+                    ojson["previous"] = None
+                    print("Error:", ojson["mid"], "has no parent")
+                    break
+                ojson["previous"] = ojson_parent["mid"]
+                ojson = ojson_parent
+            for info in tree:
+                info["thread"] = ojson["thread"]
+                elastic.index(index=elastic.db_mbox, id=info["mid"], body=info)
+
+
+def main() -> None:
+    elastic: Elastic = Elastic()
+    first_pass(elastic)
+    second_pass(elastic)
+
+
+if __name__ == "__main__":
+    main()

Reply via email to