Add WIP support

Change-Id: I3b690c3bffdb1ca534e42cef0c3c7d9b797f7e80
This commit is contained in:
James E. Blair 2020-12-18 10:56:20 -08:00
parent bb9d860dde
commit 94310529ed
6 changed files with 176 additions and 10 deletions

View File

@ -0,0 +1,31 @@
"""fix_server_table
Revision ID: 02ca927a2b55
Revises: 45d33eccc7a7
Create Date: 2020-12-18 10:41:24.274607
"""
# revision identifiers, used by Alembic.
revision = '02ca927a2b55'
down_revision = '45d33eccc7a7'
from alembic import op
import sqlalchemy as sa
# The original had a reference to own_account.key which is invalid and
# caused later upgrades to fail. Drop and recreate the table to
# correct; Gertty will fill in the data again.
def upgrade():
op.drop_table('server')
op.create_table('server',
sa.Column('key', sa.Integer(), nullable=False),
sa.Column('own_account_key', sa.Integer(), sa.ForeignKey('account.key'), index=True),
sa.PrimaryKeyConstraint('key')
)
def downgrade():
pass

View File

@ -0,0 +1,42 @@
"""wip
Revision ID: ad440301e47f
Revises: 02ca927a2b55
Create Date: 2020-12-18 10:42:07.689266
"""
# revision identifiers, used by Alembic.
revision = 'ad440301e47f'
down_revision = '02ca927a2b55'
from alembic import op
import sqlalchemy as sa
import warnings
from gertty.dbsupport import sqlite_alter_columns
def upgrade():
with warnings.catch_warnings():
warnings.simplefilter("ignore")
op.add_column('change', sa.Column('wip', sa.Boolean()))
op.add_column('change', sa.Column('pending_wip', sa.Boolean()))
op.add_column('change', sa.Column('pending_wip_message', sa.Text()))
connection = op.get_bind()
change = sa.sql.table('change',
sa.sql.column('wip', sa.Boolean()),
sa.sql.column('pending_wip', sa.Boolean()))
connection.execute(change.update().values({'wip':False,
'pending_wip':False}))
sqlite_alter_columns('change', [
sa.Column('wip', sa.Boolean(), index=True, nullable=False),
sa.Column('pending_wip', sa.Boolean(), index=True, nullable=False),
])
def downgrade():
pass

View File

@ -84,6 +84,9 @@ change_table = Table(
Column('pending_status_message', Text),
Column('last_seen', DateTime, index=True),
Column('outdated', Boolean, index=True, nullable=False),
Column('wip', Boolean, index=True, nullable=False),
Column('pending_wip', Boolean, index=True, nullable=False),
Column('pending_wip_message', Text),
)
change_conflict_table = Table(
'change_conflict', metadata,
@ -316,11 +319,12 @@ class Topic(object):
class Change(object):
def __init__(self, project, id, owner, number, branch, change_id,
subject, created, updated, status, topic=None,
hidden=False, reviewed=False, starred=False, held=False,
pending_rebase=False, pending_topic=False,
pending_starred=False, pending_status=False,
pending_status_message=None, pending_hashtags=False,
outdated=False):
hidden=False, reviewed=False, starred=False,
wip=False, held=False, pending_rebase=False,
pending_topic=False, pending_starred=False,
pending_status=False, pending_status_message=None,
pending_hashtags=False, pending_wip=False,
pending_wip_message=None, outdated=False):
self.project_key = project.key
self.account_key = owner.key
self.id = id
@ -334,6 +338,7 @@ class Change(object):
self.status = status
self.hidden = hidden
self.reviewed = reviewed
self.wip = wip
self.starred = starred
self.held = held
self.pending_rebase = pending_rebase
@ -342,6 +347,8 @@ class Change(object):
self.pending_starred = pending_starred
self.pending_status = pending_status
self.pending_status_message = pending_status_message
self.pending_wip = pending_wip
self.pending_wip_message = pending_wip_message
self.outdated = outdated
def getCategories(self):
@ -1098,6 +1105,9 @@ class DatabaseSession(object):
def getPendingStarred(self):
return self.session().query(Change).filter_by(pending_starred=True).all()
def getPendingWIP(self):
return self.session().query(Change).filter_by(pending_wip=True).all()
def getPendingStatusChanges(self):
return self.session().query(Change).filter_by(pending_status=True).all()

View File

