"""Detailed structure validation for all SQLAlchemy models.""" import sys import os # Set UTF-8 encoding for Windows console if os.name == 'nt': import io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') import api.models from sqlalchemy.orm import RelationshipProperty from sqlalchemy.schema import ForeignKeyConstraint, CheckConstraint, Index def get_table_models(): """Get all table model classes (excluding base classes).""" base_classes = {'Base', 'TimestampMixin', 'UUIDMixin'} all_classes = [attr for attr in dir(api.models) if not attr.startswith('_') and attr[0].isupper()] return sorted([m for m in all_classes if m not in base_classes]) def analyze_model(model_name): """Analyze a model's structure in detail.""" model_cls = getattr(api.models, model_name) result = { 'name': model_name, 'table': model_cls.__tablename__, 'has_uuid_mixin': False, 'has_timestamp_mixin': False, 'foreign_keys': [], 'relationships': [], 'indexes': [], 'check_constraints': [], 'columns': [] } # Check mixins for base in model_cls.__mro__: if base.__name__ == 'UUIDMixin': result['has_uuid_mixin'] = True if base.__name__ == 'TimestampMixin': result['has_timestamp_mixin'] = True # Get table object if hasattr(model_cls, '__table__'): table = model_cls.__table__ # Analyze columns for col in table.columns: col_info = { 'name': col.name, 'type': str(col.type), 'nullable': col.nullable, 'primary_key': col.primary_key } result['columns'].append(col_info) # Analyze foreign keys for fk in table.foreign_keys: result['foreign_keys'].append({ 'parent_column': fk.parent.name, 'target': str(fk.target_fullname) }) # Analyze indexes if hasattr(table, 'indexes'): for idx in table.indexes: result['indexes'].append({ 'name': idx.name, 'columns': [col.name for col in idx.columns] }) # Analyze check constraints for constraint in table.constraints: if isinstance(constraint, CheckConstraint): result['check_constraints'].append({ 'sqltext': str(constraint.sqltext) }) # Analyze relationships for attr_name in dir(model_cls): try: attr = getattr(model_cls, attr_name) if hasattr(attr, 'property') and isinstance(attr.property, RelationshipProperty): rel = attr.property result['relationships'].append({ 'name': attr_name, 'target': rel.mapper.class_.__name__, 'uselist': rel.uselist }) except (AttributeError, TypeError): continue return result def print_model_summary(result): """Print a formatted summary of model structure.""" print(f"\n{'='*70}") print(f"Model: {result['name']} (table: {result['table']})") print(f"{'='*70}") # Mixins mixins = [] if result['has_uuid_mixin']: mixins.append('UUIDMixin') if result['has_timestamp_mixin']: mixins.append('TimestampMixin') if mixins: print(f"Mixins: {', '.join(mixins)}") # Columns print(f"\nColumns ({len(result['columns'])}):") for col in result['columns'][:10]: # Limit to first 10 for readability pk = " [PK]" if col['primary_key'] else "" nullable = "NULL" if col['nullable'] else "NOT NULL" print(f" - {col['name']}: {col['type']} {nullable}{pk}") if len(result['columns']) > 10: print(f" ... and {len(result['columns']) - 10} more columns") # Foreign Keys if result['foreign_keys']: print(f"\nForeign Keys ({len(result['foreign_keys'])}):") for fk in result['foreign_keys']: print(f" - {fk['parent_column']} -> {fk['target']}") # Relationships if result['relationships']: print(f"\nRelationships ({len(result['relationships'])}):") for rel in result['relationships']: rel_type = "many" if rel['uselist'] else "one" print(f" - {rel['name']} -> {rel['target']} ({rel_type})") # Indexes if result['indexes']: print(f"\nIndexes ({len(result['indexes'])}):") for idx in result['indexes']: cols = ', '.join(idx['columns']) print(f" - {idx['name']}: ({cols})") # Check Constraints if result['check_constraints']: print(f"\nCheck Constraints ({len(result['check_constraints'])}):") for check in result['check_constraints']: print(f" - {check['sqltext']}") def main(): print("="*70) print("ClaudeTools - Detailed Model Structure Analysis") print("="*70) models = get_table_models() print(f"\nAnalyzing {len(models)} table models...\n") all_results = [] for model_name in models: try: result = analyze_model(model_name) all_results.append(result) except Exception as e: print(f"[ERROR] Error analyzing {model_name}: {e}") import traceback traceback.print_exc() # Print summary statistics print("\n" + "="*70) print("SUMMARY STATISTICS") print("="*70) total_models = len(all_results) models_with_uuid = sum(1 for r in all_results if r['has_uuid_mixin']) models_with_timestamp = sum(1 for r in all_results if r['has_timestamp_mixin']) models_with_fk = sum(1 for r in all_results if r['foreign_keys']) models_with_rel = sum(1 for r in all_results if r['relationships']) models_with_idx = sum(1 for r in all_results if r['indexes']) models_with_checks = sum(1 for r in all_results if r['check_constraints']) total_fk = sum(len(r['foreign_keys']) for r in all_results) total_rel = sum(len(r['relationships']) for r in all_results) total_idx = sum(len(r['indexes']) for r in all_results) total_checks = sum(len(r['check_constraints']) for r in all_results) print(f"\nTotal Models: {total_models}") print(f" - With UUIDMixin: {models_with_uuid}") print(f" - With TimestampMixin: {models_with_timestamp}") print(f" - With Foreign Keys: {models_with_fk} (total: {total_fk})") print(f" - With Relationships: {models_with_rel} (total: {total_rel})") print(f" - With Indexes: {models_with_idx} (total: {total_idx})") print(f" - With CHECK Constraints: {models_with_checks} (total: {total_checks})") # Print detailed info for each model print("\n" + "="*70) print("DETAILED MODEL INFORMATION") print("="*70) for result in all_results: print_model_summary(result) print("\n" + "="*70) print("[SUCCESS] Analysis complete!") print("="*70) if __name__ == "__main__": main()