From 7ea77424fe50632c157017d4b1cb0a2964685518 Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Thu, 11 Jul 2024 21:20:01 +0200 Subject: [PATCH 01/10] Add section for flight reports (and example) --- orcestra_book/_toc.yml | 2 +- .../flight_reports/HALO-20240810a.md | 44 +++++++++++++++++++ orcestra_book/operation.md | 1 + 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 orcestra_book/flight_reports/HALO-20240810a.md create mode 100644 orcestra_book/operation.md diff --git a/orcestra_book/_toc.yml b/orcestra_book/_toc.yml index a8e6567e..adbb1ef2 100644 --- a/orcestra_book/_toc.yml +++ b/orcestra_book/_toc.yml @@ -22,7 +22,7 @@ chapters: sections: - file: virtual_campaign - file: water_vapour_overview -#- file: operation +- file: operation - file: data sections: #- file: data_policy diff --git a/orcestra_book/flight_reports/HALO-20240810a.md b/orcestra_book/flight_reports/HALO-20240810a.md new file mode 100644 index 00000000..9c86ed30 --- /dev/null +++ b/orcestra_book/flight_reports/HALO-20240810a.md @@ -0,0 +1,44 @@ +--- +mission: ORCESTRA +platform: HALO +flight_id: HALO-20240810a +nickname: Example +pi: "Lukas Kluft" +takeoff: "2024-08-10 09:13:00" +landing: "2024-08-10 15:46:00" +objectives: [cat-a, cat-b] +orphan: true +--- + +# HALO-20240810a + +## Summary + +This flight took place on 2024-08-10. Everything went fine besides the "Oh no!" at 08:00h. + +```{note} +Something noteworthy happened! +``` + +## Overview + +During our flight we observed deep convection. Obviously, we didn't fly throught it because everyone was concerned we could die. + +```{figure} https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExOWl2Zzh3ZWxnY3J2ZTVramlkMDMxdjdvcnoxOXloaXF0NHdjcDIzaCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/3o6EhOYMhOTANYgHMk/giphy.gif +:alt: Deep convection. + +Development of deep-convective clouds. +``` + +## Remarks + +* The second circle was a square + +## Events + +Time | Comment +--- | --- +07:00 | All good. +08:00 | Oh no! +14:00 | Yada yada. +18:00 | Feierabend! diff --git a/orcestra_book/operation.md b/orcestra_book/operation.md new file mode 100644 index 00000000..eb90f937 --- /dev/null +++ b/orcestra_book/operation.md @@ -0,0 +1 @@ +# Operation From 1f584ff089db7ac941c5b12c10e704380bb28f6c Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Thu, 11 Jul 2024 21:56:19 +0200 Subject: [PATCH 02/10] Add python script to create flight overview --- orcestra_book/_config.yml | 1 + orcestra_book/_ext/create_flight_table.py | 49 +++++++++++++++++++++++ orcestra_book/operation.md | 1 + 3 files changed, 51 insertions(+) create mode 100644 orcestra_book/_ext/create_flight_table.py diff --git a/orcestra_book/_config.yml b/orcestra_book/_config.yml index c2b49963..1476c828 100644 --- a/orcestra_book/_config.yml +++ b/orcestra_book/_config.yml @@ -30,6 +30,7 @@ sphinx: local_extensions: apastyle: _ext/ bracket_citation_style: _ext/ + create_flight_table: _ext/ config: bibtex_default_style: myapastyle bibtex_reference_style: author_year_round diff --git a/orcestra_book/_ext/create_flight_table.py b/orcestra_book/_ext/create_flight_table.py new file mode 100644 index 00000000..6b412842 --- /dev/null +++ b/orcestra_book/_ext/create_flight_table.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +import os +import pathlib +import subprocess +from datetime import datetime + +from frontmatter import Frontmatter +from jinja2 import Template + + +templ = """ +# Operation + +Flight-ID | Date | Takeoff | Landing | PI | Nickname | Objectives +--- | --- | --- | --- | --- | --- | --- +{% for k, v in flights.items() -%} +[](flight_reports/{{ k }}) | {{ v["expr_date"] }} | {{ v["expr_takeoff"] }} | {{ v["expr_landing"] }} | {{ v["pi"] }} | {{ v["nickname"] }} | {{ v["objectives"]|join(', ') }} +{% endfor -%} +""" + + +def read_frontmatter(path): + attrs = Frontmatter.read_file(path)["attributes"] + + takeoff = datetime.fromisoformat(attrs["takeoff"]) + landing = datetime.fromisoformat(attrs["landing"]) + + attrs["expr_date"] = takeoff.strftime("%d %B %Y") + attrs["expr_takeoff"] = takeoff.strftime("%X") + attrs["expr_landing"] = landing.strftime("%X") + + return attrs + + +def collect_fronmatter(): + flights = pathlib.Path("orcestra_book/flight_reports/").glob("*[0-9]*[a-z].md") + return {fm["flight_id"]: fm for fm in map(read_frontmatter, sorted(flights))} + + +def write_flight_table(app, path="orcestra_book/operation.md"): + frontmatters = collect_fronmatter() + + with open(path, "w") as fp: + t = Template(templ) + fp.write(t.render(flights=frontmatters)) + + +def setup(app): + app.connect('builder-inited', write_flight_table) diff --git a/orcestra_book/operation.md b/orcestra_book/operation.md index eb90f937..35e502ec 100644 --- a/orcestra_book/operation.md +++ b/orcestra_book/operation.md @@ -1 +1,2 @@ + # Operation From 61b52d394cedd7c08a9b7d2e39768b7a8bf7f4a4 Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Fri, 12 Jul 2024 14:35:28 +0200 Subject: [PATCH 03/10] Create badges for each flight category --- orcestra_book/_config.yml | 1 + orcestra_book/_ext/category_role.py | 45 +++++++++++++++++++ orcestra_book/_ext/create_flight_table.py | 9 ++-- orcestra_book/_static/custom.css | 32 +++++++++++++ .../flight_reports/HALO-20240810a.md | 6 +-- 5 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 orcestra_book/_ext/category_role.py diff --git a/orcestra_book/_config.yml b/orcestra_book/_config.yml index 1476c828..02b27613 100644 --- a/orcestra_book/_config.yml +++ b/orcestra_book/_config.yml @@ -30,6 +30,7 @@ sphinx: local_extensions: apastyle: _ext/ bracket_citation_style: _ext/ + category_role: _ext/ create_flight_table: _ext/ config: bibtex_default_style: myapastyle diff --git a/orcestra_book/_ext/category_role.py b/orcestra_book/_ext/category_role.py new file mode 100644 index 00000000..88e590c4 --- /dev/null +++ b/orcestra_book/_ext/category_role.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from docutils import nodes + +from sphinx.util.docutils import SphinxRole + + +class CategoryRole(SphinxRole): + def run(self): + tier = { + # Important objectives + "ec_under": "a", + "ec_track": "a", + "two_circ": "a", + "itcz_circ": "a", + # Semi-important objectives + "south": "b", + "atr_circ": "b", + "atr_over": "b", + "sar": "b", + "pace": "b", + "gpm": "b", + "mindels": "b", + "cloud_circ": "b", + "curtain": "b", + "radar": "b", + # Nice-to-have + "cirrus": "c", + }.get(self.text, "unknown") + + node = nodes.raw( + text=f'{self.text}', + format="html", + ) + + return [node], [] + + +def setup(app): + app.add_role("cat", CategoryRole()) + + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/orcestra_book/_ext/create_flight_table.py b/orcestra_book/_ext/create_flight_table.py index 6b412842..74166479 100644 --- a/orcestra_book/_ext/create_flight_table.py +++ b/orcestra_book/_ext/create_flight_table.py @@ -11,10 +11,10 @@ templ = """ # Operation -Flight-ID | Date | Takeoff | Landing | PI | Nickname | Objectives +Flight-ID | Date | Takeoff | Landing | PI | Nickname | Categories --- | --- | --- | --- | --- | --- | --- {% for k, v in flights.items() -%} -[](flight_reports/{{ k }}) | {{ v["expr_date"] }} | {{ v["expr_takeoff"] }} | {{ v["expr_landing"] }} | {{ v["pi"] }} | {{ v["nickname"] }} | {{ v["objectives"]|join(', ') }} +[](flight_reports/{{ k }}) | {{ v["expr_date"] }} | {{ v["expr_takeoff"] }} | {{ v["expr_landing"] }} | {{ v["pi"] }} | {{ v["nickname"] }} | {{ v["expr_categories"]|join(' ') }} {% endfor -%} """ @@ -22,12 +22,13 @@ def read_frontmatter(path): attrs = Frontmatter.read_file(path)["attributes"] - takeoff = datetime.fromisoformat(attrs["takeoff"]) - landing = datetime.fromisoformat(attrs["landing"]) + takeoff = attrs["takeoff"] + landing = attrs["landing"] attrs["expr_date"] = takeoff.strftime("%d %B %Y") attrs["expr_takeoff"] = takeoff.strftime("%X") attrs["expr_landing"] = landing.strftime("%X") + attrs["expr_categories"] = map(lambda s: f"{{cat}}`{s}`", attrs.get("categories", [])) return attrs diff --git a/orcestra_book/_static/custom.css b/orcestra_book/_static/custom.css index 565ba3c0..f16f3677 100644 --- a/orcestra_book/_static/custom.css +++ b/orcestra_book/_static/custom.css @@ -45,3 +45,35 @@ html { --pst-font-family-heading: Aileron, var(--pst-font-family-base-system); --pst-font-family-base: Aileron, var(--pst-font-family-base-system); } + +.badge { + display: inline-block; + padding: 0.25em 0.5em; + font-size: tiny; + font-weight: 400; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25em; +} + +.cat-a { + background-color: #33691E; + color: #fff; +} + +.cat-b { + background-color: #8BC34A; + color: #333; +} + +.cat-c { + background-color: #C5E1A5; + color: #333; +} + +.cat-unknown { + background-color: #fff; + color: red; + font-style: italic; +} diff --git a/orcestra_book/flight_reports/HALO-20240810a.md b/orcestra_book/flight_reports/HALO-20240810a.md index 9c86ed30..ffffc933 100644 --- a/orcestra_book/flight_reports/HALO-20240810a.md +++ b/orcestra_book/flight_reports/HALO-20240810a.md @@ -4,9 +4,9 @@ platform: HALO flight_id: HALO-20240810a nickname: Example pi: "Lukas Kluft" -takeoff: "2024-08-10 09:13:00" -landing: "2024-08-10 15:46:00" -objectives: [cat-a, cat-b] +takeoff: 2024-08-10 09:13:00 +landing: 2024-08-10 15:46:00 +categories: [ec_under, ec_track, atr_over, radar, cirrus] orphan: true --- From 32ee297996912bfd5ff63006ee4fb980e4144a3c Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Mon, 15 Jul 2024 11:41:46 +0200 Subject: [PATCH 04/10] Add `badges` role to add a row of badges to a flight report --- orcestra_book/_ext/category_role.py | 71 ++++++++++++------- orcestra_book/_static/custom.css | 5 ++ .../flight_reports/HALO-20240810a.md | 2 + 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/orcestra_book/_ext/category_role.py b/orcestra_book/_ext/category_role.py index 88e590c4..bbb7ecb4 100644 --- a/orcestra_book/_ext/category_role.py +++ b/orcestra_book/_ext/category_role.py @@ -1,42 +1,63 @@ #!/usr/bin/env python3 from docutils import nodes +from frontmatter import Frontmatter from sphinx.util.docutils import SphinxRole +def create_badge(cat_id): + """Return an HTML node based on a category id.""" + cat_tier = { + # Important objectives + "ec_under": "a", + "ec_track": "a", + "two_circ": "a", + "itcz_circ": "a", + # Semi-important objectives + "south": "b", + "atr_circ": "b", + "atr_over": "b", + "sar": "b", + "pace": "b", + "gpm": "b", + "mindels": "b", + "cloud_circ": "b", + "curtain": "b", + "radar": "b", + # Nice-to-have + "cirrus": "c", + }.get(cat_id, "unknown") + + node = nodes.raw( + text=f'{cat_id}', + format="html", + ) + + return node + + class CategoryRole(SphinxRole): def run(self): - tier = { - # Important objectives - "ec_under": "a", - "ec_track": "a", - "two_circ": "a", - "itcz_circ": "a", - # Semi-important objectives - "south": "b", - "atr_circ": "b", - "atr_over": "b", - "sar": "b", - "pace": "b", - "gpm": "b", - "mindels": "b", - "cloud_circ": "b", - "curtain": "b", - "radar": "b", - # Nice-to-have - "cirrus": "c", - }.get(self.text, "unknown") - - node = nodes.raw( - text=f'{self.text}', - format="html", - ) + node = create_badge(self.text) return [node], [] +class BadgesRole(SphinxRole): + def run(self): + """(Re-)parse the frontmatter of the current document and create badges.""" + categories = Frontmatter.read_file( + self.env.doc2path(self.env.docname) + )["attributes"].get("categories", []) + + nodes = [create_badge(cat_id) for cat_id in categories] + + return nodes, [] + + def setup(app): app.add_role("cat", CategoryRole()) + app.add_role("badges", BadgesRole()) return { "version": "0.1", diff --git a/orcestra_book/_static/custom.css b/orcestra_book/_static/custom.css index f16f3677..e6cd88b6 100644 --- a/orcestra_book/_static/custom.css +++ b/orcestra_book/_static/custom.css @@ -57,6 +57,11 @@ html { border-radius: 0.25em; } +/* HACK: Fix spacing between span elements in "row view". */ +.badge + .badge { + margin-left: 0.25em; +} + .cat-a { background-color: #33691E; color: #fff; diff --git a/orcestra_book/flight_reports/HALO-20240810a.md b/orcestra_book/flight_reports/HALO-20240810a.md index ffffc933..48038e73 100644 --- a/orcestra_book/flight_reports/HALO-20240810a.md +++ b/orcestra_book/flight_reports/HALO-20240810a.md @@ -12,6 +12,8 @@ orphan: true # HALO-20240810a +{badges}`_` + ## Summary This flight took place on 2024-08-10. Everything went fine besides the "Oh no!" at 08:00h. From 96fce4a89ffa89627d70ec2975eb18ed3399ef05 Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Mon, 15 Jul 2024 14:34:51 +0200 Subject: [PATCH 05/10] Make badges clickable to search for category Co-authored-by: Yuting Wu --- orcestra_book/_ext/category_role.py | 4 +++- orcestra_book/_static/custom.css | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/orcestra_book/_ext/category_role.py b/orcestra_book/_ext/category_role.py index bbb7ecb4..c7ed1f46 100644 --- a/orcestra_book/_ext/category_role.py +++ b/orcestra_book/_ext/category_role.py @@ -28,8 +28,10 @@ def create_badge(cat_id): "cirrus": "c", }.get(cat_id, "unknown") + span = f'{cat_id}' + href = f'{span}' node = nodes.raw( - text=f'{cat_id}', + text=href, format="html", ) diff --git a/orcestra_book/_static/custom.css b/orcestra_book/_static/custom.css index e6cd88b6..8217772b 100644 --- a/orcestra_book/_static/custom.css +++ b/orcestra_book/_static/custom.css @@ -58,7 +58,7 @@ html { } /* HACK: Fix spacing between span elements in "row view". */ -.badge + .badge { +a:has(> span.badge) + a:has(> span.badge) { margin-left: 0.25em; } From ec1e8d87693fb40c9b2ac867f9dd6762ebbcf344 Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Mon, 15 Jul 2024 14:46:34 +0200 Subject: [PATCH 06/10] Parse flight categories from YAML file --- orcestra_book/_ext/category_role.py | 25 +++-------- orcestra_book/flight_reports/categories.yaml | 46 ++++++++++++++++++++ 2 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 orcestra_book/flight_reports/categories.yaml diff --git a/orcestra_book/_ext/category_role.py b/orcestra_book/_ext/category_role.py index c7ed1f46..22eaa119 100644 --- a/orcestra_book/_ext/category_role.py +++ b/orcestra_book/_ext/category_role.py @@ -1,32 +1,17 @@ #!/usr/bin/env python3 from docutils import nodes +import yaml from frontmatter import Frontmatter from sphinx.util.docutils import SphinxRole def create_badge(cat_id): """Return an HTML node based on a category id.""" - cat_tier = { - # Important objectives - "ec_under": "a", - "ec_track": "a", - "two_circ": "a", - "itcz_circ": "a", - # Semi-important objectives - "south": "b", - "atr_circ": "b", - "atr_over": "b", - "sar": "b", - "pace": "b", - "gpm": "b", - "mindels": "b", - "cloud_circ": "b", - "curtain": "b", - "radar": "b", - # Nice-to-have - "cirrus": "c", - }.get(cat_id, "unknown") + with open("orcestra_book/flight_reports/categories.yaml", "r") as fp: + cat_tier = { + key: attrs["tier"] for key, attrs in yaml.safe_load(fp)["categories"].items() + }.get(cat_id, "unknown") span = f'{cat_id}' href = f'{span}' diff --git a/orcestra_book/flight_reports/categories.yaml b/orcestra_book/flight_reports/categories.yaml new file mode 100644 index 00000000..45d61dfb --- /dev/null +++ b/orcestra_book/flight_reports/categories.yaml @@ -0,0 +1,46 @@ +categories: + ec_under: + long_name: "EarthCARE underpass" + tier: "a" + ec_track: + long_name: "Fly along EarthCARE track" + tier: "a" + two_circ: + long_name: "Two circles at 48mm" + tier: "a" + itcz_circ: + long_name: "Once circle inside the ITCZ" + tier: "a" + south: + long_name: "Southern extension (100km)" + tier: "b" + atr_circ: + long_name: "Circle around ATR" + tier: "b" + atr_over: + long_name: "ATR overpass" + tier: "b" + sar: + long_name: "SAR overpass" + tier: "b" + pace: + long_name: "PACE overpass" + tier: "b" + gpm: + long_name: "GPM/Radar satellite" + tier: "b" + mindelo: + long_name: "Mindelo overpass" + tier: "b" + cloud_circ: + long_name: "Circle a cloud" + tier: "b" + curtain: + long_name: "Dropsonde curtain thought the ITCZ" + tier: "b" + radar: + long_name: "Radar calibration" + tier: "b" + cirrus: + long_name: "Cirrus transmissivity" + tier: "c" From a8ef5ec20ce76949ee8bc6948da0d611ba0cf6c1 Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Mon, 15 Jul 2024 14:51:04 +0200 Subject: [PATCH 07/10] Add second dummy flight report as show case --- .../flight_reports/HALO-20240812a.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 orcestra_book/flight_reports/HALO-20240812a.md diff --git a/orcestra_book/flight_reports/HALO-20240812a.md b/orcestra_book/flight_reports/HALO-20240812a.md new file mode 100644 index 00000000..44284a0a --- /dev/null +++ b/orcestra_book/flight_reports/HALO-20240812a.md @@ -0,0 +1,30 @@ +--- +mission: ORCESTRA +platform: HALO +flight_id: HALO-20240812a +nickname: Yippie! +pi: "Yuting Wu" +takeoff: 2024-08-12 09:03:00 +landing: 2024-08-12 16:00:00 +categories: [ec_under, ec_track] +orphan: true +--- + +# HALO-20240812a + +{badges}`_` + +## Summary + +I am lazy. + +## Remarks + +* Nothing special. + +## Events + +Time | Comment +--- | --- +07:00 | All good. +18:00 | Feierabend! From 812315d2fe7e6d7994e2df25c5c5cbc0095e4b99 Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Mon, 15 Jul 2024 15:10:58 +0200 Subject: [PATCH 08/10] Add role to access variables defined in front matter --- orcestra_book/_ext/category_role.py | 13 +++++++++++++ orcestra_book/flight_reports/HALO-20240810a.md | 2 +- orcestra_book/flight_reports/HALO-20240812a.md | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/orcestra_book/_ext/category_role.py b/orcestra_book/_ext/category_role.py index 22eaa119..e52aa1de 100644 --- a/orcestra_book/_ext/category_role.py +++ b/orcestra_book/_ext/category_role.py @@ -42,9 +42,22 @@ def run(self): return nodes, [] +class FrontmatterRole(SphinxRole): + def run(self): + """Access variables defined in document front matter.""" + # TODO: It is likely not a good practice to parse the document again and again. + # However, it is a working solution with no sensible performance degradation. + frontmatter = Frontmatter.read_file( + self.env.doc2path(self.env.docname) + )["attributes"] + + return nodes.raw(text=frontmatter[self.text]), [] + + def setup(app): app.add_role("cat", CategoryRole()) app.add_role("badges", BadgesRole()) + app.add_role("front", FrontmatterRole()) return { "version": "0.1", diff --git a/orcestra_book/flight_reports/HALO-20240810a.md b/orcestra_book/flight_reports/HALO-20240810a.md index 48038e73..dbb1346a 100644 --- a/orcestra_book/flight_reports/HALO-20240810a.md +++ b/orcestra_book/flight_reports/HALO-20240810a.md @@ -10,7 +10,7 @@ categories: [ec_under, ec_track, atr_over, radar, cirrus] orphan: true --- -# HALO-20240810a +# {front}`flight_id` {badges}`_` diff --git a/orcestra_book/flight_reports/HALO-20240812a.md b/orcestra_book/flight_reports/HALO-20240812a.md index 44284a0a..52e69c35 100644 --- a/orcestra_book/flight_reports/HALO-20240812a.md +++ b/orcestra_book/flight_reports/HALO-20240812a.md @@ -10,7 +10,7 @@ categories: [ec_under, ec_track] orphan: true --- -# HALO-20240812a +# {front}`flight_id` {badges}`_` From a6391fb7cf9256268b6897c77b841bce40d041ea Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Mon, 15 Jul 2024 16:02:08 +0200 Subject: [PATCH 09/10] Fix parsing of YAML front matter --- orcestra_book/_ext/category_role.py | 11 ++++------- orcestra_book/_ext/create_flight_table.py | 5 +++-- requirements.txt | 1 + 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/orcestra_book/_ext/category_role.py b/orcestra_book/_ext/category_role.py index e52aa1de..ffed558c 100644 --- a/orcestra_book/_ext/category_role.py +++ b/orcestra_book/_ext/category_role.py @@ -2,7 +2,6 @@ from docutils import nodes import yaml -from frontmatter import Frontmatter from sphinx.util.docutils import SphinxRole @@ -33,9 +32,8 @@ def run(self): class BadgesRole(SphinxRole): def run(self): """(Re-)parse the frontmatter of the current document and create badges.""" - categories = Frontmatter.read_file( - self.env.doc2path(self.env.docname) - )["attributes"].get("categories", []) + with open(self.env.doc2path(self.env.docname), "r") as fp: + categories = next(yaml.safe_load_all(fp)).get("categories", []) nodes = [create_badge(cat_id) for cat_id in categories] @@ -47,9 +45,8 @@ def run(self): """Access variables defined in document front matter.""" # TODO: It is likely not a good practice to parse the document again and again. # However, it is a working solution with no sensible performance degradation. - frontmatter = Frontmatter.read_file( - self.env.doc2path(self.env.docname) - )["attributes"] + with open(self.env.doc2path(self.env.docname), "r") as fp: + frontmatter = next(yaml.safe_load_all(fp)) return nodes.raw(text=frontmatter[self.text]), [] diff --git a/orcestra_book/_ext/create_flight_table.py b/orcestra_book/_ext/create_flight_table.py index 74166479..38be7ce6 100644 --- a/orcestra_book/_ext/create_flight_table.py +++ b/orcestra_book/_ext/create_flight_table.py @@ -4,7 +4,7 @@ import subprocess from datetime import datetime -from frontmatter import Frontmatter +import yaml from jinja2 import Template @@ -20,7 +20,8 @@ def read_frontmatter(path): - attrs = Frontmatter.read_file(path)["attributes"] + with open(path, "r") as fp: + attrs = next(yaml.safe_load_all(fp)) takeoff = attrs["takeoff"] landing = attrs["landing"] diff --git a/requirements.txt b/requirements.txt index 34e7d1ed..be14136e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,4 @@ pybtex-apa-style docutils==0.17.1 # see https://github.com/executablebooks/jupyter-book/issues/1997 easygems>=0.0.3 cmocean +jinja2 From 2160a58cc90a11628bd30f9c23a4a17353d073e0 Mon Sep 17 00:00:00 2001 From: Lukas Kluft Date: Mon, 15 Jul 2024 20:18:06 +0200 Subject: [PATCH 10/10] Refactor flight report infrastructure --- .gitignore | 3 + orcestra_book/_config.yml | 4 +- orcestra_book/_ext/category_role.py | 63 ------------- orcestra_book/_ext/create_flight_table.py | 51 ----------- orcestra_book/_ext/flight_reports.py | 106 ++++++++++++++++++++++ orcestra_book/_templates/operation.md | 8 ++ orcestra_book/operation.md | 2 - 7 files changed, 119 insertions(+), 118 deletions(-) delete mode 100644 orcestra_book/_ext/category_role.py delete mode 100644 orcestra_book/_ext/create_flight_table.py create mode 100644 orcestra_book/_ext/flight_reports.py create mode 100644 orcestra_book/_templates/operation.md delete mode 100644 orcestra_book/operation.md diff --git a/.gitignore b/.gitignore index 1c128cf3..4357ffed 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ _build __pycache__/ *.py[cod] .ipynb_checkpoints + +# Auto-generated files +orcestra_book/operation.md diff --git a/orcestra_book/_config.yml b/orcestra_book/_config.yml index 02b27613..8640a35f 100644 --- a/orcestra_book/_config.yml +++ b/orcestra_book/_config.yml @@ -4,6 +4,7 @@ title: ORCESTRA author: The ORCESTRA Community logo: logos/orcestra.png +exclude_patterns: ["_templates/*"] # Force re-execution of notebooks on each build. # See https://jupyterbook.org/content/execute.html @@ -30,8 +31,7 @@ sphinx: local_extensions: apastyle: _ext/ bracket_citation_style: _ext/ - category_role: _ext/ - create_flight_table: _ext/ + flight_reports: _ext/ config: bibtex_default_style: myapastyle bibtex_reference_style: author_year_round diff --git a/orcestra_book/_ext/category_role.py b/orcestra_book/_ext/category_role.py deleted file mode 100644 index ffed558c..00000000 --- a/orcestra_book/_ext/category_role.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 -from docutils import nodes - -import yaml -from sphinx.util.docutils import SphinxRole - - -def create_badge(cat_id): - """Return an HTML node based on a category id.""" - with open("orcestra_book/flight_reports/categories.yaml", "r") as fp: - cat_tier = { - key: attrs["tier"] for key, attrs in yaml.safe_load(fp)["categories"].items() - }.get(cat_id, "unknown") - - span = f'{cat_id}' - href = f'{span}' - node = nodes.raw( - text=href, - format="html", - ) - - return node - - -class CategoryRole(SphinxRole): - def run(self): - node = create_badge(self.text) - - return [node], [] - - -class BadgesRole(SphinxRole): - def run(self): - """(Re-)parse the frontmatter of the current document and create badges.""" - with open(self.env.doc2path(self.env.docname), "r") as fp: - categories = next(yaml.safe_load_all(fp)).get("categories", []) - - nodes = [create_badge(cat_id) for cat_id in categories] - - return nodes, [] - - -class FrontmatterRole(SphinxRole): - def run(self): - """Access variables defined in document front matter.""" - # TODO: It is likely not a good practice to parse the document again and again. - # However, it is a working solution with no sensible performance degradation. - with open(self.env.doc2path(self.env.docname), "r") as fp: - frontmatter = next(yaml.safe_load_all(fp)) - - return nodes.raw(text=frontmatter[self.text]), [] - - -def setup(app): - app.add_role("cat", CategoryRole()) - app.add_role("badges", BadgesRole()) - app.add_role("front", FrontmatterRole()) - - return { - "version": "0.1", - "parallel_read_safe": True, - "parallel_write_safe": True, - } diff --git a/orcestra_book/_ext/create_flight_table.py b/orcestra_book/_ext/create_flight_table.py deleted file mode 100644 index 38be7ce6..00000000 --- a/orcestra_book/_ext/create_flight_table.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -import os -import pathlib -import subprocess -from datetime import datetime - -import yaml -from jinja2 import Template - - -templ = """ -# Operation - -Flight-ID | Date | Takeoff | Landing | PI | Nickname | Categories ---- | --- | --- | --- | --- | --- | --- -{% for k, v in flights.items() -%} -[](flight_reports/{{ k }}) | {{ v["expr_date"] }} | {{ v["expr_takeoff"] }} | {{ v["expr_landing"] }} | {{ v["pi"] }} | {{ v["nickname"] }} | {{ v["expr_categories"]|join(' ') }} -{% endfor -%} -""" - - -def read_frontmatter(path): - with open(path, "r") as fp: - attrs = next(yaml.safe_load_all(fp)) - - takeoff = attrs["takeoff"] - landing = attrs["landing"] - - attrs["expr_date"] = takeoff.strftime("%d %B %Y") - attrs["expr_takeoff"] = takeoff.strftime("%X") - attrs["expr_landing"] = landing.strftime("%X") - attrs["expr_categories"] = map(lambda s: f"{{cat}}`{s}`", attrs.get("categories", [])) - - return attrs - - -def collect_fronmatter(): - flights = pathlib.Path("orcestra_book/flight_reports/").glob("*[0-9]*[a-z].md") - return {fm["flight_id"]: fm for fm in map(read_frontmatter, sorted(flights))} - - -def write_flight_table(app, path="orcestra_book/operation.md"): - frontmatters = collect_fronmatter() - - with open(path, "w") as fp: - t = Template(templ) - fp.write(t.render(flights=frontmatters)) - - -def setup(app): - app.connect('builder-inited', write_flight_table) diff --git a/orcestra_book/_ext/flight_reports.py b/orcestra_book/_ext/flight_reports.py new file mode 100644 index 00000000..97287bbf --- /dev/null +++ b/orcestra_book/_ext/flight_reports.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +import os +import pathlib +import subprocess +from datetime import datetime +from docutils import nodes +from functools import lru_cache, partial + +import yaml +from jinja2 import Template +from sphinx.util.docutils import SphinxRole + + +@lru_cache +def load_frontmatter(path, derive_flight=False): + with open(path, "r") as fp: + frontmatter = next(yaml.safe_load_all(fp)) + + if derive_flight: + takeoff = frontmatter["takeoff"] + landing = frontmatter["landing"] + + frontmatter["expr_date"] = takeoff.strftime("%d %B %Y") + frontmatter["expr_takeoff"] = takeoff.strftime("%X") + frontmatter["expr_landing"] = landing.strftime("%X") + frontmatter["expr_categories"] = map( + lambda s: f"{{cat}}`{s}`", frontmatter.get("categories", []) + ) + + return frontmatter + + +@lru_cache +def create_badge(cat_id): + """Return an HTML node based on a category id.""" + with open("orcestra_book/flight_reports/categories.yaml", "r") as fp: + cat_tier = { + key: attrs["tier"] + for key, attrs in yaml.safe_load(fp)["categories"].items() + }.get(cat_id, "unknown") + + span = f'{cat_id}' + href = f'{span}' + node = nodes.raw( + text=href, + format="html", + ) + + return node + + +class CategoryRole(SphinxRole): + def run(self): + node = create_badge(self.text) + + return [node], [] + + +class BadgesRole(SphinxRole): + def run(self): + fm = load_frontmatter(self.env.doc2path(self.env.docname)) + categories = fm.get("categories", []) + + nodes = [create_badge(cat_id) for cat_id in categories] + + return nodes, [] + + +class FrontmatterRole(SphinxRole): + def run(self): + """Access variables defined in document front matter.""" + fm = load_frontmatter(self.env.doc2path(self.env.docname)) + + return nodes.raw(text=fm[self.text]), [] + + +def collect_fronmatter(): + flights = pathlib.Path("orcestra_book/flight_reports/").glob("*[0-9]*[a-z].md") + func = partial(load_frontmatter, derive_flight=True) + + return {fm["flight_id"]: fm for fm in map(func, sorted(flights))} + + +def write_flight_table(app): + frontmatters = collect_fronmatter() + + with open("orcestra_book/_templates/operation.md", "r") as fp: + templ = fp.read() + + with open("orcestra_book/operation.md", "w") as fp: + t = Template(templ) + fp.write(t.render(flights=frontmatters)) + + +def setup(app): + app.connect("builder-inited", write_flight_table) + + app.add_role("cat", CategoryRole()) + app.add_role("badges", BadgesRole()) + app.add_role("front", FrontmatterRole()) + + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/orcestra_book/_templates/operation.md b/orcestra_book/_templates/operation.md new file mode 100644 index 00000000..68a3c8c9 --- /dev/null +++ b/orcestra_book/_templates/operation.md @@ -0,0 +1,8 @@ + +# Operation + +Flight-ID | Date | Takeoff | Landing | PI | Nickname | Categories +--- | --- | --- | --- | --- | --- | --- +{% for k, v in flights.items() -%} +[](flight_reports/{{ k }}) | {{ v["expr_date"] }} | {{ v["expr_takeoff"] }} | {{ v["expr_landing"] }} | {{ v["pi"] }} | {{ v["nickname"] }} | {{ v["expr_categories"]|join(' ') }} +{% endfor -%} diff --git a/orcestra_book/operation.md b/orcestra_book/operation.md deleted file mode 100644 index 35e502ec..00000000 --- a/orcestra_book/operation.md +++ /dev/null @@ -1,2 +0,0 @@ - -# Operation