Project import generated by Copybara.

GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/features/rerankhotlist.py b/features/rerankhotlist.py
new file mode 100644
index 0000000..fe235db
--- /dev/null
+++ b/features/rerankhotlist.py
@@ -0,0 +1,136 @@
+# 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
+
+"""Class that implements the reranking on the hotlistissues table page."""
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+
+import logging
+
+from features import features_bizobj
+from features import hotlist_helpers
+from framework import jsonfeed
+from framework import permissions
+from framework import sorting
+from services import features_svc
+from tracker import rerank_helpers
+
+
+class RerankHotlistIssue(jsonfeed.JsonFeed):
+  """Rerank an issue in a hotlist."""
+
+  def AssertBasePermission(self, mr):
+    super(RerankHotlistIssue, self).AssertBasePermission(mr)
+    if mr.target_id and mr.moved_ids and mr.split_above:
+      try:
+        hotlist = self._GetHotlist(mr)
+      except features_svc.NoSuchHotlistException:
+        return
+      edit_perm = permissions.CanEditHotlist(
+          mr.auth.effective_ids, mr.perms, hotlist)
+      if not edit_perm:
+        raise permissions.PermissionException(
+            'User is not allowed to re-rank this hotlist')
+
+  def HandleRequest(self, mr):
+    changed_ranks = self._GetNewRankings(mr)
+
+    if changed_ranks:
+      relations_to_change = dict(
+          (issue_id, rank) for issue_id, rank in changed_ranks)
+
+      self.services.features.UpdateHotlistItemsFields(
+          mr.cnxn, mr.hotlist_id, new_ranks=relations_to_change)
+
+      hotlist_items = self.services.features.GetHotlist(
+          mr.cnxn, mr.hotlist_id).items
+
+      # Note: Cannot use mr.hotlist because hotlist_issues
+      # of mr.hotlist is not updated
+
+      sorting.InvalidateArtValuesKeys(
+          mr.cnxn, [hotlist_item.issue_id for hotlist_item in hotlist_items])
+      (table_data, _) = hotlist_helpers.CreateHotlistTableData(
+          mr, hotlist_items, self.services)
+
+      json_table_data = [{
+          'cells': [{
+              'type': cell.type,
+              'values': [{
+                  'item': value.item,
+                  'isDerived': value.is_derived,
+              } for value in cell.values],
+              'colIndex': cell.col_index,
+              'align': cell.align,
+              'noWrap': cell.NOWRAP,
+              'nonColLabels': [{
+                  'value': label.value,
+                  'isDerived': label.is_derived,
+              } for label in cell.non_column_labels],
+          } for cell in table_row.cells],
+          'issueRef': table_row.issue_ref,
+          'idx': table_row.idx,
+          'projectName': table_row.project_name,
+          'projectURL': table_row.project_url,
+          'localID': table_row.local_id,
+          'issueID': table_row.issue_id,
+          'isStarred': table_row.starred,
+          'issueCleanURL': table_row.issue_clean_url,
+          'issueContextURL': table_row.issue_ctx_url,
+      } for table_row in table_data]
+
+      for row, json_row in zip(
+          [table_row for table_row in table_data], json_table_data):
+        if (row.group and row.group.cells):
+          json_row.update({'group': {
+              'rowsInGroup': row.group.rows_in_group,
+              'cells': [{'groupName': cell.group_name,
+                         'values': [{
+                          # TODO(jojwang): check if this gives error when there
+                          # is no value.item
+                             'item': value.item if value.item else 'None',
+                         } for value in cell.values],
+              } for cell in row.group.cells],
+          }})
+        else:
+          json_row['group'] = 'no'
+
+      return {'table_data': json_table_data}
+    else:
+      return {'table_data': ''}
+
+  def _GetHotlist(self, mr):
+    """Retrieve the current hotlist."""
+    if mr.hotlist_id is None:
+      return None
+    try:
+      hotlist = self.services.features.GetHotlist( mr.cnxn, mr.hotlist_id)
+    except features_svc.NoSuchHotlistException:
+      self.abort(404, 'hotlist not found')
+    return hotlist
+
+  def _GetNewRankings(self, mr):
+    """Compute new issue reference rankings."""
+    missing = False
+    if not (mr.target_id):
+      logging.info('No target_id.')
+      missing = True
+    if not (mr.moved_ids):
+      logging.info('No moved_ids.')
+      missing = True
+    if mr.split_above is None:
+      logging.info('No split_above.')
+      missing = True
+    if missing:
+      return
+
+    untouched_items = [
+        (item.issue_id, item.rank) for item in
+        mr.hotlist.items if item.issue_id not in mr.moved_ids]
+
+    lower, higher = features_bizobj.SplitHotlistIssueRanks(
+        mr.target_id, mr.split_above, untouched_items)
+    return rerank_helpers.GetInsertRankings(lower, higher, mr.moved_ids)