Refactor code for improved readability and consistency
Test / test (push) Successful in 15s

- Reformatted function signatures in `organization_service.py` and `task_service.py` for better alignment.
- Updated import statements across multiple files for consistency and organization.
- Enhanced test files by improving formatting and ensuring consistent use of async session factories.
- Added type hints and improved type safety in various service and test files.
- Adjusted `pyproject.toml` to include configuration for isort, mypy, and ruff for better code quality checks.
- Cleaned up unused imports and organized existing ones in several test files.
This commit is contained in:
Artem Kashaev
2025-12-01 16:18:03 +05:00
parent eecb74c523
commit 5fcb574aca
62 changed files with 765 additions and 476 deletions
+9 -4
View File
@@ -1,9 +1,11 @@
"""Reusable FastAPI dependencies."""
from collections.abc import AsyncGenerator
import jwt
from fastapi import Depends, Header, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from redis.asyncio.client import Redis
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.cache import get_cache_client
@@ -18,9 +20,9 @@ from app.repositories.deal_repo import DealRepository
from app.repositories.org_repo import OrganizationRepository
from app.repositories.task_repo import TaskRepository
from app.repositories.user_repo import UserRepository
from app.services.activity_service import ActivityService
from app.services.analytics_service import AnalyticsService
from app.services.auth_service import AuthService
from app.services.activity_service import ActivityService
from app.services.contact_service import ContactService
from app.services.deal_service import DealService
from app.services.organization_service import (
@@ -30,7 +32,6 @@ from app.services.organization_service import (
OrganizationService,
)
from app.services.task_service import TaskService
from redis.asyncio.client import Redis
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{settings.api_v1_prefix}/auth/token")
@@ -45,7 +46,9 @@ def get_user_repository(session: AsyncSession = Depends(get_db_session)) -> User
return UserRepository(session=session)
def get_organization_repository(session: AsyncSession = Depends(get_db_session)) -> OrganizationRepository:
def get_organization_repository(
session: AsyncSession = Depends(get_db_session),
) -> OrganizationRepository:
return OrganizationRepository(session=session)
@@ -65,7 +68,9 @@ def get_activity_repository(session: AsyncSession = Depends(get_db_session)) ->
return ActivityRepository(session=session)
def get_analytics_repository(session: AsyncSession = Depends(get_db_session)) -> AnalyticsRepository:
def get_analytics_repository(
session: AsyncSession = Depends(get_db_session),
) -> AnalyticsRepository:
return AnalyticsRepository(session=session)
+8 -7
View File
@@ -1,14 +1,15 @@
"""Root API router that aggregates versioned routers."""
from fastapi import APIRouter
from app.api.v1 import (
activities,
analytics,
auth,
contacts,
deals,
organizations,
tasks,
activities,
analytics,
auth,
contacts,
deals,
organizations,
tasks,
)
from app.core.config import settings
+15 -14
View File
@@ -1,20 +1,21 @@
"""Version 1 API routers."""
from . import (
activities,
analytics,
auth,
contacts,
deals,
organizations,
tasks,
activities,
analytics,
auth,
contacts,
deals,
organizations,
tasks,
)
__all__ = [
"activities",
"analytics",
"auth",
"contacts",
"deals",
"organizations",
"tasks",
"activities",
"analytics",
"auth",
"contacts",
"deals",
"organizations",
"tasks",
]
+1
View File
@@ -1,4 +1,5 @@
"""Activity timeline endpoints and payload schemas."""
from __future__ import annotations
from typing import Literal
+5 -1
View File
@@ -1,4 +1,5 @@
"""Analytics API endpoints for summaries and funnels."""
from __future__ import annotations
from decimal import Decimal
@@ -16,6 +17,7 @@ def _decimal_to_str(value: Decimal) -> str:
normalized = value.normalize()
return format(normalized, "f")
router = APIRouter(prefix="/analytics", tags=["analytics"])
@@ -92,4 +94,6 @@ async def deals_funnel(
"""Return funnel breakdown by stages and statuses."""
breakdowns: list[StageBreakdown] = await service.get_deal_funnel(context.organization_id)
return DealFunnelResponse(stages=[StageBreakdownModel.model_validate(item) for item in breakdowns])
return DealFunnelResponse(
stages=[StageBreakdownModel.model_validate(item) for item in breakdowns]
)
+3 -2
View File
@@ -1,8 +1,9 @@
"""Authentication API endpoints and payloads."""
from __future__ import annotations
from pydantic import BaseModel, EmailStr
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, EmailStr
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
@@ -41,7 +42,7 @@ async def register_user(
organization: Organization | None = None
if payload.organization_name:
existing_org = await repo.session.scalar(
select(Organization).where(Organization.name == payload.organization_name)
select(Organization).where(Organization.name == payload.organization_name),
)
if existing_org is not None:
raise HTTPException(
+4 -1
View File
@@ -1,4 +1,5 @@
"""Contact API endpoints."""
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Query, status
@@ -81,7 +82,9 @@ async def create_contact(
context: OrganizationContext = Depends(get_organization_context),
service: ContactService = Depends(get_contact_service),
) -> ContactRead:
data = payload.to_domain(organization_id=context.organization_id, fallback_owner=context.user_id)
data = payload.to_domain(
organization_id=context.organization_id, fallback_owner=context.user_id
)
try:
contact = await service.create_contact(data, context=context)
except ContactForbiddenError as exc:
+8 -3
View File
@@ -1,4 +1,5 @@
"""Deal API endpoints backed by DealService with inline payload schemas."""
from __future__ import annotations
from decimal import Decimal
@@ -8,7 +9,7 @@ from pydantic import BaseModel
from app.api.deps import get_deal_repository, get_deal_service, get_organization_context
from app.models.deal import DealCreate, DealRead, DealStage, DealStatus
from app.repositories.deal_repo import DealRepository, DealAccessError, DealQueryParams
from app.repositories.deal_repo import DealAccessError, DealQueryParams, DealRepository
from app.services.deal_service import (
DealService,
DealStageTransitionError,
@@ -66,7 +67,9 @@ async def list_deals(
statuses_value = [DealStatus(value) for value in status_filter] if status_filter else None
stage_value = DealStage(stage) if stage else None
except ValueError as exc:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid deal filter") from exc
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid deal filter"
) from exc
params = DealQueryParams(
organization_id=context.organization_id,
@@ -96,7 +99,9 @@ async def create_deal(
) -> DealRead:
"""Create a new deal within the current organization."""
data = payload.to_domain(organization_id=context.organization_id, fallback_owner=context.user_id)
data = payload.to_domain(
organization_id=context.organization_id, fallback_owner=context.user_id
)
try:
deal = await service.create_deal(data, context=context)
except DealAccessError as exc:
+1
View File
@@ -1,4 +1,5 @@
"""Organization-related API endpoints."""
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, status
+1
View File
@@ -1,4 +1,5 @@
"""Task API endpoints with inline schemas."""
from __future__ import annotations
from datetime import date, datetime, time, timezone