generated from gilles/template-webapp
172 lines
5.2 KiB
Python
172 lines
5.2 KiB
Python
"""Repository pour les emplacements."""
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.orm import selectinload
|
|
|
|
from app.models.location import Location, LocationType
|
|
from app.repositories.base import BaseRepository
|
|
|
|
|
|
class LocationRepository(BaseRepository[Location]):
|
|
"""Repository pour les opérations sur les emplacements."""
|
|
|
|
def __init__(self, db: AsyncSession) -> None:
|
|
"""Initialise le repository."""
|
|
super().__init__(Location, db)
|
|
|
|
async def get_with_children(self, id: int) -> Location | None:
|
|
"""Récupère un emplacement avec ses enfants.
|
|
|
|
Args:
|
|
id: ID de l'emplacement
|
|
|
|
Returns:
|
|
L'emplacement avec ses enfants ou None
|
|
"""
|
|
result = await self.db.execute(
|
|
select(Location)
|
|
.options(selectinload(Location.children))
|
|
.where(Location.id == id)
|
|
)
|
|
return result.scalar_one_or_none()
|
|
|
|
async def get_root_locations(self) -> list[Location]:
|
|
"""Récupère tous les emplacements racine (sans parent).
|
|
|
|
Returns:
|
|
Liste des emplacements racine
|
|
"""
|
|
result = await self.db.execute(
|
|
select(Location)
|
|
.where(Location.parent_id.is_(None))
|
|
.order_by(Location.name)
|
|
)
|
|
return list(result.scalars().all())
|
|
|
|
async def get_children(self, parent_id: int) -> list[Location]:
|
|
"""Récupère les enfants directs d'un emplacement.
|
|
|
|
Args:
|
|
parent_id: ID du parent
|
|
|
|
Returns:
|
|
Liste des enfants
|
|
"""
|
|
result = await self.db.execute(
|
|
select(Location)
|
|
.where(Location.parent_id == parent_id)
|
|
.order_by(Location.name)
|
|
)
|
|
return list(result.scalars().all())
|
|
|
|
async def get_by_type(self, type: LocationType) -> list[Location]:
|
|
"""Récupère tous les emplacements d'un type donné.
|
|
|
|
Args:
|
|
type: Type d'emplacement
|
|
|
|
Returns:
|
|
Liste des emplacements
|
|
"""
|
|
result = await self.db.execute(
|
|
select(Location)
|
|
.where(Location.type == type)
|
|
.order_by(Location.path)
|
|
)
|
|
return list(result.scalars().all())
|
|
|
|
async def get_full_tree(self) -> list[Location]:
|
|
"""Récupère l'arborescence complète des emplacements.
|
|
|
|
Returns:
|
|
Liste des emplacements racine avec enfants chargés récursivement
|
|
"""
|
|
# Charger tous les emplacements avec leurs enfants
|
|
result = await self.db.execute(
|
|
select(Location)
|
|
.options(selectinload(Location.children))
|
|
.order_by(Location.path)
|
|
)
|
|
all_locations = list(result.scalars().all())
|
|
|
|
# Retourner seulement les racines (les enfants sont déjà chargés)
|
|
return [loc for loc in all_locations if loc.parent_id is None]
|
|
|
|
async def get_with_item_count(self, id: int) -> tuple[Location, int] | None:
|
|
"""Récupère un emplacement avec le nombre d'objets.
|
|
|
|
Args:
|
|
id: ID de l'emplacement
|
|
|
|
Returns:
|
|
Tuple (emplacement, nombre d'objets) ou None
|
|
"""
|
|
result = await self.db.execute(
|
|
select(Location)
|
|
.options(selectinload(Location.items))
|
|
.where(Location.id == id)
|
|
)
|
|
location = result.scalar_one_or_none()
|
|
if location is None:
|
|
return None
|
|
return location, len(location.items)
|
|
|
|
async def create_with_path(
|
|
self,
|
|
name: str,
|
|
type: LocationType,
|
|
parent_id: int | None = None,
|
|
description: str | None = None,
|
|
) -> Location:
|
|
"""Crée un emplacement avec calcul automatique du chemin.
|
|
|
|
Args:
|
|
name: Nom de l'emplacement
|
|
type: Type d'emplacement
|
|
parent_id: ID du parent (None si racine)
|
|
description: Description optionnelle
|
|
|
|
Returns:
|
|
L'emplacement créé
|
|
"""
|
|
# Calculer le chemin
|
|
if parent_id is None:
|
|
path = name
|
|
else:
|
|
parent = await self.get(parent_id)
|
|
if parent is None:
|
|
path = name
|
|
else:
|
|
path = f"{parent.path} > {name}"
|
|
|
|
return await self.create(
|
|
name=name,
|
|
type=type,
|
|
parent_id=parent_id,
|
|
path=path,
|
|
description=description,
|
|
)
|
|
|
|
async def update_paths_recursive(self, location: Location) -> None:
|
|
"""Met à jour récursivement les chemins après modification.
|
|
|
|
Args:
|
|
location: Emplacement modifié
|
|
"""
|
|
# Mettre à jour le chemin de cet emplacement
|
|
if location.parent_id is None:
|
|
location.path = location.name
|
|
else:
|
|
parent = await self.get(location.parent_id)
|
|
if parent:
|
|
location.path = f"{parent.path} > {location.name}"
|
|
else:
|
|
location.path = location.name
|
|
|
|
# Mettre à jour les enfants
|
|
children = await self.get_children(location.id)
|
|
for child in children:
|
|
child.path = f"{location.path} > {child.name}"
|
|
await self.update_paths_recursive(child)
|