From 5c26c92fe6e7584265daef3b10f7d744fdc7e38d Mon Sep 17 00:00:00 2001
From: charliemirabile <46761267+charliemirabile@users.noreply.github.com>
Date: Thu, 19 Sep 2024 12:08:03 -0400
Subject: [PATCH 1/2] mailman: avoid hard coded list of assignments

giving mailman access to the denis db allows querying the real list
of assignments to determine whether or not a patch is addressed to
one of the special mailboxes.
---
 container-compose.yml | 6 ++++++
 mailman/Containerfile | 1 +
 mailman/submit.py     | 9 +++------
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/container-compose.yml b/container-compose.yml
index cb3aeda8..deaa10e2 100644
--- a/container-compose.yml
+++ b/container-compose.yml
@@ -124,6 +124,7 @@ services:
       dockerfile: Containerfile
       additional_contexts:
         - watcher_source=./watcher
+        - denis_source=./denis
     volumes:
       - type: volume
         source: email-mail
@@ -133,12 +134,17 @@ services:
         source: email-logs
         target: /var/lib/email/logs
         read_only: true
+      - type: volume
+        source: denis-db
+        target: /var/lib/denis
+        read_only: true
       - type: volume
         source: submissions-db
         target: /var/lib/mailman
         read_only: false
     depends_on:
       - smtp
+      - denis
     networks:
       - mailman
   denis:
diff --git a/mailman/Containerfile b/mailman/Containerfile
index 55a1b422..9ea8f87d 100644
--- a/mailman/Containerfile
+++ b/mailman/Containerfile
@@ -27,6 +27,7 @@ RUN mkdir -p /var/lib/mailman/ && \
 RUN chown -R 100:100 /var/lib/mailman
 
 COPY --from=build /watcher/watcher /usr/local/bin/watcher
+COPY --from=denis_source . ./denis
 
 VOLUME /var/lib/email/
 
diff --git a/mailman/submit.py b/mailman/submit.py
index 9f3fffb6..023a9831 100755
--- a/mailman/submit.py
+++ b/mailman/submit.py
@@ -6,11 +6,7 @@
 
 import patchset
 import db
-
-ASSIGNMENT_LIST = ["introductions",
-                   "exercise0", "exercise1", "exercise2",
-                   "programming0", "programming1", "programming2",
-                   "final0", "final1"]
+import denis.db
 
 
 Email = collections.namedtuple('Email', ['rcpt', 'msg_id'])
@@ -40,7 +36,8 @@ def main(argv):
     # if the 'cover letter' is not addressed to
     # an assignment inbox, this email session
     # isn't a patchset at all
-    if cover_letter.rcpt not in ASSIGNMENT_LIST:
+    asn_db = denis.db.Assignment
+    if not asn_db.get_or_none(asn_db.name == cover_letter.rcpt):
         # TODO process peer review
         return 0
 

From ad1a2377917bd0dbe988f4f39e1d1db3565b594d Mon Sep 17 00:00:00 2001
From: charliemirabile <46761267+charliemirabile@users.noreply.github.com>
Date: Mon, 16 Sep 2024 17:14:20 -0400
Subject: [PATCH 2/2] mailman: unify all submissions under common table for
 dashboard

Remove special processing for assignments and just store peer reviews
alongside submissions so that students can see all activity as it
occurs and know that their submission as being counted.
---
 mailman/db.py     |  5 +++--
 mailman/submit.py | 54 ++++++++++++++++++-----------------------------
 orbit/radius.py   |  5 +++--
 3 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/mailman/db.py b/mailman/db.py
index 8c6a4f16..8e80a745 100755
--- a/mailman/db.py
+++ b/mailman/db.py
@@ -13,10 +13,11 @@ class Meta:
 
 class Submission(BaseModel):
     submission_id = peewee.TextField(unique=True)
-    assignment = peewee.TextField()
     timestamp = peewee.IntegerField()
     user = peewee.TextField()
