Add submit functionality
Add a schema change to store whether the user can submit a revision. Offer the submit button on that revision, as well as on the review dialog if applicable. Allow the submit action to be bound to reviewkeys. Also bind it to a key command. Change-Id: I341663ac84d2ac09f9b1ef8c9e0dff45d2658e1d
This commit is contained in:
parent
c245f846bf
commit
e47cc2f607
@ -47,8 +47,10 @@ dashboards:
|
||||
# change screen. Any pending comments or review messages will be
|
||||
# attached to the review; otherwise an empty review will be left. The
|
||||
# approvals list is exhaustive, so if you specify an empty list,
|
||||
# Gertty will submit a review that clears any previous approvals.
|
||||
# They will appear in the help text for the change screen.
|
||||
# Gertty will submit a review that clears any previous approvals. To
|
||||
# submit the change with the review, include 'submit: True' with the
|
||||
# reviewkey. Reviewkeys appear in the help text for the change
|
||||
# screen.
|
||||
reviewkeys:
|
||||
- key: 'meta 0'
|
||||
approvals: []
|
||||
@ -60,3 +62,8 @@ reviewkeys:
|
||||
approvals:
|
||||
- category: 'Code-Review'
|
||||
value: 2
|
||||
- key: 'meta 3'
|
||||
approvals:
|
||||
- category: 'Code-Review'
|
||||
value: 2
|
||||
submit: True
|
||||
|
@ -157,8 +157,10 @@ dashboards:
|
||||
# change screen. Any pending comments or review messages will be
|
||||
# attached to the review; otherwise an empty review will be left. The
|
||||
# approvals list is exhaustive, so if you specify an empty list,
|
||||
# Gertty will submit a review that clears any previous approvals.
|
||||
# They will appear in the help text for the change screen.
|
||||
# Gertty will submit a review that clears any previous approvals. To
|
||||
# submit the change with the review, include 'submit: True' with the
|
||||
# reviewkey. Reviewkeys appear in the help text for the change
|
||||
# screen.
|
||||
reviewkeys:
|
||||
- key: 'meta 0'
|
||||
approvals: []
|
||||
@ -170,3 +172,8 @@ reviewkeys:
|
||||
approvals:
|
||||
- category: 'Code-Review'
|
||||
value: 2
|
||||
- key: 'meta 3'
|
||||
approvals:
|
||||
- category: 'Code-Review'
|
||||
value: 2
|
||||
submit: True
|
@ -0,0 +1,36 @@
|
||||
"""add can_submit column
|
||||
|
||||
Revision ID: 312cd5a9f878
|
||||
Revises: 46b175bfa277
|
||||
Create Date: 2014-09-18 16:37:13.149729
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '312cd5a9f878'
|
||||
down_revision = '46b175bfa277'
|
||||
|
||||
import warnings
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from gertty.dbsupport import sqlite_alter_columns
|
||||
|
||||
|
||||
def upgrade():
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
op.add_column('revision', sa.Column('can_submit', sa.Boolean()))
|
||||
|
||||
conn = op.get_bind()
|
||||
q = sa.text('update revision set can_submit=:submit')
|
||||
res = conn.execute(q, submit=False)
|
||||
|
||||
sqlite_alter_columns('revision', [
|
||||
sa.Column('can_submit', sa.Boolean(), nullable=False),
|
||||
])
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
@ -83,6 +83,7 @@ class ConfigSchema(object):
|
||||
v.Required('value'): int}
|
||||
|
||||
reviewkey = {v.Required('approvals'): [reviewkey_approval],
|
||||
'submit': bool,
|
||||
v.Required('key'): str}
|
||||
|
||||
reviewkeys = [reviewkey]
|
||||
|
@ -74,6 +74,7 @@ revision_table = Table(
|
||||
Column('fetch_auth', Boolean, nullable=False),
|
||||
Column('fetch_ref', String(255), nullable=False),
|
||||
Column('pending_message', Boolean, index=True, nullable=False),
|
||||
Column('can_submit', Boolean, nullable=False),
|
||||
)
|
||||
message_table = Table(
|
||||
'message', metadata,
|
||||
@ -268,7 +269,8 @@ class Change(object):
|
||||
|
||||
class Revision(object):
|
||||
def __init__(self, change, number, message, commit, parent,
|
||||
fetch_auth, fetch_ref, pending_message=False):
|
||||
fetch_auth, fetch_ref, pending_message=False,
|
||||
can_submit=False):
|
||||
self.change_key = change.key
|
||||
self.number = number
|
||||
self.message = message
|
||||
@ -277,6 +279,7 @@ class Revision(object):
|
||||
self.fetch_auth = fetch_auth
|
||||
self.fetch_ref = fetch_ref
|
||||
self.pending_message = pending_message
|
||||
self.can_submit = can_submit
|
||||
|
||||
def createMessage(self, *args, **kw):
|
||||
session = Session.object_session(self)
|
||||
|
@ -52,6 +52,7 @@ CHERRY_PICK_CHANGE = 'cherry pick change'
|
||||
REFRESH = 'refresh'
|
||||
EDIT_TOPIC = 'edit topic'
|
||||
EDIT_COMMIT_MESSAGE = 'edit commit message'
|
||||
SUBMIT_CHANGE = 'submit change'
|
||||
# Project list screen:
|
||||
TOGGLE_LIST_REVIEWED = 'toggle list reviewed'
|
||||
TOGGLE_LIST_SUBSCRIBED = 'toggle list subscribed'
|
||||
@ -95,6 +96,7 @@ DEFAULT_KEYMAP = {
|
||||
REFRESH: 'ctrl r',
|
||||
EDIT_TOPIC: 'ctrl t',
|
||||
EDIT_COMMIT_MESSAGE: 'ctrl d',
|
||||
SUBMIT_CHANGE: 'ctrl u',
|
||||
|
||||
TOGGLE_LIST_REVIEWED: 'l',
|
||||
TOGGLE_LIST_SUBSCRIBED: 'L',
|
||||
|
@ -306,7 +306,7 @@ class SyncChangeTask(Task):
|
||||
|
||||
def run(self, sync):
|
||||
app = sync.app
|
||||
remote_change = sync.get('changes/%s?o=DETAILED_LABELS&o=ALL_REVISIONS&o=ALL_COMMITS&o=MESSAGES&o=DETAILED_ACCOUNTS' % self.change_id)
|
||||
remote_change = sync.get('changes/%s?o=DETAILED_LABELS&o=ALL_REVISIONS&o=ALL_COMMITS&o=MESSAGES&o=DETAILED_ACCOUNTS&o=CURRENT_ACTIONS' % self.change_id)
|
||||
# Perform subqueries this task will need outside of the db session
|
||||
for remote_commit, remote_revision in remote_change.get('revisions', {}).items():
|
||||
remote_comments_data = sync.get('changes/%s/revisions/%s/comments' % (self.change_id, remote_commit))
|
||||
@ -360,6 +360,8 @@ class SyncChangeTask(Task):
|
||||
revision.message = remote_revision['commit']['message']
|
||||
# TODO: handle multiple parents
|
||||
parent_revision = session.getRevisionByCommit(revision.parent)
|
||||
actions = remote_revision.get('actions', {})
|
||||
revision.can_submit = 'submit' in actions
|
||||
# TODO: use a singleton list of closed states
|
||||
if not parent_revision and change.status not in ['MERGED', 'ABANDONED']:
|
||||
sync.submitTask(SyncChangeByCommitTask(revision.parent, self.priority))
|
||||
@ -667,6 +669,8 @@ class ChangeStatusTask(Task):
|
||||
elif change.status == 'NEW':
|
||||
sync.post('changes/%s/restore' % (change.id,),
|
||||
data)
|
||||
elif change.status == 'SUBMITTED':
|
||||
sync.post('changes/%s/submit' % (change.id,), {})
|
||||
sync.submitTask(SyncChangeTask(change.id, priority=self.priority))
|
||||
|
||||
class SendCherryPickTask(Task):
|
||||
@ -722,11 +726,16 @@ class UploadReviewTask(Task):
|
||||
|
||||
def run(self, sync):
|
||||
app = sync.app
|
||||
submit = False
|
||||
change_id = None
|
||||
with app.db.getSession() as session:
|
||||
message = session.getMessage(self.message_key)
|
||||
revision = message.revision
|
||||
change = message.revision.change
|
||||
change_id = change.id
|
||||
current_revision = change.revisions[-1]
|
||||
if change.pending_status and change.status == 'SUBMITTED':
|
||||
submit = True
|
||||
data = dict(message=message.message,
|
||||
strict_labels=False)
|
||||
if revision == current_revision:
|
||||
@ -753,7 +762,15 @@ class UploadReviewTask(Task):
|
||||
# Inside db session for rollback
|
||||
sync.post('changes/%s/revisions/%s/review' % (change.id, revision.commit),
|
||||
data)
|
||||
sync.submitTask(SyncChangeTask(change.id, priority=self.priority))
|
||||
if submit:
|
||||
# In another db session in case submit fails after posting
|
||||
# the message succeeds
|
||||
with app.db.getSession() as session:
|
||||
change = session.getChangeByID(change_id)
|
||||
change.pending_status = False
|
||||
change.pending_status_message = None
|
||||
sync.post('changes/%s/submit' % (change_id,), {})
|
||||
sync.submitTask(SyncChangeTask(change_id, priority=self.priority))
|
||||
|
||||
class Sync(object):
|
||||
def __init__(self, app):
|
||||
|
@ -81,19 +81,26 @@ class CherryPickDialog(urwid.WidgetWrap):
|
||||
'Propose Change to Branch'))
|
||||
|
||||
class ReviewDialog(urwid.WidgetWrap):
|
||||
signals = ['save', 'cancel']
|
||||
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
|
||||
save_button = mywid.FixedButton(u'Save')
|
||||
submit_button = mywid.FixedButton(u'Save and Submit')
|
||||
cancel_button = mywid.FixedButton(u'Cancel')
|
||||
urwid.connect_signal(save_button, 'click',
|
||||
lambda button:self._emit('save'))
|
||||
urwid.connect_signal(submit_button, 'click',
|
||||
lambda button:self._emit('submit'))
|
||||
urwid.connect_signal(cancel_button, 'click',
|
||||
lambda button:self._emit('cancel'))
|
||||
buttons = urwid.Columns([('pack', save_button), ('pack', cancel_button)],
|
||||
dividechars=2)
|
||||
|
||||
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 = {}
|
||||
@ -143,7 +150,7 @@ class ReviewDialog(urwid.WidgetWrap):
|
||||
fill = urwid.Filler(pile, valign='top')
|
||||
super(ReviewDialog, self).__init__(urwid.LineBox(fill, 'Review'))
|
||||
|
||||
def save(self, upload=False):
|
||||
def save(self, upload=False, submit=False):
|
||||
approvals = {}
|
||||
for category, group in self.button_groups.items():
|
||||
for button in group:
|
||||
@ -151,7 +158,7 @@ class ReviewDialog(urwid.WidgetWrap):
|
||||
approvals[category] = int(button.get_label())
|
||||
message = self.message.edit_text.strip()
|
||||
self.change_view.saveReview(self.revision_row.revision_key, approvals,
|
||||
message, upload)
|
||||
message, upload, submit)
|
||||
|
||||
def keypress(self, size, key):
|
||||
r = super(ReviewDialog, self).keypress(size, key)
|
||||
@ -172,15 +179,17 @@ class ReviewButton(mywid.FixedButton):
|
||||
def openReview(self):
|
||||
self.dialog = ReviewDialog(self.revision_row)
|
||||
urwid.connect_signal(self.dialog, 'save',
|
||||
lambda button: self.closeReview(True))
|
||||
lambda button: self.closeReview(True, False))
|
||||
urwid.connect_signal(self.dialog, 'submit',
|
||||
lambda button: self.closeReview(True, True))
|
||||
urwid.connect_signal(self.dialog, 'cancel',
|
||||
lambda button: self.closeReview(False))
|
||||
lambda button: self.closeReview(False, False))
|
||||
self.change_view.app.popup(self.dialog,
|
||||
relative_width=50, relative_height=75,
|
||||
min_width=60, min_height=20)
|
||||
|
||||
def closeReview(self, upload):
|
||||
self.dialog.save(upload)
|
||||
def closeReview(self, upload, submit):
|
||||
self.dialog.save(upload, submit)
|
||||
self.change_view.app.backScreen()
|
||||
|
||||
class RevisionRow(urwid.WidgetWrap):
|
||||
@ -198,6 +207,7 @@ class RevisionRow(urwid.WidgetWrap):
|
||||
self.revision_key = revision.key
|
||||
self.project_name = revision.change.project.name
|
||||
self.commit_sha = revision.commit
|
||||
self.can_submit = revision.can_submit
|
||||
self.title = mywid.TextButton(u'', on_press = self.expandContract)
|
||||
stats = repo.diffstat(revision.parent, revision.commit)
|
||||
table = mywid.Table(columns=3)
|
||||
@ -233,6 +243,10 @@ class RevisionRow(urwid.WidgetWrap):
|
||||
on_press=self.checkout),
|
||||
mywid.FixedButton(('revision-button', "Local Cherry-Pick"),
|
||||
on_press=self.cherryPick)]
|
||||
if self.can_submit:
|
||||
buttons.append(mywid.FixedButton(('revision-button', "Submit"),
|
||||
on_press=lambda x: self.change_view.doSubmitChange()))
|
||||
|
||||
buttons = [('pack', urwid.AttrMap(b, None, focus_map=focus_map)) for b in buttons]
|
||||
buttons = urwid.Columns(buttons + [urwid.Text('')], dividechars=2)
|
||||
buttons = urwid.AttrMap(buttons, 'revision-button')
|
||||
@ -387,6 +401,8 @@ class ChangeView(urwid.WidgetWrap):
|
||||
"Refresh this change"),
|
||||
(key(keymap.EDIT_TOPIC),
|
||||
"Edit the topic of this change"),
|
||||
(key(keymap.SUBMIT_CHANGE),
|
||||
"Submit this change"),
|
||||
(key(keymap.CHERRY_PICK_CHANGE),
|
||||
"Propose this change to another branch"),
|
||||
]
|
||||
@ -774,6 +790,9 @@ class ChangeView(urwid.WidgetWrap):
|
||||
sync.SyncChangeTask(self.change_rest_id, priority=sync.HIGH_PRIORITY))
|
||||
self.app.status.update()
|
||||
return None
|
||||
if keymap.SUBMIT_CHANGE in commands:
|
||||
self.doSubmitChange()
|
||||
return None
|
||||
if keymap.EDIT_TOPIC in commands:
|
||||
self.editTopic()
|
||||
return None
|
||||
@ -896,6 +915,18 @@ class ChangeView(urwid.WidgetWrap):
|
||||
self.app.backScreen()
|
||||
self.refresh()
|
||||
|
||||
def doSubmitChange(self):
|
||||
change_key = None
|
||||
with self.app.db.getSession() as session:
|
||||
change = session.getChange(self.change_key)
|
||||
change.status = 'SUBMITTED'
|
||||
change.pending_status = True
|
||||
change.pending_status_message = None
|
||||
change_key = change.key
|
||||
self.app.sync.submitTask(
|
||||
sync.ChangeStatusTask(change_key, sync.HIGH_PRIORITY))
|
||||
self.refresh()
|
||||
|
||||
def editTopic(self):
|
||||
dialog = EditTopicDialog(self.app, self.topic)
|
||||
urwid.connect_signal(dialog, 'save',
|
||||
@ -924,9 +955,10 @@ class ChangeView(urwid.WidgetWrap):
|
||||
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, '', True)
|
||||
submit = reviewkey.get('submit', False)
|
||||
self.saveReview(row.revision_key, approvals, '', True, submit)
|
||||
|
||||
def saveReview(self, revision_key, approvals, message, upload):
|
||||
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)
|
||||
@ -961,6 +993,10 @@ class ChangeView(urwid.WidgetWrap):
|
||||
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
|
||||
if upload:
|
||||
self.app.sync.submitTask(
|
||||
|
Loading…
x
Reference in New Issue
Block a user