Source code for ClearMap.config.config_adjusters.group_adjusters

from copy import deepcopy

from ClearMap.Utils.utilities import _ensure_list, _dedupe_preserve_order

from ClearMap.config.config_adjusters.type_hints import AdjustmentContext
from ClearMap.config.config_adjusters.adjusters_api import Phase, Step, patch_adjuster, AdjusterScope
from ClearMap.config.config_adjusters.type_hints import ConfigView, ConfigPatch


def _normalize_groups(groups: dict) -> tuple[dict, bool]:
    changed = False
    out = deepcopy(groups or {})
    for g, items in list(out.items()):
        norm = [s for s in _ensure_list(items) if s]
        norm = _dedupe_preserve_order(norm, key=lambda x: x)
        if norm != items:
            out[g] = norm
            changed = True
    return out, changed


[docs] @patch_adjuster( scope=AdjusterScope.GROUP, requires_sample_manager=False, step=Step.ADJUST, phase=Phase.PRE_VALIDATE, watched_keys=('group_analysis.groups',), owned_keys=('group_analysis',), order=20, ) def adjust_group_analysis_groups(view: ConfigView, ctx: AdjustmentContext) -> ConfigPatch: """ Normalize group lists: make lists, drop falsy items, and de-dupe while preserving order. """ section = deepcopy(view.get('group_analysis') or {}) groups = deepcopy(section.get('groups') or {}) if not groups: return {} groups, changed = _normalize_groups(groups) if not changed: return {} section['groups'] = groups return {'group_analysis': section}
[docs] @patch_adjuster( scope=AdjusterScope.GROUP, requires_sample_manager=False, step=Step.ADJUST, phase=Phase.PRE_VALIDATE, watched_keys=('batch_processing.groups', 'batch_processing.comparisons'), owned_keys=('batch_processing',), order=20, ) def adjust_batch_groups_and_comparisons(view: ConfigView, ctx: AdjustmentContext) -> ConfigPatch: """ Normalize batch groups and validate comparisons. Performs two main operations: 1. Normalize groups using the same logic as group_analysis. 2. Validate and de-duplicate comparisons: - Each comparison must be a 2-sequence of distinct group names. - Both groups must exist in the groups configuration. - De-duplicates comparisons while preserving order. .. note:: No lexicographic canonicalization applied; ["A","B"] and ["B","A"] remain distinct. Parameters ---------- view : ConfigView Configuration view providing access to current configuration state. ctx : AdjustmentContext Adjustment context containing metadata and state for this operation. Returns ------- ConfigPatch Patch containing normalized groups and validated, de-duplicated comparisons. """ section = deepcopy(view.get('batch_processing') or {}) groups = deepcopy(section.get('groups') or {}) comps = deepcopy(section.get('comparisons') or []) if groups is None and comps is None: return {} groups, groups_changed = _normalize_groups(groups) valid_group_names = set(groups.keys()) # if isinstance(groups, dict) else set() # Normalize + validate comparisons normalized_pairs = [] for item in comps or []: # Accept tuples/lists; ignore other shapes if not isinstance(item, (list, tuple)) or len(item) != 2: continue a, b = item[0], item[1] if not isinstance(a, str) or not isinstance(b, str): continue if a == b: continue if valid_group_names and (a not in valid_group_names or b not in valid_group_names): continue normalized_pairs.append([a, b]) # keep original order deduped_pairs = _dedupe_preserve_order(normalized_pairs, key=lambda x: tuple(x)) if deduped_pairs != comps: comps = deduped_pairs groups_changed = True if not groups_changed: return {} section['groups'] = groups section['comparisons'] = comps return {'batch_processing': section}
# @patch_adjuster(step=Step.CREATE_PIPELINE_SECTIONS, phase=Phase.PRE_VALIDATE, sections=('group_analysis',), # watched_keys=('group_analysis',), owned_keys=None, order=10) # def populate_group_analysis_defaults(view: ConfigView, ctx: AdjustmentContext) -> ConfigPatch: # """Seed group_analysis with defaults (paths, groups) without overwriting user values.""" # defaults = DEFAULTS_PROVIDER.get('group_analysis') # section = deepcopy(view.get('group_analysis') or {}) # before = deepcopy(section) # # # carry over, fill if missing # if 'paths' not in section or section['paths'] is None: # section['paths'] = deepcopy(defaults.get('paths') or {}) # if 'groups' not in section or section['groups'] is None: # section['groups'] = {} # # if section != before: # return {'group_analysis': section} # return {} # # @patch_adjuster(step=Step.CREATE_PIPELINE_SECTIONS, phase=Phase.PRE_VALIDATE, sections=('batch_processing',), # watched_keys=('batch_processing',), owned_keys=None, order=10) # def populate_batch_defaults(view: ConfigView, ctx: AdjustmentContext) -> ConfigPatch: # """Seed batch_processing with defaults (paths, groups, comparisons) without overwriting user values.""" # defaults = DEFAULTS_PROVIDER.get('batch_processing') # section = deepcopy(view.get('batch_processing') or {}) # before = deepcopy(section) # # section.setdefault('paths', deepcopy(defaults.get('paths') or {})) # section.setdefault('groups', {}) # section.setdefault('comparisons', []) # # if section != before: # return {'batch_processing': section} # return {}