-    status = peewee.TextField()
+    recipient = peewee.TextField()
+    email_count = peewee.IntegerField()
+    in_reply_to = peewee.TextField(null=True)
 
 
 if __name__ == '__main__':
diff --git a/mailman/submit.py b/mailman/submit.py
index 023a9831..e031633f 100755
--- a/mailman/submit.py
+++ b/mailman/submit.py
@@ -4,9 +4,7 @@
 from pathlib import Path
 import sys
 
-import patchset
 import db
-import denis.db
 
 
 Email = collections.namedtuple('Email', ['rcpt', 'msg_id'])
@@ -31,38 +29,26 @@ def main(argv):
     if not emails:
         return 0
 
-    cover_letter, *patches = emails
-
-    # if the 'cover letter' is not addressed to
-    # an assignment inbox, this email session
-    # isn't a patchset at all
-    asn_db = denis.db.Assignment
-    if not asn_db.get_or_none(asn_db.name == cover_letter.rcpt):
-        # TODO process peer review
-        return 0
-
-    sub = db.Submission(submission_id=logfile, assignment=cover_letter.rcpt,
-                        timestamp=timestamp, user=user, status='new')
-
-    # only one email
-    if not patches:
-        # only one patch, but addressed to an
-        # assignment inbox. This cannot be valid.
-        sub.status = 'no patches or no cover letter'
-        sub.save()
-        return 0
-
-    mis_addressed_patches = [str(i+1) for i, patch in enumerate(patches)
-                             if patch.rcpt != cover_letter.rcpt]
-
-    if mis_addressed_patches:
-        sub.status = (f'patch(es) {",".join(mis_addressed_patches)} '
-                      f'not addressed to {cover_letter.rcpt}')
-        sub.save()
-        return 0
-
-    sub.status = patchset.check(cover_letter, patches, submission_id=logfile)
-    sub.save()
+    irt_header = 'In-Reply-To: <'
+    reply_id = None
+    with open(f'/var/lib/email/mail/{emails[0].msg_id}') as f:
+        for line in f:
+            if not line:
+                break
+            if not line.startswith(irt_header):
+                continue
+            at_sign = line.find('@', len(irt_header))
+            if -1 == at_sign:
+                continue
+            reply_email_id = line[len(irt_header):at_sign]
+
+            # "clear the lower 16 bits" to get the reviewee patchset id
+            reply_id = reply_email_id[:-4] + '0000'
+            break
+
+    db.Submission.create(submission_id=logfile, timestamp=timestamp,
+                         user=user, recipient=emails[0].rcpt,
+                         email_count=len(emails), in_reply_to=reply_id)
 
 
 if __name__ == "__main__":
diff --git a/orbit/radius.py b/orbit/radius.py
index 5985cd7f..84204431 100644
--- a/orbit/radius.py
+++ b/orbit/radius.py
@@ -389,7 +389,8 @@ def handle_dashboard(rocket):
 
     def submission_fields(sub):
         return (datetime.fromtimestamp(sub.timestamp).isoformat(),
-                sub.assignment, sub.submission_id, sub.status)
+                sub.recipient, sub.email_count, sub.in_reply_to or '-',
+                sub.submission_id, )
 
     # Split data from Submission table into values for HTML table
     table_data = [[f'<td>{val}</td>' for val in submission_fields(sub)]
@@ -397,7 +398,7 @@ def submission_fields(sub):
     table_content = '</tr>\n<tr>'.join(''.join(row) for row in table_data)
 
     return rocket.respond(f"""<table>
-<tr><th>Timestamp</th><th>Assignment</th><th>Submission ID</th><th>Status</th></tr>
+<tr><th>Timestamp</th><th>Recipient</th><th>Email Count</th><th>In Reply To</th><th>Submission ID</th></tr>
 <tr>{table_content}</tr>
 </table>
 """)
