""" Generate service files "service and route", CRUD. READ models on a modules, and generate code usin template Jija2 """ import os from pathlib import Path from typing import Dict, List, Any # # from jinja2 import Environment, FileSystemLoader from sqlalchemy import inspect, Table, Column from sqlalchemy.engine import create_mock_engine ##### from database import Base import importlib import enum class CodeGenerator: def __init__(self, templates_dir: str = "templates", output_dir: str = "generated"): self.templates_dir = Path(templates_dir) self.output_dir = Path(output_dir) self.env = Environment(loader=FileSystemLoader(templates_dir)) # create directory of output self.output_dir.mkdir(parents=True, exist_ok=True) (self.output_dir / "services").mkdir(exist_ok=True) (self.output_dir / "routes").mkdir(exist_ok=True) (self.output_dir / "schemas").mkdir(exist_ok=True) def get_model_metadata(self, model_class) -> Dict[str, Any]: """" Extract metadata from SQLAlchemy """ if not hasattr(model_class, '__table__'): print(f"{model_class.__name__} no tiene __table__, jumping") return None fields = [] required_fields = [] unique_fields = [] for column in model_class.__table__.columns: column_type = str(column.type) if 'enum' in column_type.lower() or 'ENUM' in column_type: # extract possible values of enum if hasattr(column.type, 'enum_class'): enum_values = list(column.type.enum_class.__members__.keys()) python_type = "str" else: python_type = "str" else: python_type = self._sqlalchemy_to_python_type(column.type) field_info = { "name": column.name, "type": column_type, "python_type": python_type, "nullable": column.nullable , "primary_key": column.primary_key, "unique": column.unique, "default": column.default is None, "is_enum": 'enum' in column_type.lower(), } fields.append(field_info) if not column.nullable and not column.primary_key: required_fields.append(column.name) if column.unique: unique_fields.append(column.name) return{ "class_name" : model_class.__name__, "entity_name" : model_class.__name__.lower(), "model_name" : model_class.__name__, "model_folder" : model_class.__module__.split(".")[-2] if "." in model_class.__module__ else "models", "schema_folder" : "schemas" , "fields" : fields, "required_fields" : required_fields, "unique_fields" : unique_fields, "has_soft_delete" : any(c.name in ["is_active", "deleted_at"] for c in model_class.__table__.columns), } def _sqlalchemy_to_python_type(self, sqlalchemy_type) -> str: """ Convert SQAlchemy types on a python""" type_str = str(sqlalchemy_type).lower() if "int" in type_str: return "int" elif "str" in type_str or "varchar" in type_str or "text" in type_str: return "str" elif "bool" in type_str: return "bool" elif "datetime" in type_str or "date" in type_str: return "datetime" elif "float" in type_str or "decimal" in type_str: return "float" else: return "Any" def generate_service(self, model_metada: Dict[str, Any]): """ Generate file of service using template""" template = self.env.get_template("service_template.j2") output = template.render(**model_metada) output_file = self.output_dir / "services" /f"{model_metada['entity_name']}_service.py" with open(output_file, "w", encoding="utf-8") as f: f.write(output) print(f"Generated: {output_file}") def generate_route(self, model_metadata: Dict[str, Any]): """Generate file of route using template""" template = self.env.get_template("route_template.j2") output = template.render(**model_metadata) output_file = self.output_dir / "routers" / f"{model_metadata['entity_name']}_router.py" with open(output_file, "w", encoding="utf-8") as f: f.write(output) print(f"Generated: {output_file}") def generate_all(self, models_list: List): """ Generate services and router for a list models""" print(f"Intializing generation") for model in models_list: print(f"Processing model: {model.__name__}") metadata = self.get_model_metadata(model) if metadata is None: print(f"Error: can't obtain metadara, jumped \n") continue self.generate_service(metadata) self.generate_route(metadata) print("Generation completed") def get_all_models(): """Import and retunr all models SQLAlchemy""" from app.modules.coments.models import Coments from app.modules.configuration.models import Configuration from app.modules.credits.models import Credits from app.modules.edos.models import EDOS from app.modules.efos.models import EFOS from app.modules.feed.models import Feed from app.modules.files.models import Files from app.modules.interactions.models import Interaccion from app.modules.invoices.models import Invoices from app.modules.license.models import License from app.modules.moves.models import Moves from app.modules.location.models import Locations from app.modules.suppliers.models import Suppliers from app.modules.branches.models import Branches return [Branches, Coments, Configuration, Credits, EDOS, EFOS, Feed, Files, Interaccion, Invoices, License, Moves, Locations, Suppliers] def main(): #configure routes generator = CodeGenerator( templates_dir = "templates", output_dir = "output/generated" ) models = get_all_models() generator.generate_all(models) if __name__ == "__main__": main()