@ -57,6 +57,8 @@ PREV_CHANGE = 'previous change'
TOGGLE_HIDDEN_COMMENTS = 'toggle hidden comments'
ABANDON_CHANGE = 'abandon change'
RESTORE_CHANGE = 'restore change'
WIP_CHANGE = 'mark change work in progress'
READY_CHANGE = 'mark change ready for review'
REBASE_CHANGE = 'rebase change'
CHERRY_PICK_CHANGE = 'cherry pick change'
REFRESH = 'refresh'
@ -126,6 +128,8 @@ DEFAULT_KEYMAP = {
TOGGLE_HIDDEN_COMMENTS: 't',
ABANDON_CHANGE: 'ctrl a',
RESTORE_CHANGE: 'ctrl e',
WIP_CHANGE: 'ctrl w',
READY_CHANGE: 'ctrl y',
REBASE_CHANGE: 'ctrl b',
CHERRY_PICK_CHANGE: 'ctrl x',
REFRESH: 'ctrl r',

View File

@ -688,6 +688,10 @@ class SyncChangeTask(Task):
change.starred = True
else:
change.starred = False
if remote_change.get('work_in_progress'):
change.wip = True
else:
change.wip = False
change.subject = remote_change['subject']
change.updated = dateutil.parser.parse(remote_change['updated'])
change.topic = remote_change.get('topic')
@ -1079,6 +1083,8 @@ class UploadReviewsTask(Task):
sync.submitTask(ChangeStatusTask(c.key, self.priority))
for c in session.getPendingStarred():
sync.submitTask(ChangeStarredTask(c.key, self.priority))
for c in session.getPendingWIP():
sync.submitTask(ChangeWIPTask(c.key, self.priority))
for c in session.getPendingCherryPicks():
sync.submitTask(SendCherryPickTask(c.key, self.priority))
for r in session.getPendingCommitMessages():
@ -1228,15 +1234,44 @@ class ChangeStatusTask(Task):
change.pending_status_message = None
# Inside db session for rollback
if change.status == 'ABANDONED':
sync.post('changes/%s/abandon' % (change.id,),
data)
sync.post('changes/%s/abandon' % (change.id,), data)
elif change.status == 'NEW':
sync.post('changes/%s/restore' % (change.id,),
data)
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 ChangeWIPTask(Task):
def __init__(self, change_key, priority=NORMAL_PRIORITY):
super(ChangeWIPTask, self).__init__(priority)
self.change_key = change_key
def __repr__(self):
return '<ChangeWIPTask %s>' % (self.change_key,)
def __eq__(self, other):
if (other.__class__ == self.__class__ and
other.change_key == self.change_key):
return True
return False
def run(self, sync):
app = sync.app
with app.db.getSession() as session:
change = session.getChange(self.change_key)
if change.pending_wip_message:
data = dict(message=change.pending_wip_message)
else:
data = {}
change.pending_wip = False
change.pending_wip_message = None
# Inside db session for rollback
if change.wip:
sync.post('changes/%s/wip' % (change.id,), data)
else:
sync.post('changes/%s/ready' % (change.id,), data)
sync.submitTask(SyncChangeTask(change.id, priority=self.priority))
class SendCherryPickTask(Task):
def __init__(self, cp_key, priority=NORMAL_PRIORITY):
super(SendCherryPickTask, self).__init__(priority)

View File

@ -555,6 +555,8 @@ class ChangeView(urwid.WidgetWrap):
"Rebase this change (remotely)"),
(keymap.RESTORE_CHANGE,
"Restore this change"),
(keymap.READY_CHANGE,
"Mark this change ready for review"),
(keymap.REFRESH,
"Refresh this change"),
(keymap.EDIT_TOPIC,
@ -565,6 +567,8 @@ class ChangeView(urwid.WidgetWrap):
"Submit this change"),
(keymap.CHERRY_PICK_CHANGE,
"Propose this change to another branch"),
(keymap.WIP_CHANGE,
"Mark this change work in progress"),
]
def help(self):
@ -734,7 +738,8 @@ class ChangeView(urwid.WidgetWrap):
self.hashtags_label.set_text(('change-data', ' '.join([x.name for x in change.hashtags])))
self.created_label.set_text(('change-data', str(self.app.time(change.created))))
self.updated_label.set_text(('change-data', str(self.app.time(change.updated))))
self.status_label.set_text(('change-data', change.status))
stat = change.wip and 'WIP' or change.status
self.status_label.set_text(('change-data', stat))
self.permalink_url = urlparse.urljoin(self.app.config.url, str(change.number))
self.permalink_label.text.set_text(('change-data', self.permalink_url))
self.commit_message.set_text(change.revisions[-1].message)
@ -1043,6 +1048,12 @@ class ChangeView(urwid.WidgetWrap):
if keymap.ABANDON_CHANGE in commands:
self.abandonChange()
return None
if keymap.WIP_CHANGE in commands:
self.wipChange()
return None
if keymap.READY_CHANGE in commands:
self.readyChange()
return None
if keymap.EDIT_COMMIT_MESSAGE in commands:
self.editCommitMessage()
return None
@ -1112,6 +1123,39 @@ class ChangeView(urwid.WidgetWrap):
self.app.backScreen()
self.refresh()
def wipChange(self):
dialog = mywid.TextEditDialog(u'Mark Change Work In Progress',
u'WIP message:',
u'WIP Change',
self.pending_status_message)
urwid.connect_signal(dialog, 'cancel', self.app.backScreen)
urwid.connect_signal(dialog, 'save', lambda button:
self.doWip(dialog, True))
self.app.popup(dialog)
def readyChange(self):
dialog = mywid.TextEditDialog(u'Mark Change Ready For Review',
u'Ready message:',
u'Ready Change',
self.pending_status_message)
urwid.connect_signal(dialog, 'cancel', self.app.backScreen)
urwid.connect_signal(dialog, 'save', lambda button:
self.doWip(dialog, False))
self.app.popup(dialog)
def doWip(self, dialog, state):
change_key = None
with self.app.db.getSession() as session:
change = session.getChange(self.change_key)
change.wip = state
change.pending_wip = True
change.pending_wip_message = dialog.entry.edit_text
change_key = change.key
self.app.sync.submitTask(
sync.ChangeWIPTask(change_key, sync.HIGH_PRIORITY))
self.app.backScreen()
self.refresh()
def editCommitMessage(self):
with self.app.db.getSession() as session:
change = session.getChange(self.change_key)