From 6fb28d62026a1a2f5a01a812162e23b36343aa99 Mon Sep 17 00:00:00 2001 From: Bo Peng Date: Fri, 18 May 2018 12:51:16 -0500 Subject: [PATCH] Add a %revisions magic to display document revisions #74 --- src/sos_notebook/kernel.js | 2 +- src/sos_notebook/kernel.py | 71 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/sos_notebook/kernel.js b/src/sos_notebook/kernel.js index a84c5ab..34f7b2d 100644 --- a/src/sos_notebook/kernel.js +++ b/src/sos_notebook/kernel.js @@ -250,8 +250,8 @@ define([ // Running %sossave --to html needs to save notebook nb.save_notebook(); options.sos.workflow = getNotebookWorkflow(cells); - options.sos.filename = window.document.getElementById("notebook_name").innerHTML; } + options.sos.filename = window.document.getElementById("notebook_name").innerHTML; options.sos.use_panel = nb.metadata["sos"]["panel"].displayed; options.sos.default_kernel = nb.metadata["sos"].default_kernel; options.sos.rerun = false; diff --git a/src/sos_notebook/kernel.py b/src/sos_notebook/kernel.py index 38f1d46..d2de630 100644 --- a/src/sos_notebook/kernel.py +++ b/src/sos_notebook/kernel.py @@ -386,6 +386,7 @@ class SoS_Kernel(IPythonKernel): 'put', 'render', 'rerun', + 'revisions', 'run', 'save', 'sandbox', @@ -417,6 +418,7 @@ class SoS_Kernel(IPythonKernel): MAGIC_PUT = re.compile('^%put(\s|$)') MAGIC_RENDER = re.compile('^%render(\s|$)') MAGIC_RERUN = re.compile('^%rerun(\s|$)') + MAGIC_REVISIONS = re.compile('^%revisions(\s|$)') MAGIC_RUN = re.compile('^%run(\s|$)') MAGIC_SAVE = re.compile('^%save(\s|$)') MAGIC_SANDBOX = re.compile('^%sandbox(\s|$)') @@ -613,6 +615,19 @@ def get_run_parser(self): parser.error = self._parse_error return parser + def get_revisions_parser(self): + parser = argparse.ArgumentParser(prog='%revision', + description='''Revision history of the document, parsed from the log + message of the notebook if it is kept in a git repository. Additional parameters to git log command + (e.g. -n 5 --since --after) to limit the revisions to display.''') + parser.add_argument('-s', '--source', help='''Source URL with revision interpolated with revision ID + and filename as current filename. Because sos interpolates command line by default, revision should be + included with double braceses (e.g. --source https://github.com/ORG/USER/blob/{{revision}}/analysis/{{filename}}).''') + parser.add_argument('-l', '--links', nargs='+', help='''Name and URL or additional links for related + files (e.g. --links report URL_to_repo ) with URL interpolated as option --source.''') + parser.error = self._parse_error + return parser + def get_save_parser(self): parser = argparse.ArgumentParser(prog='%save', description='''Save the content of the cell (after the magic itself) to specified file''') @@ -883,6 +898,50 @@ def __init__(self, **kwargs): _workflow_mode = property(lambda self: self._meta['workflow_mode']) _resume_execution = property(lambda self: self._meta['resume_execution']) + def handle_magic_revisions(self, args, unknown_args): + import git + # default + repo = git.Git() + filename = self._meta['notebook_name'] + '.ipynb' + revisions = repo.log(*(unknown_args + ['--date=short', '--pretty=%h!%cN!%cd!%s', + '--', filename])).splitlines() + if not revisions: + return + text = ''' + + + + + + + + ''' + for line in revisions: + fields = line.split('!', 3) + revision = fields[0] + if args.source: + # source URL + URL = interpolate(args.source, {'revision': revision, 'filename': filename}) + fields[0] = f'{revision}' + links = [] + if args.links: + for i in range(len(args.links) // 2): + name = args.links[2 * i] + if len(args.links) == 2 * i + 1: + continue + URL = interpolate(args.links[2 * i + 1], + {'revision': revision, 'filename': filename}) + links.append(f'{name}') + if links: + fields[0] += ' (' + ', '.join(links) + ')' + text += '' + '\n'.join(f'' for x in fields) + '' + text += '
RevisionAuthorDateMessage
{x}
' + self.send_response(self.iopub_socket, 'display_data', + { + 'metadata': {}, + 'data': {'text/html': HTML(text).data} + }) + def handle_taskinfo(self, task_id, task_queue, side_panel=None): # requesting information on task from sos.hosts import Host @@ -2899,6 +2958,18 @@ def _do_execute(self, code, silent, store_history=True, user_expressions=None, env.sos_dict = old_dict self._meta['workflow_mode'] = False self.options = old_options + elif self.MAGIC_REVISIONS.match(code): + options, remaining_code = self.get_magic_and_code(code, True) + parser = self.get_revisions_parser() + try: + args, unknown_args = parser.parse_known_args(shlex.split(options)) + except SystemExit: + return + try: + self.handle_magic_revisions(args, unknown_args) + except Exception as e: + self.warn(f'Failed to retrieve revisions of notebook: {e}') + return self._do_execute(self.remaining_code, silent, store_history, user_expressions, allow_stdin) elif self.MAGIC_SANDBOX.match(code): import tempfile import shutil