010 hide solution

010 hide solution#

gegeben:

  • Ein Jupyter Notebook als .ipynb oder als .md-Datei.

  • Der Dateiname enthält den Teilstring LOESUNG

  • Die Notebooks enthalten Code-Zeilen, die mit dem String #... (ACHTUNG: keine Leerzeichen) enden

Gesucht:

  • Ersetze Code-Zeilen, die mit #... enden,

  • duch Code-Zeilen, die – korrekt eingerückt – nur noch aus # TBD bestehen

  • schreibe die so “maskierten” Notebooks wieder ‘raus, aber ersetze im Dateinamen den Teilstring LOESUNG durch den Teilstring AUTO.

Beispiel:

Ausführung#

Da Notebooks von Jupyterbook in alphabetischer Reihenfolge ausgeführt werfen, wird dieses Notebook wegen seinem Namen 010_xxx.ipynb sehr früh ausgeführt – und das ist gut so, das es neue Notebooks erzeugt, die dann noch im gleichen Durchlauf wiederum von Juypterbook ausgeführt werden.

Allerdings wird dieses Notebook nur dann ausgeführt, wenn es selbst geändert wurde, oder es nicht mehr im Cache verfügbar ist.

Am einfachsten uns sichersten ist es daher, dieses Notebook z.B. in einem Makefile vor der eigentlichen Ausführung von Jupyterbook auzuführen, z.B. so:

python 010_hide-solution.py 

Wenn man das so machen will, muss das ipynb-Notebook mit Juyptext z.B. mit Percent-Script gepaart werden.

code#

import glob
import json
import regex as re
import os

verbose = 2
HIDE = True
if verbose >= 1:
    print("010 hide solution")
010 hide solution
def hide(string):
    def substfn(match):
        #print([ match[i] for i in range(len(match.groups()) +1) ])
        result = "".join([ match[1], "...", match[4] ])
        #print(result)
        return result
    regex = r'( *)(.*)(#\.\.\.) *(.*)'
    return re.sub(regex, 
                  # lambda match: "".join([ match[1], "... # ", match[4] ]),# until 2024-03-20
                  lambda match: "".join([ match[1], match[4], "# TBD" ]), # since 2024-03-21
                  string, 
                  0) if ("#..." in string and HIDE) else string
hide("x = [ i for i in range(3) ] #...           x = ...     ")
'x = ...     # TBD'
loesung_files = glob.glob("*LOESUNG*.md") + glob.glob("*LOESUNG*.ipynb")
loesung_to_be_processed = []
for loesung in loesung_files:
    auto = "AUTO".join(loesung.split('LOESUNG'))
    t_loesung = os.path.getmtime(loesung)
    t_auto = os.path.getmtime(auto) if os.path.exists(auto) else 0
    if t_loesung > t_auto:
        loesung_to_be_processed.append((loesung, auto))
loesung_to_be_processed
[]
md_dict = {}
json_dict = {}
for l_path, a_path in loesung_to_be_processed:
    if verbose >= 1: print(f"reading {l_path}")
    with open(l_path, 'r') as file:
        if l_path.endswith(".ipynb"):
            json_dict[a_path] = json.load(file)
        elif l_path.endswith(".md"):
            md_dict[a_path] = file.readlines()
        else:
            print("Warning: unknown file type")

md#

md_dict
{}
for auto, notebook in md_dict.items():
    source_hide = [ hide(line) for line in notebook ]
    with open(auto, 'w') as md_file:
        md_file.writelines(source_hide)
        if verbose >= 1: print(f"wrote {auto}")
    if verbose >= 2: print(source_hide)

ipynb#

json_dict.keys()
dict_keys([])
for auto, notebook in json_dict.items():
    for cell in notebook['cells']:
        # print(type(cell), "\n", cell)
        if cell['cell_type'] == 'code':
            source = cell['source']
            # print(source)
            source_hide = [ hide(line) for line in cell['source'] ]
            # print(source_hide)
            cell['source'] = source_hide
    with open(auto, 'w') as json_file:
        json.dump(notebook, json_file)
        if verbose >= 1: print(f"wrote {auto}")