Skip to content

Commit 4edac77

Browse files
committed
Write index in correct form in finish_website
Non-useful entries are removed from the index before it is written, so it only contains information the client will need.
1 parent b08e276 commit 4edac77

File tree

2 files changed

+65
-58
lines changed

2 files changed

+65
-58
lines changed

src/CSET/cset_workflow/app/finish_website/bin/finish_website.py

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from importlib.metadata import version
3030
from pathlib import Path
3131

32-
from CSET._common import combine_dicts, sort_dict
32+
from CSET._common import sort_dict
3333

3434
logging.basicConfig(
3535
level=os.getenv("LOGLEVEL", "INFO"), format="%(asctime)s %(levelname)s %(message)s"
@@ -61,42 +61,28 @@ def install_website_skeleton(www_root_link: Path, www_content: Path):
6161
www_root_link.symlink_to(www_content)
6262

6363

64-
def construct_index():
65-
"""Construct the plot index.
66-
67-
Index should has the form ``{"Category Name": {"recipe_id": "Plot Name"}}``
68-
where ``recipe_id`` is the name of the plot's directory.
69-
"""
70-
index = {}
71-
plots_dir = Path(os.environ["CYLC_WORKFLOW_SHARE_DIR"]) / "web/plots"
72-
# Loop over all diagnostics and append to index.
73-
for metadata_file in plots_dir.glob("**/*/meta.json"):
74-
try:
75-
with open(metadata_file, "rt", encoding="UTF-8") as fp:
76-
plot_metadata = json.load(fp)
77-
78-
category = plot_metadata["category"]
79-
case_date = plot_metadata.get("case_date", "")
80-
relative_url = str(metadata_file.parent.relative_to(plots_dir))
81-
82-
record = {
83-
category: {
84-
case_date if case_date else "Aggregation": {
85-
relative_url: plot_metadata["title"].strip()
86-
}
87-
}
88-
}
89-
except (json.JSONDecodeError, KeyError, TypeError) as err:
90-
logging.error("%s is invalid, skipping.\n%s", metadata_file, err)
91-
continue
92-
index = combine_dicts(index, record)
93-
94-
# Sort index of diagnostics.
95-
index = sort_dict(index)
96-
97-
# Write out website index.
98-
with open(plots_dir / "index.json", "wt", encoding="UTF-8") as fp:
99-
json.dump(index, fp, indent=2)
64+
def construct_index(www_content: Path):
65+
"""Construct the plot index."""
66+
plots_dir = www_content / "plots"
67+
with open(plots_dir / "index.jsonl", "wt", encoding="UTF-8") as index_fp:
68+
# Loop over all diagnostics and append to index. The glob is sorted to
69+
# ensure a consistent ordering.
70+
for metadata_file in sorted(plots_dir.glob("**/*/meta.json")):
71+
try:
72+
with open(metadata_file, "rt", encoding="UTF-8") as plot_fp:
73+
plot_metadata = json.load(plot_fp)
74+
plot_metadata["path"] = str(metadata_file.parent.relative_to(plots_dir))
75+
# Remove keys that are not useful for the index.
76+
plot_metadata.pop("description", None)
77+
plot_metadata.pop("plots", None)
78+
# Sort plot metadata.
79+
plot_metadata = sort_dict(plot_metadata)
80+
# Write metadata into website index.
81+
json.dump(plot_metadata, index_fp, separators=(",", ":"))
82+
index_fp.write("\n")
83+
except (json.JSONDecodeError, KeyError, TypeError) as err:
84+
logging.error("%s is invalid, skipping.\n%s", metadata_file, err)
85+
continue
10086

10187

10288
def bust_cache(www_content: Path):
@@ -149,7 +135,7 @@ def run():
149135

150136
install_website_skeleton(www_root_link, www_content)
151137
copy_rose_config(www_content)
152-
construct_index()
138+
construct_index(www_content)
153139
bust_cache(www_content)
154140
update_workflow_status(www_content)
155141

tests/workflow_utils/test_finish_website.py

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,8 @@ def test_write_workflow_status(tmp_path):
7979
assert re.search(pattern, content)
8080

8181

82-
def test_construct_index(monkeypatch, tmp_path):
82+
def test_construct_index(tmp_path):
8383
"""Test putting the index together."""
84-
monkeypatch.setenv("CYLC_WORKFLOW_SHARE_DIR", str(tmp_path))
8584
plots_dir = tmp_path / "web/plots"
8685
plots_dir.mkdir(parents=True)
8786

@@ -99,20 +98,22 @@ def test_construct_index(monkeypatch, tmp_path):
9998
static_resource.touch()
10099

101100
# Construct index.
102-
finish_website.construct_index()
101+
finish_website.construct_index(plots_dir.parent)
103102

104103
# Check index.
105-
index_file = plots_dir / "index.json"
104+
index_file = plots_dir / "index.jsonl"
106105
assert index_file.is_file()
107106
with open(index_file, "rt", encoding="UTF-8") as fp:
108-
index = json.load(fp)
109-
expected = {"Category": {"20250101": {"p1": "P1", "p2": "P2"}}}
107+
index = fp.read()
108+
expected = (
109+
'{"case_date":"20250101","category":"Category","path":"p1","title":"P1"}\n'
110+
'{"case_date":"20250101","category":"Category","path":"p2","title":"P2"}\n'
111+
)
110112
assert index == expected
111113

112114

113-
def test_construct_index_aggregation_case(monkeypatch, tmp_path):
115+
def test_construct_index_aggregation_case(tmp_path):
114116
"""Construct the index from a diagnostics without a case date."""
115-
monkeypatch.setenv("CYLC_WORKFLOW_SHARE_DIR", str(tmp_path))
116117
plots_dir = tmp_path / "web/plots"
117118
plots_dir.mkdir(parents=True)
118119

@@ -122,20 +123,43 @@ def test_construct_index_aggregation_case(monkeypatch, tmp_path):
122123
plot1.write_text('{"category": "Category", "title": "P1"}')
123124

124125
# Construct index.
125-
finish_website.construct_index()
126+
finish_website.construct_index(plots_dir.parent)
126127

127128
# Check index.
128-
index_file = plots_dir / "index.json"
129+
index_file = plots_dir / "index.jsonl"
129130
assert index_file.is_file()
130131
with open(index_file, "rt", encoding="UTF-8") as fp:
131132
index = json.load(fp)
132-
expected = {"Category": {"Aggregation": {"p1": "P1"}}}
133+
expected = {"category": "Category", "path": "p1", "title": "P1"}
133134
assert index == expected
134135

135136

136-
def test_construct_index_invalid(monkeypatch, tmp_path, caplog):
137+
def test_construct_index_remove_keys(tmp_path):
138+
"""Unneeded keys are removed from the index."""
139+
plots_dir = tmp_path / "web/plots"
140+
plots_dir.mkdir(parents=True)
141+
142+
# Plot directories.
143+
plot1 = plots_dir / "p1/meta.json"
144+
plot1.parent.mkdir()
145+
plot1.write_text(
146+
'{"category": "Category", "title": "P1", "case_date": "20250101", "plots": ["a.png"], "description": "Foo"}'
147+
)
148+
149+
# Construct index.
150+
finish_website.construct_index(plots_dir.parent)
151+
152+
# Check index.
153+
index_file = plots_dir / "index.jsonl"
154+
assert index_file.is_file()
155+
with open(index_file, "rt", encoding="UTF-8") as fp:
156+
index = json.loads(fp.readline())
157+
assert "plots" not in index
158+
assert "description" not in index
159+
160+
161+
def test_construct_index_invalid(tmp_path, caplog):
137162
"""Test constructing index when metadata is invalid."""
138-
monkeypatch.setenv("CYLC_WORKFLOW_SHARE_DIR", str(tmp_path))
139163
plots_dir = tmp_path / "web/plots"
140164
plots_dir.mkdir(parents=True)
141165

@@ -145,19 +169,16 @@ def test_construct_index_invalid(monkeypatch, tmp_path, caplog):
145169
plot.write_text('"Not JSON!"')
146170

147171
# Construct index.
148-
finish_website.construct_index()
172+
finish_website.construct_index(plots_dir.parent)
149173

150174
# Check log message.
151175
_, level, message = caplog.record_tuples[0]
152176
assert level == logging.ERROR
153177
assert "p1/meta.json is invalid, skipping." in message
154178

155-
index_file = plots_dir / "index.json"
179+
index_file = plots_dir / "index.jsonl"
156180
assert index_file.is_file()
157-
with open(index_file, "rt", encoding="UTF-8") as fp:
158-
index = json.load(fp)
159-
expected = {}
160-
assert index == expected
181+
assert index_file.stat().st_size == 0
161182

162183

163184
def test_entrypoint(monkeypatch):
@@ -181,7 +202,7 @@ def increment_counter(*args, **kwargs):
181202

182203
monkeypatch.setattr(finish_website, "install_website_skeleton", check_args)
183204
monkeypatch.setattr(finish_website, "copy_rose_config", check_single_arg)
184-
monkeypatch.setattr(finish_website, "construct_index", increment_counter)
205+
monkeypatch.setattr(finish_website, "construct_index", check_single_arg)
185206
monkeypatch.setattr(finish_website, "bust_cache", check_single_arg)
186207
monkeypatch.setattr(finish_website, "update_workflow_status", check_single_arg)
187208
monkeypatch.setenv("WEB_DIR", "/var/www/cset")

0 commit comments

Comments
 (0)