diff --git a/gertty/app.py b/gertty/app.py index a69fab1..6c35934 100644 --- a/gertty/app.py +++ b/gertty/app.py @@ -14,6 +14,7 @@ # under the License. import argparse +import datetime import dateutil import logging import os @@ -602,6 +603,58 @@ class App(object): lambda button: self.backScreen()) self.popup(dialog, min_height=min_height) + def saveReviews(self, revision_keys, approvals, message, upload, submit): + message_keys = [] + with self.db.getSession() as session: + account = session.getAccountByUsername(self.config.username) + for revision_key in revision_keys: + k = self._saveReview(session, account, revision_key, + approvals, message, upload, submit) + if k: + message_keys.append(k) + return message_keys + + def _saveReview(self, session, account, revision_key, + approvals, message, upload, submit): + message_key = None + revision = session.getRevision(revision_key) + change = revision.change + draft_approvals = {} + for approval in change.draft_approvals: + draft_approvals[approval.category] = approval + + categories = set() + for label in change.permitted_labels: + categories.add(label.category) + for category in categories: + value = approvals.get(category, 0) + approval = draft_approvals.get(category) + if not approval: + approval = change.createApproval(account, category, 0, draft=True) + draft_approvals[category] = approval + approval.value = value + draft_message = revision.getPendingMessage() + if not draft_message: + draft_message = revision.getDraftMessage() + if not draft_message: + if message or upload: + draft_message = revision.createMessage(None, account, + datetime.datetime.utcnow(), + '', draft=True) + if draft_message: + draft_message.created = datetime.datetime.utcnow() + draft_message.message = message + draft_message.pending = upload + message_key = draft_message.key + if upload: + change.reviewed = True + if submit: + change.status = 'SUBMITTED' + change.pending_status = True + change.pending_status_message = None + return message_key + + def version(): return "Gertty version: %s" % gertty.version.version_info.version_string() diff --git a/gertty/keymap.py b/gertty/keymap.py index a1bd006..61c00f0 100644 --- a/gertty/keymap.py +++ b/gertty/keymap.py @@ -41,6 +41,7 @@ TOGGLE_REVIEWED = 'toggle reviewed' TOGGLE_HIDDEN = 'toggle hidden' TOGGLE_STARRED = 'toggle starred' TOGGLE_HELD = 'toggle held' +TOGGLE_MARK = 'toggle process mark' REVIEW = 'review' DIFF = 'diff' LOCAL_CHECKOUT = 'local checkout' @@ -92,6 +93,7 @@ DEFAULT_KEYMAP = { TOGGLE_HIDDEN: 'k', TOGGLE_STARRED: '*', TOGGLE_HELD: '!', + TOGGLE_MARK: '%', REVIEW: 'r', DIFF: 'd', LOCAL_CHECKOUT: 'c', diff --git a/gertty/palette.py b/gertty/palette.py index 7a4ab11..fae8756 100644 --- a/gertty/palette.py +++ b/gertty/palette.py @@ -86,6 +86,8 @@ DEFAULT_PALETTE={ 'focused-starred-change': ['light cyan,standout', ''], 'held-change': ['light red', ''], 'focused-held-change': ['light red,standout', ''], + 'marked-change': ['dark cyan', ''], + 'focused-marked-change': ['dark cyan,standout', ''], } # A delta from the default palette diff --git a/gertty/view/change.py b/gertty/view/change.py index 659714f..7f401df 100644 --- a/gertty/view/change.py +++ b/gertty/view/change.py @@ -84,10 +84,9 @@ class CherryPickDialog(urwid.WidgetWrap): class ReviewDialog(urwid.WidgetWrap): signals = ['submit', 'save', 'cancel'] - def __init__(self, revision_row): - self.revision_row = revision_row - self.change_view = revision_row.change_view - self.app = self.change_view.app + def __init__(self, app, revision_key): + self.revision_key = revision_key + self.app = app save_button = mywid.FixedButton(u'Save') submit_button = mywid.FixedButton(u'Save and Submit') cancel_button = mywid.FixedButton(u'Cancel') @@ -98,11 +97,6 @@ class ReviewDialog(urwid.WidgetWrap): urwid.connect_signal(cancel_button, 'click', lambda button:self._emit('cancel')) - buttons = [('pack', save_button)] - if revision_row.can_submit: - buttons.append(('pack', submit_button)) - buttons.append(('pack', cancel_button)) - buttons = urwid.Columns(buttons, dividechars=2) rows = [] categories = [] values = {} @@ -110,8 +104,13 @@ class ReviewDialog(urwid.WidgetWrap): self.button_groups = {} message = '' with self.app.db.getSession() as session: - revision = session.getRevision(self.revision_row.revision_key) + revision = session.getRevision(self.revision_key) change = revision.change + buttons = [('pack', save_button)] + if revision.can_submit: + buttons.append(('pack', submit_button)) + buttons.append(('pack', cancel_button)) + buttons = urwid.Columns(buttons, dividechars=2) if revision == change.revisions[-1]: for label in change.labels: d = descriptions.setdefault(label.category, {}) @@ -174,15 +173,14 @@ class ReviewDialog(urwid.WidgetWrap): fill = urwid.Filler(pile, valign='top') super(ReviewDialog, self).__init__(urwid.LineBox(fill, 'Review')) - def save(self, upload=False, submit=False): + def getValues(self): approvals = {} for category, group in self.button_groups.items(): for button in group: if button.state: approvals[category] = button._value message = self.message.edit_text.strip() - self.change_view.saveReview(self.revision_row.revision_key, approvals, - message, upload, submit) + return (approvals, message) def keypress(self, size, key): r = super(ReviewDialog, self).keypress(size, key) @@ -201,7 +199,8 @@ class ReviewButton(mywid.FixedButton): lambda button: self.openReview()) def openReview(self): - self.dialog = ReviewDialog(self.revision_row) + self.dialog = ReviewDialog(self.change_view.app, + self.revision_row.revision_key) urwid.connect_signal(self.dialog, 'save', lambda button: self.closeReview(True, False)) urwid.connect_signal(self.dialog, 'submit', @@ -213,7 +212,9 @@ class ReviewButton(mywid.FixedButton): min_width=60, min_height=20) def closeReview(self, upload, submit): - self.dialog.save(upload, submit) + approvals, message = self.dialog.getValues() + self.change_view.saveReview(self.revision_row.revision_key, approvals, + message, upload, submit) self.change_view.app.backScreen() class RevisionRow(urwid.WidgetWrap): @@ -1037,46 +1038,10 @@ class ChangeView(urwid.WidgetWrap): self.saveReview(row.revision_key, approvals, '', True, submit) def saveReview(self, revision_key, approvals, message, upload, submit): - message_key = None - with self.app.db.getSession() as session: - account = session.getAccountByUsername(self.app.config.username) - revision = session.getRevision(revision_key) - change = revision.change - draft_approvals = {} - for approval in change.draft_approvals: - draft_approvals[approval.category] = approval - - categories = set() - for label in change.permitted_labels: - categories.add(label.category) - for category in categories: - value = approvals.get(category, 0) - approval = draft_approvals.get(category) - if not approval: - approval = change.createApproval(account, category, 0, draft=True) - draft_approvals[category] = approval - approval.value = value - draft_message = revision.getPendingMessage() - if not draft_message: - draft_message = revision.getDraftMessage() - if not draft_message: - if message or upload: - draft_message = revision.createMessage(None, account, - datetime.datetime.utcnow(), - '', draft=True) - if draft_message: - draft_message.created = datetime.datetime.utcnow() - draft_message.message = message - draft_message.pending = upload - message_key = draft_message.key - if upload: - change.reviewed = True - if submit: - change.status = 'SUBMITTED' - change.pending_status = True - change.pending_status_message = None - # Outside of db session + message_keys = self.app.saveReviews([revision_key], approvals, + message, upload, submit) if upload: - self.app.sync.submitTask( - sync.UploadReviewTask(message_key, sync.HIGH_PRIORITY)) + for message_key in message_keys: + self.app.sync.submitTask( + sync.UploadReviewTask(message_key, sync.HIGH_PRIORITY)) self.refresh() diff --git a/gertty/view/change_list.py b/gertty/view/change_list.py index 8475207..430465a 100644 --- a/gertty/view/change_list.py +++ b/gertty/view/change_list.py @@ -45,12 +45,14 @@ class ThreadStack(object): def countChildren(self): return [len(x[1]) for x in self.stack] + class ChangeRow(urwid.Button): change_focus_map = {None: 'focused', 'unreviewed-change': 'focused-unreviewed-change', 'reviewed-change': 'focused-reviewed-change', 'starred-change': 'focused-starred-change', 'held-change': 'focused-held-change', + 'marked-change': 'focused-marked-change', 'positive-label': 'focused-positive-label', 'negative-label': 'focused-negative-label', 'min-label': 'focused-min-label', @@ -70,6 +72,7 @@ class ChangeRow(urwid.Button): self.updated = urwid.Text(u'') self.project = urwid.Text(u'', wrap='clip') self.owner = urwid.Text(u'', wrap='clip') + self.mark = False cols = [(6, self.number), ('weight', 4, self.subject)] if project: cols.append(('weight', 1, self.project)) @@ -99,6 +102,9 @@ class ChangeRow(urwid.Button): if change.held: flag = '!' style = 'held-change' + if self.mark: + flag = '%' + style = 'marked-change' subject = flag + subject self.row_style.set_attr_map({None: style}) self.subject.set_text(subject) @@ -107,6 +113,7 @@ class ChangeRow(urwid.Button): self.owner.set_text(change.owner_name) self.project_name = change.project.name self.commit_sha = change.revisions[-1].commit + self.current_revision_key = change.revisions[-1].key today = self.app.time(datetime.datetime.utcnow()).date() updated_time = self.app.time(change.updated) if today == updated_time.date(): @@ -171,8 +178,12 @@ class ChangeListView(urwid.WidgetWrap): "Toggle the reviewed flag for the currently selected change"), (key(keymap.TOGGLE_STARRED), "Toggle the starred flag for the currently selected change"), + (key(keymap.TOGGLE_MARK), + "Toggle the process mark for the currently selected change"), (key(keymap.REFRESH), refresh_help), + (key(keymap.REVIEW), + "Leave reviews for the marked changes"), (key(keymap.SORT_BY_NUMBER), "Sort changes by number"), (key(keymap.SORT_BY_UPDATED), @@ -458,6 +469,20 @@ class ChangeListView(urwid.WidgetWrap): change = session.getChange(change_key) row.update(change, self.categories) return None + if keymap.TOGGLE_MARK in commands: + if not len(self.listbox.body): + return None + pos = self.listbox.focus_position + change_key = self.listbox.body[pos].change_key + row = self.change_rows[change_key] + row.mark = not row.mark + with self.app.db.getSession() as session: + change = session.getChange(change_key) + row.update(change, self.categories) + if pos < len(self.listbox.body)-1: + pos += 1 + self.listbox.focus_position = pos + return None if keymap.REFRESH in commands: if self.project_key: self.app.sync.submitTask( @@ -467,6 +492,11 @@ class ChangeListView(urwid.WidgetWrap): sync.SyncSubscribedProjectsTask(sync.HIGH_PRIORITY)) self.app.status.update() return None + if keymap.REVIEW in commands: + marked = [row for row in self.change_rows.values() if row.mark] + if marked: + self.openReview(marked) + return None if keymap.SORT_BY_NUMBER in commands: if not len(self.listbox.body): return None @@ -513,3 +543,27 @@ class ChangeListView(urwid.WidgetWrap): self.app.changeScreen(view) except gertty.view.DisplayError as e: self.app.error(e.message) + + def openReview(self, rows): + dialog = view_change.ReviewDialog(self.app, rows[0].current_revision_key) + urwid.connect_signal(dialog, 'save', + lambda button: self.closeReview(dialog, rows, True, False)) + urwid.connect_signal(dialog, 'submit', + lambda button: self.closeReview(dialog, rows, True, True)) + urwid.connect_signal(dialog, 'cancel', + lambda button: self.closeReview(dialog, rows, False, False)) + self.app.popup(dialog, + relative_width=50, relative_height=75, + min_width=60, min_height=20) + + def closeReview(self, dialog, rows, upload, submit): + approvals, message = dialog.getValues() + revision_keys = [row.current_revision_key for row in rows] + message_keys = self.app.saveReviews(revision_keys, approvals, + message, upload, submit) + if upload: + for message_key in message_keys: + self.app.sync.submitTask( + sync.UploadReviewTask(message_key, sync.HIGH_PRIORITY)) + self.refresh() + self.app.backScreen()