#!/usr/bin/env python3 """ Add missing aliases to refactored slot files. For files like: - has_X.yaml -> add alias "has_or_had_X" - X.yaml (non-has_) -> add alias "is_or_was_X" """ import os import re import yaml from pathlib import Path SLOTS_DIR = Path("/Users/kempersc/apps/glam/schemas/20251121/linkml/modules/slots/20260202_matang") def add_alias_to_file(filepath: Path, old_name: str) -> bool: """Add old_name as an alias to the slot file if not already present.""" with open(filepath, 'r') as f: content = f.read() # Check if alias already exists if old_name in content: return False # Parse YAML try: data = yaml.safe_load(content) except Exception as e: print(f"ERROR parsing {filepath}: {e}") return False if not data or 'slots' not in data: print(f"SKIP (no slots): {filepath}") return False # Get the slot name (first key in slots) slot_name = list(data['slots'].keys())[0] slot_data = data['slots'][slot_name] # Add or update aliases if 'aliases' not in slot_data: slot_data['aliases'] = [] if old_name not in slot_data['aliases']: slot_data['aliases'].append(old_name) # Write back # Preserve comments by using regex replacement instead of full rewrite if 'aliases:' in content: # Find aliases section and add to it pattern = r'(aliases:\s*\n)((?:\s*-\s*[^\n]+\n)*)' def add_alias(match): aliases_header = match.group(1) existing_aliases = match.group(2) indent = ' ' # Standard indent for aliases return f"{aliases_header}{existing_aliases}{indent}- {old_name}\n" new_content = re.sub(pattern, add_alias, content) else: # Add aliases section after range or description # Find a good insertion point - after 'range:' line or before 'annotations:' lines = content.split('\n') new_lines = [] inserted = False for i, line in enumerate(lines): new_lines.append(line) # Insert after range, multivalued, or slot_uri lines at the slot level if not inserted and re.match(r'\s{4}(range|multivalued|slot_uri):', line): # Check next lines to find the right spot # Insert aliases after the last property before annotations/examples/comments next_line = lines[i+1] if i+1 < len(lines) else '' if not re.match(r'\s{4}(range|multivalued|slot_uri|description):', next_line): new_lines.append(' aliases:') new_lines.append(f' - {old_name}') inserted = True if not inserted: # Fallback: insert before annotations if present new_lines = [] for line in lines: if 'annotations:' in line and not inserted: new_lines.append(' aliases:') new_lines.append(f' - {old_name}') inserted = True new_lines.append(line) new_content = '\n'.join(new_lines) with open(filepath, 'w') as f: f.write(new_content) return True def process_slots(): """Process all slot files and add missing aliases.""" updated = 0 skipped = 0 for filepath in SLOTS_DIR.glob('*.yaml'): name = filepath.stem # Determine the old name based on current name if name.startswith('has_'): # has_X -> has_or_had_X old_name = 'has_or_had_' + name[4:] elif name.startswith('contains_'): # contains_X -> contains_or_contained_X old_name = 'contains_or_contained_' + name[9:] else: # X -> is_or_was_X old_name = 'is_or_was_' + name # Check if alias already exists in file with open(filepath, 'r') as f: content = f.read() if old_name in content: skipped += 1 continue # Add alias if add_alias_to_file(filepath, old_name): print(f"UPDATED: {name} -> added alias '{old_name}'") updated += 1 else: skipped += 1 print(f"\nSummary: Updated {updated}, Skipped {skipped}") return updated, skipped if __name__ == '__main__': process_slots()