Redesign1 #1

Merged
yaro merged 36 commits from Redesign1 into main 2025-09-22 14:12:39 -05:00
8 changed files with 91 additions and 5 deletions
Showing only changes of commit c43b17662d - Show all commits

View file

@ -1,4 +1,4 @@
from sqlalchemy import Column, Integer, DateTime, Boolean, func from sqlalchemy import Column, Integer, DateTime, Boolean, String, JSON, func
from sqlalchemy.orm import declarative_mixin, declarative_base from sqlalchemy.orm import declarative_mixin, declarative_base
Base = declarative_base() Base = declarative_base()
@ -11,3 +11,16 @@ class CRUDMixin:
def as_dict(self): def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns} return {c.name: getattr(self, c.name) for c in self.__table__.columns}
class Version(Base):
__tablename__ = "versions"
id = Column(Integer, primary_key=True)
model_name = Column(String, nullable=False)
object_id = Column(Integer, nullable=False)
change_type = Column(String, nullable=False)
data = Column(JSON, nullable=True)
timestamp = Column(DateTime, default=func.now())
actor = Column(String, nullable=True)
metadata = Column(JSON, nullable=True)

View file

@ -1,5 +1,6 @@
from typing import Type, TypeVar, Generic from typing import Type, TypeVar, Generic
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from crudkit.core.base import Version
from crudkit.core.spec import CRUDSpec from crudkit.core.spec import CRUDSpec
T = TypeVar("T") T = TypeVar("T")
@ -48,13 +49,15 @@ class CRUDService(Generic[T]):
query = query.offset(offset).limit(limit) query = query.offset(offset).limit(limit)
return query.all() return query.all()
def create(self, data: dict) -> T: def create(self, data: dict, actor=None) -> T:
obj = self.model(**data) obj = self.model(**data)
self.session.add(obj) self.session.add(obj)
self.session.commit() self.session.commit()
self._log_version("create", obj, actor)
return obj return obj
def update(self, id: int, data: dict) -> T: def update(self, id: int, data: dict, actor=None) -> T:
obj = self.get(id) obj = self.get(id)
if not obj: if not obj:
raise ValueError(f"{self.model.__name__} with ID {id} not found.") raise ValueError(f"{self.model.__name__} with ID {id} not found.")
@ -64,9 +67,11 @@ class CRUDService(Generic[T]):
if k in valid_fields: if k in valid_fields:
setattr(obj, k, v) setattr(obj, k, v)
self.session.commit() self.session.commit()
self._log_version("update", obj, actor)
return obj return obj
def delete(self, id: int, hard: bool = False): def delete(self, id: int, hard: bool = False, actor = False):
obj = self.session.get(self.model, id) obj = self.session.get(self.model, id)
if not obj: if not obj:
return None return None
@ -77,4 +82,23 @@ class CRUDService(Generic[T]):
obj.is_deleted = True obj.is_deleted = True
self.session.commit() self.session.commit()
self._log_version("delete", obj, actor)
return obj return obj
def _log_version(self, change_type: str, obj: T, actor=None, metadata: dict = {}):
try:
data = obj.as_dict()
except Exception:
data = {"error": "Failed to serialize object."}
version = Version(
model_name=self.model.__name__,
object_id=obj.id,
change_type=change_type,
data=data,
actor=str(actor) if actor else None,
metadata=metadata
)
self.session.add(version)
self.session.commit()

View file

@ -1,4 +1,4 @@
from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy import Column, Integer, String, ForeignKey, Boolean
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from crudkit.core.base import CRUDMixin, Base from crudkit.core.base import CRUDMixin, Base
@ -7,6 +7,7 @@ class Dbref(Base, CRUDMixin):
type = Column(String, nullable=False) type = Column(String, nullable=False)
name = Column(String, nullable=False) name = Column(String, nullable=False)
is_deleted = Column(Boolean, nullable=False, default=False)
owner_id = Column(Integer, ForeignKey("dbref.id")) owner_id = Column(Integer, ForeignKey("dbref.id"))
location_id = Column(Integer, ForeignKey("dbref.id")) location_id = Column(Integer, ForeignKey("dbref.id"))
@ -21,3 +22,21 @@ class Dbref(Base, CRUDMixin):
def __str__(self): def __str__(self):
return f"#{self.id} ({self.type}): {self.name}" return f"#{self.id} ({self.type}): {self.name}"
def is_type(self, *types: str) -> bool:
return self.type in types
@property
def is_room(self): return self.is_type("room")
@property
def is_thing(self): return self.is_type("thing")
@property
def is_exit(self): return self.is_type("exit")
@property
def is_player(self): return self.is_type("player")
@property
def is_program(self): return self.is_type("programI ho")

6
muck/models/exit.py Normal file
View file

@ -0,0 +1,6 @@
from muck.models.dbref import Dbref
class Exit(Dbref):
__mapper_args__ = {
"polymorphic_identity": "exit"
}

6
muck/models/player.py Normal file
View file

@ -0,0 +1,6 @@
from muck.models.dbref import Dbref
class Player(Dbref):
__mapper_args__ = {
"polymorphic_identity": "player"
}

6
muck/models/program.py Normal file
View file

@ -0,0 +1,6 @@
from muck.models.dbref import Dbref
class Program(Dbref):
__mapper_args__ = {
"polymorphic_identity": "program"
}

6
muck/models/room.py Normal file
View file

@ -0,0 +1,6 @@
from muck.models.dbref import Dbref
class Room(Dbref):
__mapper_args__ = {
"polymorphic_identity": "room"
}

6
muck/models/thing.py Normal file
View file

@ -0,0 +1,6 @@
from muck.models.dbref import Dbref
class Thing(Dbref):
__mapper_args__ = {
"polymorphic_identity": "thing"
}