Merge branch 'main' into avm99963-monorail

Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266

GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/services/spam_svc.py b/services/spam_svc.py
index e916830..02ec7d8 100644
--- a/services/spam_svc.py
+++ b/services/spam_svc.py
@@ -1,7 +1,6 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file or at
-# https://developers.google.com/open-source/licenses/bsd
+# Copyright 2016 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 
 """ Set of functions for detaling with spam reports.
 """
@@ -12,6 +11,7 @@
 import collections
 import logging
 import settings
+import time
 
 from collections import defaultdict
 from framework import sql
@@ -173,7 +173,10 @@
 
   def FlagIssues(self, cnxn, issue_service, issues, reporting_user_id,
                  flagged_spam):
-    """Creates or deletes a spam report on an issue."""
+    """Creates or deletes a spam report on an issue.
+
+    This function is run when a user flags an issue as spam but does not
+    have 'VerdictSpam' permission."""
     verdict_updates = []
     if flagged_spam:
       rows = [(issue.issue_id, issue.reporter_id, reporting_user_id)
@@ -215,9 +218,11 @@
     self.verdict_tbl.InsertRows(cnxn, THRESHVERDICT_ISSUE_COLS, rows,
         ignore=True)
     update_issues = []
+    current_time = int(time.time())
     for issue in issues:
       if issue.issue_id in verdict_updates:
         issue.is_spam = flagged_spam
+        issue.migration_modified_timestamp = current_time
         update_issues.append(issue)
 
     if flagged_spam:
@@ -230,7 +235,8 @@
                 'issue': issue_ref
             })
 
-    issue_service.UpdateIssues(cnxn, update_issues, update_cols=['is_spam'])
+    issue_service.UpdateIssues(
+        cnxn, update_issues, update_cols=['is_spam', 'migration_modified'])
 
   def FlagComment(
       self, cnxn, issue, comment_id, reported_user_id, reporting_user_id,
@@ -262,10 +268,19 @@
 
   def RecordClassifierIssueVerdict(self, cnxn, issue, is_spam, confidence,
         fail_open):
+    """Records a judgment call on whether a new issue is spam.
+
+    Only run when an issue is newly filed. If the issue is determined to be
+    likely spam, the code increments a counter."""
     reason = REASON_FAIL_OPEN if fail_open else REASON_CLASSIFIER
-    self.verdict_tbl.InsertRow(cnxn, issue_id=issue.issue_id, is_spam=is_spam,
-        reason=reason, classifier_confidence=confidence,
-        project_id=issue.project_id)
+    self.verdict_tbl.InsertRow(
+        cnxn,
+        issue_id=issue.issue_id,
+        is_spam=is_spam,
+        reason=reason,
+        classifier_confidence=confidence,
+        project_id=issue.project_id,
+        overruled=False)
     if is_spam:
       issue_ref = '%s:%s' % (issue.project_name, issue.local_id)
       self.issue_actions.increment(
@@ -278,6 +293,9 @@
 
   def RecordManualIssueVerdicts(self, cnxn, issue_service, issues, user_id,
                                 is_spam):
+    """Bypasses the classifier to manually classify an issue as spam.
+
+    This code can only be run by users with the 'VerdictSpam' permission."""
     rows = [(user_id, issue.issue_id, is_spam, REASON_MANUAL, issue.project_id)
         for issue in issues]
     issue_ids = [issue.issue_id for issue in issues]
@@ -290,8 +308,10 @@
     self.verdict_tbl.InsertRows(cnxn, MANUALVERDICT_ISSUE_COLS, rows,
         ignore=True)
 
+    current_time = int(time.time())
     for issue in issues:
       issue.is_spam = is_spam
+      issue.migration_modified_timestamp = current_time
 
     if is_spam:
       for issue in issues:
@@ -306,10 +326,14 @@
       issue_service.AllocateNewLocalIDs(cnxn, issues)
 
     # This will commit the transaction.
-    issue_service.UpdateIssues(cnxn, issues, update_cols=['is_spam'])
+    issue_service.UpdateIssues(
+        cnxn, issues, update_cols=['is_spam', 'migration_modified'])
 
   def RecordManualCommentVerdict(self, cnxn, issue_service, user_service,
         comment_id, user_id, is_spam):
+    """Bypasses the classifier to manually classify a comment as spam.
+
+    This code can only be run by users with the 'VerdictSpam' permission."""
     # TODO(seanmccullough): Bulk comment verdicts? There's no UI for that.
     self.verdict_tbl.InsertRow(cnxn, ignore=True,
       user_id=user_id, comment_id=comment_id, is_spam=is_spam,