Skip to content

Commit 6f669aa

Browse files
authored
Merge pull request #100 from nfdi4cat/Dev-Marc
debugging inbox-pipeline Fixed a bug in the inbox_to_schema.py pipeline, where slots for which a specific domain was specified were “silently” skipped without GitHub Actions reporting an error.
2 parents d417b08 + 7836d39 commit 6f669aa

2 files changed

Lines changed: 332 additions & 22 deletions

File tree

scripts/inbox_to_schema.py

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@
8686
"date", "datetime", "time", "decimal", "double", "Any",
8787
})
8888

89+
# The schema's default_range (coremeta4cat.yaml / coremeta4cat_common.yaml).
90+
# A slot with no explicit `range` key is equivalent to one with `range: string`,
91+
# so the two must be treated as equal when diffing against the Excel (which always
92+
# emits a range value via schema_to_excel).
93+
DEFAULT_RANGE = "string"
94+
8995
# ─────────────────────────────────────────────────────────────────────────────
9096
# Diagnostic / reporting system
9197
# ─────────────────────────────────────────────────────────────────────────────
@@ -531,25 +537,35 @@ def plan_changes(
531537
effective_class, slot_origin, class_origin,
532538
changes, reporter,
533539
)
534-
else:
535-
# Slot belongs to the subclass hierarchy (mixin,
536-
# attribute, or imported slot) and cannot be modified
537-
# via the inbox workflow. Skip silently — these rows
538-
# are structural display information from the Excel
540+
elif derived_name in get_all_class_slots(schema, effective_class):
541+
# Slot already belongs to the subclass hierarchy (its own
542+
# slots: list or a mixin) and cannot be modified via the
543+
# inbox workflow. Skip silently — these rows are
544+
# structural display information from the Excel
539545
# generator, not editable fields.
540546
reporter.info(
541547
sheet_title, f"slot '{label}'",
542548
f"Skipped: belongs to sub-class `{effective_class}` "
543549
f"and is not modifiable via the inbox workflow "
544550
f"(edit the YAML directly).",
545551
)
552+
else:
553+
# Unknown slot assigned to a domain class that does not
554+
# already define it → a genuinely new slot to be added to
555+
# that subclass (e.g. anode/cathode on ElectrochemicalReactor).
556+
# _plan_new_slot resolves the owner class from the domain.
557+
_plan_new_slot(
558+
row, sheet_title, schema_class,
559+
schema, class_origin, slot_origin, label_to_slot,
560+
label_to_class, changes, reporter,
561+
)
546562

547563
else:
548564
# ── Unknown label + empty domain → new top-level slot ──
549565
_plan_new_slot(
550566
row, sheet_title, schema_class,
551567
schema, class_origin, slot_origin, label_to_slot,
552-
changes, reporter,
568+
label_to_class, changes, reporter,
553569
)
554570

555571
elif row_type == "class":
@@ -689,7 +705,9 @@ def _plan_slot_changes(
689705
# -- range --
690706
new_range = row["range"]
691707
if new_range:
692-
cur_range = _str(_effective(slot_def, su, "range", ""))
708+
# An absent range key means the schema default (string); normalise so a
709+
# default-range slot is not seen as "changing" to string on the next run.
710+
cur_range = _str(_effective(slot_def, su, "range", "")) or DEFAULT_RANGE
693711
if new_range != cur_range:
694712
if not _valid_range(new_range, schema):
695713
reporter.error(
@@ -778,23 +796,31 @@ def _plan_new_slot(
778796
class_origin: dict[str, Path],
779797
slot_origin: dict[str, Path],
780798
label_to_slot: dict[str, str],
799+
label_to_class: dict[str, str],
781800
changes: list,
782801
reporter: Reporter,
783802
) -> None:
784803
label = row["label"]
785804
domain = row["domain"]
786805
slot_name = _label_to_slot_name(label)
787806

788-
# Only top-level new slots are supported (domain must be empty)
807+
# Resolve the class that will own the new slot:
808+
# • empty domain → the sheet's top-level data class (schema_class)
809+
# • non-empty domain → the named subclass (e.g. ElectrochemicalReactor),
810+
# added to that class exactly like the top-level case.
789811
if domain:
790-
reporter.error(
791-
sheet, f"new slot '{label}'",
792-
f"New slots may only be added at the top level of a data class "
793-
f"(the **domain** column must be empty). Got domain=`{domain}`.",
794-
hint="To add a slot inside a subclass, edit the YAML directly. "
795-
"For a top-level slot, leave the domain column empty.",
796-
)
797-
return
812+
owner_class = label_to_class.get(domain) or domain
813+
if owner_class not in schema.get("classes", {}):
814+
reporter.error(
815+
sheet, f"new slot '{label}'",
816+
f"The domain `{domain}` is not a recognised class. "
817+
f"Set the domain column to an existing class label, or leave it "
818+
f"empty to add a top-level slot.",
819+
hint="Look at the class rows in this sheet for valid domain names.",
820+
)
821+
return
822+
else:
823+
owner_class = schema_class
798824

799825
# Name conflict: same derived name as an existing slot
800826
if slot_name in schema.get("slots", {}):
@@ -818,7 +844,7 @@ def _plan_new_slot(
818844
return
819845

820846
# Range validation
821-
range_val = row["range"] or "string"
847+
range_val = row["range"] or DEFAULT_RANGE
822848
if not _valid_range(range_val, schema):
823849
reporter.error(
824850
sheet, f"new slot '{label}'",
@@ -827,16 +853,16 @@ def _plan_new_slot(
827853
)
828854
return
829855

830-
target = class_origin.get(schema_class, SCHEMA_DIR / MODULE_FILES[-1])
856+
target = class_origin.get(owner_class, SCHEMA_DIR / MODULE_FILES[-1])
831857
reporter.info(
832858
sheet, f"new slot `{slot_name}`",
833-
f"Will add `{slot_name}` to `{schema_class}`.",
859+
f"Will add `{slot_name}` to `{owner_class}`.",
834860
)
835861
changes.append({
836862
"type": "slot_add",
837863
"name": slot_name,
838864
"label": label,
839-
"schema_class": schema_class,
865+
"schema_class": owner_class,
840866
"range": range_val,
841867
"mro": mro,
842868
"description": row["description"],
@@ -1171,7 +1197,7 @@ def apply_changes(changes: list[dict], reporter: Reporter) -> None:
11711197
slot_def: dict[str, Any] = {}
11721198
if ch["description"]:
11731199
slot_def["description"] = _as_literal(ch["description"])
1174-
if ch["range"] and ch["range"] != "string":
1200+
if ch["range"]:
11751201
slot_def["range"] = ch["range"]
11761202
if ch["uri"]:
11771203
slot_def["slot_uri"] = ch["uri"]
@@ -1206,7 +1232,7 @@ def apply_changes(changes: list[dict], reporter: Reporter) -> None:
12061232
_save_yaml(target, doc)
12071233
reporter.applied(
12081234
f"New slot `{slot_name}` added to `{schema_class}` "
1209-
f"(M/R/O: {mro}, range: {ch['range'] or 'string'})"
1235+
f"(M/R/O: {mro}, range: {ch['range']})"
12101236
)
12111237

12121238
# ── add new class ─────────────────────────────────────────────────

0 commit comments

Comments
 (0)