Add project topics
Adds project topics so that projects may be grouped together. Change-Id: I0216d802ccc0586ffce0182c2c8806d5df54cc2f
This commit is contained in:
@@ -17,6 +17,7 @@ import logging
|
||||
import urwid
|
||||
|
||||
from gertty import keymap
|
||||
from gertty import mywid
|
||||
from gertty import sync
|
||||
from gertty.view import change_list as view_change_list
|
||||
from gertty.view import mouse_scroll_decorator
|
||||
@@ -31,10 +32,14 @@ class ProjectRow(urwid.Button):
|
||||
def selectable(self):
|
||||
return True
|
||||
|
||||
def __init__(self, project, callback=None):
|
||||
def __init__(self, project, topic, callback=None):
|
||||
super(ProjectRow, self).__init__('', on_press=callback,
|
||||
user_data=(project.key, project.name))
|
||||
self.project_key = project.key
|
||||
if topic:
|
||||
self.topic_key = topic.key
|
||||
else:
|
||||
self.topic_key = None
|
||||
name = urwid.Text(project.name)
|
||||
name.set_wrap_mode('clip')
|
||||
self.unreviewed_changes = urwid.Text(u'', align=urwid.RIGHT)
|
||||
@@ -60,6 +65,47 @@ class ProjectRow(urwid.Button):
|
||||
self.unreviewed_changes.set_text('%i ' % len(project.unreviewed_changes))
|
||||
self.open_changes.set_text('%i ' % len(project.open_changes))
|
||||
|
||||
class TopicRow(urwid.Button):
|
||||
project_focus_map = {None: 'focused',
|
||||
'subscribed-project': 'focused-subscribed-project',
|
||||
}
|
||||
|
||||
def selectable(self):
|
||||
return True
|
||||
|
||||
def _setName(self, name):
|
||||
self.name.set_text('[[ '+name+' ]]')
|
||||
|
||||
def __init__(self, topic, callback=None):
|
||||
super(TopicRow, self).__init__('', on_press=callback,
|
||||
user_data=(topic.key, topic.name))
|
||||
self.topic_key = topic.key
|
||||
self.name = urwid.Text('')
|
||||
self._setName(topic.name)
|
||||
self.name.set_wrap_mode('clip')
|
||||
self.unreviewed_changes = urwid.Text(u'', align=urwid.RIGHT)
|
||||
self.open_changes = urwid.Text(u'', align=urwid.RIGHT)
|
||||
col = urwid.Columns([
|
||||
self.name,
|
||||
('fixed', 11, self.unreviewed_changes),
|
||||
('fixed', 5, self.open_changes),
|
||||
])
|
||||
self.row_style = urwid.AttrMap(col, '')
|
||||
self._w = urwid.AttrMap(self.row_style, None, focus_map=self.project_focus_map)
|
||||
self.row_style.set_attr_map({None: 'subscribed-project'})
|
||||
self.update(topic)
|
||||
|
||||
def update(self, topic, unreviewed_changes=None, open_changes=None):
|
||||
self._setName(topic.name)
|
||||
if unreviewed_changes is None:
|
||||
self.unreviewed_changes.set_text('')
|
||||
else:
|
||||
self.unreviewed_changes.set_text('%i ' % unreviewed_changes)
|
||||
if open_changes is None:
|
||||
self.open_changes.set_text('')
|
||||
else:
|
||||
self.open_changes.set_text('%i ' % open_changes)
|
||||
|
||||
class ProjectListHeader(urwid.WidgetWrap):
|
||||
def __init__(self):
|
||||
cols = [urwid.Text(u'Project'),
|
||||
@@ -79,8 +125,20 @@ class ProjectListView(urwid.WidgetWrap):
|
||||
(key(keymap.TOGGLE_SUBSCRIBED),
|
||||
"Toggle the subscription flag for the currently selected project"),
|
||||
(key(keymap.REFRESH),
|
||||
"Sync subscribed projects")
|
||||
]
|
||||
"Sync subscribed projects"),
|
||||
(key(keymap.NEW_PROJECT_TOPIC),
|
||||
"Create project topic"),
|
||||
(key(keymap.DELETE_PROJECT_TOPIC),
|
||||
"Delete selected project topic"),
|
||||
(key(keymap.MOVE_PROJECT_TOPIC),
|
||||
"Move selected project to topic"),
|
||||
(key(keymap.COPY_PROJECT_TOPIC),
|
||||
"Copy selected project to topic"),
|
||||
(key(keymap.REMOVE_PROJECT_TOPIC),
|
||||
"Remove selected project from topic"),
|
||||
(key(keymap.RENAME_PROJECT_TOPIC),
|
||||
"Rename selected project topic"),
|
||||
]
|
||||
|
||||
def __init__(self, app):
|
||||
super(ProjectListView, self).__init__(urwid.Pile([]))
|
||||
@@ -89,6 +147,8 @@ class ProjectListView(urwid.WidgetWrap):
|
||||
self.unreviewed = True
|
||||
self.subscribed = True
|
||||
self.project_rows = {}
|
||||
self.topic_rows = {}
|
||||
self.open_topics = set()
|
||||
self.listbox = urwid.ListBox(urwid.SimpleFocusListWalker([]))
|
||||
self.header = ProjectListHeader()
|
||||
self.refresh()
|
||||
@@ -110,6 +170,56 @@ class ProjectListView(urwid.WidgetWrap):
|
||||
self.log.debug("Refreshing project list due to event %s" % (event,))
|
||||
return True
|
||||
|
||||
def _deleteRow(self, row):
|
||||
if row in self.listbox.body:
|
||||
self.listbox.body.remove(row)
|
||||
if isinstance(row, ProjectRow):
|
||||
del self.project_rows[(row.topic_key, row.project_key)]
|
||||
else:
|
||||
del self.topic_rows[row.topic_key]
|
||||
|
||||
def _projectRow(self, i, project, topic):
|
||||
# Ensure that the row at i is the given project. If the row
|
||||
# already exists somewhere in the list, delete all rows
|
||||
# between i and the row and then update the row. If the row
|
||||
# does not exist, insert the row at position i.
|
||||
topic_key = topic and topic.key or None
|
||||
key = (topic_key, project.key)
|
||||
row = self.project_rows.get(key)
|
||||
while row: # This is "if row: while True:".
|
||||
if i >= len(self.listbox.body):
|
||||
break
|
||||
current_row = self.listbox.body[i]
|
||||
if (isinstance(current_row, ProjectRow) and
|
||||
current_row.project_key == project.key):
|
||||
break
|
||||
self._deleteRow(current_row)
|
||||
if not row:
|
||||
row = ProjectRow(project, topic, self.onSelect)
|
||||
self.listbox.body.insert(i, row)
|
||||
self.project_rows[key] = row
|
||||
else:
|
||||
row.update(project)
|
||||
return i+1
|
||||
|
||||
def _topicRow(self, i, topic):
|
||||
row = self.topic_rows.get(topic.key)
|
||||
while row: # This is "if row: while True:".
|
||||
if i >= len(self.listbox.body):
|
||||
break
|
||||
current_row = self.listbox.body[i]
|
||||
if (isinstance(current_row, TopicRow) and
|
||||
current_row.topic_key == topic.key):
|
||||
break
|
||||
self._deleteRow(current_row)
|
||||
if not row:
|
||||
row = TopicRow(topic, self.onSelectTopic)
|
||||
self.listbox.body.insert(i, row)
|
||||
self.topic_rows[topic.key] = row
|
||||
else:
|
||||
row.update(topic)
|
||||
return i + 1
|
||||
|
||||
def refresh(self):
|
||||
if self.subscribed:
|
||||
self.title = u'Subscribed projects'
|
||||
@@ -118,24 +228,33 @@ class ProjectListView(urwid.WidgetWrap):
|
||||
else:
|
||||
self.title = u'All projects'
|
||||
self.app.status.update(title=self.title)
|
||||
unseen_keys = set(self.project_rows.keys())
|
||||
with self.app.db.getSession() as session:
|
||||
i = 0
|
||||
for project in session.getProjects(
|
||||
for project in session.getProjects(topicless=True,
|
||||
subscribed=self.subscribed, unreviewed=self.unreviewed):
|
||||
row = self.project_rows.get(project.key)
|
||||
if not row:
|
||||
row = ProjectRow(project, self.onSelect)
|
||||
self.listbox.body.insert(i, row)
|
||||
self.project_rows[project.key] = row
|
||||
else:
|
||||
row.update(project)
|
||||
unseen_keys.remove(project.key)
|
||||
i += 1
|
||||
for key in unseen_keys:
|
||||
row = self.project_rows[key]
|
||||
self.listbox.body.remove(row)
|
||||
del self.project_rows[key]
|
||||
#self.log.debug("project: %s" % project.name)
|
||||
i = self._projectRow(i, project, None)
|
||||
for topic in session.getTopics():
|
||||
#self.log.debug("topic: %s" % topic.name)
|
||||
i = self._topicRow(i, topic)
|
||||
topic_unreviewed = 0
|
||||
topic_open = 0
|
||||
for project in topic.projects:
|
||||
#self.log.debug(" project: %s" % project.name)
|
||||
topic_unreviewed += len(project.unreviewed_changes)
|
||||
topic_open += len(project.open_changes)
|
||||
if self.subscribed:
|
||||
if not project.subscribed:
|
||||
continue
|
||||
if self.unreviewed and not project.unreviewed_changes:
|
||||
continue
|
||||
if topic.key in self.open_topics:
|
||||
i = self._projectRow(i, project, topic)
|
||||
topic_row = self.topic_rows.get(topic.key)
|
||||
topic_row.update(topic, topic_unreviewed, topic_open)
|
||||
while i < len(self.listbox.body):
|
||||
current_row = self.listbox.body[i]
|
||||
self._deleteRow(current_row)
|
||||
|
||||
def toggleSubscribed(self, project_key):
|
||||
with self.app.db.getSession() as session:
|
||||
@@ -151,6 +270,126 @@ class ProjectListView(urwid.WidgetWrap):
|
||||
"_project_key:%s %s" % (project_key, self.app.config.project_change_list_query),
|
||||
project_name, project_key=project_key, unreviewed=True))
|
||||
|
||||
def onSelectTopic(self, button, data):
|
||||
topic_key = data[0]
|
||||
self.open_topics ^= set([topic_key])
|
||||
self.refresh()
|
||||
|
||||
def createTopic(self):
|
||||
dialog = mywid.LineEditDialog(self.app, 'Topic', 'Create a new topic.',
|
||||
'Topic: ', '', self.app.ring)
|
||||
urwid.connect_signal(dialog, 'save',
|
||||
lambda button: self.closeCreateTopic(dialog, True))
|
||||
urwid.connect_signal(dialog, 'cancel',
|
||||
lambda button: self.closeCreateTopic(dialog, False))
|
||||
self.app.popup(dialog)
|
||||
|
||||
def closeCreateTopic(self, dialog, save):
|
||||
if save:
|
||||
last_topic_key = None
|
||||
for row in self.listbox.body:
|
||||
if isinstance(row, TopicRow):
|
||||
last_topic_key = row.topic_key
|
||||
with self.app.db.getSession() as session:
|
||||
if last_topic_key:
|
||||
last_topic = session.getTopic(last_topic_key)
|
||||
seq = last_topic.sequence + 1
|
||||
else:
|
||||
seq = 0
|
||||
t = session.createTopic(dialog.entry.edit_text, seq)
|
||||
self.app.backScreen()
|
||||
|
||||
def deleteTopic(self):
|
||||
pos = self.listbox.focus_position
|
||||
row = self.listbox.body[pos]
|
||||
if not isinstance(row, TopicRow):
|
||||
return
|
||||
with self.app.db.getSession() as session:
|
||||
topic = session.getTopic(row.topic_key)
|
||||
session.delete(topic)
|
||||
self.refresh()
|
||||
|
||||
def renameTopic(self):
|
||||
pos = self.listbox.focus_position
|
||||
row = self.listbox.body[pos]
|
||||
if not isinstance(row, TopicRow):
|
||||
return
|
||||
with self.app.db.getSession() as session:
|
||||
topic = session.getTopic(row.topic_key)
|
||||
name = topic.name
|
||||
key = topic.key
|
||||
dialog = mywid.LineEditDialog(self.app, 'Topic', 'Rename a new topic.',
|
||||
'Topic: ', name, self.app.ring)
|
||||
urwid.connect_signal(dialog, 'save',
|
||||
lambda button: self.closeRenameTopic(dialog, True, key))
|
||||
urwid.connect_signal(dialog, 'cancel',
|
||||
lambda button: self.closeRenameTopic(dialog, False, key))
|
||||
self.app.popup(dialog)
|
||||
|
||||
def closeRenameTopic(self, dialog, save, key):
|
||||
if save:
|
||||
with self.app.db.getSession() as session:
|
||||
topic = session.getTopic(key)
|
||||
topic.name = dialog.entry.edit_text
|
||||
self.app.backScreen()
|
||||
|
||||
def copyMoveToTopic(self, move):
|
||||
if move:
|
||||
verb = 'Move'
|
||||
else:
|
||||
verb = 'Copy'
|
||||
pos = self.listbox.focus_position
|
||||
row = self.listbox.body[pos]
|
||||
if not isinstance(row, ProjectRow):
|
||||
return
|
||||
dialog = mywid.LineEditDialog(self.app, 'Topic', '%s to topic.' % verb,
|
||||
'Topic: ', '', self.app.ring)
|
||||
urwid.connect_signal(dialog, 'save',
|
||||
lambda button: self.closeCopyMoveToTopic(dialog, True, row, move))
|
||||
urwid.connect_signal(dialog, 'cancel',
|
||||
lambda button: self.closeCopyMoveToTopic(dialog, False, row, move))
|
||||
self.app.popup(dialog)
|
||||
|
||||
def closeCopyMoveToTopic(self, dialog, save, row, move):
|
||||
error = None
|
||||
if save:
|
||||
with self.app.db.getSession() as session:
|
||||
project = session.getProject(row.project_key)
|
||||
topic_name = dialog.entry.edit_text
|
||||
new_topic = session.getTopicByName(topic_name)
|
||||
if not new_topic:
|
||||
error = "Unable to find topic %s" % topic_name
|
||||
else:
|
||||
if move and row.topic_key:
|
||||
old_topic = session.getTopic(row.topic_key)
|
||||
self.log.debug("Remove %s from %s" % (project, old_topic))
|
||||
old_topic.removeProject(project)
|
||||
self.log.debug("Add %s to %s" % (project, new_topic))
|
||||
new_topic.addProject(project)
|
||||
self.app.backScreen()
|
||||
if error:
|
||||
self.app.error(error)
|
||||
|
||||
def moveToTopic(self):
|
||||
self.copyMoveToTopic(True)
|
||||
|
||||
def copyToTopic(self):
|
||||
self.copyMoveToTopic(False)
|
||||
|
||||
def removeFromTopic(self):
|
||||
pos = self.listbox.focus_position
|
||||
row = self.listbox.body[pos]
|
||||
if not isinstance(row, ProjectRow):
|
||||
return
|
||||
if not row.topic_key:
|
||||
return
|
||||
with self.app.db.getSession() as session:
|
||||
project = session.getProject(row.project_key)
|
||||
topic = session.getTopic(row.topic_key)
|
||||
self.log.debug("Remove %s from %s" % (project, topic))
|
||||
topic.removeProject(project)
|
||||
self.refresh()
|
||||
|
||||
def keypress(self, size, key):
|
||||
if not self.app.input_buffer:
|
||||
key = super(ProjectListView, self).keypress(size, key)
|
||||
@@ -182,6 +421,24 @@ class ProjectListView(urwid.WidgetWrap):
|
||||
if subscribed:
|
||||
self.app.sync.submitTask(sync.SyncProjectTask(project_key))
|
||||
return None
|
||||
if keymap.NEW_PROJECT_TOPIC in commands:
|
||||
self.createTopic()
|
||||
return None
|
||||
if keymap.DELETE_PROJECT_TOPIC in commands:
|
||||
self.deleteTopic()
|
||||
return None
|
||||
if keymap.COPY_PROJECT_TOPIC in commands:
|
||||
self.copyToTopic()
|
||||
return None
|
||||
if keymap.MOVE_PROJECT_TOPIC in commands:
|
||||
self.moveToTopic()
|
||||
return None
|
||||
if keymap.REMOVE_PROJECT_TOPIC in commands:
|
||||
self.removeFromTopic()
|
||||
return None
|
||||
if keymap.RENAME_PROJECT_TOPIC in commands:
|
||||
self.renameTopic()
|
||||
return None
|
||||
if keymap.REFRESH in commands:
|
||||
self.app.sync.submitTask(
|
||||
sync.SyncSubscribedProjectsTask(sync.HIGH_PRIORITY))
|
||||
|
||||
Reference in New Issue
Block a user