From 4a18f6a6f812ed8d0f263dfeac14254b6c39e0bc Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Sun, 7 Feb 2016 08:19:42 -0800 Subject: [PATCH] Add support for last_seen Change-Id: I01f2735b9d72d41b99427a39dbb1e31246a1c531 --- examples/googlesource-gertty.yaml | 14 ++++++ examples/openstack-gertty.yaml | 11 +++++ examples/reference-gertty.yaml | 14 ++++++ ...02b7f58e_add_last_seen_column_to_change.py | 26 +++++++++++ gertty/db.py | 1 + gertty/search/__init__.py | 2 +- gertty/search/parser.py | 44 +++++++++++++------ gertty/search/tokenizer.py | 1 + gertty/view/change.py | 6 +++ 9 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 gertty/alembic/versions/37a702b7f58e_add_last_seen_column_to_change.py diff --git a/examples/googlesource-gertty.yaml b/examples/googlesource-gertty.yaml index d85dbe7..1f5da9a 100644 --- a/examples/googlesource-gertty.yaml +++ b/examples/googlesource-gertty.yaml @@ -41,10 +41,24 @@ commentlinks: # the global help text, and pressing the key anywhere in Gertty will # discard the current display stack and replace it with the results of # the query. +# +# NB: "recentlyseen:24 hours" does not just return changes seen in the +# last 24 hours -- it returns changes seen within 24 hours of the most +# recently seen change. So you can take the weekend off and pick up +# where you were. dashboards: - name: "My changes" query: "owner:self status:open" key: "f2" + - name: "Incoming reviews" + query: "is:open is:reviewer" + key: "f3" + - name: "Starred changes" + query: "is:starred" + key: "f4" + - name: "Recently seen changes" + query: "recentlyseen:24 hours" + key: "f5" # Reviewkeys are hotkeys that perform immediate reviews within the # change screen. Any pending comments or review messages will be diff --git a/examples/openstack-gertty.yaml b/examples/openstack-gertty.yaml index 42e3d96..444319b 100644 --- a/examples/openstack-gertty.yaml +++ b/examples/openstack-gertty.yaml @@ -84,6 +84,11 @@ hide-comments: # the global help text, and pressing the key anywhere in Gertty will # discard the current display stack and replace it with the results of # the query. +# +# NB: "recentlyseen:24 hours" does not just return changes seen in the +# last 24 hours -- it returns changes seen within 24 hours of the most +# recently seen change. So you can take the weekend off and pick up +# where you were. dashboards: - name: "My changes" query: "owner:self status:open" @@ -91,6 +96,12 @@ dashboards: - name: "Incoming reviews" query: "is:open is:reviewer" key: "f3" + - name: "Starred changes" + query: "is:starred" + key: "f4" + - name: "Recently seen changes" + query: "recentlyseen:24 hours" + key: "f5" # Reviewkeys are hotkeys that perform immediate reviews within the # change screen. Any pending comments or review messages will be diff --git a/examples/reference-gertty.yaml b/examples/reference-gertty.yaml index 089e270..9149bfa 100644 --- a/examples/reference-gertty.yaml +++ b/examples/reference-gertty.yaml @@ -190,10 +190,24 @@ commentlinks: # the global help text, and pressing the key anywhere in Gertty will # discard the current display stack and replace it with the results of # the query. +# +# NB: "recentlyseen:24 hours" does not just return changes seen in the +# last 24 hours -- it returns changes seen within 24 hours of the most +# recently seen change. So you can take the weekend off and pick up +# where you were. dashboards: - name: "My changes" query: "owner:self status:open" key: "f2" + - name: "Incoming reviews" + query: "is:open is:reviewer" + key: "f3" + - name: "Starred changes" + query: "is:starred" + key: "f4" + - name: "Recently seen changes" + query: "recentlyseen:24 hours" + key: "f5" # Reviewkeys are hotkeys that perform immediate reviews within the # change screen. Any pending comments or review messages will be diff --git a/gertty/alembic/versions/37a702b7f58e_add_last_seen_column_to_change.py b/gertty/alembic/versions/37a702b7f58e_add_last_seen_column_to_change.py new file mode 100644 index 0000000..740ab21 --- /dev/null +++ b/gertty/alembic/versions/37a702b7f58e_add_last_seen_column_to_change.py @@ -0,0 +1,26 @@ +"""add last_seen column to change + +Revision ID: 37a702b7f58e +Revises: 3610c2543e07 +Create Date: 2016-02-06 09:09:38.728225 + +""" + +# revision identifiers, used by Alembic. +revision = '37a702b7f58e' +down_revision = '3610c2543e07' + +import warnings + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + op.add_column('change', sa.Column('last_seen', sa.DateTime, index=True)) + + +def downgrade(): + pass diff --git a/gertty/db.py b/gertty/db.py index c16454a..43e1f9a 100644 --- a/gertty/db.py +++ b/gertty/db.py @@ -81,6 +81,7 @@ change_table = Table( Column('pending_starred', Boolean, index=True, nullable=False), Column('pending_status', Boolean, index=True, nullable=False), Column('pending_status_message', Text), + Column('last_seen', DateTime, index=True), ) change_conflict_table = Table( 'change_conflict', metadata, diff --git a/gertty/search/__init__.py b/gertty/search/__init__.py index 6a689b5..8babda9 100644 --- a/gertty/search/__init__.py +++ b/gertty/search/__init__.py @@ -67,7 +67,7 @@ class SearchCompiler(object): if __name__ == '__main__': class Dummy(object): pass - query = 'status:open limit:50 age:2months' + query = 'recentlyseen:24 hours' lexer = tokenizer.SearchTokenizer() lexer.input(query) while True: diff --git a/gertty/search/parser.py b/gertty/search/parser.py index a4d315a..a18d488 100644 --- a/gertty/search/parser.py +++ b/gertty/search/parser.py @@ -22,6 +22,23 @@ import gertty.db import gertty.search from gertty.search.tokenizer import tokens # NOQA +def age_to_delta(delta, unit): + if unit in ['seconds', 'second', 'sec', 's']: + pass + elif unit in ['minutes', 'minute', 'min', 'm']: + delta = delta * 60 + elif unit in ['hours', 'hour', 'hr', 'h']: + delta = delta * 60 * 60 + elif unit in ['days', 'day', 'd']: + delta = delta * 60 * 60 * 24 + elif unit in ['weeks', 'week', 'w']: + delta = delta * 60 * 60 * 24 * 7 + elif unit in ['months', 'month', 'mon']: + delta = delta * 60 * 60 * 24 * 30 + elif unit in ['years', 'year', 'y']: + delta = delta * 60 * 60 * 24 * 365 + return delta + def SearchParser(): precedence = ( # NOQA ('left', 'NOT', 'NEG'), @@ -60,6 +77,7 @@ def SearchParser(): def p_term(p): '''term : age_term + | recentlyseen_term | change_term | owner_term | reviewer_term @@ -91,22 +109,20 @@ def SearchParser(): now = datetime.datetime.utcnow() delta = p[2] unit = p[3] - if unit in ['seconds', 'second', 'sec', 's']: - pass - elif unit in ['minutes', 'minute', 'min', 'm']: - delta = delta * 60 - elif unit in ['hours', 'hour', 'hr', 'h']: - delta = delta * 60 * 60 - elif unit in ['days', 'day', 'd']: - delta = delta * 60 * 60 * 24 - elif unit in ['weeks', 'week', 'w']: - delta = delta * 60 * 60 * 24 * 7 - elif unit in ['months', 'month', 'mon']: - delta = delta * 60 * 60 * 24 * 30 - elif unit in ['years', 'year', 'y']: - delta = delta * 60 * 60 * 24 * 365 + delta = age_to_delta(delta, unit) p[0] = gertty.db.change_table.c.updated < (now-datetime.timedelta(seconds=delta)) + def p_recentlyseen_term(p): + '''recentlyseen_term : OP_RECENTLYSEEN NUMBER string''' + # A gertty extension + now = datetime.datetime.utcnow() + delta = p[2] + unit = p[3] + delta = age_to_delta(delta, unit) + s = select([func.datetime(func.max(gertty.db.change_table.c.last_seen), '-%s seconds' % delta)], + correlate=False) + p[0] = gertty.db.change_table.c.last_seen >= s + def p_change_term(p): '''change_term : OP_CHANGE CHANGE_ID | OP_CHANGE NUMBER''' diff --git a/gertty/search/tokenizer.py b/gertty/search/tokenizer.py index c9aaa3c..a4e0cb1 100644 --- a/gertty/search/tokenizer.py +++ b/gertty/search/tokenizer.py @@ -17,6 +17,7 @@ import six operators = { 'age': 'OP_AGE', + 'recentlyseen': 'OP_RECENTLYSEEN', # Gertty extension 'change': 'OP_CHANGE', 'owner': 'OP_OWNER', #'OP_OWNERIN', # needs local group membership diff --git a/gertty/view/change.py b/gertty/view/change.py index b9212af..330f15b 100644 --- a/gertty/view/change.py +++ b/gertty/view/change.py @@ -445,6 +445,7 @@ class ChangeView(urwid.WidgetWrap): self.message_rows = {} self.last_revision_key = None self.hide_comments = True + self.marked_seen = False self.change_id_label = mywid.TextButton(u'', on_press=self.searchChangeId) self.owner_label = mywid.TextButton(u'', on_press=self.searchOwner) self.project_label = mywid.TextButton(u'', on_press=self.searchProject) @@ -550,6 +551,11 @@ class ChangeView(urwid.WidgetWrap): def refresh(self): with self.app.db.getSession() as session: change = session.getChange(self.change_key) + # When we first open the change, update its last_seen + # time. + if not self.marked_seen: + change.last_seen = datetime.datetime.utcnow() + self.marked_seen = True self.topic = change.topic or '' self.pending_status_message = change.pending_status_message or '' reviewed = hidden = starred = held = ''