#!/usr/bin/env python3 """ Migrate old-style slot files to concise naming convention. Transforms: - is_or_was_X -> X - has_or_had_X -> has_X - contains_or_contained_X -> contains_X """ import os import re import yaml from pathlib import Path from datetime import datetime SLOTS_DIR = Path("/Users/kempersc/apps/glam/schemas/20251121/linkml/modules/slots") NEW_SLOTS_DIR = SLOTS_DIR / "20260202_matang" ARCHIVE_DIR = SLOTS_DIR / "archive" # Create directories if they don't exist NEW_SLOTS_DIR.mkdir(exist_ok=True) ARCHIVE_DIR.mkdir(exist_ok=True) def to_camel_case(snake_str: str) -> str: """Convert snake_case to camelCase.""" components = snake_str.split('_') return components[0] + ''.join(x.title() for x in components[1:]) def migrate_slot_name(old_name: str) -> str: """Convert old slot name to new concise name.""" if old_name.startswith("is_or_was_"): return old_name.replace("is_or_was_", "") elif old_name.startswith("has_or_had_"): return old_name.replace("has_or_had_", "has_") elif old_name.startswith("contains_or_contained_"): return old_name.replace("contains_or_contained_", "contains_") return old_name def create_new_slot_file(old_file: Path, new_name: str) -> str: """Create new slot file content from old file.""" with open(old_file, 'r') as f: old_content = yaml.safe_load(f) old_name = old_file.stem # Get old slot data old_slots = old_content.get('slots', {}) if old_name not in old_slots: # Try without suffix for key in old_slots: if key.startswith(old_name.replace('_slot', '')): old_name = key break old_slot = old_slots.get(old_name, {}) # Extract key properties from old slot description = old_slot.get('description', f'Migrated from {old_name}') slot_uri = old_slot.get('slot_uri', f'hc:{to_camel_case(new_name)}') range_val = old_slot.get('range', 'string') multivalued = old_slot.get('multivalued', True) # Get mappings exact_mappings = old_slot.get('exact_mappings', []) close_mappings = old_slot.get('close_mappings', []) related_mappings = old_slot.get('related_mappings', []) broad_mappings = old_slot.get('broad_mappings', []) narrow_mappings = old_slot.get('narrow_mappings', []) # Get annotations annotations = old_slot.get('annotations', {'custodian_types': '["*"]'}) # Get examples and comments examples = old_slot.get('examples', []) comments = old_slot.get('comments', []) # Build new slot content new_slot = { 'slot_uri': f'hc:{to_camel_case(new_name)}', 'description': f'{description}\n\nMIGRATED {datetime.now().strftime("%Y-%m-%d")} from {old_name} for conciseness.', 'range': range_val, 'multivalued': multivalued, 'aliases': [old_name], } # Add mappings if present if exact_mappings: new_slot['exact_mappings'] = exact_mappings if close_mappings: new_slot['close_mappings'] = close_mappings if related_mappings: new_slot['related_mappings'] = related_mappings if broad_mappings: new_slot['broad_mappings'] = broad_mappings if narrow_mappings: new_slot['narrow_mappings'] = narrow_mappings # Add annotations new_slot['annotations'] = annotations # Add examples if present if examples: new_slot['examples'] = examples # Add comments if present if comments: new_slot['comments'] = comments # Build full YAML structure new_content = { 'id': f'https://nde.nl/ontology/hc/slot/{new_name}', 'name': new_name, 'title': new_name.replace('_', ' '), 'prefixes': { 'linkml': 'https://w3id.org/linkml/', 'hc': 'https://nde.nl/ontology/hc/', 'schema': 'http://schema.org/', 'dcterms': 'http://purl.org/dc/terms/', 'rico': 'https://www.ica.org/standards/RiC/ontology#', 'org': 'http://www.w3.org/ns/org#', 'oa': 'http://www.w3.org/ns/oa#', }, 'default_prefix': 'hc', 'imports': ['linkml:types'], 'slots': { new_name: new_slot } } return yaml.dump(new_content, default_flow_style=False, allow_unicode=True, sort_keys=False) def process_slots(): """Process all old-style slot files.""" patterns = [ ('is_or_was_*.yaml', lambda x: x.replace('is_or_was_', '')), ('has_or_had_*.yaml', lambda x: x.replace('has_or_had_', 'has_')), ('contains_or_contained_*.yaml', lambda x: x.replace('contains_or_contained_', 'contains_')), ] created = 0 skipped = 0 for pattern, transform_fn in patterns: for old_file in SLOTS_DIR.glob(pattern): if old_file.is_file(): old_name = old_file.stem new_name = transform_fn(old_name) new_file = NEW_SLOTS_DIR / f'{new_name}.yaml' # Check if new file already exists if new_file.exists(): print(f"SKIP (exists): {old_name} -> {new_name}") skipped += 1 continue # Create new slot file try: new_content = create_new_slot_file(old_file, new_name) with open(new_file, 'w') as f: f.write(new_content) print(f"CREATED: {old_name} -> {new_name}") created += 1 except Exception as e: print(f"ERROR: {old_name} -> {e}") print(f"\nSummary: Created {created}, Skipped {skipped}") return created, skipped if __name__ == '__main__': process_slots()