From 01b575d702a9692f596ff75de01544f45523b20c Mon Sep 17 00:00:00 2001
From: "James E. Blair" <jeblair@hp.com>
Date: Sun, 31 Aug 2014 13:28:31 -0700
Subject: [PATCH] Add support for abandon/restore

Change-Id: I5f85220978c769c16405e188ae88822844a74d95
---
 gertty/db.py          |  3 +++
 gertty/keymap.py      |  4 +++
 gertty/sync.py        | 29 ++++++++++++++++++++
 gertty/view/change.py | 61 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 97 insertions(+)

diff --git a/gertty/db.py b/gertty/db.py
index 65eac56..4b230b3 100644
--- a/gertty/db.py
+++ b/gertty/db.py
@@ -608,6 +608,9 @@ class DatabaseSession(object):
     def getPendingRebases(self):
         return self.session().query(Change).filter_by(pending_rebase=True).all()
 
+    def getPendingStatusChanges(self):
+        return self.session().query(Change).filter_by(pending_status=True).all()
+
     def getAccountByID(self, id, name=None, username=None, email=None):
         try:
             account = self.session().query(Account).filter_by(id=id).one()
diff --git a/gertty/keymap.py b/gertty/keymap.py
index e732e0b..070ba84 100644
--- a/gertty/keymap.py
+++ b/gertty/keymap.py
@@ -45,6 +45,8 @@ SEARCH_RESULTS = 'search results'
 NEXT_CHANGE = 'next change'
 PREV_CHANGE = 'previous change'
 TOGGLE_HIDDEN_COMMENTS = 'toggle hidden comments'
+ABANDON_CHANGE = 'abandon change'
+RESTORE_CHANGE = 'restore change'
 REBASE_CHANGE = 'rebase change'
 REFRESH = 'refresh'
 EDIT_TOPIC = 'edit topic'
@@ -84,6 +86,8 @@ DEFAULT_KEYMAP = {
     NEXT_CHANGE: 'n',
     PREV_CHANGE: 'p',
     TOGGLE_HIDDEN_COMMENTS: 't',
+    ABANDON_CHANGE: 'ctrl a',
+    RESTORE_CHANGE: 'ctrl e',
     REBASE_CHANGE: 'ctrl b',
     REFRESH: 'ctrl r',
     EDIT_TOPIC: 'ctrl t',
diff --git a/gertty/sync.py b/gertty/sync.py
index 242a80b..d5e1b70 100644
--- a/gertty/sync.py
+++ b/gertty/sync.py
@@ -535,6 +535,8 @@ class UploadReviewsTask(Task):
                 sync.submitTask(SetTopicTask(c.key, self.priority))
             for c in session.getPendingRebases():
                 sync.submitTask(RebaseChangeTask(c.key, self.priority))
+            for c in session.getPendingStatusChanges():
+                sync.submitTask(ChangeStatusTask(c.key, self.priority))
             for m in session.getPendingMessages():
                 sync.submitTask(UploadReviewTask(m.key, self.priority))
 
@@ -574,6 +576,33 @@ class RebaseChangeTask(Task):
             sync.post('changes/%s/rebase' % (change.id,), {})
             sync.submitTask(SyncChangeTask(change.id, priority=self.priority))
 
+class ChangeStatusTask(Task):
+    def __init__(self, change_key, priority=NORMAL_PRIORITY):
+        super(ChangeStatusTask, self).__init__(priority)
+        self.change_key = change_key
+
+    def __repr__(self):
+        return '<ChangeStatusTask %s>' % (self.change_key,)
+
+    def run(self, sync):
+        app = sync.app
+        with app.db.getSession() as session:
+            change = session.getChange(self.change_key)
+            if change.pending_status_message:
+                data = dict(message=change.pending_status_message)
+            else:
+                data = {}
+            change.pending_status = False
+            change.pending_status_message = None
+            # Inside db session for rollback
+            if change.status == 'ABANDONED':
+                sync.post('changes/%s/abandon' % (change.id,),
+                          data)
+            elif change.status == 'NEW':
+                sync.post('changes/%s/restore' % (change.id,),
+                          data)
+            sync.submitTask(SyncChangeTask(change.id, priority=self.priority))
+
 class UploadReviewTask(Task):
     def __init__(self, message_key, priority=NORMAL_PRIORITY):
         super(UploadReviewTask, self).__init__(priority)
diff --git a/gertty/view/change.py b/gertty/view/change.py
index a003783..3e0b1cf 100644
--- a/gertty/view/change.py
+++ b/gertty/view/change.py
@@ -50,6 +50,29 @@ class EditTopicDialog(mywid.ButtonDialog):
             return None
         return r
 
+class AbandonRestoreDialog(urwid.WidgetWrap):
+    signals = ['save', 'cancel']
+    def __init__(self, action, text):
+        self.action = action
+        save_button = mywid.FixedButton(action)
+        cancel_button = mywid.FixedButton('Cancel')
+        urwid.connect_signal(save_button, 'click',
+                             lambda button:self._emit('save'))
+        urwid.connect_signal(cancel_button, 'click',
+                             lambda button:self._emit('cancel'))
+        button_widgets = [('pack', save_button),
+                          ('pack', cancel_button)]
+        button_columns = urwid.Columns(button_widgets, dividechars=2)
+        rows = []
+        self.entry = urwid.Edit(edit_text=text, multiline=True)
+        rows.append(urwid.Text(u"%s message: " % action))
+        rows.append(self.entry)
+        rows.append(urwid.Divider())
+        rows.append(button_columns)
+        pile = urwid.Pile(rows)
+        fill = urwid.Filler(pile, valign='top')
+        super(AbandonRestoreDialog, self).__init__(urwid.LineBox(fill, '%s Change' % (action,)))
+
 class ReviewDialog(urwid.WidgetWrap):
     signals = ['save', 'cancel']
     def __init__(self, revision_row):
@@ -345,8 +368,12 @@ class ChangeView(urwid.WidgetWrap):
              "Toggle the reviewed flag for the current change"),
             (key(keymap.CHERRY_PICK),
              "Cherry-pick the most recent revision onto the local repo"),
