ajuste de cruds incompleto
This commit is contained in:
179
generator.py
Normal file
179
generator.py
Normal file
@@ -0,0 +1,179 @@
|
||||
""" 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()
|
||||
|
||||
Reference in New Issue
Block a user