[Findit] Flake Detection - Adding status fields to FlakeIssue

Adding:
* status - 'Assigned', 'Available', etc.
* last_updated_time_in_monorail - timestamp of the last update on the bug
* priority - The priority of the bug
* Update() - A method to update any specified field(s).

Bug: 911841
Change-Id: I9b3e9c17468f21b3e194cf8f553bee802578d9c7
Reviewed-on: https://chromium-review.googlesource.com/c/1361676
Commit-Queue: Jeffrey Li <lijeffrey@chromium.org>
Reviewed-by: Chan Li <chanli@chromium.org>
Reviewed-by: Yuke Liao <liaoyuke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#19381}
diff --git a/appengine/findit/handlers/flake/detection/test/flake_detection_utils_test.py b/appengine/findit/handlers/flake/detection/test/flake_detection_utils_test.py
index a6d05de..11d5a69 100644
--- a/appengine/findit/handlers/flake/detection/test/flake_detection_utils_test.py
+++ b/appengine/findit/handlers/flake/detection/test/flake_detection_utils_test.py
@@ -151,7 +151,13 @@
             'issue_link': ('https://monorail-prod.appspot.com/p/chromium/'
                            'issues/detail?id=900'),
             'merge_destination_key':
-                None
+                None,
+            'last_updated_time_in_monorail':
+                None,
+            'priority':
+                None,
+            'status':
+                None,
         },
         'component':
             'Mock>Component',
diff --git a/appengine/findit/handlers/flake/detection/test/show_flake_test.py b/appengine/findit/handlers/flake/detection/test/show_flake_test.py
index 4c5d937..fdc7f4d 100644
--- a/appengine/findit/handlers/flake/detection/test/show_flake_test.py
+++ b/appengine/findit/handlers/flake/detection/test/show_flake_test.py
@@ -114,7 +114,13 @@
             'monorail_project':
                 'chromium',
             'merge_destination_key':
-                None
+                None,
+            'last_updated_time_in_monorail':
+                None,
+            'priority':
+                None,
+            'status':
+                None,
         },
         'flake_issue_key':
             flake_issue.key,
diff --git a/appengine/findit/model/flake/flake_issue.py b/appengine/findit/model/flake/flake_issue.py
index 3b1bb1d..1dff24d 100644
--- a/appengine/findit/model/flake/flake_issue.py
+++ b/appengine/findit/model/flake/flake_issue.py
@@ -37,6 +37,17 @@
   # Puts FlakeIssue in quotes because the key refers to the same data model.
   merge_destination_key = ndb.KeyProperty(kind='FlakeIssue', indexed=True)
 
+  # The most recent updated time the issue was updated in Monorail. Indexed for
+  # querying by timestamp.
+  last_updated_time_in_monorail = ndb.DateTimeProperty(indexed=True)
+
+  # The status of the issue in monorail, e.g. 'Assigned'. Indexed for querying
+  # by status.
+  status = ndb.StringProperty(indexed=True)
+
+  # The priority of the bug.
+  priority = ndb.IntegerProperty(indexed=False)
+
   @staticmethod
   def _CreateKey(monorail_project, issue_id):  # pragma: no cover
     return ndb.Key(FlakeIssue, '%s@%d' % (monorail_project, issue_id))
@@ -91,3 +102,16 @@
 
   def GetMostUpdatedIssue(self):
     return self.GetMergeDestination() or self
+
+  def Update(self, **kwargs):
+    """Updates arbitrary fields as specified in kwargs."""
+    any_changes = False
+
+    for arg, value in kwargs.iteritems():
+      current_value = getattr(self, arg, None)
+      if current_value != value:
+        setattr(self, arg, value)
+        any_changes = True
+
+    if any_changes:
+      self.put()
diff --git a/appengine/findit/model/flake/test/flake_issue_test.py b/appengine/findit/model/flake/test/flake_issue_test.py
index 5c94990..f7af318 100644
--- a/appengine/findit/model/flake/test/flake_issue_test.py
+++ b/appengine/findit/model/flake/test/flake_issue_test.py
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from datetime import datetime
 import mock
 
 from google.appengine.api import app_identity
@@ -40,8 +41,8 @@
     monorail_project = 'chromium'
     issue_id = 12345
     self.assertEqual(
-        'https://monorail-staging.appspot.com/p/chromium/issues/detail?id=12345',  # pylint: disable=line-too-long
-        FlakeIssue.GetLinkForIssue(monorail_project, issue_id))
+        ('https://monorail-staging.appspot.com/p/chromium/issues/detail?'
+         'id=12345'), FlakeIssue.GetLinkForIssue(monorail_project, issue_id))
 
   @mock.patch.object(
       app_identity, 'get_application_id', return_value='findit-for-me')
@@ -77,3 +78,24 @@
     flake_issue.put()
 
     self.assertEqual(flake_issue, flake_issue.GetMostUpdatedIssue())
+
+  def testUpdate(self):
+    monorail_project = 'chromium'
+    issue_id = 12345
+    updated_time = datetime(2018, 12, 4, 0, 0, 0)
+    status = 'Assigned'
+    priority = 1
+
+    flake_issue = FlakeIssue.Create(
+        monorail_project=monorail_project, issue_id=issue_id)
+    flake_issue.put()
+
+    flake_issue.Update(
+        last_updated_time_in_monorail=updated_time,
+        status=status,
+        priority=priority)
+    flake_issue = flake_issue.key.get()
+
+    self.assertEqual(status, flake_issue.status)
+    self.assertEqual(priority, flake_issue.priority)
+    self.assertEqual(updated_time, flake_issue.last_updated_time_in_monorail)