Make the open change dialog a search

The "Open change" dialog now supports the full search syntax (which
is still just a placeholder).  It is also slightly more robust
about dealing with non-local changes when offline.  The same checks
are now applied to inter-change links.

Change-Id: I3edf6dd66dc95b60eb7507ece813376f08815f80
This commit is contained in:
James E. Blair 2014-07-12 18:51:14 -07:00
parent 141ef95aa9
commit 37cfa0b9bb
4 changed files with 81 additions and 48 deletions

View File

@ -27,6 +27,7 @@ from gertty import config
from gertty import gitrepo
from gertty import mywid
from gertty import sync
from gertty.view import change_list as view_change_list
from gertty.view import project_list as view_project_list
from gertty.view import change as view_change
import gertty.view
@ -70,25 +71,25 @@ class StatusHeader(urwid.WidgetWrap):
self.error.set_text(u'')
self.sync.set_text(u' Sync: %i' % self.app.sync.queue.qsize())
class OpenChangeDialog(mywid.ButtonDialog):
signals = ['open', 'cancel']
class SearchDialog(mywid.ButtonDialog):
signals = ['search', 'cancel']
def __init__(self):
open_button = mywid.FixedButton('Open')
search_button = mywid.FixedButton('Search')
cancel_button = mywid.FixedButton('Cancel')
urwid.connect_signal(open_button, 'click',
lambda button:self._emit('open'))
urwid.connect_signal(search_button, 'click',
lambda button:self._emit('search'))
urwid.connect_signal(cancel_button, 'click',
lambda button:self._emit('cancel'))
super(OpenChangeDialog, self).__init__("Open Change",
"Enter a change number to open that change.",
entry_prompt="Number: ",
buttons=[open_button,
cancel_button])
super(SearchDialog, self).__init__("Search",
"Enter a change number or search string.",
entry_prompt="Search: ",
buttons=[search_button,
cancel_button])
def keypress(self, size, key):
r = super(OpenChangeDialog, self).keypress(size, key)
r = super(SearchDialog, self).keypress(size, key)
if r == 'enter':
self._emit('open')
self._emit('search')
return None
return r
@ -193,8 +194,48 @@ class App(object):
lambda button: self.backScreen())
self.popup(dialog, min_width=76, min_height=len(lines)+4)
def _syncOneChangeFromQuery(self, query):
number = changeid = None
if query.startswith("number:"):
number = query.split(':')[1].strip()
try:
number = int(number)
except Exception:
pass
if query.startswith("changeid:"):
changeid = query.split(':')[1].strip()
if not (number or changeid):
return
with self.db.getSession() as session:
if number:
change = session.getChangeByNumber(number)
elif changeid:
change = session.getChangeByChangeID(changeid)
change_key = change and change.key or None
if change_key is None:
if self.sync.offline:
raise Exception('Can not sync change while offline.')
task = sync.SyncChangeByNumberTask(number, sync.HIGH_PRIORITY)
self.sync.submitTask(task)
succeeded = task.wait(300)
if not succeeded:
raise Exception('Unable to find change.')
for subtask in task.tasks:
succeeded = task.wait(300)
if not succeeded:
raise Exception('Unable to sync change.')
with self.db.getSession() as session:
change = session.getChangeByNumber(number)
change_key = change and change.key or None
if change_key is None:
raise Exception('Change is not in local database.')
def search(self, query):
self.log.debug("Search query: %s" % query)
try:
self._syncOneChangeFromQuery(query)
except Exception as e:
return self.error(e.message)
with self.db.getSession() as session:
changes = session.getChanges(query)
change_key = None
@ -209,46 +250,22 @@ class App(object):
except gertty.view.DisplayError as e:
self.error(e.message)
def openChange(self):
dialog = OpenChangeDialog()
def searchDialog(self):
dialog = SearchDialog()
urwid.connect_signal(dialog, 'cancel',
lambda button: self.backScreen())
urwid.connect_signal(dialog, 'open',
lambda button: self._openChange(dialog))
urwid.connect_signal(dialog, 'search',
lambda button: self._searchDialog(dialog))
self.popup(dialog, min_width=76, min_height=8)
def _openChange(self, open_change_dialog):
def _searchDialog(self, dialog):
self.backScreen()
number = open_change_dialog.entry.edit_text
query = dialog.entry.edit_text
try:
number = int(number)
query = 'number:%s' % int(query)
except Exception:
return self.error('Change number must be an integer.')
with self.db.getSession() as session:
change = session.getChangeByNumber(number)
change_key = change and change.key or None
if change_key is None:
if self.sync.offline:
return self.error('Can not sync change while offline.')
task = sync.SyncChangeByNumberTask(number, sync.HIGH_PRIORITY)
self.sync.submitTask(task)
succeeded = task.wait(300)
if not succeeded:
return self.error('Unable to find change.')
for subtask in task.tasks:
succeeded = task.wait(300)
if not succeeded:
return self.error('Unable to sync change.')
with self.db.getSession() as session:
change = session.getChangeByNumber(number)
change_key = change and change.key or None
if change_key is None:
return self.error('Change is not in local database.')
try:
view = view_change.ChangeView(self, change_key)
self.changeScreen(view)
except gertty.view.DisplayError as e:
self.error(e.message)
pass
self.search(query)
def error(self, message):
dialog = mywid.MessageDialog('Error', message)
@ -265,7 +282,7 @@ class App(object):
elif key == 'ctrl q':
self.quit()
elif key == 'ctrl o':
self.openChange()
self.searchDialog()
def getRepo(self, project_name):
local_path = os.path.join(self.config.git_root, project_name)

