From 5dffc27d163ba1fdcf3b14adc674a950301ecd32 Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Thu, 12 Feb 2015 18:50:15 -0800 Subject: [PATCH] Add support for starred changes Change-Id: I2d57f0471f009e5e38d3652dbe3b0d9ac8854d7c --- .../versions/4a802b741d2f_add_starred.py | 41 +++++++++++++++++++ gertty/db.py | 16 ++++++-- gertty/keymap.py | 2 + gertty/search/parser.py | 6 ++- gertty/sync.py | 38 +++++++++++++++++ gertty/view/change.py | 21 +++++++++- 6 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 gertty/alembic/versions/4a802b741d2f_add_starred.py diff --git a/gertty/alembic/versions/4a802b741d2f_add_starred.py b/gertty/alembic/versions/4a802b741d2f_add_starred.py new file mode 100644 index 0000000..57c28b2 --- /dev/null +++ b/gertty/alembic/versions/4a802b741d2f_add_starred.py @@ -0,0 +1,41 @@ +"""add starred + +Revision ID: 4a802b741d2f +Revises: 312cd5a9f878 +Create Date: 2015-02-12 18:10:19.187733 + +""" + +# revision identifiers, used by Alembic. +revision = '4a802b741d2f' +down_revision = '312cd5a9f878' + +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('change', sa.Column('starred', sa.Boolean())) + op.add_column('change', sa.Column('pending_starred', sa.Boolean())) + + connection = op.get_bind() + change = sa.sql.table('change', + sa.sql.column('starred', sa.Boolean()), + sa.sql.column('pending_starred', sa.Boolean())) + connection.execute(change.update().values({'starred':False, + 'pending_starred':False})) + + sqlite_alter_columns('change', [ + sa.Column('starred', sa.Boolean(), index=True, nullable=False), + sa.Column('pending_starred', sa.Boolean(), index=True, nullable=False), + ]) + + +def downgrade(): + pass diff --git a/gertty/db.py b/gertty/db.py index d894de9..fc2d375 100644 --- a/gertty/db.py +++ b/gertty/db.py @@ -58,8 +58,10 @@ change_table = Table( Column('status', String(16), index=True, nullable=False), Column('hidden', Boolean, index=True, nullable=False), Column('reviewed', Boolean, index=True, nullable=False), + Column('starred', Boolean, index=True, nullable=False), Column('pending_rebase', Boolean, index=True, nullable=False), Column('pending_topic', Boolean, index=True, nullable=False), + Column('pending_starred', Boolean, index=True, nullable=False), Column('pending_status', Boolean, index=True, nullable=False), Column('pending_status_message', Text), ) @@ -180,11 +182,12 @@ class Branch(object): self.name = name class Change(object): - def __init__(self, project, id, owner, number, branch, - change_id, subject, created, updated, status, - topic=None, hidden=False, reviewed=False, + def __init__(self, project, id, owner, number, branch, change_id, + subject, created, updated, status, topic=None, + hidden=False, reviewed=False, starred=False, pending_rebase=False, pending_topic=False, - pending_status=False, pending_status_message=None): + pending_starred=False, pending_status=False, + pending_status_message=None): self.project_key = project.key self.account_key = owner.key self.id = id @@ -198,8 +201,10 @@ class Change(object): self.status = status self.hidden = hidden self.reviewed = reviewed + self.starred = starred self.pending_rebase = pending_rebase self.pending_topic = pending_topic + self.pending_starred = pending_starred self.pending_status = pending_status self.pending_status_message = pending_status_message @@ -668,6 +673,9 @@ class DatabaseSession(object): def getPendingRebases(self): return self.session().query(Change).filter_by(pending_rebase=True).all() + def getPendingStarred(self): + return self.session().query(Change).filter_by(pending_starred=True).all() + def getPendingStatusChanges(self): return self.session().query(Change).filter_by(pending_status=True).all() diff --git a/gertty/keymap.py b/gertty/keymap.py index 17fb200..a1ad47a 100644 --- a/gertty/keymap.py +++ b/gertty/keymap.py @@ -37,6 +37,7 @@ CHANGE_SEARCH = 'change search' # Change screen: TOGGLE_REVIEWED = 'toggle reviewed' TOGGLE_HIDDEN = 'toggle hidden' +TOGGLE_STARRED = 'toggle starred' REVIEW = 'review' DIFF = 'diff' LOCAL_CHECKOUT = 'local checkout' @@ -84,6 +85,7 @@ DEFAULT_KEYMAP = { TOGGLE_REVIEWED: 'v', TOGGLE_HIDDEN: 'k', + TOGGLE_STARRED: '*', REVIEW: 'r', DIFF: 'd', LOCAL_CHECKOUT: 'c', diff --git a/gertty/search/parser.py b/gertty/search/parser.py index 1dc44b7..b0ccfc0 100644 --- a/gertty/search/parser.py +++ b/gertty/search/parser.py @@ -249,7 +249,7 @@ def SearchParser(): def p_is_term(p): '''is_term : OP_IS string''' - #TODO: implement starred, watched, owner, reviewer, draft + #TODO: implement watched, draft username = p.parser.username if p[2] == 'reviewed': filters = [] @@ -269,6 +269,8 @@ def SearchParser(): p[0] = gertty.db.change_table.c.status == 'ABANDONED' elif p[2] == 'owner': p[0] = gertty.db.account_table.c.username == username + elif p[2] == 'starred': + p[0] = gertty.db.change_table.c.starred == True elif p[2] == 'reviewer': filters = [] filters.append(gertty.db.approval_table.c.change_key == gertty.db.change_table.c.key) @@ -277,7 +279,7 @@ def SearchParser(): s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters)) p[0] = gertty.db.change_table.c.key.in_(s) else: - raise gertty.search.SearchSyntaxError('Syntax error: has:%s is not supported' % p[2]) + raise gertty.search.SearchSyntaxError('Syntax error: is:%s is not supported' % p[2]) def p_status_term(p): '''status_term : OP_STATUS string''' diff --git a/gertty/sync.py b/gertty/sync.py index f036bbd..481b231 100644 --- a/gertty/sync.py +++ b/gertty/sync.py @@ -382,6 +382,10 @@ class SyncChangeTask(Task): if change.status != remote_change['status']: change.status = remote_change['status'] result.status_changed = True + if remote_change.get('starred'): + change.starred = True + else: + change.starred = False change.subject = remote_change['subject'] change.updated = dateutil.parser.parse(remote_change['updated']) change.topic = remote_change.get('topic') @@ -660,6 +664,8 @@ class UploadReviewsTask(Task): sync.submitTask(RebaseChangeTask(c.key, self.priority)) for c in session.getPendingStatusChanges(): sync.submitTask(ChangeStatusTask(c.key, self.priority)) + for c in session.getPendingStarred(): + sync.submitTask(ChangeStarredTask(c.key, self.priority)) for c in session.getPendingCherryPicks(): sync.submitTask(SendCherryPickTask(c.key, self.priority)) for r in session.getPendingCommitMessages(): @@ -703,6 +709,27 @@ class RebaseChangeTask(Task): sync.post('changes/%s/rebase' % (change.id,), {}) sync.submitTask(SyncChangeTask(change.id, priority=self.priority)) +class ChangeStarredTask(Task): + def __init__(self, change_key, priority=NORMAL_PRIORITY): + super(ChangeStarredTask, self).__init__(priority) + self.change_key = change_key + + def __repr__(self): + return '' % (self.change_key,) + + def run(self, sync): + app = sync.app + with app.db.getSession() as session: + change = session.getChange(self.change_key) + if change.starred: + sync.put('accounts/self/starred.changes/%s' % (change.id,), + data={}) + else: + sync.delete('accounts/self/starred.changes/%s' % (change.id,), + data={}) + change.pending_starred = False + sync.submitTask(SyncChangeTask(change.id, priority=self.priority)) + class ChangeStatusTask(Task): def __init__(self, change_key, priority=NORMAL_PRIORITY): super(ChangeStatusTask, self).__init__(priority) @@ -949,6 +976,17 @@ class Sync(object): 'User-Agent': self.user_agent}) self.log.debug('Received: %s' % (r.text,)) + def delete(self, path, data): + url = self.url(path) + self.log.debug('DELETE: %s' % (url,)) + self.log.debug('data: %s' % (data,)) + r = self.session.delete(url, data=json.dumps(data).encode('utf8'), + verify=self.app.config.verify_ssl, + auth=self.auth, + headers = {'Content-Type': 'application/json;charset=UTF-8', + 'User-Agent': self.user_agent}) + self.log.debug('Received: %s' % (r.text,)) + def syncSubscribedProjects(self): task = SyncSubscribedProjectsTask(LOW_PRIORITY) self.submitTask(task) diff --git a/gertty/view/change.py b/gertty/view/change.py index bd23122..e33d1e8 100644 --- a/gertty/view/change.py +++ b/gertty/view/change.py @@ -410,6 +410,8 @@ class ChangeView(urwid.WidgetWrap): "Back to the list of changes"), (key(keymap.TOGGLE_REVIEWED), "Toggle the reviewed flag for the current change"), + (key(keymap.TOGGLE_STARRED), + "Toggle the starred flag for the current change"), (key(keymap.LOCAL_CHERRY_PICK), "Cherry-pick the most recent revision onto the local repo"), (key(keymap.ABANDON_CHANGE), @@ -549,7 +551,12 @@ class ChangeView(urwid.WidgetWrap): hidden = ' (hidden)' else: hidden = '' - self.title = 'Change %s%s%s' % (change.number, reviewed, hidden) + if change.starred: + starred = '* ' + else: + starred = '' + self.title = '%sChange %s%s%s' % (starred, change.number, + reviewed, hidden) self.app.status.update(title=self.title) self.project_key = change.project.key self.change_rest_id = change.id @@ -759,6 +766,14 @@ class ChangeView(urwid.WidgetWrap): change = session.getChange(self.change_key) change.hidden = not change.hidden + def toggleStarred(self): + with self.app.db.getSession() as session: + change = session.getChange(self.change_key) + change.starred = not change.starred + change.pending_starred = True + self.app.sync.submitTask( + sync.ChangeStarredTask(self.change_key, sync.HIGH_PRIORITY)) + def keypress(self, size, key): r = super(ChangeView, self).keypress(size, key) commands = self.app.config.keymap.getCommands(r) @@ -770,6 +785,10 @@ class ChangeView(urwid.WidgetWrap): self.toggleHidden() self.refresh() return None + if keymap.TOGGLE_STARRED in commands: + self.toggleStarred() + self.refresh() + return None if keymap.REVIEW in commands: row = self.revision_rows[self.last_revision_key] row.review_button.openReview()