From 705ca5c7660e02cc118df0ecaf3bb3b25f909e16 Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Sat, 12 Jul 2014 20:17:17 -0700 Subject: [PATCH] Add reviewkeys Reviewkeys are a customizable shortcut for leaving predefined votes on a change. For example, if you want to leave a +2 review with no comment, you might simply hit the "meta-2" key. The following configuration block is recommended for use with OpenStack's Gerrit: reviewkeys: - key: 'meta 0' approvals: [] - key: 'meta 1' approvals: - category: 'Code-Review' value: 1 - key: 'meta 2' approvals: - category: 'Code-Review' value: 2 - key: 'meta 3' approvals: - category: 'Code-Review' value: 2 - category: 'Workflow' value: 1 Change-Id: I466024d71d5b1d89fcdc32355e2a9fe13abf5c28 --- gertty/app.py | 2 +- gertty/config.py | 25 ++++++++- gertty/view/change.py | 108 +++++++++++++++++++++++------------- gertty/view/change_list.py | 5 +- gertty/view/diff.py | 5 +- gertty/view/project_list.py | 5 +- 6 files changed, 107 insertions(+), 43 deletions(-) diff --git a/gertty/app.py b/gertty/app.py index 46675b4..091950c 100644 --- a/gertty/app.py +++ b/gertty/app.py @@ -192,7 +192,7 @@ class App(object): text += '<%s>%s %s\n' % (d['key'], space, d['name']) text += "\nThis Screen\n" text += "===========\n" - text += self.loop.widget.help + text += self.loop.widget.help() dialog = mywid.MessageDialog('Help', text) lines = text.split('\n') urwid.connect_signal(dialog, 'close', diff --git a/gertty/config.py b/gertty/config.py index 07db3d2..695256a 100644 --- a/gertty/config.py +++ b/gertty/config.py @@ -13,8 +13,13 @@ # License for the specific language governing permissions and limitations # under the License. +import collections import getpass import os +try: + import ordereddict +except: + pass import yaml import voluptuous as v @@ -22,6 +27,11 @@ import voluptuous as v import gertty.commentlink import gertty.palette +try: + OrderedDict = collections.OrderedDict +except AttributeError: + OrderedDict = ordereddict.OrderedDict + DEFAULT_CONFIG_PATH='~/.gertty.yaml' class ConfigSchema(object): @@ -65,11 +75,20 @@ class ConfigSchema(object): dashboards = [dashboard] + reviewkey_approval = {v.Required('category'): str, + v.Required('value'): int} + + reviewkey = {v.Required('approvals'): [reviewkey_approval], + v.Required('key'): str} + + reviewkeys = [reviewkey] + def getSchema(self, data): schema = v.Schema({v.Required('servers'): self.servers, 'palettes': self.palettes, 'commentlinks': self.commentlinks, 'dashboards': self.dashboards, + 'reviewkeys': self.reviewkeys, }) return schema @@ -122,10 +141,14 @@ class Config(object): text="{url}", url="{url}"))]))) - self.dashboards = {} + self.dashboards = OrderedDict() for d in self.config.get('dashboards', []): self.dashboards[d['key']] = d + self.reviewkeys = OrderedDict() + for k in self.config.get('reviewkeys', []): + self.reviewkeys[k['key']] = k + def getServer(self, name=None): for server in self.config['servers']: if name is None or name == server['name']: diff --git a/gertty/view/change.py b/gertty/view/change.py index 06fc676..bba5916 100644 --- a/gertty/view/change.py +++ b/gertty/view/change.py @@ -1,4 +1,5 @@ # Copyright 2014 OpenStack Foundation +# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -85,34 +86,13 @@ class ReviewDialog(urwid.WidgetWrap): super(ReviewDialog, self).__init__(urwid.LineBox(fill, 'Review')) def save(self): - message_key = None - with self.app.db.getSession() as session: - revision = session.getRevision(self.revision_row.revision_key) - change = revision.change - pending_approvals = {} - for approval in change.pending_approvals: - pending_approvals[approval.category] = approval - for category, group in self.button_groups.items(): - approval = pending_approvals.get(category) - if not approval: - approval = change.createApproval(u'(draft)', category, 0, pending=True) - pending_approvals[category] = approval - for button in group: - if button.state: - approval.value = int(button.get_label()) - message = None - for m in revision.messages: - if m.pending: - message = m - break - if not message: - message = revision.createMessage(None, - datetime.datetime.utcnow(), - u'(draft)', '', pending=True) - message.message = self.message.edit_text.strip() - message_key = message.key - change.reviewed = True - return message_key + approvals = {} + for category, group in self.button_groups.items(): + for button in group: + if button.state: + approvals[category] = int(button.get_label()) + message = self.message.edit_text.strip() + self.change_view.saveReview(self.revision_row.revision_key, approvals, message) def keypress(self, size, key): r = super(ReviewDialog, self).keypress(size, key) @@ -142,9 +122,6 @@ class ReviewButton(mywid.FixedButton): def closeReview(self, save): if save: message_key = self.dialog.save() - self.change_view.app.sync.submitTask( - sync.UploadReviewTask(message_key, sync.HIGH_PRIORITY)) - self.change_view.refresh() self.change_view.app.backScreen() class RevisionRow(urwid.WidgetWrap): @@ -293,15 +270,23 @@ class ChangeMessageBox(mywid.HyperText): self.set_text(text+comment_text) class ChangeView(urwid.WidgetWrap): - help = """ - Checkout the most recent revision into the local repo. - Show the diff of the mont recent revision. - Toggle the hidden flag for the current change. - Leave a review for the most recent revision. - Toggle the reviewed flag for the current change. - Cherry-pick the most recent revision onto the local repo. + _help = """ + Checkout the most recent revision into the local repo. + Show the diff of the mont recent revision. + Toggle the hidden flag for the current change. + Leave a review for the most recent revision. + Toggle the reviewed flag for the current change. + Cherry-pick the most recent revision onto the local repo. """ + def help(self): + text = self._help + for k in self.app.config.reviewkeys.values(): + space = max(6 - len(k['key']), 0) * ' ' + action = ', '.join(['{category}:{value}'.format(**a) for a in k['approvals']]) + text += '<%s>%s %s\n' % (k['key'], space, action) + return text + def __init__(self, app, change_key): super(ChangeView, self).__init__(urwid.Pile([])) self.app = app @@ -579,7 +564,54 @@ class ChangeView(urwid.WidgetWrap): row = self.revision_rows[self.last_revision_key] row.cherryPick(None) return None + if r in self.app.config.reviewkeys: + self.reviewKey(self.app.config.reviewkeys[r]) + return None return r def diff(self, revision_key): self.app.changeScreen(view_diff.DiffView(self.app, revision_key)) + + def reviewKey(self, reviewkey): + approvals = {} + for a in reviewkey['approvals']: + approvals[a['category']] = a['value'] + self.app.log.debug("Reviewkey %s with approvals %s" % + (reviewkey['key'], approvals)) + row = self.revision_rows[self.last_revision_key] + self.saveReview(row.revision_key, approvals, '') + + def saveReview(self, revision_key, approvals, message): + message_key = None + with self.app.db.getSession() as session: + revision = session.getRevision(revision_key) + change = revision.change + pending_approvals = {} + for approval in change.pending_approvals: + pending_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 = pending_approvals.get(category) + if not approval: + approval = change.createApproval(u'(draft)', category, 0, pending=True) + pending_approvals[category] = approval + approval.value = value + pending_message = None + for m in revision.messages: + if m.pending: + pending_message = m + break + if not pending_message: + pending_message = revision.createMessage(None, + datetime.datetime.utcnow(), + u'(draft)', '', pending=True) + pending_message.message = message + message_key = pending_message.key + change.reviewed = True + 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 2fe5e7d..c12cb92 100644 --- a/gertty/view/change_list.py +++ b/gertty/view/change_list.py @@ -67,12 +67,15 @@ class ChangeListHeader(urwid.WidgetWrap): self._w.contents.append((urwid.Text(' %s' % category[0]), self._w.options('given', 3))) class ChangeListView(urwid.WidgetWrap): - help = """ + _help = """ Toggle the hidden flag for the currently selected change. Toggle whether only unreviewed or all changes are displayed. Toggle the reviewed flag for the currently selected change. """ + def help(self): + return self._help + def __init__(self, app, query, query_desc=None, unreviewed=False): super(ChangeListView, self).__init__(urwid.Pile([])) self.app = app diff --git a/gertty/view/diff.py b/gertty/view/diff.py index 39e651b..564d154 100644 --- a/gertty/view/diff.py +++ b/gertty/view/diff.py @@ -210,11 +210,14 @@ class DiffContextButton(urwid.WidgetWrap): self.view.expandChunk(self.diff, self.chunk, from_end=-10) class DiffView(urwid.WidgetWrap): - help = """ + _help = """ Add an inline comment

Select old/new patchsets to diff """ + def help(self): + return self._help + def __init__(self, app, new_revision_key): super(DiffView, self).__init__(urwid.Pile([])) self.log = logging.getLogger('gertty.view.diff') diff --git a/gertty/view/project_list.py b/gertty/view/project_list.py index 9cb8a12..120acf6 100644 --- a/gertty/view/project_list.py +++ b/gertty/view/project_list.py @@ -66,11 +66,14 @@ class ProjectListHeader(urwid.WidgetWrap): super(ProjectListHeader, self).__init__(urwid.Columns(cols)) class ProjectListView(urwid.WidgetWrap): - help = """ + _help = """ Toggle whether only subscribed projects or all projects are listed. Toggle the subscription flag for the currently selected project. """ + def help(self): + return self._help + def __init__(self, app): super(ProjectListView, self).__init__(urwid.Pile([])) self.app = app