feat: modules dependencies

This commit is contained in:
2025-03-15 09:37:14 +04:00
parent d4b6720188
commit a466e46f28
3 changed files with 59 additions and 5 deletions

View File

@@ -12,8 +12,16 @@ if TYPE_CHECKING:
project_module = Table( project_module = Table(
'project_module', 'project_module',
BaseModel.metadata, BaseModel.metadata,
Column('project_id', ForeignKey('projects.id')), Column('project_id', ForeignKey('projects.id'), primary_key=True),
Column('module_id', ForeignKey('modules.id')), Column('module_id', ForeignKey('modules.id'), primary_key=True),
)
module_dependencies = Table(
'module_dependencies',
BaseModel.metadata,
Column('module_id', ForeignKey('modules.id'), primary_key=True),
Column('depends_on_id', ForeignKey('modules.id'), primary_key=True),
) )
@@ -26,6 +34,24 @@ class Module(BaseModel):
icon_name: Mapped[Optional[str]] = mapped_column(unique=True, nullable=False) icon_name: Mapped[Optional[str]] = mapped_column(unique=True, nullable=False)
is_deleted: Mapped[bool] = mapped_column(default=False) is_deleted: Mapped[bool] = mapped_column(default=False)
depends_on: Mapped[list['Module']] = relationship(
'Module',
secondary=module_dependencies,
primaryjoin='Module.id == module_dependencies.c.module_id',
secondaryjoin='Module.id == module_dependencies.c.depends_on_id',
back_populates='depended_on_by',
lazy='immediate',
)
depended_on_by: Mapped[list['Module']] = relationship(
'Module',
secondary='module_dependencies',
primaryjoin='Module.id == module_dependencies.c.depends_on_id',
secondaryjoin='Module.id == module_dependencies.c.module_id',
back_populates='depends_on',
lazy='noload',
)
projects: Mapped[list['Project']] = relationship( projects: Mapped[list['Project']] = relationship(
'Project', 'Project',
uselist=True, uselist=True,

View File

@@ -22,6 +22,7 @@ class ModuleSchema(BaseSchema):
label: str label: str
icon_name: Optional[str] = None icon_name: Optional[str] = None
is_deleted: bool is_deleted: bool
depends_on: list["ModuleSchema"] = []
class ProjectSchema(ProjectGeneralInfoSchema): class ProjectSchema(ProjectGeneralInfoSchema):

View File

@@ -1,7 +1,7 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import select, update, func, delete from sqlalchemy import select, update, func, delete
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload, immediateload
from card_attributes import CardAttributesCommandHandler from card_attributes import CardAttributesCommandHandler
from models import Project, Board, Module from models import Project, Board, Module
@@ -27,7 +27,8 @@ class ProjectService(BaseService):
.outerjoin(board_count_sub, Project.id == board_count_sub.c.project_id) .outerjoin(board_count_sub, Project.id == board_count_sub.c.project_id)
.options( .options(
selectinload(Project.attributes), selectinload(Project.attributes),
selectinload(Project.modules), selectinload(Project.modules)
.selectinload(Module.depends_on),
) )
.where(Project.is_deleted == False) .where(Project.is_deleted == False)
.order_by(Project.id) .order_by(Project.id)
@@ -90,22 +91,48 @@ class ProjectService(BaseService):
stmt = ( stmt = (
select(Module) select(Module)
.where(Module.is_deleted == False) .where(Module.is_deleted == False)
.options(
selectinload(Module.depends_on),
)
) )
modules = await self.session.scalars(stmt) modules = await self.session.scalars(stmt)
return GetAllModulesResponse(modules=modules.all()) return GetAllModulesResponse(modules=modules.all())
def get_module_with_dependencies(self, module: Module, visited_ids: set[int]) -> set[Module]:
result_modules = {module}
for dependency in module.depends_on:
if dependency.id not in visited_ids:
visited_ids.add(dependency.id)
result_modules.update(
self.get_module_with_dependencies(dependency, visited_ids)
)
return result_modules
async def update_project_modules(self, request: UpdateModulesRequest) -> UpdateModulesResponse: async def update_project_modules(self, request: UpdateModulesRequest) -> UpdateModulesResponse:
project: Optional[Project] = await self.session.get(Project, request.project_id) project: Optional[Project] = await self.session.get(Project, request.project_id)
if not project: if not project:
return UpdateModulesResponse(ok=False, message=f"Проект с ID {request.project_id} не найден") return UpdateModulesResponse(ok=False, message=f"Проект с ID {request.project_id} не найден")
load_dependencies = immediateload(Module.depends_on)
for i in range(5):
load_dependencies = load_dependencies.immediateload(Module.depends_on)
modules_stmt = ( modules_stmt = (
select(Module) select(Module)
.where(Module.id.in_(request.module_ids)) .where(Module.id.in_(request.module_ids))
.options(load_dependencies)
) )
modules = (await self.session.scalars(modules_stmt)).all() modules = (await self.session.scalars(modules_stmt)).all()
project.modules = modules result_modules = set()
visited_module_ids = set(request.module_ids)
for module in modules:
result_modules.update(
self.get_module_with_dependencies(module, visited_module_ids)
)
project.modules = list(result_modules)
await self.session.commit() await self.session.commit()
return UpdateModulesResponse(ok=True, message="Модули успешно обновлены") return UpdateModulesResponse(ok=True, message="Модули успешно обновлены")