diff --git a/doc/taskwiki.txt b/doc/taskwiki.txt index 790e27641..485b094e4 100644 --- a/doc/taskwiki.txt +++ b/doc/taskwiki.txt @@ -474,6 +474,31 @@ in: ~ * [ ] This is a habit task #H:2f45ae +---------------------------------------------------------------------------- +5.4. Reviewing tasks *taskwiki-features-review* + +Taskwiki supports a task review process. Using the |TaskWikiReview| command, +you can specify a taskwarrior filter and the corresponding non-reviewed tasks +will be opened in a new temporary file where you can review them one by one. +For example: + +~ :TaskWikiReview project:work + +Once finished, you can close the buffer, at which moment you will be asked +whether to mark the listed tasks as reviewed. + +Alternatively, you can place the vim cursor on a viewport header and run +|TaskWikiReview| and the viewport's filter will be used to populate the +review. + +This functionality mirrors the review command of tasksh (bundled with +taskwarrior), but provides a better interface to review your tasks. + +Before using this command, you need to run the review command of tasksh which +will create the needed UDAs and taskwarrior report for the |TaskWikiReview| +command to work correctly. + + ============================================================================= 6. MAPPING *taskwiki-mapping* @@ -506,6 +531,7 @@ vn m |:TaskWikiMod| n t |:TaskWikiTags| vn + |:TaskWikiStart| vn - |:TaskWikiStop| + n r |:TaskWikiReview| ============================================================================= 7. COMMANDS *taskwiki-commands* @@ -604,6 +630,9 @@ selected tasks. *:TaskWikiMod* [mods] Opens a prompt for task modification, for selected task(s). +*:TaskWikiReview* [filter] + Start the task review process as described in |taskwiki-features-review|. + ---------------------------------------------------------------------------- Interactive commands. diff --git a/ftplugin/vimwiki/taskwiki.vim b/ftplugin/vimwiki/taskwiki.vim index ea97996a6..ccaf7da32 100644 --- a/ftplugin/vimwiki/taskwiki.vim +++ b/ftplugin/vimwiki/taskwiki.vim @@ -34,6 +34,7 @@ endif " Global update commands execute "command! -buffer -nargs=* TaskWikiBufferSave :" . g:taskwiki_py . "WholeBuffer.update_to_tw()" execute "command! -buffer -nargs=* TaskWikiBufferLoad :" . g:taskwiki_py . "WholeBuffer.update_from_tw()" +execute "command! -buffer -nargs=* TaskWikiReview :" . g:taskwiki_py . "TaskWikiReview.run()" augroup taskwiki autocmd! * @@ -53,6 +54,9 @@ augroup taskwiki else TaskWikiBufferLoad endif + + " Update reviewed date when exiting a review + execute "autocmd BufUnload :" . g:taskwiki_py . "TaskWikiReview().update()" augroup END " Split reports commands @@ -134,6 +138,7 @@ if !exists('g:taskwiki_suppress_mappings') nnoremap . :TaskWikiRedo nnoremap + :TaskWikiStart nnoremap - :TaskWikiStop + nnoremap r :TaskWikiReview " Mappings for visual mode. vnoremap a :TaskWikiAnnotate diff --git a/taskwiki/main.py b/taskwiki/main.py index fb781ac9f..e79d834a2 100644 --- a/taskwiki/main.py +++ b/taskwiki/main.py @@ -1,4 +1,5 @@ from __future__ import print_function +import tempfile import base64 import re import os @@ -6,6 +7,7 @@ import six import sys import vim # pylint: disable=F0401 +from datetime import datetime # Insert the taskwiki on the python path BASE_DIR = vim.eval("s:plugin_path") @@ -63,6 +65,96 @@ def update_to_tw(): c.buffer.push() +class TaskWikiReview(object): + @staticmethod + @errors.pretty_exception_handler + @decorators.hold_vim_cursor + def run(filter=None): + """ + Updates all the incomplete tasks in the vimwiki file if the info from TW is different. + """ + if filter is None: + position = util.get_current_line_number() + port = viewport.ViewPort.from_line(position, cache()) + + if port is None: + raise ValueError("No filter given and cursor not on a viewport") + + filter = port.raw_filter + print(filter) + + review_file = tempfile.mkstemp(dir='/tmp/', suffix='.twreview', text=True)[1] + + # TODO add user configurable count to filter + # TODO make the interval configurable + filter += ' (reviewed.none: or reviewed.before:tod-2weeks)' + with open(review_file, 'w') as f: + # TODO do not support only vimwiki syntax + f.write("== Pending review | %s ==\n" % filter) + f.seek(0) + + vim.command("e {0}".format(review_file)) + + # TODO read this from vimwiki + vim.command('set ft=vimwiki.markdown.twreview') + vim.command('setlocal foldlevel=9999') + + c = cache.load_current() + c.reset() + c.load_tasks() + c.load_presets() + c.load_vwtasks(buffer_has_authority=False) + c.load_viewports() + c.update_vwtasks_from_tasks() + c.update_vwtasks_in_buffer() + c.evaluate_viewports() + c.buffer.push() + + vim.command('w') + + @staticmethod + @errors.pretty_exception_handler + @decorators.hold_vim_cursor + def update(): + closing_buffer = vim.eval("expand('')") + + if re.match(r".*\.twreview", closing_buffer): + user_choice = vim.eval( + "confirm('Update review status of reviewed tasks?', '&Yes\n&No', 1)" + ) + + if user_choice == '2': + return + + for b in vim.buffers: + if b.name == closing_buffer: + break + + print(b.name) + c = cache(b.number) + # c.reset() + c.load_tasks() + c.load_presets() + c.load_vwtasks(buffer_has_authority=False) + c.load_viewports() + for vwtask in c.vwtask.values(): + vwtask.task['reviewed'] = datetime.now() + c.save_tasks() + + @staticmethod + @errors.pretty_exception_handler + @decorators.hold_vim_cursor + def from_line(): + position = util.get_current_line_number() + port = viewport.ViewPort.from_line(position, cache()) + + if port is None: + print('Not a viewport') + return + + TaskWikiReview.run(port.raw_filter) + + class SelectedTasks(object): # Keeps track of the last action performed on any selected tasks diff --git a/taskwiki/sort.py b/taskwiki/sort.py index 1738e7901..1a2e8f145 100644 --- a/taskwiki/sort.py +++ b/taskwiki/sort.py @@ -181,7 +181,7 @@ def full_list(self): return full_list def __repr__(self): - return u"Node for with ID: {0}".format(self.vwtask.task['id']) + return u"Node for with ID: {0}".format(self.vwtask.task['id'] or self.vwtask.task['uuid']) def __lt__(self, other): return self.comparator.lt(self, other)