frontend añadido y backend de creacion de notas
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# Mapper.py
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TypeVar, Generic, Type
|
||||
import json
|
||||
|
||||
TDominio = TypeVar("TDominio")
|
||||
TModelo = TypeVar("TModelo")
|
||||
|
||||
class Mapper_base(ABC, Generic[TDominio, TModelo]):
|
||||
# ----------------------------
|
||||
# Conversiones individuales
|
||||
# ----------------------------
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def to_model(obj: TDominio) -> TModelo:
|
||||
"""Convierte objeto de dominio a modelo ORM"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def from_model(model: TModelo) -> TDominio:
|
||||
"""Convierte modelo ORM a objeto de dominio"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def to_dict(obj: TDominio) -> dict:
|
||||
"""Convierte objeto de dominio a diccionario plano"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def from_dict(d: dict) -> TDominio:
|
||||
"""Convierte diccionario plano a objeto de dominio"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def to_json(cls, obj: TDominio) -> str:
|
||||
"""Convierte objeto de dominio a JSON string"""
|
||||
return json.dumps(cls.to_dict(obj), default=str)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str: str) -> TDominio:
|
||||
"""Convierte JSON string a objeto de dominio"""
|
||||
return cls.from_dict(json.loads(json_str))
|
||||
|
||||
# ----------------------------
|
||||
# Conversiones en lote (bulk)
|
||||
# ----------------------------
|
||||
|
||||
@classmethod
|
||||
def to_model_list(cls, objs: list[TDominio]) -> list[TModelo]:
|
||||
return [cls.to_model(o) for o in objs]
|
||||
|
||||
@classmethod
|
||||
def from_model_list(cls, models: list[TModelo]) -> list[TDominio]:
|
||||
return [cls.from_model(m) for m in models]
|
||||
|
||||
@classmethod
|
||||
def to_dict_list(cls, objs: list[TDominio]) -> list[dict]:
|
||||
return [cls.to_dict(o) for o in objs]
|
||||
|
||||
@classmethod
|
||||
def from_dict_list(cls, dicts: list[dict]) -> list[TDominio]:
|
||||
return [cls.from_dict(d) for d in dicts]
|
||||
|
||||
@classmethod
|
||||
def to_json_list(cls, objs: list[TDominio]) -> str:
|
||||
return json.dumps(cls.to_dict_list(objs), default=str)
|
||||
|
||||
@classmethod
|
||||
def from_json_list(cls, json_str: str) -> list[TDominio]:
|
||||
return cls.from_dict_list(json.loads(json_str))
|
||||
@@ -0,0 +1,58 @@
|
||||
# Model.py
|
||||
from sqlalchemy import Column, DateTime, String, Integer, Text, BigInteger, func
|
||||
from sqlalchemy.ext.declarative import declared_attr, as_declarative
|
||||
from datetime import datetime
|
||||
from backend.db.base import Base # tu Base declarativa
|
||||
|
||||
class Model_base(Base):
|
||||
__abstract__ = True
|
||||
|
||||
# ID autoincremental por defecto en todos los modelos
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
|
||||
@declared_attr
|
||||
def sys_created_at(cls):
|
||||
# timestamptz + default en BD
|
||||
return Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
|
||||
@declared_attr
|
||||
def sys_created_by(cls):
|
||||
return Column(String, nullable=True)
|
||||
|
||||
@declared_attr
|
||||
def sys_updated_at(cls):
|
||||
# onupdate lo pone la BD al actualizar
|
||||
return Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
|
||||
@declared_attr
|
||||
def sys_updated_by(cls):
|
||||
return Column(String, nullable=True)
|
||||
|
||||
@declared_attr
|
||||
def sys_version(cls):
|
||||
return Column(Integer, default=1, nullable=False)
|
||||
|
||||
@declared_attr
|
||||
def sys_notes(cls):
|
||||
return Column(Text, nullable=True)
|
||||
|
||||
@declared_attr
|
||||
def sys_deleted_at(cls):
|
||||
return Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
def __repr__(self):
|
||||
id_val = getattr(self, "id", None)
|
||||
return f"<{self.__class__.__name__} id={id_val}>"
|
||||
|
||||
def __str__(self):
|
||||
cls = self.__class__.__name__
|
||||
id_val = getattr(self, "id", None)
|
||||
return f"{cls}(id={id_val})"
|
||||
|
||||
def __json__(self) -> dict:
|
||||
out = {}
|
||||
if not hasattr(self, "__table__"): return out
|
||||
for c in self.__table__.columns:
|
||||
v = getattr(self, c.name, None)
|
||||
out[c.name] = v.isoformat() if isinstance(v, datetime) else v
|
||||
return out
|
||||
@@ -0,0 +1,149 @@
|
||||
# Repo.py
|
||||
|
||||
from abc import ABC
|
||||
from typing import Type, TypeVar, Generic, Optional
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from .Mapper import Mapper_base # Asegúrate de importar tu ABC base
|
||||
|
||||
TModelo = TypeVar("TModelo")
|
||||
TDominio = TypeVar("TDominio")
|
||||
|
||||
class Repo_base(ABC, Generic[TModelo, TDominio]):
|
||||
def __init__(self, session: Session, modelo: type[TModelo], mapper: type[Mapper_base[TDominio, TModelo]]):
|
||||
self.session = session
|
||||
self.Modelo = modelo
|
||||
self.Mapper = mapper
|
||||
|
||||
# ----------------------
|
||||
# ADD
|
||||
# ----------------------
|
||||
|
||||
def add(self, dominio: TDominio, created_by: Optional[str] = None, notes: Optional[str] = None) -> str:
|
||||
data = self.Mapper.to_dict(dominio)
|
||||
data.update({
|
||||
"sys_created_by": created_by,
|
||||
"sys_notes": notes,
|
||||
"sys_version": 1
|
||||
})
|
||||
model = self.Modelo(**data)
|
||||
self.session.add(model)
|
||||
self.session.commit()
|
||||
return model.id
|
||||
|
||||
def add_many(self, dominios: list[TDominio], created_by: Optional[str] = None, notes: Optional[str] = None) -> list[str]:
|
||||
ids = []
|
||||
for dominio in dominios:
|
||||
data = self.Mapper.to_dict(dominio)
|
||||
data.update({
|
||||
"sys_created_by": created_by,
|
||||
"sys_notes": notes,
|
||||
"sys_version": 1
|
||||
})
|
||||
model = self.Modelo(**data)
|
||||
self.session.add(model)
|
||||
ids.append(model.id)
|
||||
self.session.commit()
|
||||
return ids
|
||||
|
||||
# ----------------------
|
||||
# GET
|
||||
# ----------------------
|
||||
|
||||
def get_by_id(self, id_: str) -> Optional[TDominio]:
|
||||
model = self.session.query(self.Modelo).filter_by(id=id_, sys_deleted_at=None).first()
|
||||
return self.Mapper.from_model(model) if model else None
|
||||
|
||||
def get_all(self) -> list[TDominio]:
|
||||
models = self.session.query(self.Modelo).filter_by(sys_deleted_at=None).all()
|
||||
return self.Mapper.from_model_list(models)
|
||||
|
||||
def get_paginated(self, offset: int = 0, limit: int = 10) -> list[TDominio]:
|
||||
models = self.session.query(self.Modelo).filter_by(sys_deleted_at=None).offset(offset).limit(limit).all()
|
||||
return self.Mapper.from_model_list(models)
|
||||
|
||||
def get_deleted(self) -> list[TDominio]:
|
||||
models = self.session.query(self.Modelo).filter(self.Modelo.sys_deleted_at.isnot(None)).all()
|
||||
return self.Mapper.from_model_list(models)
|
||||
|
||||
# ----------------------
|
||||
# UPDATE
|
||||
# ----------------------
|
||||
|
||||
def update(self, id_: str, new_data: dict, updated_by: Optional[str] = None, notes: Optional[str] = None) -> bool:
|
||||
model = self.session.query(self.Modelo).filter_by(id=id_, sys_deleted_at=None).first()
|
||||
if not model:
|
||||
return False
|
||||
|
||||
for key, value in new_data.items():
|
||||
if hasattr(model, key):
|
||||
setattr(model, key, value)
|
||||
|
||||
model.sys_updated_by = updated_by
|
||||
model.sys_notes = notes
|
||||
model.sys_version = (model.sys_version or 1) + 1
|
||||
self.session.commit()
|
||||
return True
|
||||
|
||||
def bulk_update(self, updates: list[tuple[str, dict]], updated_by: Optional[str] = None, notes: Optional[str] = None) -> int:
|
||||
count = 0
|
||||
for id_, data in updates:
|
||||
if self.update(id_, data, updated_by=updated_by, notes=notes):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
# ----------------------
|
||||
# DELETE
|
||||
# ----------------------
|
||||
|
||||
def delete_by_id(self, id_: str) -> bool:
|
||||
model = self.session.query(self.Modelo).filter_by(id=id_).first()
|
||||
if model:
|
||||
self.session.delete(model)
|
||||
self.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
def delete_all(self) -> int:
|
||||
deleted = self.session.query(self.Modelo).delete()
|
||||
self.session.commit()
|
||||
return deleted
|
||||
|
||||
# ----------------------
|
||||
# SOFT DELETE
|
||||
# ----------------------
|
||||
|
||||
def soft_delete(self, id_: str, deleted_by: Optional[str] = None, notes: Optional[str] = None) -> bool:
|
||||
model = self.session.query(self.Modelo).filter_by(id=id_, sys_deleted_at=None).first()
|
||||
if model:
|
||||
model.sys_deleted_at = datetime.now(timezone.utc) # TZ-aware
|
||||
model.sys_updated_by = deleted_by
|
||||
model.sys_notes = notes
|
||||
model.sys_version = (model.sys_version or 1) + 1
|
||||
self.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
def soft_restore(self, id_: str, restored_by: Optional[str] = None, notes: Optional[str] = None) -> bool:
|
||||
model = self.session.query(self.Modelo).filter_by(id=id_).first()
|
||||
if model and model.sys_deleted_at is not None:
|
||||
model.sys_deleted_at = None
|
||||
model.sys_updated_by = restored_by
|
||||
model.sys_notes = notes
|
||||
model.sys_version = (model.sys_version or 1) + 1
|
||||
self.session.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
# ----------------------
|
||||
# OTROS
|
||||
# ----------------------
|
||||
|
||||
def exists(self, id_: str) -> bool:
|
||||
return self.session.query(self.Modelo).filter_by(id=id_, sys_deleted_at=None).first() is not None
|
||||
|
||||
def count(self) -> int:
|
||||
return self.session.query(self.Modelo).filter_by(sys_deleted_at=None).count()
|
||||
@@ -0,0 +1,6 @@
|
||||
# domains/arquitecture_layer/__init__.py
|
||||
from .Repo import *
|
||||
from .Mapper import *
|
||||
from .Model import *
|
||||
|
||||
__all__ = ["Repo", "Mapper", "Model"]
|
||||
Reference in New Issue
Block a user