diff json_to_saqc_config.py @ 1:724dcbb35c9a draft default tip

planemo upload for repository https://github.com/Helmholtz-UFZ/galaxy-tools/blob/main/tools/saqc/ commit b674325a07b6e964e25cd65967149018dc2671fe
author ufz
date Sat, 16 Aug 2025 11:43:23 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json_to_saqc_config.py	Sat Aug 16 11:43:23 2025 +0000
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+
+import json
+import math
+import sys
+
+
+def format_saqc_value_repr(value: any) -> str:
+    """
+    Konvertiert einen Python-Wert in seine korrekte String-Darstellung für die SaQC-Konfiguration.
+    Behandelt None, Bools, Floats (inkl. inf/nan) und Strings zentral.
+    """
+    if value is None:
+        return "None"
+    if isinstance(value, bool):
+        return str(value)
+    if isinstance(value, float):
+        if math.isinf(value):
+            return "float('inf')" if value > 0 else "float('-inf')"
+        if math.isnan(value):
+            return "float('nan')"
+        return repr(value)
+    if isinstance(value, int):
+        return str(value)
+    if isinstance(value, str):
+        val_lower = value.lower()
+        if val_lower == "inf":
+            return "float('inf')"
+        if val_lower == "-inf":
+            return "float('-inf')"
+        if val_lower == "nan":
+            return "float('nan')"
+        escaped_v = value.replace("\\", "\\\\").replace('"', '\\"')
+        return f'"{escaped_v}"'
+    sys.stderr.write(
+        f"Warning: Unhandled type {type(value)}. Converting to string representation: '{str(value)}'.\n"
+    )
+    return repr(value)
+
+
+print("varname; function")
+
+try:
+    infile = sys.argv[1]
+    with open(infile) as fh:
+        params_from_galaxy = json.load(fh)
+except Exception as e:
+    sys.stderr.write(
+        f"Error opening or reading JSON file {infile}: {type(e).__name__} - {e}\n"
+    )
+    sys.exit(1)
+
+EMPTY_STRING_IS_NONE_PARAMS = {
+    "xscope",
+    "yscope",
+    "max_gap",
+    "min_periods",
+    "min_residuals",
+    "min_offset",
+}
+
+for r_method_set in params_from_galaxy.get("methods_repeat", []):
+    method_str_for_error = "unknown_method_in_repeat"
+    field_str_for_error = "unknown_field_in_repeat"
+    try:
+        method_cond_params = r_method_set.get("module_cond", {}).get("method_cond", {})
+        if not method_cond_params:
+            sys.stderr.write(
+                f"Warning: Skipping a methods_repeat entry due to missing/empty method_cond: {r_method_set}\n"
+            )
+            continue
+
+        params_to_process = method_cond_params.copy()
+
+        method = params_to_process.pop("method_select", "unknown_method")
+        method_str_for_error = method
+
+        raw_field_val = None
+        if "field" in params_to_process:
+            raw_field_val = params_to_process.pop("field")
+        elif "field_repeat" in params_to_process:
+            field_repeat_data = params_to_process.pop("field_repeat", [])
+            if isinstance(field_repeat_data, list) and len(field_repeat_data) > 0:
+                first_field_item = field_repeat_data[0]
+                if isinstance(first_field_item, dict):
+                    raw_field_val = first_field_item.get("field")
+
+        if raw_field_val is None or str(raw_field_val).strip() == "":
+            field_str = "undefined_field"
+            field_str_for_error = "undefined_field (extraction failed or empty)"
+            sys.stderr.write(
+                f"Warning: Field name could not be determined for method '{method}'. Using '{field_str}'.\n"
+            )
+        else:
+            field_str = str(raw_field_val) if not isinstance(raw_field_val, list) else ",".join(map(str, raw_field_val))
+            field_str_for_error = field_str
+
+        saqc_args_dict = {}
+        for param_key, param_value_json in params_to_process.items():
+            if param_key.endswith("_select_type"):
+                continue
+
+            actual_param_name_for_saqc = param_key
+            current_value_for_saqc = param_value_json
+
+            if isinstance(param_value_json, dict) and param_key.endswith("_cond"):
+                actual_param_name_for_saqc = param_key[:-5]
+                value_found = False
+                for inner_k, inner_v in param_value_json.items():
+                    if not inner_k.endswith("_select_type"):
+                        current_value_for_saqc = inner_v
+                        value_found = True
+                        break
+                if not value_found:
+                    current_value_for_saqc = None
+
+            if current_value_for_saqc == "__none__":
+                saqc_args_dict[actual_param_name_for_saqc] = None
+            elif isinstance(current_value_for_saqc, str) and not current_value_for_saqc and actual_param_name_for_saqc in EMPTY_STRING_IS_NONE_PARAMS:
+                saqc_args_dict[actual_param_name_for_saqc] = None
+            else:
+                saqc_args_dict[actual_param_name_for_saqc] = current_value_for_saqc
+        param_strings_for_saqc_call = [
+            f"{k_saqc}={format_saqc_value_repr(v_saqc)}"
+            for k_saqc, v_saqc in sorted(saqc_args_dict.items())
+        ]
+
+        print(
+            f"{field_str}; {method}({', '.join(param_strings_for_saqc_call)})",
+            flush=True,
+        )
+
+    except Exception as e:
+        sys.stderr.write(
+            f"FATAL Error processing a method entry: {type(e).__name__} - {e}\n"
+        )
+        sys.stderr.write(
+            f"Offending entry: {r_method_set}\n"
+        )
+        sys.stderr.write(
+            f"Method context: {method_str_for_error}, Field context: {field_str_for_error}\n"
+        )
+        import traceback
+        traceback.print_exc(file=sys.stderr)
+        print(
+            f"{field_str_for_error}; ERROR_PROCESSING_METHOD({method_str_for_error})",
+            flush=True)
+        continue