From 9929ef2810563081eafbb133164ca0b54c26bed5 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov Date: Tue, 1 Jan 2019 16:22:47 +0100 Subject: [PATCH 01/18] release all at once --- scripts/converter.py | 11 ++++------- scripts/release_dates | 16 ---------------- 2 files changed, 4 insertions(+), 23 deletions(-) delete mode 100644 scripts/release_dates diff --git a/scripts/converter.py b/scripts/converter.py index 547214b..8f79175 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -2,7 +2,6 @@ import argparse import datetime -import io from itertools import dropwhile import os import re @@ -73,9 +72,6 @@ iFrameResize({{ """ -with open(os.path.join(scripts_path, 'release_dates')) as f: - release_dates = eval(f.read()) - def date_to_edx(date, add_days=0): tmp = strptime(date, '%d %b %Y') @@ -90,8 +86,8 @@ def parse_syllabus(syllabus_file, content_folder=''): # loading raw syllabus syll = nbformat.read(syllabus_file, as_version=4).cells[0].source - section = '^\* \*\*(?P
.*)\*\*$' - subsection = '^ \* \[(?P.*)\]\((?P<filename>.*)\)$' + section = r'^\* \*\*(?P<section>.*)\*\*$' + subsection = r'^ \* \[(?P<title>.*)\]\((?P<filename>.*)\)$' syllabus_line = section + '|' + subsection syllabus = [] @@ -101,7 +97,8 @@ def parse_syllabus(syllabus_file, content_folder=''): continue name = match.group('section') if name is not None: - syllabus.append([name, release_dates.get(name), []]) + # Release date in the past, customize if necessary. + syllabus.append([name, '1 Jan 2000', []]) continue name, filename = match.group('title'), match.group('filename') syllabus[-1][-1].append((name, filename)) diff --git a/scripts/release_dates b/scripts/release_dates deleted file mode 100644 index fd67ee5..0000000 --- a/scripts/release_dates +++ /dev/null @@ -1,16 +0,0 @@ -{ -'Before you begin': '8 Jan 2016', -'Topology in toy models': '8 Jan 2016', -'Majoranas I': '8 Jan 2016', -'More parameters: charge pumping': '8 Feb 2016', -'Chern insulators': '29 Feb 2016', -'Quantum spin Hall effect': '7 Mar 2016', -'Three-dimensional topological insulators': '14 Mar 2016', -'Topological defects': '21 Mar 2016', -'General approach to topological classification': '28 Mar 2016', -'Anderson localization and topology': '4 Apr 2016', -'Extensions of classification I': '11 Apr 2016', -'Extensions of classification II': '18 Apr 2016', -'Beyond single-particle physics': '25 Apr 2016', -'Tell us what you think': '25 Apr 2016', -} -- GitLab From 713b3ad20031da38d46c8f460195f04a6e73cd51 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 17:03:18 +0100 Subject: [PATCH 02/18] remove unused files --- edx_skeleton/tabs/.keep | 0 edx_skeleton/tabs/map.html | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 edx_skeleton/tabs/.keep delete mode 100644 edx_skeleton/tabs/map.html diff --git a/edx_skeleton/tabs/.keep b/edx_skeleton/tabs/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/edx_skeleton/tabs/map.html b/edx_skeleton/tabs/map.html deleted file mode 100644 index e69de29..0000000 -- GitLab From 88b425bc3159819da98b049bf860b47999bcdd61 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 17:03:40 +0100 Subject: [PATCH 03/18] use yaml for specifying course structure --- scripts/converter.py | 54 ++++++++-------------- toc.yml | 104 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 36 deletions(-) create mode 100644 toc.yml diff --git a/scripts/converter.py b/scripts/converter.py index 8f79175..10f10cc 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -14,6 +14,7 @@ import urllib.request from xml.etree.ElementTree import SubElement from xml.etree import ElementTree +from ruamel.yaml import YAML import nbformat from nbformat import v4 as current from nbconvert import HTMLExporter @@ -24,6 +25,8 @@ try: except KeyError: os.environ['PYTHONPATH'] = './code' +START_DATE = "1 Jan 2000" # Any date in the past. + scripts_path = os.path.dirname(os.path.realpath(__file__)) mooc_folder = os.path.join(scripts_path, os.pardir) @@ -82,47 +85,30 @@ def date_to_edx(date, add_days=0): return date -def parse_syllabus(syllabus_file, content_folder=''): - # loading raw syllabus - syll = nbformat.read(syllabus_file, as_version=4).cells[0].source - - section = r'^\* \*\*(?P<section>.*)\*\*$' - subsection = r'^ \* \[(?P<title>.*)\]\((?P<filename>.*)\)$' - syllabus_line = section + '|' + subsection - - syllabus = [] - for line in syll.split('\n'): - match = re.match(syllabus_line, line) - if match is None: - continue - name = match.group('section') - if name is not None: - # Release date in the past, customize if necessary. - syllabus.append([name, '1 Jan 2000', []]) - continue - name, filename = match.group('title'), match.group('filename') - syllabus[-1][-1].append((name, filename)) +def parse_syllabus(table_of_contents, content_folder=''): + with open(table_of_contents) as f: + source = YAML().load(f) data = SimpleNamespace(category='main', chapters=[]) - for i, section in enumerate(syllabus): - if section[1] is None: - continue + for i, section in enumerate(source): # creating chapter chapter = SimpleNamespace(category='chapter', sequentials=[]) - chapter.name = section[0] - chapter.date = section[1] + chapter.name = section['title'] + chapter.date = START_DATE chapter.url = f"sec_{i:02}" - for j, subsection in enumerate(section[2]): + for j, subsection in enumerate(section['sections']): # creating sequential sequential = SimpleNamespace(category='sequential', verticals=[]) - sequential.name = subsection[0] - sequential.date = chapter.date + sequential.name = subsection['title'] + sequential.date = START_DATE sequential.url = f"subsec_{i:02}_{j:02}" - sequential.source_notebook = content_folder + '/' + subsection[1] + sequential.source_notebook = ( + f"{content_folder}/{subsection['location']}.ipynb" + ) chapter.sequentials.append(sequential) @@ -171,10 +157,6 @@ def split_into_units(nb_name): def convert_normal_cells(normal_cells): """ Convert normal_cells into html. """ - for cell in normal_cells: - if cell.cell_type == 'markdown': - cell.source = re.sub(r'\\begin\{ *equation *\}', '\[', cell.source) - cell.source = re.sub(r'\\end\{ *equation *\}', '\]', cell.source) tmp = current.new_notebook(cells=normal_cells) html = exportHtml.from_notebook_node(tmp)[0] return html @@ -252,9 +234,9 @@ def converter(mooc_folder, args, content_folder=None): skeleton = mooc_folder + '/edx_skeleton' shutil.copytree(skeleton, dirpath) - # Loading data from syllabus - syllabus_nb = os.path.join(content_folder, 'syllabus.ipynb') - data = parse_syllabus(syllabus_nb, content_folder) + # Loading data from toc + toc = os.path.join(content_folder, 'toc.yml') + data = parse_syllabus(toc, content_folder) course_xml_path = os.path.join(dirpath, 'course.xml') with open(course_xml_path) as f: diff --git a/toc.yml b/toc.yml new file mode 100644 index 0000000..e45e241 --- /dev/null +++ b/toc.yml @@ -0,0 +1,104 @@ +- title: "Before you begin" + sections: + - title: "About this course" + location: w0_background/intro +- title: "Topology in toy models" + sections: + - title: "Hamiltonians, topology, and symmetry" + location: w1_topointro/0d + - title: "Bulk-edge correspondence in the Kitaev chain" + location: w1_topointro/1D + - title: "Assignments" + location: w1_topointro/w1_assignments +- title: "Majoranas I" + sections: + - title: "From Kitaev chain to a nanowire" + location: w2_majorana/nanowire + - title: "Majorana signatures: 4π-periodic Josephson effect, Andreev conductance quantization" + location: w2_majorana/signatures + - title: "Why Majoranas are cool: braiding and quantum computation" + location: w2_majorana/braiding + - title: "Assignments" + location: w2_majorana/w2_assignments +- title: "More parameters: charge pumping" + sections: + - title: "Thouless pumps and winding invariant" + location: w3_pump_QHE/pumps + - title: "Quantum Hall effect: pumping electrons in Landau levels" + location: w3_pump_QHE/Laughlinargument + - title: "Quantum Hall effect: edge states" + location: w3_pump_QHE/QHEedgestates + - title: "Assignments" + location: w3_pump_QHE/w3_assignments +- title: "Chern insulators" + sections: + - title: "Quantum Hall Effect on the lattice and Dirac Hamiltonian" + location: w4_haldane/QHE_lattice + - title: "Haldane model, Berry curvature, and Chern number" + location: w4_haldane/haldane_model + - title: "Assignments" + location: w4_haldane/w4_assignments +- title: "Quantum spin Hall effect" + sections: + - title: "Time-reversal symmetry and fermion parity pumps" + location: w5_qshe/fermion_parity_pump + - title: "Experimental progress and candidate materials" + location: w5_qshe/qshe_experiments + - title: "Assignments" + location: w5_qshe/w5_assignments +- title: "Three-dimensional topological insulators" + sections: + - title: "Dirac equation of the surface states, 3D Bernevig-Hughes-Zhang model" + location: w6_3dti/bhz + - title: "Experimental progress and candidate materials" + location: w6_3dti/3dti_signatures + - title: "Assignments" + location: w6_3dti/w6_assignments +- title: "Topological defects" + sections: + - title: "Majoranas in topological insulators and superconductors" + location: w7_defects/ti_majoranas + - title: "Crystalline defects in weak topological insulators" + location: w7_defects/crystalline_defects + - title: "Assignments" + location: w7_defects/w7_assignments +- title: "General approach to topological classification" + sections: + - title: "10 symmetry classes and the periodic table of topological insulators" + location: w8_general/classification + - title: "Different approaches to topological invariants" + location: w8_general/invariants + - title: "Assignments" + location: w8_general/w8_assignments +- title: "Anderson localization and topology" + sections: + - title: "Disorder and the scaling theory of localization" + location: w9_disorder/scaling + - title: "Flow diagram of topological insulators" + location: w9_disorder/topoflow + - title: "Assignments" + location: w9_disorder/w9_assignments +- title: "Extensions of classification I" + sections: + - title: "Topology in gapless systems" + location: w10_extensions/gapless + - title: "Topological mechanics" + location: w10_extensions/mechanics + - title: "Assignments" + location: w10_extensions/w10_assignments +- title: "Extensions of classification II" + sections: + - title: "Floquet topological insulators" + location: w11_extensions2/floquet + - title: "Crystalline topological insulators" + location: w11_extensions2/cti + - title: "Assignments" + location: w11_extensions2/w11_assignments +- title: "Beyond single-particle physics" + sections: + - title: "Fractional quantum Hall effect and topological particles" + location: w12_manybody/fqhe + - title: "Topological order and the toric code" + location: w12_manybody/topoorder + - title: "Assignments" + location: w12_manybody/w12_assignments -- GitLab From 3c4abb4cf8db34f7ae8dd07d884ff89b4a97ed24 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 18:08:02 +0100 Subject: [PATCH 04/18] use pathlib.Path in most cases --- scripts/converter.py | 88 +++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 55 deletions(-) diff --git a/scripts/converter.py b/scripts/converter.py index 10f10cc..a261043 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -13,31 +13,28 @@ import shutil import urllib.request from xml.etree.ElementTree import SubElement from xml.etree import ElementTree +from pathlib import Path from ruamel.yaml import YAML +import jinja2 import nbformat from nbformat import v4 as current from nbconvert import HTMLExporter -from traitlets.config import Config try: os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + ':./code' except KeyError: os.environ['PYTHONPATH'] = './code' -START_DATE = "1 Jan 2000" # Any date in the past. +START_DATE = "2000-01-01T10:00:00Z" # Any date in the past. -scripts_path = os.path.dirname(os.path.realpath(__file__)) -mooc_folder = os.path.join(scripts_path, os.pardir) - -cfg = Config({ +exportHtml = HTMLExporter(config={ 'HTMLExporter': { 'template_file': 'edx', - 'template_path': ['.', scripts_path], + 'template_path': ['.', str(Path(__file__).parent)], 'exclude_input': True, } }) -exportHtml = HTMLExporter(config=cfg) url = ( "https://cdnjs.cloudflare.com/ajax/libs" @@ -76,18 +73,8 @@ iFrameResize({{ """ -def date_to_edx(date, add_days=0): - tmp = strptime(date, '%d %b %Y') - - date = datetime.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, 10) - date = date + datetime.timedelta(days=add_days) - date = date.strftime('%Y-%m-%dT%H:%M:%SZ') - return date - - def parse_syllabus(table_of_contents, content_folder=''): - with open(table_of_contents) as f: - source = YAML().load(f) + source = YAML().load(Path(table_of_contents)) data = SimpleNamespace(category='main', chapters=[]) for i, section in enumerate(source): @@ -96,7 +83,6 @@ def parse_syllabus(table_of_contents, content_folder=''): chapter = SimpleNamespace(category='chapter', sequentials=[]) chapter.name = section['title'] - chapter.date = START_DATE chapter.url = f"sec_{i:02}" for j, subsection in enumerate(section['sections']): @@ -104,7 +90,6 @@ def parse_syllabus(table_of_contents, content_folder=''): sequential = SimpleNamespace(category='sequential', verticals=[]) sequential.name = subsection['title'] - sequential.date = START_DATE sequential.url = f"subsec_{i:02}_{j:02}" sequential.source_notebook = ( f"{content_folder}/{subsection['location']}.ipynb" @@ -158,11 +143,10 @@ def split_into_units(nb_name): def convert_normal_cells(normal_cells): """ Convert normal_cells into html. """ tmp = current.new_notebook(cells=normal_cells) - html = exportHtml.from_notebook_node(tmp)[0] - return html + return exportHtml.from_notebook_node(tmp)[0] -def convert_unit(unit, date): +def convert_unit(unit): """ Convert unit into html and special xml componenets. """ cells = unit.cells @@ -212,41 +196,38 @@ def convert_unit(unit, date): return unit_output -def converter(mooc_folder, args, content_folder=None): +def converter(mooc_folder, content_folder=None): """ Do converting job. """ # Mooc content location if content_folder is None: content_folder = mooc_folder # copying figures - target = os.path.join(mooc_folder, 'generated') - os.makedirs(os.path.join(target, 'html/edx/figures'), exist_ok=True) - for entry, *_ in os.walk(content_folder): - if re.match(content_folder + r'/w\d+_.+/figures', entry): - for filename in os.listdir(entry): - shutil.copy(os.path.join(entry, filename), - os.path.join(target, 'html/edx/figures')) - html_folder = os.path.join(target, 'html/edx') + target = mooc_folder / 'generated' + figures_path = target / 'html/edx/figures' + os.makedirs(figures_path, exist_ok=True) + for figure in content_folder.glob('w*/figures/*'): + shutil.copy(figure, figures_path) + html_folder = target / 'html/edx' # Temporary locations dirpath = tempfile.mkdtemp() + '/course' - skeleton = mooc_folder + '/edx_skeleton' + skeleton = mooc_folder / 'edx_skeleton' shutil.copytree(skeleton, dirpath) # Loading data from toc - toc = os.path.join(content_folder, 'toc.yml') + toc = content_folder / 'toc.yml' data = parse_syllabus(toc, content_folder) - course_xml_path = os.path.join(dirpath, 'course.xml') - with open(course_xml_path) as f: - xml_course = ElementTree.fromstring(f.read()) + course_xml_path = dirpath / 'course.xml' + xml_course = ElementTree.fromstring(course_xml_path.read_text()) for chapter in data.chapters: chapter_xml = SubElement(xml_course, 'chapter', attrib=dict( url_name=chapter.url, display_name=chapter.name, - start=date_to_edx(chapter.date), + start=START_DATE, )) for sequential in chapter.sequentials: @@ -271,7 +252,7 @@ def converter(mooc_folder, args, content_folder=None): display_name=unit.metadata.name, )) - unit_output = convert_unit(unit, date=sequential.date) + unit_output = convert_unit(unit) for (j, out) in enumerate(unit_output): out_url = vertical_url + f"_out_{j:02}" if isinstance(out, str): @@ -282,17 +263,13 @@ def converter(mooc_folder, args, content_folder=None): filename=out_url )) - html_path = os.path.join(html_folder, - out_url + '.html') - with open(html_path, 'w') as f: - f.write(out) + html_path = html_folder / out_url + '.html' + html_path.write_text(out) - html_path = os.path.join(dirpath, 'html', - out_url + '.html') - with open(html_path, 'w') as f: - f.write(IFRAME_TEMPLATE.format( - id=out_url, url=url, js=js - )) + html_path = dirpath / 'html' / out_url + '.html' + html_path.write_text( + IFRAME_TEMPLATE.format(id=out_url, url=url, js=js) + ) else: # adding video subelement @@ -300,11 +277,12 @@ def converter(mooc_folder, args, content_folder=None): if 'url_name' not in out.attrib: out.attrib['url_name'] = out_url - with open(course_xml_path, 'w') as f: - f.write(ElementTree.tostring(xml_course, encoding='unicode')) + course_xml_path.write_text( + ElementTree.tostring(xml_course, encoding='unicode') + ) # Creating tar - tar_filepath = os.path.join(target, 'import_to_edx.tar.gz') + tar_filepath = target / 'import_to_edx.tar.gz' tar = tarfile.open(name=tar_filepath, mode='w:gz') tar.add(dirpath, arcname='') tar.close() @@ -314,14 +292,14 @@ def converter(mooc_folder, args, content_folder=None): def main(): - mooc_folder = os.path.join(os.path.dirname(__file__), os.path.pardir) + mooc_folder = Path(__file__).parent.parent parser = argparse.ArgumentParser() parser.add_argument('source', nargs='?', help='folder to convert') args = parser.parse_args() print('Path to mooc folder:', mooc_folder) print('Path to notebooks:', args.source) - converter(mooc_folder, args, content_folder=args.source) + converter(mooc_folder, content_folder=Path(args.source)) if __name__ == "__main__": -- GitLab From fbfa528cd9ada84e790e749caadc2291051217b2 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 18:41:40 +0100 Subject: [PATCH 05/18] update docker configuration --- docker/Dockerfile | 6 +++--- docker/python3.yaml | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 9510e0c..71f4efe 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -48,9 +48,9 @@ ENV LANGUAGE en_US.UTF-8 RUN cd /tmp && \ mkdir -p $CONDA_DIR && \ - wget --quiet https://repo.continuum.io/miniconda/Miniconda3-4.3.11-Linux-x86_64.sh && \ - /bin/bash Miniconda3-4.3.11-Linux-x86_64.sh -f -b -p $CONDA_DIR && \ - rm Miniconda3-4.3.11-Linux-x86_64.sh + wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-4.5.11-Linux-x86_64.sh && \ + /bin/bash Miniconda3-4.5.11-Linux-x86_64.sh -f -b -p $CONDA_DIR && \ + rm Miniconda3-4.5.11-Linux-x86_64.sh # Add environment file COPY python3.yaml . diff --git a/docker/python3.yaml b/docker/python3.yaml index 290744a..72127f7 100644 --- a/docker/python3.yaml +++ b/docker/python3.yaml @@ -4,7 +4,7 @@ channels: dependencies: - python=3.6 - matplotlib=2.0.0 - - kwant=1.3* # Untill v1.3 is out + - kwant=1.3* - scipy=1.1* - holoviews=1.7* - feedparser=5.2* @@ -14,3 +14,5 @@ dependencies: - notebook - markdown - notedown + - pip: + - ruamel.yaml -- GitLab From e61653025b18151790204406072d431d74be11a2 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 18:43:18 +0100 Subject: [PATCH 06/18] use a correct location for the converter --- scripts/converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/converter.py b/scripts/converter.py index a261043..2736163 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -217,7 +217,7 @@ def converter(mooc_folder, content_folder=None): shutil.copytree(skeleton, dirpath) # Loading data from toc - toc = content_folder / 'toc.yml' + toc = mooc_folder / 'toc.yml' data = parse_syllabus(toc, content_folder) course_xml_path = dirpath / 'course.xml' -- GitLab From 152bc580bacd32693c7e4a1a5b9500940fda5665 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 19:25:40 +0100 Subject: [PATCH 07/18] automatically generate syllabus --- .gitlab-ci.yml | 1 + scripts/converter.py | 8 ++++ syllabus.md => syllabus.md.j2 | 70 ++++++----------------------------- 3 files changed, 20 insertions(+), 59 deletions(-) rename syllabus.md => syllabus.md.j2 (56%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 462ff5a..a153c75 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,6 +12,7 @@ execute_ipynbs: - export OPENBLAS_NUM_THREADS=1 OMP_NUM_THREADS=1 MKL_NUM_THREADS=1 MKL_DYNAMIC=FALSE - export DEST=generated/with_output - mkdir -p $DEST + - python -c "from scripts import converter; converter.expand_syllabus('toc.yml', 'syllabus.md.j2', 'syllabus.md')" - cp syllabus.md $DEST - cp -r data $DEST - cp -r w[!e]*_* $DEST diff --git a/scripts/converter.py b/scripts/converter.py index 2736163..405c0af 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -291,6 +291,14 @@ def converter(mooc_folder, content_folder=None): shutil.rmtree(dirpath) +def expand_syllabus(toc, template, out): + """Plug the TOC data into a syllabus template.""" + Path(out).write_text( + jinja2.Template(Path(template).read_text()) + .render(chapters=YAML().load(Path(toc))) + ) + + def main(): mooc_folder = Path(__file__).parent.parent parser = argparse.ArgumentParser() diff --git a/syllabus.md b/syllabus.md.j2 similarity index 56% rename from syllabus.md rename to syllabus.md.j2 index 6dddbb0..d25f576 100644 --- a/syllabus.md +++ b/syllabus.md.j2 @@ -1,58 +1,10 @@ # Syllabus -* **Before you begin** - * [About this course](w0_background/intro.ipynb) -* **Topology in toy models** - * [Hamiltonians, topology, and symmetry](w1_topointro/0d.ipynb) - * [Bulk-edge correspondence in the Kitaev chain](w1_topointro/1D.ipynb) - * [Assignments](w1_topointro/w1_assignments.ipynb) -* **Majoranas I** - * [From Kitaev chain to a nanowire](w2_majorana/nanowire.ipynb) - * [Majorana signatures: 4π-periodic Josephson effect, Andreev conductance quantization](w2_majorana/signatures.ipynb) - * [Why Majoranas are cool: braiding and quantum computation](w2_majorana/braiding.ipynb) - * [Assignments](w2_majorana/w2_assignments.ipynb) -* **More parameters: charge pumping** - * [Thouless pumps and winding invariant](w3_pump_QHE/pumps.ipynb) - * [Quantum Hall effect: pumping electrons in Landau levels](w3_pump_QHE/Laughlinargument.ipynb) - * [Quantum Hall effect: edge states](w3_pump_QHE/QHEedgestates.ipynb) - * [Assignments](w3_pump_QHE/w3_assignments.ipynb) -* **Chern insulators** - * [Quantum Hall Effect on the lattice and Dirac Hamiltonian](w4_haldane/QHE_lattice.ipynb) - * [Haldane model, Berry curvature, and Chern number](w4_haldane/haldane_model.ipynb) - * [Assignments](w4_haldane/w4_assignments.ipynb) -* **Quantum spin Hall effect** - * [Time-reversal symmetry and fermion parity pumps](w5_qshe/fermion_parity_pump.ipynb) - * [Experimental progress and candidate materials](w5_qshe/qshe_experiments.ipynb) - * [Assignments](w5_qshe/w5_assignments.ipynb) -* **Three-dimensional topological insulators** - * [Dirac equation of the surface states, 3D Bernevig-Hughes-Zhang model](w6_3dti/bhz.ipynb) - * [Experimental progress and candidate materials](w6_3dti/3dti_signatures.ipynb) - * [Assignments](w6_3dti/w6_assignments.ipynb) -* **Topological defects** - * [Majoranas in topological insulators and superconductors](w7_defects/ti_majoranas.ipynb) - * [Crystalline defects in weak topological insulators](w7_defects/crystalline_defects.ipynb) - * [Assignments](w7_defects/w7_assignments.ipynb) -* **General approach to topological classification** - * [10 symmetry classes and the periodic table of topological insulators](w8_general/classification.ipynb) - * [Different approaches to topological invariants](w8_general/invariants.ipynb) - * [Assignments](w8_general/w8_assignments.ipynb) -* **Anderson localization and topology** - * [Disorder and the scaling theory of localization](w9_disorder/scaling.ipynb) - * [Flow diagram of topological insulators](w9_disorder/topoflow.ipynb) - * [Assignments](w9_disorder/w9_assignments.ipynb) -* **Extensions of classification I** - * [Topology in gapless systems](w10_extensions/gapless.ipynb) - * [Topological mechanics](w10_extensions/mechanics.ipynb) - * [Assignments](w10_extensions/w10_assignments.ipynb) -* **Extensions of classification II** - * [Floquet topological insulators](w11_extensions2/floquet.ipynb) - * [Crystalline topological insulators](w11_extensions2/cti.ipynb) - * [Assignments](w11_extensions2/w11_assignments.ipynb) -* **Beyond single-particle physics** - * [Fractional quantum Hall effect and topological particles](w12_manybody/fqhe.ipynb) - * [Topological order and the toric code](w12_manybody/topoorder.ipynb) - * [Assignments](w12_manybody/w12_assignments.ipynb) - +{% for chapter in chapters -%} +* **{{chapter.title}}** +{% for section in chapter.sections %} * [{{section.title}}]({{section.location}}.ipynb) +{% endfor -%} +{%- endfor %} ## Course structure * Each section consists of several relatively self-contained topics, each introduced and summarized by an expert. @@ -72,12 +24,12 @@ ## Other course developers -The initial verison of the course was created by: -Anton Akhmerov -Jay Sau -Bernard van Heck -Sebastian Rubbert -Rafał Skolasiński. +The initial verison of the course was created by: +* Anton Akhmerov +* Jay Sau +* Bernard van Heck +* Sebastian Rubbert +* Rafał Skolasiński ## Acknowledgements -- GitLab From 9a090ab7f816c48a5bf2b175230d840e9a323fc9 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 19:49:35 +0100 Subject: [PATCH 08/18] install yaml in CI --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a153c75..ad2f70d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,8 @@ stages: execute_ipynbs: stage: execute + before_script: + - pip install ruamel.yaml script: - export PYTHONPATH=$PYTHONPATH:${PWD}/code - export OPENBLAS_NUM_THREADS=1 OMP_NUM_THREADS=1 MKL_NUM_THREADS=1 MKL_DYNAMIC=FALSE -- GitLab From 78d01d663bb605c516067673aa71ca397aa444e0 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 20:34:11 +0100 Subject: [PATCH 09/18] install yaml in all jobs --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ad2f70d..f33bf41 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,6 +48,8 @@ mirror to github: edx archive: stage: build + before_script: + - pip install ruamel.yaml script: ./scripts/converter.py ./generated/with_output artifacts: paths: @@ -60,6 +62,8 @@ pelican website: stage: build variables: DEST: "generated/html" + before_script: + - pip install ruamel.yaml script: - python scripts/converter_pelican.py - pelican -o $DEST/ -s website_assets/pelicanconf.py generated/pelican_content @@ -72,6 +76,8 @@ pelican website: ocw website: stage: build + before_script: + - pip install ruamel.yaml script: - python scripts/converter_ocw.py - mv website_assets/iframes.txt generated/html/ocw -- GitLab From 11932b426443cca65aeb43b2a685334e621c544b Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 20:47:12 +0100 Subject: [PATCH 10/18] bugfix: convert another string to Path --- scripts/converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/converter.py b/scripts/converter.py index 405c0af..2605935 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -211,7 +211,7 @@ def converter(mooc_folder, content_folder=None): html_folder = target / 'html/edx' # Temporary locations - dirpath = tempfile.mkdtemp() + '/course' + dirpath = Path(tempfile.mkdtemp()) / 'course' skeleton = mooc_folder / 'edx_skeleton' shutil.copytree(skeleton, dirpath) -- GitLab From 0ec73dd5ed187de59870d1eeefe6a6ef9eca4698 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 21:03:14 +0100 Subject: [PATCH 11/18] remove a separate function for converting TOC format --- scripts/converter.py | 62 +++++++++++++------------------------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/scripts/converter.py b/scripts/converter.py index 2605935..dd4064c 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -73,37 +73,9 @@ iFrameResize({{ """ -def parse_syllabus(table_of_contents, content_folder=''): - source = YAML().load(Path(table_of_contents)) - - data = SimpleNamespace(category='main', chapters=[]) - for i, section in enumerate(source): - - # creating chapter - chapter = SimpleNamespace(category='chapter', sequentials=[]) - - chapter.name = section['title'] - chapter.url = f"sec_{i:02}" - - for j, subsection in enumerate(section['sections']): - # creating sequential - sequential = SimpleNamespace(category='sequential', verticals=[]) - - sequential.name = subsection['title'] - sequential.url = f"subsec_{i:02}_{j:02}" - sequential.source_notebook = ( - f"{content_folder}/{subsection['location']}.ipynb" - ) - - chapter.sequentials.append(sequential) - - data.chapters.append(chapter) - return data - - def split_into_units(nb_name): """Split notebook into units where top level headings occur.""" - nb = nbformat.read(nb_name, as_version=4) + nb = nbformat.read(str(nb_name), as_version=4) # Split markdown cells on titles. def split_cells(): @@ -217,35 +189,37 @@ def converter(mooc_folder, content_folder=None): shutil.copytree(skeleton, dirpath) # Loading data from toc - toc = mooc_folder / 'toc.yml' - data = parse_syllabus(toc, content_folder) + chapters = YAML().load(Path(mooc_folder / 'toc.yml').read_text()) course_xml_path = dirpath / 'course.xml' xml_course = ElementTree.fromstring(course_xml_path.read_text()) - for chapter in data.chapters: + for chapter_number, chapter in enumerate(chapters): chapter_xml = SubElement(xml_course, 'chapter', attrib=dict( - url_name=chapter.url, - display_name=chapter.name, + url_name=f"sec_{chapter_number:02}", + display_name=chapter['title'], start=START_DATE, )) - for sequential in chapter.sequentials: + for section_number, section in enumerate(chapter['sections']): + section_url = f"subsec_{chapter_number:02}_{section_number:02}" sequential_xml = SubElement(chapter_xml, 'sequential', attrib=dict( - url_name=sequential.url, - display_name=sequential.name, - graded=('true' if chapter.url != 'sec_00' else 'false'), + url_name=section_url, + display_name=section['title'], + graded=bool(chapter_number), )) - if sequential.name == 'Assignments': + if section['title'] == 'Assignments': sequential_xml.attrib['format'] = "Research" - elif chapter.url != 'sec_00': + elif chapter_number: sequential_xml.attrib['format'] = "Self-check" - units = split_into_units(sequential.source_notebook) + units = split_into_units( + content_folder / (section['location'] + '.ipynb') + ) for i, unit in enumerate(units): - vertical_url = sequential.url + f'_{i:02}' + vertical_url = section_url + f'_{i:02}' # add vertical info to sequential_xml vertical = SubElement(sequential_xml, 'vertical', attrib=dict( url_name=vertical_url, @@ -263,10 +237,10 @@ def converter(mooc_folder, content_folder=None): filename=out_url )) - html_path = html_folder / out_url + '.html' + html_path = html_folder / (out_url + '.html') html_path.write_text(out) - html_path = dirpath / 'html' / out_url + '.html' + html_path = dirpath / 'html' / (out_url + '.html') html_path.write_text( IFRAME_TEMPLATE.format(id=out_url, url=url, js=js) ) -- GitLab From b544b35acacf6a2a161dc269851d5bdc1289ac33 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 21:25:49 +0100 Subject: [PATCH 12/18] correct xml value of boolean --- scripts/converter.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/converter.py b/scripts/converter.py index dd4064c..371a26f 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -203,11 +203,15 @@ def converter(mooc_folder, content_folder=None): for section_number, section in enumerate(chapter['sections']): section_url = f"subsec_{chapter_number:02}_{section_number:02}" - sequential_xml = SubElement(chapter_xml, 'sequential', attrib=dict( - url_name=section_url, - display_name=section['title'], - graded=bool(chapter_number), - )) + sequential_xml = SubElement( + chapter_xml, + 'sequential', + attrib={ + 'url_name': section_url, + 'display_name': section['title'], + 'graded': ('true' if chapter_number else 'false'), + }, + ) if section['title'] == 'Assignments': sequential_xml.attrib['format'] = "Research" -- GitLab From 83df6429cd026d4b2b27adb28bed486d916e7c91 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 21:58:44 +0100 Subject: [PATCH 13/18] update OCW converter --- scripts/converter_ocw.py | 51 +++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/scripts/converter_ocw.py b/scripts/converter_ocw.py index 38b07af..8d8197a 100644 --- a/scripts/converter_ocw.py +++ b/scripts/converter_ocw.py @@ -4,14 +4,11 @@ import os import datetime import glob import shutil -from traitlets.config import Config +from pathlib import Path + from nbconvert import HTMLExporter from nbconvert.filters.markdown import markdown2html_pandoc -from converter import (mooc_folder, - parse_syllabus, - scripts_path, - split_into_units, - url) +from converter import split_into_units, url IFRAME_TEMPLATE = r""" <iframe id="{id}" scrolling="no" width="100%" height="600px", frameborder=0> @@ -40,34 +37,34 @@ with open('website_assets/iframes.txt', 'w') as f: now = datetime.datetime.now() print('Executed on {} at {}.'.format(now.date(), now.time()), file=f) -cfg = Config({'HTMLExporter': {'template_file': 'ocw', - 'template_path': ['.', scripts_path], - 'filters': {'markdown2html': - markdown2html_pandoc}}}) - -exportHtml = HTMLExporter(config=cfg) +exportHtml = HTMLExporter(config={ + 'HTMLExporter': { + 'template_file': 'ocw', + 'template_path': ['.', str(Path(__file__).parent)], + 'filters': {'markdown2html': markdown2html_pandoc} + } +}) # Mooc content location -output_dir = os.path.join(mooc_folder, 'generated/html/ocw') -generated_ipynbs = os.path.join(mooc_folder, 'generated/with_output') +mooc_folder = Path(__file__).parent.parent +output_dir = mooc_folder / 'generated/html/ocw' +generated_ipynbs = mooc_folder / 'generated/with_output' # Loading data from syllabus -syllabus_nb = os.path.join(generated_ipynbs, 'syllabus.ipynb') -data = parse_syllabus(syllabus_nb, generated_ipynbs) +syllabus_nb = generated_ipynbs / 'syllabus.ipynb' +chapters = YAML().load(Path(mooc_folder / 'toc.yml').read_text()) -for chapter in data.chapters: - chap_num = int(chapter.url[-2:]) - for sequential in chapter.sequentials: - units = split_into_units(sequential.source_notebook) - folder, fname = sequential.source_notebook.split('/')[-2:] +for chapter in chapters: + for section in chapter['sections']: + notebook = generated_ipynbs / (section['location'] + '.ipynb') + units = split_into_units(notebook) + folder, fname = notebook.parent, notebook.name for i, unit in enumerate(units): fname = fname.replace('.ipynb', '') - new_fname = '{}_{}'.format(fname, i) - new_path = os.path.join(mooc_folder, output_dir, folder, new_fname + '.html') - os.makedirs(os.path.dirname(new_path), exist_ok=True) - html = exportHtml.from_notebook_node(unit)[0] - with open(new_path, 'w') as f: - f.write(html) + new_fname = f'{fname}_{i}' + new_path = mooc_folder / output_dir / folder / (new_fname + '.html') + os.makedirs(new_path.parent, exist_ok=True) + new_path.write_text(exportHtml.from_notebook_node(unit)[0]) with open('website_assets/iframes.txt', 'a') as f: ID = '{}_{}'.format(folder, new_fname) -- GitLab From 1b9fce667457ef26746dcec0f1a6e01d936092b3 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 22:24:06 +0100 Subject: [PATCH 14/18] restore saving syllabus --- edx_skeleton/tabs/.keep | 0 scripts/converter.py | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 edx_skeleton/tabs/.keep diff --git a/edx_skeleton/tabs/.keep b/edx_skeleton/tabs/.keep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/converter.py b/scripts/converter.py index 371a26f..5c68cfd 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -191,6 +191,11 @@ def converter(mooc_folder, content_folder=None): # Loading data from toc chapters = YAML().load(Path(mooc_folder / 'toc.yml').read_text()) + # Convert the syllabus and save it in /tabs + (skeleton / 'tabs' / 'syllabus.html').write_text( + exportHtml.from_filename(content_folder / 'syllabus.ipynb')[0] + ) + course_xml_path = dirpath / 'course.xml' xml_course = ElementTree.fromstring(course_xml_path.read_text()) -- GitLab From 27313be9f6d58b3faa3dfd05fbe8b7b69ec5e8c5 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 22:26:04 +0100 Subject: [PATCH 15/18] add missing import --- scripts/converter_ocw.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/converter_ocw.py b/scripts/converter_ocw.py index 8d8197a..440ec33 100644 --- a/scripts/converter_ocw.py +++ b/scripts/converter_ocw.py @@ -8,6 +8,8 @@ from pathlib import Path from nbconvert import HTMLExporter from nbconvert.filters.markdown import markdown2html_pandoc +from ruamel.yaml import YAML + from converter import split_into_units, url IFRAME_TEMPLATE = r""" -- GitLab From a65e3534ce367e536a2d5fe1df01f73e76c81301 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 23:21:25 +0100 Subject: [PATCH 16/18] remove unused imports --- scripts/converter.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/converter.py b/scripts/converter.py index 5c68cfd..a99f97c 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -1,14 +1,11 @@ #!/usr/bin/env python3 import argparse -import datetime from itertools import dropwhile import os import re import tarfile import tempfile -from time import strptime -from types import SimpleNamespace import shutil import urllib.request from xml.etree.ElementTree import SubElement -- GitLab From 612382c82bd34727e7d8682dabc21bdcd5a96624 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Tue, 1 Jan 2019 23:44:28 +0100 Subject: [PATCH 17/18] correct question formatting --- code/edx_components.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/code/edx_components.py b/code/edx_components.py index d66b376..1c6f5e6 100644 --- a/code/edx_components.py +++ b/code/edx_components.py @@ -1,5 +1,6 @@ import sys import os +import re import secrets from textwrap import dedent @@ -33,6 +34,10 @@ def _add_solution(el, text): content.text = text +def _replace_latex_delimiters(text): + return re.sub(r'\$(.+?)\$', (lambda match: fr"\({match.group(1)}\)"), text) + + class MoocComponent: def _repr_mimebundle_(self, include, exclude): return { @@ -153,12 +158,12 @@ class MoocCheckboxesAssessment(MoocComponent): max_attempts=str(max_attempts), )) - SubElement(xml, 'p', text='question') - SubElement(xml, 'p', text='Select the answers that match') sub = SubElement(xml, 'choiceresponse') + SubElement(sub, 'label').text = _replace_latex_delimiters(question) + SubElement(sub, 'description').text = 'Select the answers that match' + sub = SubElement(sub, 'checkboxgroup') - sub.attrib['label'] = "Select the answers that match" sub.attrib['direction'] = "vertical" for i, ans in enumerate(answers): @@ -167,10 +172,10 @@ class MoocCheckboxesAssessment(MoocComponent): choice.attrib['correct'] = 'true' else: choice.attrib['correct'] = 'false' - choice.text = ans + choice.text = _replace_latex_delimiters(ans) if explanation is not None: - _add_solution(xml, explanation) + _add_solution(xml, _replace_latex_delimiters(explanation)) self.question = question self.answers = answers @@ -226,12 +231,11 @@ class MoocMultipleChoiceAssessment(MoocComponent): max_attempts=str(max_attempts), )) - SubElement(xml, 'p', text=question) - SubElement(xml, 'p', text='Please select correct answer') - sub = SubElement(xml, 'multiplechoiceresponse') + SubElement(sub, 'label').text = _replace_latex_delimiters(question) + SubElement(sub, 'description').text = 'Select the correct answer' + sub = SubElement(sub, 'choicegroup') - sub.attrib['label'] = "Please select correct answer" sub.attrib['type'] = "MultipleChoice" for i, ans in enumerate(answers): @@ -240,10 +244,10 @@ class MoocMultipleChoiceAssessment(MoocComponent): choice.attrib['correct'] = 'true' else: choice.attrib['correct'] = 'false' - choice.text = ans + choice.text = _replace_latex_delimiters(ans) if explanation is not None: - _add_solution(xml, explanation) + _add_solution(xml, _replace_latex_delimiters(explanation)) self.question = question self.answers = answers -- GitLab From a35c536fe2439169f2467ffcf7399f4e5dcf3b14 Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Wed, 2 Jan 2019 00:18:03 +0100 Subject: [PATCH 18/18] hide anchors in edx output --- scripts/converter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/converter.py b/scripts/converter.py index a99f97c..756f4b4 100755 --- a/scripts/converter.py +++ b/scripts/converter.py @@ -30,7 +30,8 @@ exportHtml = HTMLExporter(config={ 'template_file': 'edx', 'template_path': ['.', str(Path(__file__).parent)], 'exclude_input': True, - } + 'anchor_link_text': ' ', + }, }) url = ( -- GitLab