From 5fb1106c0ead23f6356fed5516aedb35e7985f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 4 Oct 2024 18:27:29 +0200 Subject: [PATCH] DevOps: Fix tutorial scripts and CI/CD Make the exercise/solution conversion script reversible. Remove empty tutorial code cells. --- doc/tutorials/Readme.md | 2 +- doc/tutorials/convert.py | 18 ++++-- .../lennard_jones/lennard_jones.ipynb | 64 ------------------- testsuite/scripts/tutorials/test_convert.py | 22 ++++++- 4 files changed, 33 insertions(+), 73 deletions(-) diff --git a/doc/tutorials/Readme.md b/doc/tutorials/Readme.md index 435919a9c1..d3f8d7bb73 100644 --- a/doc/tutorials/Readme.md +++ b/doc/tutorials/Readme.md @@ -70,7 +70,7 @@ physical systems. Measuring the excess chemical potential of a salt solution using the Widom particle insertion method. [Guide](widom_insertion/widom_insertion.ipynb) * **Grand-Canonical Monte Carlo** - Simulating a polyelectrolyte solution coupled to a reservoir of salt. + Simulating a polyelectrolyte solution coupled to a reservoir of salt. [Guide](grand_canonical_monte_carlo/grand_canonical_monte_carlo.ipynb) [comment]: # (End of tutorials landing page) diff --git a/doc/tutorials/convert.py b/doc/tutorials/convert.py index a54505de5c..7625ee0144 100644 --- a/doc/tutorials/convert.py +++ b/doc/tutorials/convert.py @@ -75,7 +75,7 @@ def add_cell_from_script(nb, filepath): def remove_empty_cells(nb): - for i in range(len(nb['cells']) - 1, 0, -1): + for i in range(len(nb["cells"]))[::-1]: cell = nb['cells'][i] if cell['source'].strip() == '': nb['cells'].pop(i) @@ -90,11 +90,19 @@ def parse_solution_cell(cell): def convert_exercise2_to_code(nb): - for i in range(len(nb["cells"]) - 1, 0, -1): + for i in range(len(nb["cells"]))[::-1]: cell = nb["cells"][i] - solution = parse_solution_cell(cell) - if solution is not None: - cell["source"] = solution + if cell["cell_type"] != "markdown": + continue + source = cell["source"] + if source.startswith(""): + m = re.search("```python\n(.+)\n```", source, flags=re.DOTALL) + solution = "# SOLUTION CELL\n" + m.group(1) + nb["cells"][i] = nbformat.v4.new_code_cell(source=solution) + if (i + 1) != len(nb["cells"]) and \ + nb["cells"][i + 1]["cell_type"] == "code" and \ + not nb["cells"][i + 1]["source"]: + del nb["cells"][i + 1] def disable_plot_interactivity(nb): diff --git a/doc/tutorials/lennard_jones/lennard_jones.ipynb b/doc/tutorials/lennard_jones/lennard_jones.ipynb index 3913acfea3..93ac166699 100644 --- a/doc/tutorials/lennard_jones/lennard_jones.ipynb +++ b/doc/tutorials/lennard_jones/lennard_jones.ipynb @@ -273,14 +273,6 @@ "system = espressomd.System(box_l=BOX_L)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "13275327", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -356,14 +348,6 @@ "particles = system.part.add(type=[0] * N_PART, pos=np.random.random((N_PART, 3)) * system.box_l)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "655d49e6", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -498,14 +482,6 @@ " epsilon=LJ_EPS, sigma=LJ_SIG, cutoff=LJ_CUT, shift=0)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "91ac5e57", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "5e6332fb", @@ -717,14 +693,6 @@ "T_inst = 2. / 3. * e_kin / N_PART" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "c53290b6", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -843,14 +811,6 @@ "steps_per_subsample = int(np.ceil(3 * corr_time / system.time_step))" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "94fc76b6", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -923,14 +883,6 @@ "SEM_pot_energy = np.std(pot_energies) / np.sqrt(len(pot_energies))" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "3e4a203c", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -1034,14 +986,6 @@ "system.auto_update_accumulators.add(rdf_acc)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "8de0795a", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "51edfcf1", @@ -1082,14 +1026,6 @@ "rs = rdf_obs.bin_centers()" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "18a10a12", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, diff --git a/testsuite/scripts/tutorials/test_convert.py b/testsuite/scripts/tutorials/test_convert.py index a2dd24a842..025307d4be 100644 --- a/testsuite/scripts/tutorials/test_convert.py +++ b/testsuite/scripts/tutorials/test_convert.py @@ -188,7 +188,7 @@ def test_cells_prepare_for_html(self): self.assertEqual(cell['source'], 'Question 1') cell = next(cells) self.assertEqual(cell['cell_type'], 'code') - self.assertEqual(cell['source'], '1') + self.assertEqual(cell['source'], '# SOLUTION CELL\n1') cell = next(cells) self.assertEqual(cell['cell_type'], 'markdown') self.assertEqual(cell['source'], '1b') @@ -199,7 +199,7 @@ def test_cells_prepare_for_html(self): self.assertEqual(cell['cell_type'], 'code') self.assertEqual( cell['source'], - '2\nimport matplotlib.pyplot\nglobal_var = 20') + '# SOLUTION CELL\n2\nimport matplotlib.pyplot\nglobal_var = 20') cell = next(cells) self.assertEqual(cell['cell_type'], 'code') self.assertEqual(cell['source'], '3') @@ -231,7 +231,7 @@ def test_cells_conversion(self): self.assertEqual(cell['source'], 'Question 1') cell = next(cells) self.assertEqual(cell['cell_type'], 'code') - self.assertEqual(cell['source'], '1') + self.assertEqual(cell['source'], '# SOLUTION CELL\n1') self.assertEqual(next(cells, 'EOF'), 'EOF') with open(f_input, 'w', encoding='utf-8') as f: @@ -255,6 +255,22 @@ def test_cells_conversion(self): self.assertEqual(cell['source'], '') self.assertEqual(next(cells, 'EOF'), 'EOF') + # check operation is reversible + cmd = ['cells', '--to-py', str(f_input)] + self.run_command(cmd, f_input) + # read processed notebook + with open(f_input, encoding='utf-8') as f: + nb_output = nbformat.read(f, as_version=4) + # check cells + cells = iter(nb_output['cells']) + cell = next(cells) + self.assertEqual(cell['cell_type'], 'markdown') + self.assertEqual(cell['source'], 'Question 1') + cell = next(cells) + self.assertEqual(cell['cell_type'], 'code') + self.assertEqual(cell['source'], '# SOLUTION CELL\n1') + self.assertEqual(next(cells, 'EOF'), 'EOF') + @skipIfMissingModules def test_cells_autopep8(self): root = pathlib.Path("@CMAKE_CURRENT_BINARY_DIR@")