Files
pragma-stack/backend/app/services/organization_service.py
Felipe Cardoso 4c6bf55bcc Refactor(backend): improve formatting in services, repositories & tests
- Consistently format multi-line function headers, exception handling, and repository method calls for readability.
- Reorganize misplaced imports across modules (e.g., services & tests) into proper sorted order.
- Adjust indentation, line breaks, and spacing inconsistencies in tests and migration files.
- Cleanup unnecessary trailing newlines and reorganize `__all__` declarations for consistency.
2026-02-28 18:37:56 +01:00

156 lines
5.3 KiB
Python

# app/services/organization_service.py
"""Service layer for organization operations — delegates to OrganizationRepository."""
import logging
from typing import Any
from uuid import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.exceptions import NotFoundError
from app.models.organization import Organization
from app.models.user_organization import OrganizationRole, UserOrganization
from app.repositories.organization import OrganizationRepository, organization_repo
from app.schemas.organizations import OrganizationCreate, OrganizationUpdate
logger = logging.getLogger(__name__)
class OrganizationService:
"""Service for organization management operations."""
def __init__(
self, organization_repository: OrganizationRepository | None = None
) -> None:
self._repo = organization_repository or organization_repo
async def get_organization(self, db: AsyncSession, org_id: str) -> Organization:
"""Get organization by ID, raising NotFoundError if not found."""
org = await self._repo.get(db, id=org_id)
if not org:
raise NotFoundError(f"Organization {org_id} not found")
return org
async def create_organization(
self, db: AsyncSession, *, obj_in: OrganizationCreate
) -> Organization:
"""Create a new organization."""
return await self._repo.create(db, obj_in=obj_in)
async def update_organization(
self,
db: AsyncSession,
*,
org: Organization,
obj_in: OrganizationUpdate | dict[str, Any],
) -> Organization:
"""Update an existing organization."""
return await self._repo.update(db, db_obj=org, obj_in=obj_in)
async def remove_organization(self, db: AsyncSession, org_id: str) -> None:
"""Permanently delete an organization by ID."""
await self._repo.remove(db, id=org_id)
async def get_member_count(self, db: AsyncSession, *, organization_id: UUID) -> int:
"""Get number of active members in an organization."""
return await self._repo.get_member_count(db, organization_id=organization_id)
async def get_multi_with_member_counts(
self,
db: AsyncSession,
*,
skip: int = 0,
limit: int = 100,
is_active: bool | None = None,
search: str | None = None,
) -> tuple[list[dict[str, Any]], int]:
"""List organizations with member counts and pagination."""
return await self._repo.get_multi_with_member_counts(
db, skip=skip, limit=limit, is_active=is_active, search=search
)
async def get_user_organizations_with_details(
self,
db: AsyncSession,
*,
user_id: UUID,
is_active: bool | None = None,
) -> list[dict[str, Any]]:
"""Get all organizations a user belongs to, with membership details."""
return await self._repo.get_user_organizations_with_details(
db, user_id=user_id, is_active=is_active
)
async def get_organization_members(
self,
db: AsyncSession,
*,
organization_id: UUID,
skip: int = 0,
limit: int = 100,
is_active: bool | None = True,
) -> tuple[list[dict[str, Any]], int]:
"""Get members of an organization with pagination."""
return await self._repo.get_organization_members(
db,
organization_id=organization_id,
skip=skip,
limit=limit,
is_active=is_active,
)
async def add_member(
self,
db: AsyncSession,
*,
organization_id: UUID,
user_id: UUID,
role: OrganizationRole = OrganizationRole.MEMBER,
) -> UserOrganization:
"""Add a user to an organization."""
return await self._repo.add_user(
db, organization_id=organization_id, user_id=user_id, role=role
)
async def remove_member(
self,
db: AsyncSession,
*,
organization_id: UUID,
user_id: UUID,
) -> bool:
"""Remove a user from an organization. Returns True if found and removed."""
return await self._repo.remove_user(
db, organization_id=organization_id, user_id=user_id
)
async def get_user_role_in_org(
self, db: AsyncSession, *, user_id: UUID, organization_id: UUID
) -> OrganizationRole | None:
"""Get the role of a user in an organization."""
return await self._repo.get_user_role_in_org(
db, user_id=user_id, organization_id=organization_id
)
async def get_org_distribution(
self, db: AsyncSession, *, limit: int = 6
) -> list[dict[str, Any]]:
"""Return top organizations by member count for admin dashboard."""
from sqlalchemy import func, select
result = await db.execute(
select(
Organization.name,
func.count(UserOrganization.user_id).label("count"),
)
.join(UserOrganization, Organization.id == UserOrganization.organization_id)
.group_by(Organization.name)
.order_by(func.count(UserOrganization.user_id).desc())
.limit(limit)
)
return [{"name": row.name, "value": row.count} for row in result.all()]
# Default singleton
organization_service = OrganizationService()