diff --git a/gertty/alembic/versions/a18731009699_add_server_table.py b/gertty/alembic/versions/a18731009699_add_server_table.py new file mode 100644 index 0000000..7a5d920 --- /dev/null +++ b/gertty/alembic/versions/a18731009699_add_server_table.py @@ -0,0 +1,26 @@ +"""add_server_table + +Revision ID: a18731009699 +Revises: 399c4b3dcc9a +Create Date: 2019-08-28 14:12:22.657691 + +""" + +# revision identifiers, used by Alembic. +revision = 'a18731009699' +down_revision = '399c4b3dcc9a' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.create_table('server', + sa.Column('key', sa.Integer(), nullable=False), + sa.Column('own_account_key', sa.Integer(), sa.ForeignKey('own_account.key'), index=True), + sa.PrimaryKeyConstraint('key') + ) + + +def downgrade(): + pass diff --git a/gertty/app.py b/gertty/app.py index 19e186e..e430a81 100644 --- a/gertty/app.py +++ b/gertty/app.py @@ -289,8 +289,15 @@ class App(object): self.fetch_missing_refs = fetch_missing_refs self.config.keymap.updateCommandMap() - self.search = search.SearchCompiler(self.config.username) + self.search = search.SearchCompiler(self.getOwnAccountId) self.db = db.Database(self, self.config.dburi, self.search) + + self.own_account_id = None + with self.db.getSession() as session: + account = session.getOwnAccount() + if account: + self.own_account_id = account.id + self.sync = sync.Sync(self, disable_background_sync) self.status = StatusHeader(self) @@ -342,6 +349,12 @@ class App(object): self.sync.offline = True self.status.update(offline=True) + def getOwnAccountId(self): + return self.own_account_id + + def isOwnAccount(self, account): + return account.id == self.own_account_id + def run(self): try: self.loop.run() @@ -801,8 +814,7 @@ class App(object): def saveReviews(self, revision_keys, approvals, message, upload, submit): message_keys = [] with self.db.getSession() as session: - account = session.getAccountByUsername(self.config.username) - account = session.getAccountByID(self.sync.account_id) + account = session.getOwnAccount() for revision_key in revision_keys: k = self._saveReview(session, account, revision_key, approvals, message, upload, submit) diff --git a/gertty/db.py b/gertty/db.py index 7f05eeb..e39a050 100644 --- a/gertty/db.py +++ b/gertty/db.py @@ -192,6 +192,11 @@ file_table = Table( Column('deleted', Integer), Column('status', String(1), nullable=False), ) +server_table = Table( + 'server', metadata, + Column('key', Integer, primary_key=True), + Column('own_account_key', Integer, ForeignKey("account.key"), index=True), + ) class Account(object): @@ -644,6 +649,10 @@ class File(object): session.flush() return c +class Server(object): + def __init__(self): + pass + mapper(Account, account_table) mapper(Project, project_table, properties=dict( @@ -750,6 +759,9 @@ mapper(Approval, approval_table, properties=dict( mapper(PendingCherryPick, pending_cherry_pick_table) mapper(SyncQuery, sync_query_table) mapper(Hashtag, hashtag_table) +mapper(Server, server_table, properties=dict( + own_account=relationship(Account) + )) def match(expr, item): if item is None: @@ -763,6 +775,7 @@ def add_sqlite_match(dbapi_connection, connection_record): class Database(object): def __init__(self, app, dburi, search): self.log = logging.getLogger('gertty.db') + self.own_account_key = None self.dburi = dburi self.search = search self.engine = create_engine(self.dburi) @@ -1057,6 +1070,28 @@ class DatabaseSession(object): def getSystemAccount(self): return self.getAccountByID(0, 'Gerrit Code Review') + def setOwnAccount(self, account): + try: + server = self.session().query(Server).one() + except sqlalchemy.orm.exc.NoResultFound: + server = Server() + self.session().add(server) + self.session().flush() + server.own_account = account + self.database.own_account_key = account.key + + def getOwnAccount(self): + if self.database.own_account_key is None: + try: + server = self.session().query(Server).one() + except sqlalchemy.orm.exc.NoResultFound: + return None + self.database.own_account_key = server.own_account.key + try: + return self.session().query(Account).filter_by(key=self.database.own_account_key).one() + except sqlalchemy.orm.exc.NoResultFound: + return None + def createProject(self, *args, **kw): o = Project(*args, **kw) self.session().add(o) diff --git a/gertty/search/__init__.py b/gertty/search/__init__.py index 8a93a6f..83a7185 100644 --- a/gertty/search/__init__.py +++ b/gertty/search/__init__.py @@ -25,10 +25,11 @@ class SearchSyntaxError(Exception): class SearchCompiler(object): - def __init__(self, username): - self.username = username + def __init__(self, get_account_id): + self.get_account_id = get_account_id self.lexer = tokenizer.SearchTokenizer() self.parser = parser.SearchParser() + self.parser.account_id = None def findTables(self, expression): tables = set() @@ -45,7 +46,10 @@ class SearchCompiler(object): return tables def parse(self, data): - self.parser.username = self.username + if self.parser.account_id is None: + self.parser.account_id = self.get_account_id() + if self.parser.account_id is None: + raise Exception("Own account is unknown") result = self.parser.parse(data, lexer=self.lexer) tables = self.findTables(result) if gertty.db.project_table in tables: diff --git a/gertty/search/parser.py b/gertty/search/parser.py index f60421f..9186290 100644 --- a/gertty/search/parser.py +++ b/gertty/search/parser.py @@ -136,8 +136,8 @@ def SearchParser(): def p_owner_term(p): '''owner_term : OP_OWNER string''' if p[2] == 'self': - username = p.parser.username - p[0] = gertty.db.account_table.c.username == username + account_id = p.parser.account_id + p[0] = gertty.db.account_table.c.id == account_id else: p[0] = or_(gertty.db.account_table.c.username == p[2], gertty.db.account_table.c.email == p[2], @@ -156,8 +156,8 @@ def SearchParser(): if number is not None: filters.append(gertty.db.account_table.c.id == number) elif p[2] == 'self': - username = p.parser.username - filters.append(gertty.db.account_table.c.username == username) + account_id = p.parser.account_id + filters.append(gertty.db.account_table.c.id == account_id) else: filters.append(or_(gertty.db.account_table.c.username == p[2], gertty.db.account_table.c.email == p[2], @@ -234,7 +234,7 @@ def SearchParser(): if user is not None: filters.append(gertty.db.approval_table.c.account_key == gertty.db.account_table.c.key) if user == 'self': - filters.append(gertty.db.account_table.c.username == p.parser.username) + filters.append(gertty.db.account_table.c.id == p.parser.account_id) else: filters.append( or_(gertty.db.account_table.c.username == user, @@ -281,7 +281,7 @@ def SearchParser(): def p_is_term(p): '''is_term : OP_IS string''' #TODO: implement draft - username = p.parser.username + account_id = p.parser.account_id if p[2] == 'reviewed': filters = [] filters.append(gertty.db.approval_table.c.change_key == gertty.db.change_table.c.key) @@ -299,7 +299,7 @@ def SearchParser(): elif p[2] == 'abandoned': p[0] = gertty.db.change_table.c.status == 'ABANDONED' elif p[2] == 'owner': - p[0] = gertty.db.account_table.c.username == username + p[0] = gertty.db.account_table.c.id == account_id elif p[2] == 'starred': p[0] = gertty.db.change_table.c.starred == True elif p[2] == 'held': @@ -309,7 +309,7 @@ def SearchParser(): filters = [] filters.append(gertty.db.approval_table.c.change_key == gertty.db.change_table.c.key) filters.append(gertty.db.approval_table.c.account_key == gertty.db.account_table.c.key) - filters.append(gertty.db.account_table.c.username == username) + filters.append(gertty.db.account_table.c.id == account_id) s = select([gertty.db.change_table.c.key], correlate=False).where(and_(*filters)) p[0] = gertty.db.change_table.c.key.in_(s) elif p[2] == 'watched': diff --git a/gertty/sync.py b/gertty/sync.py index 4200bae..fd6bace 100644 --- a/gertty/sync.py +++ b/gertty/sync.py @@ -199,10 +199,12 @@ class SyncOwnAccountTask(Task): remote = sync.get('accounts/self') sync.account_id = remote['_account_id'] with app.db.getSession() as session: - session.getAccountByID(remote['_account_id'], - remote.get('name'), - remote.get('username'), - remote.get('email')) + account = session.getAccountByID(remote['_account_id'], + remote.get('name'), + remote.get('username'), + remote.get('email')) + session.setOwnAccount(account) + app.own_account_id = remote['_account_id'] class GetVersionTask(Task): def __repr__(self): diff --git a/gertty/view/change.py b/gertty/view/change.py index 017c63d..a1dca9d 100644 --- a/gertty/view/change.py +++ b/gertty/view/change.py @@ -172,7 +172,7 @@ class ReviewDialog(urwid.WidgetWrap, mywid.LineBoxTitlePropertyMixin): draft_approvals = {} prior_approvals = {} for approval in change.approvals: - if approval.reviewer.username == self.app.config.username: + if self.app.isOwnAccount(approval.reviewer): if approval.draft: draft_approvals[approval.category] = approval else: @@ -440,7 +440,7 @@ class ChangeMessageBox(mywid.HyperText): if message.draft: lines.insert(0, '') lines.insert(0, 'Patch Set %s:' % (message.revision.number,)) - if message.author.username == self.app.config.username: + if self.app.isOwnAccount(message.author): name_style = 'change-message-own-name' header_style = 'change-message-own-header' reviewer_string = message.author_name @@ -748,7 +748,7 @@ class ChangeView(urwid.WidgetWrap): if not approvals: approvals = {} row = [] - if approval.reviewer.username == self.app.config.username: + if self.app.isOwnAccount(approval.reviewer): style = 'reviewer-own-name' else: style = 'reviewer-name' diff --git a/gertty/view/diff.py b/gertty/view/diff.py index 62b9f20..88fd256 100644 --- a/gertty/view/diff.py +++ b/gertty/view/diff.py @@ -530,7 +530,7 @@ class BaseDiffView(urwid.WidgetWrap, mywid.Searchable): raise Exception("Comment is not associated with a file") with self.app.db.getSession() as session: fileobj = session.getFile(file_key) - account = session.getAccountByUsername(self.app.config.username) + account = session.getOwnAccount() comment = fileobj.createComment(None, account, None, datetime.datetime.utcnow(), parent,