Save draft cover messages
DB migration to rename pending -> draft. Keep pending column on messages to mean "ready to upload". Indicate whether messages are drafts in change display. Update display of (draft) messages when their contents change. Also store draft votes, but don't display them until finalized (pending upload). Also remove 'drafts' flag from revisions if that revision has a pending upload. Change-Id: Iff427c0c1664351ce8e2d61be2f79f3bfa1f989d
This commit is contained in:
parent
fe8c123407
commit
eb2a491129
49
gertty/alembic/versions/3d429503a29a_add_draft_fields.py
Normal file
49
gertty/alembic/versions/3d429503a29a_add_draft_fields.py
Normal file
@ -0,0 +1,49 @@
|
||||
"""add draft fields
|
||||
|
||||
Revision ID: 3d429503a29a
|
||||
Revises: 2a11dd14665
|
||||
Create Date: 2014-08-30 13:26:03.698902
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3d429503a29a'
|
||||
down_revision = '2a11dd14665'
|
||||
|
||||
import warnings
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from gertty.dbsupport import sqlite_alter_columns, sqlite_drop_columns
|
||||
|
||||
def upgrade():
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
op.add_column('message', sa.Column('draft', sa.Boolean()))
|
||||
op.add_column('comment', sa.Column('draft', sa.Boolean()))
|
||||
op.add_column('approval', sa.Column('draft', sa.Boolean()))
|
||||
|
||||
conn = op.get_bind()
|
||||
res = conn.execute("update message set draft=pending")
|
||||
res = conn.execute("update comment set draft=pending")
|
||||
res = conn.execute("update approval set draft=pending")
|
||||
|
||||
sqlite_alter_columns('message', [
|
||||
sa.Column('draft', sa.Boolean(), index=True, nullable=False),
|
||||
])
|
||||
|
||||
sqlite_alter_columns('comment', [
|
||||
sa.Column('draft', sa.Boolean(), index=True, nullable=False),
|
||||
])
|
||||
|
||||
sqlite_alter_columns('approval', [
|
||||
sa.Column('draft', sa.Boolean(), index=True, nullable=False),
|
||||
])
|
||||
|
||||
sqlite_drop_columns('comment', ['pending'])
|
||||
sqlite_drop_columns('approval', ['pending'])
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
49
gertty/db.py
49
gertty/db.py
@ -72,6 +72,7 @@ message_table = Table(
|
||||
Column('id', String(255), index=True), #, unique=True, nullable=False),
|
||||
Column('created', DateTime, index=True, nullable=False),
|
||||
Column('message', Text, nullable=False),
|
||||
Column('draft', Boolean, index=True, nullable=False),
|
||||
Column('pending', Boolean, index=True, nullable=False),
|
||||
)
|
||||
comment_table = Table(
|
||||
@ -86,7 +87,7 @@ comment_table = Table(
|
||||
Column('parent', Boolean, nullable=False),
|
||||
Column('line', Integer),
|
||||
Column('message', Text, nullable=False),
|
||||
Column('pending', Boolean, index=True, nullable=False),
|
||||
Column('draft', Boolean, index=True, nullable=False),
|
||||
)
|
||||
label_table = Table(
|
||||
'label', metadata,
|
||||
@ -110,7 +111,7 @@ approval_table = Table(
|
||||
Column('account_key', Integer, ForeignKey("account.key"), index=True),
|
||||
Column('category', String(255), nullable=False),
|
||||
Column('value', Integer, nullable=False),
|
||||
Column('pending', Boolean, index=True, nullable=False),
|
||||
Column('draft', Boolean, index=True, nullable=False),
|
||||
)
|
||||
account_table = Table(
|
||||
'account', metadata,
|
||||
@ -257,17 +258,31 @@ class Revision(object):
|
||||
session.flush()
|
||||
return c
|
||||
|
||||
def getPendingMessage(self):
|
||||
for m in self.messages:
|
||||
if m.pending:
|
||||
return m
|
||||
return None
|
||||
|
||||
def getDraftMessage(self):
|
||||
for m in self.messages:
|
||||
if m.draft:
|
||||
return m
|
||||
return None
|
||||
|
||||
|
||||
class Message(object):
|
||||
def __init__(self, revision, id, author, created, message, pending=False):
|
||||
def __init__(self, revision, id, author, created, message, draft=False, pending=False):
|
||||
self.revision_key = revision.key
|
||||
self.account_key = author.key
|
||||
self.id = id
|
||||
self.created = created
|
||||
self.message = message
|
||||
self.draft = draft
|
||||
self.pending = pending
|
||||
|
||||
class Comment(object):
|
||||
def __init__(self, revision, id, author, in_reply_to, created, file, parent, line, message, pending=False):
|
||||
def __init__(self, revision, id, author, in_reply_to, created, file, parent, line, message, draft=False):
|
||||
self.revision_key = revision.key
|
||||
self.account_key = author.key
|
||||
self.id = id
|
||||
@ -277,7 +292,7 @@ class Comment(object):
|
||||
self.parent = parent
|
||||
self.line = line
|
||||
self.message = message
|
||||
self.pending = pending
|
||||
self.draft = draft
|
||||
|
||||
class Label(object):
|
||||
def __init__(self, change, category, value, description):
|
||||
@ -293,12 +308,12 @@ class PermittedLabel(object):
|
||||
self.value = value
|
||||
|
||||
class Approval(object):
|
||||
def __init__(self, change, reviewer, category, value, pending=False):
|
||||
def __init__(self, change, reviewer, category, value, draft=False):
|
||||
self.change_key = change.key
|
||||
self.account_key = reviewer.key
|
||||
self.category = category
|
||||
self.value = value
|
||||
self.pending = pending
|
||||
self.draft = draft
|
||||
|
||||
mapper(Account, account_table)
|
||||
mapper(Project, project_table, properties=dict(
|
||||
@ -333,22 +348,22 @@ mapper(Change, change_table, properties=dict(
|
||||
permitted_label_table.c.value)),
|
||||
approvals=relationship(Approval, backref='change', order_by=(approval_table.c.category,
|
||||
approval_table.c.value)),
|
||||
pending_approvals=relationship(Approval,
|
||||
primaryjoin=and_(change_table.c.key==approval_table.c.change_key,
|
||||
approval_table.c.pending==True),
|
||||
order_by=(approval_table.c.category,
|
||||
approval_table.c.value))
|
||||
draft_approvals=relationship(Approval,
|
||||
primaryjoin=and_(change_table.c.key==approval_table.c.change_key,
|
||||
approval_table.c.draft==True),
|
||||
order_by=(approval_table.c.category,
|
||||
approval_table.c.value))
|
||||
))
|
||||
mapper(Revision, revision_table, properties=dict(
|
||||
messages=relationship(Message, backref='revision'),
|
||||
comments=relationship(Comment, backref='revision',
|
||||
order_by=(comment_table.c.line,
|
||||
comment_table.c.created)),
|
||||
pending_comments=relationship(Comment,
|
||||
primaryjoin=and_(revision_table.c.key==comment_table.c.revision_key,
|
||||
comment_table.c.pending==True),
|
||||
order_by=(comment_table.c.line,
|
||||
comment_table.c.created)),
|
||||
draft_comments=relationship(Comment,
|
||||
primaryjoin=and_(revision_table.c.key==comment_table.c.revision_key,
|
||||
comment_table.c.draft==True),
|
||||
order_by=(comment_table.c.line,
|
||||
comment_table.c.created)),
|
||||
))
|
||||
mapper(Message, message_table, properties=dict(
|
||||
author=relationship(Account)))
|
||||
|
@ -56,6 +56,7 @@ DEFAULT_PALETTE={
|
||||
'focused-revision-drafts': ['dark red,standout', ''],
|
||||
'change-message-name': ['yellow', ''],
|
||||
'change-message-header': ['brown', ''],
|
||||
'change-message-draft': ['dark red', ''],
|
||||
'revision-button': ['dark magenta', ''],
|
||||
'focused-revision-button': ['light magenta', ''],
|
||||
'lines-added': ['light green', ''],
|
||||
|
@ -236,7 +236,7 @@ def SearchParser():
|
||||
filters = []
|
||||
filters.append(gertty.db.revision_table.c.change_key == gertty.db.change_table.c.key)
|
||||
filters.append(gertty.db.message_table.c.revision_key == gertty.db.revision_table.c.key)
|
||||
filters.append(gertty.db.message_table.c.pending == True)
|
||||
filters.append(gertty.db.message_table.c.draft == True)
|
||||
s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters))
|
||||
p[0] = gertty.db.change_table.c.key.in_(s)
|
||||
else:
|
||||
|
@ -540,14 +540,14 @@ class UploadReviewTask(Task):
|
||||
strict_labels=False)
|
||||
if revision == current_revision:
|
||||
data['labels'] = {}
|
||||
for approval in change.pending_approvals:
|
||||
for approval in change.draft_approvals:
|
||||
data['labels'][approval.category] = approval.value
|
||||
session.delete(approval)
|
||||
if revision.pending_comments:
|
||||
if revision.draft_comments:
|
||||
data['comments'] = {}
|
||||
last_file = None
|
||||
comment_list = []
|
||||
for comment in revision.pending_comments:
|
||||
for comment in revision.draft_comments:
|
||||
if comment.file != last_file:
|
||||
last_file = comment.file
|
||||
comment_list = []
|
||||
|
@ -53,14 +53,14 @@ class ReviewDialog(urwid.WidgetWrap):
|
||||
categories.append(label.category)
|
||||
values[label.category] = []
|
||||
values[label.category].append(label.value)
|
||||
pending_approvals = {}
|
||||
for approval in change.pending_approvals:
|
||||
pending_approvals[approval.category] = approval
|
||||
draft_approvals = {}
|
||||
for approval in change.draft_approvals:
|
||||
draft_approvals[approval.category] = approval
|
||||
for category in categories:
|
||||
rows.append(urwid.Text(category))
|
||||
group = []
|
||||
self.button_groups[category] = group
|
||||
current = pending_approvals.get(category)
|
||||
current = draft_approvals.get(category)
|
||||
if current is None:
|
||||
current = 0
|
||||
else:
|
||||
@ -75,10 +75,11 @@ class ReviewDialog(urwid.WidgetWrap):
|
||||
b = urwid.RadioButton(group, strvalue, state=(value == current))
|
||||
rows.append(b)
|
||||
rows.append(urwid.Divider())
|
||||
for m in revision.messages:
|
||||
if m.pending:
|
||||
message = m.message
|
||||
break
|
||||
m = revision.getPendingMessage()
|
||||
if not m:
|
||||
m = revision.getDraftMessage()
|
||||
if m:
|
||||
message = m.message
|
||||
self.message = urwid.Edit("Message: \n", edit_text=message, multiline=True)
|
||||
rows.append(self.message)
|
||||
rows.append(urwid.Divider())
|
||||
@ -87,14 +88,15 @@ class ReviewDialog(urwid.WidgetWrap):
|
||||
fill = urwid.Filler(pile, valign='top')
|
||||
super(ReviewDialog, self).__init__(urwid.LineBox(fill, 'Review'))
|
||||
|
||||
def save(self):
|
||||
def save(self, upload=False):
|
||||
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)
|
||||
self.change_view.saveReview(self.revision_row.revision_key, approvals,
|
||||
message, upload)
|
||||
|
||||
def keypress(self, size, key):
|
||||
r = super(ReviewDialog, self).keypress(size, key)
|
||||
@ -122,9 +124,8 @@ class ReviewButton(mywid.FixedButton):
|
||||
relative_width=50, relative_height=75,
|
||||
min_width=60, min_height=20)
|
||||
|
||||
def closeReview(self, save):
|
||||
if save:
|
||||
message_key = self.dialog.save()
|
||||
def closeReview(self, upload):
|
||||
self.dialog.save(upload)
|
||||
self.change_view.app.backScreen()
|
||||
|
||||
class RevisionRow(urwid.WidgetWrap):
|
||||
@ -192,10 +193,12 @@ class RevisionRow(urwid.WidgetWrap):
|
||||
def update(self, revision):
|
||||
line = [('revision-name', 'Patch Set %s ' % revision.number),
|
||||
('revision-commit', revision.commit)]
|
||||
num_drafts = len(revision.pending_comments)
|
||||
num_drafts = len(revision.draft_comments)
|
||||
if num_drafts:
|
||||
line.append(('revision-drafts', ' (%s draft%s)' % (
|
||||
num_drafts, num_drafts>1 and 's' or '')))
|
||||
pending_message = revision.getPendingMessage()
|
||||
if not pending_message:
|
||||
line.append(('revision-drafts', ' (%s draft%s)' % (
|
||||
num_drafts, num_drafts>1 and 's' or '')))
|
||||
num_comments = len(revision.comments) - num_drafts
|
||||
if num_comments:
|
||||
line.append(('revision-comments', ' (%s inline comment%s)' % (
|
||||
@ -260,16 +263,26 @@ class ChangeButton(mywid.FixedButton):
|
||||
class ChangeMessageBox(mywid.HyperText):
|
||||
def __init__(self, app, message):
|
||||
super(ChangeMessageBox, self).__init__(u'')
|
||||
self.app = app
|
||||
self.refresh(message)
|
||||
|
||||
def refresh(self, message):
|
||||
self.message_created = message.created
|
||||
lines = message.message.split('\n')
|
||||
if message.draft:
|
||||
lines.insert(0, '')
|
||||
lines.insert(0, 'Patch Set %s:' % (message.revision.number,))
|
||||
text = [('change-message-name', message.author.name),
|
||||
('change-message-header', ': '+lines.pop(0)),
|
||||
('change-message-header',
|
||||
message.created.strftime(' (%Y-%m-%d %H:%M:%S%z)'))]
|
||||
if message.draft and not message.pending:
|
||||
text.append(('change-message-draft', ' (draft)'))
|
||||
if lines and lines[-1]:
|
||||
lines.append('')
|
||||
comment_text = ['\n'.join(lines)]
|
||||
for commentlink in app.config.commentlinks:
|
||||
comment_text = commentlink.run(app, comment_text)
|
||||
for commentlink in self.app.config.commentlinks:
|
||||
comment_text = commentlink.run(self.app, comment_text)
|
||||
self.set_text(text+comment_text)
|
||||
|
||||
class CommitMessageBox(mywid.HyperText):
|
||||
@ -443,7 +456,11 @@ class ChangeView(urwid.WidgetWrap):
|
||||
categories.append(label.category)
|
||||
votes = mywid.Table(approval_headers)
|
||||
approvals_for_name = {}
|
||||
pending_message = change.revisions[-1].getPendingMessage()
|
||||
for approval in change.approvals:
|
||||
# Don't display draft approvals unless they are pending-upload
|
||||
if approval.draft and not pending_message:
|
||||
continue
|
||||
approvals = approvals_for_name.get(approval.reviewer.name)
|
||||
if not approvals:
|
||||
approvals = {}
|
||||
@ -529,6 +546,8 @@ class ChangeView(urwid.WidgetWrap):
|
||||
self.message_rows[message.key] = row
|
||||
else:
|
||||
unseen_keys.remove(message.key)
|
||||
if message.created != row.original_widget.message_created:
|
||||
row.original_widget.refresh(message)
|
||||
listbox_index += 1
|
||||
# Remove any messages that should not be displayed
|
||||
for key in unseen_keys:
|
||||
@ -692,40 +711,45 @@ 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, '')
|
||||
self.saveReview(row.revision_key, approvals, '', True)
|
||||
|
||||
def saveReview(self, revision_key, approvals, message):
|
||||
def saveReview(self, revision_key, approvals, message, upload):
|
||||
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
|
||||
pending_approvals = {}
|
||||
for approval in change.pending_approvals:
|
||||
pending_approvals[approval.category] = approval
|
||||
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 = pending_approvals.get(category)
|
||||
approval = draft_approvals.get(category)
|
||||
if not approval:
|
||||
approval = change.createApproval(account, category, 0, pending=True)
|
||||
pending_approvals[category] = approval
|
||||
approval = change.createApproval(account, category, 0, draft=True)
|
||||
draft_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, account,
|
||||
datetime.datetime.utcnow(),
|
||||
'', 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))
|
||||
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
|
||||
# Outside of db session
|
||||
if upload:
|
||||
self.app.sync.submitTask(
|
||||
sync.UploadReviewTask(message_key, sync.HIGH_PRIORITY))
|
||||
self.refresh()
|
||||
|
@ -197,12 +197,12 @@ class BaseDiffView(urwid.WidgetWrap):
|
||||
key = 'old'
|
||||
else:
|
||||
key = 'new'
|
||||
if comment.pending:
|
||||
if comment.draft:
|
||||
key += 'draft'
|
||||
key += '-' + str(comment.line)
|
||||
key += '-' + str(comment.file)
|
||||
comment_list = comment_lists.get(key, [])
|
||||
if comment.pending:
|
||||
if comment.draft:
|
||||
message = comment.message
|
||||
else:
|
||||
message = [('comment-name', comment.author.name),
|
||||
@ -214,12 +214,12 @@ class BaseDiffView(urwid.WidgetWrap):
|
||||
if comment.parent:
|
||||
continue
|
||||
key = 'old'
|
||||
if comment.pending:
|
||||
if comment.draft:
|
||||
key += 'draft'
|
||||
key += '-' + str(comment.line)
|
||||
key += '-' + str(comment.file)
|
||||
comment_list = comment_lists.get(key, [])
|
||||
if comment.pending:
|
||||
if comment.draft:
|
||||
message = comment.message
|
||||
else:
|
||||
message = [('comment-name', comment.author.name),
|
||||
@ -404,7 +404,7 @@ class BaseDiffView(urwid.WidgetWrap):
|
||||
comment = revision.createComment(None, account, None,
|
||||
datetime.datetime.utcnow(),
|
||||
filename, parent,
|
||||
line_num, text, pending=True)
|
||||
line_num, text, draft=True)
|
||||
key = comment.key
|
||||
return key
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user