diff --git a/boartty/db.py b/boartty/db.py index ce4781e..ea8b332 100644 --- a/boartty/db.py +++ b/boartty/db.py @@ -715,12 +715,25 @@ class DatabaseSession(object): except sqlalchemy.orm.exc.NoResultFound: return [] + def getTag(self, name): + try: + return self.session().query(Tag).filter_by(name=name).one() + except sqlalchemy.orm.exc.NoResultFound: + return None + def getTagByID(self, id): try: return self.session().query(Tag).filter_by(id=id).one() except sqlalchemy.orm.exc.NoResultFound: return None + def getStoryTag(self, story_key, tag_key): + try: + return self.session().query(StoryTag).filter_by( + story_key=story_key, tag_key=tag_key).one() + except sqlalchemy.orm.exc.NoResultFound: + return None + def getTask(self, key): try: return self.session().query(Task).filter_by(key=key).one() @@ -890,6 +903,12 @@ class DatabaseSession(object): self.session().flush() return o + def createStoryTag(self, *args, **kw): + o = StoryTag(*args, **kw) + self.session().add(o) + self.session().flush() + return o + def createSystem(self, *args, **kw): o = System(*args, **kw) self.session().add(o) diff --git a/boartty/keymap.py b/boartty/keymap.py index d8923da..e33f345 100644 --- a/boartty/keymap.py +++ b/boartty/keymap.py @@ -57,6 +57,7 @@ NEW_TASK = 'new task' DELETE_TASK = 'delete task' REFRESH = 'refresh' EDIT_TITLE = 'edit title' +EDIT_TAGS = 'edit tags' EDIT_DESCRIPTION = 'edit description' SORT_BY_NUMBER = 'sort by number' SORT_BY_UPDATED = 'sort by updated' @@ -116,6 +117,7 @@ DEFAULT_KEYMAP = { DELETE_TASK: 'delete', REFRESH: 'ctrl r', EDIT_TITLE: 'ctrl t', + EDIT_TAGS: 'ctrl g', EDIT_DESCRIPTION: 'ctrl d', SORT_BY_NUMBER: [['S', 'n']], SORT_BY_UPDATED: [['S', 'u']], diff --git a/boartty/sync.py b/boartty/sync.py index 264e353..088f90c 100644 --- a/boartty/sync.py +++ b/boartty/sync.py @@ -1027,6 +1027,8 @@ class UpdateStoryTask(Task): # story.pending_description = False data['description'] = story.description data['title'] = story.title + tags_data = [] + tags_data = list([t.name for t in story.tags]) story.pending = False if story.id is None: @@ -1035,6 +1037,16 @@ class UpdateStoryTask(Task): else: result = sync.put('v1/stories/%s' % (story.id,), data) + local_tags = set(tags_data) + remote_tags = set(sync.get('v1/stories/%s/tags' % (story.id,))) + added = list(local_tags - remote_tags) + removed = list(remote_tags - local_tags) + if removed: + sync.delete('v1/tags/%s' % (story.id,), + removed) + if added: + result = sync.put('v1/tags/%s' % (story.id,), + added) sync.submitTask(SyncStoryTask(story.id, result, priority=self.priority)) diff --git a/boartty/view/story.py b/boartty/view/story.py index 4d9bf48..51fd108 100644 --- a/boartty/view/story.py +++ b/boartty/view/story.py @@ -589,8 +589,8 @@ class StoryView(urwid.WidgetWrap, mywid.Searchable): else: creator_string = story.creator_name self.creator_label.text.set_text(('story-data', creator_string)) - tags_string = ' '.join([t.name for t in story.tags]) - self.tags_label.set_text(('story-data', tags_string)) + self.tags_string = ' '.join([t.name for t in story.tags]) + self.tags_label.set_text(('story-data', self.tags_string)) self.title_label.set_text(('story-data', story.title)) self.created_label.set_text(('story-data', str(self.app.time(story.created)))) self.updated_label.set_text(('story-data', str(self.app.time(story.updated)))) @@ -728,6 +728,9 @@ class StoryView(urwid.WidgetWrap, mywid.Searchable): if keymap.EDIT_TITLE in commands: self.editTitle() return None + if keymap.EDIT_TAGS in commands: + self.editTags() + return None if keymap.INTERACTIVE_SEARCH in commands: self.searchStart() if keymap.FURTHER_INPUT not in commands: @@ -836,6 +839,17 @@ class StoryView(urwid.WidgetWrap, mywid.Searchable): lambda button: self.updateTitle(dialog, False)) self.app.popup(dialog) + def editTags(self): + dialog = mywid.LineEditDialog(self.app, + 'Edit Story Tags (Space Separated)', + '', 'Tags: ', self.tags_string, + ring=self.app.ring) + urwid.connect_signal(dialog, 'save', + lambda button: self.updateTags(dialog, True)) + urwid.connect_signal(dialog, 'cancel', + lambda button: self.updateTags(dialog, False)) + self.app.popup(dialog) + def updateTitle(self, dialog, save): if save: with self.app.db.getSession() as session: @@ -846,6 +860,23 @@ class StoryView(urwid.WidgetWrap, mywid.Searchable): self.app.backScreen() self.refresh() + def updateTags(self, dialog, save): + if save: + with self.app.db.getSession() as session: + story = session.getStory(self.story_key) + new_tags = dialog.entry.edit_text.split(' ') + for tag_name in new_tags: + tag = session.getTag(tag_name) + if tag is None: + tag = session.createTag(tag_name) + story_tag = session.getStoryTag(story.key, tag_name) + if story_tag is None: + session.createStoryTag(story, tag) + self.app.sync.submitTask( + sync.UpdateStoryTask(story.key, sync.HIGH_PRIORITY)) + self.app.backScreen() + self.refresh() + def openPermalink(self, widget): self.app.openURL(self.permalink_url)