import uuid from fastapi import HTTPException, status from sqlalchemy import or_, select from sqlalchemy.ext.asyncio import AsyncSession from app.models.skill import Skill async def get_accessible_skills( db: AsyncSession, user_id: uuid.UUID, include_general: bool = True ) -> list[Skill]: conditions = [Skill.user_id == user_id] if include_general: conditions.append(Skill.user_id.is_(None)) stmt = select(Skill).where(or_(*conditions), Skill.is_active == True).order_by(Skill.sort_order) # noqa: E712 result = await db.execute(stmt) return list(result.scalars().all()) async def get_skill(db: AsyncSession, skill_id: uuid.UUID, user_id: uuid.UUID | None = None) -> Skill: result = await db.execute(select(Skill).where(Skill.id == skill_id)) skill = result.scalar_one_or_none() if not skill: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Skill not found") # Access check: must be general or owned by user if user_id and skill.user_id is not None and skill.user_id != user_id: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Skill not found") return skill async def validate_skill_accessible(db: AsyncSession, skill_id: uuid.UUID, user_id: uuid.UUID) -> None: """Validate skill exists and is accessible by user (general or owned). Raises 404 if not.""" await get_skill(db, skill_id, user_id) # --- Personal skills --- async def create_personal_skill(db: AsyncSession, user_id: uuid.UUID, **kwargs) -> Skill: skill = Skill(user_id=user_id, **kwargs) db.add(skill) await db.flush() return skill async def update_personal_skill(db: AsyncSession, skill_id: uuid.UUID, user_id: uuid.UUID, **kwargs) -> Skill: skill = await get_skill(db, skill_id, user_id) if skill.user_id != user_id: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Cannot edit general skills") for key, value in kwargs.items(): if value is not None: setattr(skill, key, value) await db.flush() return skill async def delete_personal_skill(db: AsyncSession, skill_id: uuid.UUID, user_id: uuid.UUID) -> None: skill = await get_skill(db, skill_id, user_id) if skill.user_id != user_id: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Cannot delete general skills") await db.delete(skill) # --- General (admin) skills --- async def get_general_skills(db: AsyncSession) -> list[Skill]: result = await db.execute( select(Skill).where(Skill.user_id.is_(None)).order_by(Skill.sort_order) ) return list(result.scalars().all()) async def create_general_skill(db: AsyncSession, **kwargs) -> Skill: skill = Skill(user_id=None, **kwargs) db.add(skill) await db.flush() return skill async def update_general_skill(db: AsyncSession, skill_id: uuid.UUID, **kwargs) -> Skill: result = await db.execute(select(Skill).where(Skill.id == skill_id, Skill.user_id.is_(None))) skill = result.scalar_one_or_none() if not skill: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="General skill not found") for key, value in kwargs.items(): if value is not None: setattr(skill, key, value) await db.flush() return skill async def delete_general_skill(db: AsyncSession, skill_id: uuid.UUID) -> None: result = await db.execute(select(Skill).where(Skill.id == skill_id, Skill.user_id.is_(None))) skill = result.scalar_one_or_none() if not skill: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="General skill not found") await db.delete(skill)