维护ipython笔记本的两个版本

时间:2014-06-06 21:05:49

标签: python json mercurial ipython-notebook

我经常需要创建两个版本的ipython笔记本:一个包含要执行的任务(通常包括一些python代码和输出),另一个包含相同的文本和解决方案。我们称之为assignmentsolution

首先生成解决方案文档很容易,然后剥离答案以生成分配(反之亦然)。但如果我随后需要进行更改(我总是这样做),我需要重复剥离过程。是否有合理的工作流程允许将assignment中的更改传播到solutions文档?

部分自我回答:我已经尝试利用mercurial的hg copy,这将让两个名称不同的文件共享历史记录。但是,如果assignmentsolution位于两个链接的hg存储库中的不同目录中,我只能使其工作。我更喜欢更简单的设置。我还注意到,当一个JSON文件比另一个JSON文件有更多部分时,diff会变得非常困惑,这使得基于VCS的解决方案更具吸引力。 (需要明确的是:普通使用带有笔记本电脑的VCS很好;它是绊倒的并行版本)。

This question涵盖了类似的理由,但并未解决我的问题。事实上,我的问题的答案将解决OP的第二个问题,即“拉动变化”(参见更新部分)。

2 个答案:

答案 0 :(得分:1)

听起来你正在维护某种作业和答案键,并希望能够将作业(没有解决方案)分发给学生,并且仍然有自己或TA的答案。

对于这样的事情,我会创建两个分支"未解决的"并且"解决了#34;。首先在"未解决的"上写下问题。科。然后创建"解决"从那里分支并添加解决方案。如果您需要更新问题,请更新回"未解决的问题"分支,进行更新并将更改合并到"已解决"并修复解决方案。

你可以试着走另一条路,但我的预感就是向前走"倒退"从解决到未解决可能很难保持。

答案 1 :(得分:1)

经过一些实验,我得出结论,最好通过处理笔记本的JSON代码来解决这个问题。由于以下原因,版本控制系统不是正确的方法:

    添加或删除单元格时,
  1. JSON doesn't diff very well。最小的变化导致不匹配的括号和非常混乱的差异。

  2. 在我的用例中,文件的超集版本(包含赋值及其解决方案)必须是源文档。这是因为作业包括示例代码和输出,这些代码和输出取决于学生要编写的早期部分。正如@ChrisPhillips在回答中指出的那样,这种模式不适合版本控制。

  3. 我最终过滤了笔记本的JSON结构并剥离了解决方案单元格;它们可以通过特殊元数据识别(可以使用界面中的metadata按钮以交互方式设置),也可以通过单元格内容的模式匹配来识别。以下代码段显示了如何过滤掉第一行以# SOLUTION:

    开头的单元格
    def stripcell(cell, pattern):
        """Check if the first line of the cell's content matches `pattern`"""
        if cell["cell_type"] == "code":
            content = cell["input"]
        else:
            content = cell["source"]    
        return ( len(content) > 0 and re.search(pattern, content[0]) )
    
    pattern = r"^# SOLUTION:"
    
    struct = json.load(open("input.ipynb"))    
    cells = struct["worksheets"][0]["cells"]
    struct["worksheets"][0]["cells"] = [ c for c in cells if not stripcell(c, pattern) ]
    
    json.dump(struct, open("output.ipynb", "wb"), indent=1)
    

    我使用了通用json库,而不是笔记本API。如果有更好的方法,请告诉我。