View File

@ -430,6 +430,12 @@ class DatabaseSession(object):
except sqlalchemy.orm.exc.NoResultFound:
return None
def getChangeByChangeID(self, change_id):
try:
return self.session().query(Change).filter_by(change_id=change_id).one()
except sqlalchemy.orm.exc.NoResultFound:
return None
def getChangeByNumber(self, number):
try:
return self.session().query(Change).filter_by(number=number).one()
@ -441,7 +447,9 @@ class DatabaseSession(object):
q = self.session().query(Change)
for term in query.split():
key, data = term.split(':')
if key == 'changeid':
if key == 'number':
q = q.filter(change_table.c.number==data)
elif key == 'changeid':
q = q.filter(change_table.c.change_id==data)
elif key == 'project_key':
q = q.filter(change_table.c.project_key==data)

View File

@ -20,7 +20,7 @@ Global Keys
<F1> or <?> Help
<ESC> Back to previous screen
<CTRL-Q> Quit Gertty
<CTRL-O> Open Change
<CTRL-O> Search for changes
"""
class TextButton(urwid.Button):
@ -120,6 +120,7 @@ class HyperText(urwid.Text):
self._mouse_press_item = None
self.selectable_items = []
self.focused_index = None
self.last_focused_index = None
super(HyperText, self).__init__(markup, align, wrap, layout)
def focusFirstItem(self):
@ -137,6 +138,8 @@ class HyperText(urwid.Text):
def focusPreviousItem(self):
if len(self.selectable_items) == 0:
return False
if self.focused_index is None:
self.focusItem(self.last_focused_index)
item = max(0, self.focused_index-1)
if item != self.focused_index:
self.focusItem(item)
@ -146,6 +149,8 @@ class HyperText(urwid.Text):
def focusNextItem(self):
if len(self.selectable_items) == 0:
return False
if self.focused_index is None:
self.focusItem(self.last_focused_index)
item = min(len(self.selectable_items)-1, self.focused_index+1)
if item != self.focused_index:
self.focusItem(item)
@ -153,6 +158,7 @@ class HyperText(urwid.Text):
return False
def focusItem(self, item):
self.last_focused_index = self.focused_index
self.focused_index = item
self.set_text(self._markup)
self._invalidate()

View File

@ -375,6 +375,8 @@ This Screen
if missing_revisions:
break
if missing_revisions:
if self.app.sync.offline:
raise gertty.view.DisplayError("Git commits not present in local repository")
self.app.log.warning("Missing some commits for change %s %s",
change_number, missing_revisions)
task = sync.SyncChangeTask(change_id, force_fetch=True,