+            (key(keymap.ABANDON_CHANGE),
+             "Abandon this change"),
             (key(keymap.REBASE_CHANGE),
              "Rebase this change (remotely)"),
+            (key(keymap.RESTORE_CHANGE),
+             "Restore this change"),
             (key(keymap.REFRESH),
              "Refresh this change"),
             (key(keymap.EDIT_TOPIC),
@@ -448,6 +475,7 @@ class ChangeView(urwid.WidgetWrap):
         with self.app.db.getSession() as session:
             change = session.getChange(self.change_key)
             self.topic = change.topic or ''
+            self.pending_status_message = change.pending_status_message or ''
             if change.reviewed:
                 reviewed = ' (reviewed)'
             else:
@@ -717,9 +745,15 @@ class ChangeView(urwid.WidgetWrap):
             self.hide_comments = not self.hide_comments
             self.refresh()
             return None
+        if keymap.ABANDON_CHANGE in commands:
+            self.abandonChange()
+            return None
         if keymap.REBASE_CHANGE in commands:
             self.rebaseChange()
             return None
+        if keymap.RESTORE_CHANGE in commands:
+            self.restoreChange()
+            return None
         if keymap.REFRESH in commands:
             self.app.sync.submitTask(
                 sync.SyncChangeTask(self.change_rest_id, priority=sync.HIGH_PRIORITY))
@@ -740,6 +774,33 @@ class ChangeView(urwid.WidgetWrap):
             screen = view_side_diff.SideDiffView(self.app, revision_key)
         self.app.changeScreen(screen)
 
+    def abandonChange(self):
+        dialog = AbandonRestoreDialog(u'Abandon', self.pending_status_message)
+        urwid.connect_signal(dialog, 'cancel', self.app.backScreen)
+        urwid.connect_signal(dialog, 'save', lambda button:
+                                 self.doAbandonRestoreChange(dialog, 'ABANDONED'))
+        self.app.popup(dialog)
+
+    def restoreChange(self):
+        dialog = AbandonRestoreDialog(u'Restore', self.pending_status_message)
+        urwid.connect_signal(dialog, 'cancel', self.app.backScreen)
+        urwid.connect_signal(dialog, 'save', lambda button:
+                                 self.doAbandonRestoreChange(dialog, 'NEW'))
+        self.app.popup(dialog)
+
+    def doAbandonRestoreChange(self, dialog, state):
+        change_key = None
+        with self.app.db.getSession() as session:
+            change = session.getChange(self.change_key)
+            change.status = state
+            change.pending_status = True
+            change.pending_status_message = dialog.entry.edit_text
+            change_key = change.key
+        self.app.sync.submitTask(
+            sync.ChangeStatusTask(change_key, sync.HIGH_PRIORITY))
+        self.app.backScreen()
+        self.refresh()
+
     def rebaseChange(self):
         dialog = mywid.YesNoDialog(u'Rebase Change',
                                    u'Perform a remote rebase of this change?')