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
+23 -16
View File
@@ -1,11 +1,13 @@
"""Analytics-related business logic."""
from __future__ import annotations
import logging
from collections.abc import Iterable
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from decimal import Decimal, InvalidOperation
from typing import Any, Iterable
from typing import Any
from redis.asyncio.client import Redis
from redis.exceptions import RedisError
@@ -105,9 +107,7 @@ class AnalyticsService:
won_amount_count = row.amount_count
won_count = row.deal_count
won_average = (
(won_amount_sum / won_amount_count) if won_amount_count > 0 else Decimal("0")
)
won_average = (won_amount_sum / won_amount_count) if won_amount_count > 0 else Decimal("0")
window_threshold = _threshold_from_days(days)
new_deals = await self._repository.count_new_deals_since(organization_id, window_threshold)
@@ -137,7 +137,7 @@ class AnalyticsService:
breakdowns: list[StageBreakdown] = []
totals = {stage: sum(by_status.values()) for stage, by_status in stage_map.items()}
for index, stage in enumerate(_STAGE_ORDER):
by_status = stage_map.get(stage, {status: 0 for status in DealStatus})
by_status = stage_map.get(stage, dict.fromkeys(DealStatus, 0))
total = totals.get(stage, 0)
conversion = None
if index < len(_STAGE_ORDER) - 1:
@@ -151,7 +151,7 @@ class AnalyticsService:
total=total,
by_status=by_status,
conversion_to_next=conversion,
)
),
)
await self._store_funnel_cache(organization_id, breakdowns)
return breakdowns
@@ -168,7 +168,9 @@ class AnalyticsService:
return None
return _deserialize_summary(payload)
async def _store_summary_cache(self, organization_id: int, days: int, summary: DealSummary) -> None:
async def _store_summary_cache(
self, organization_id: int, days: int, summary: DealSummary
) -> None:
if not self._is_cache_enabled() or self._cache is None:
return
key = _summary_cache_key(organization_id, days)
@@ -184,7 +186,9 @@ class AnalyticsService:
return None
return _deserialize_funnel(payload)
async def _store_funnel_cache(self, organization_id: int, breakdowns: list[StageBreakdown]) -> None:
async def _store_funnel_cache(
self, organization_id: int, breakdowns: list[StageBreakdown]
) -> None:
if not self._is_cache_enabled() or self._cache is None:
return
key = _funnel_cache_key(organization_id)
@@ -198,11 +202,10 @@ def _threshold_from_days(days: int) -> datetime:
def _build_stage_map(rollup: Iterable[StageStatusRollup]) -> dict[DealStage, dict[DealStatus, int]]:
stage_map: dict[DealStage, dict[DealStatus, int]] = {
stage: {status: 0 for status in DealStatus}
for stage in _STAGE_ORDER
stage: dict.fromkeys(DealStatus, 0) for stage in _STAGE_ORDER
}
for item in rollup:
stage_map.setdefault(item.stage, {status: 0 for status in DealStatus})
stage_map.setdefault(item.stage, dict.fromkeys(DealStatus, 0))
stage_map[item.stage][item.status] = item.deal_count
return stage_map
@@ -263,7 +266,7 @@ def _deserialize_summary(payload: Any) -> DealSummary | None:
status=DealStatus(item["status"]),
count=int(item["count"]),
amount_sum=Decimal(item["amount_sum"]),
)
),
)
won = WonStatistics(
count=int(won_payload["count"]),
@@ -289,7 +292,7 @@ def _serialize_funnel(breakdowns: list[StageBreakdown]) -> list[dict[str, Any]]:
"total": item.total,
"by_status": {status.value: count for status, count in item.by_status.items()},
"conversion_to_next": item.conversion_to_next,
}
},
)
return serialized
@@ -307,15 +310,19 @@ def _deserialize_funnel(payload: Any) -> list[StageBreakdown] | None:
stage=DealStage(item["stage"]),
total=int(item["total"]),
by_status=by_status,
conversion_to_next=float(item["conversion_to_next"]) if item["conversion_to_next"] is not None else None,
)
conversion_to_next=float(item["conversion_to_next"])
if item["conversion_to_next"] is not None
else None,
),
)
except (KeyError, TypeError, ValueError):
return None
return breakdowns
async def invalidate_analytics_cache(cache: Redis | None, organization_id: int, backoff_ms: int) -> None:
async def invalidate_analytics_cache(
cache: Redis | None, organization_id: int, backoff_ms: int
) -> None:
"""Remove cached analytics payloads for the organization."""
if cache is None: