chore: fix all ruff lint errors + format — 0 errors, 306 tests pass

This commit is contained in:
leocrm-bot
2026-06-29 17:43:56 +02:00
parent 316f323ff4
commit a2452cc04b
81 changed files with 2317 additions and 1128 deletions
+12 -7
View File
@@ -5,20 +5,20 @@ from __future__ import annotations
import hashlib
import secrets
import uuid
from datetime import datetime, timedelta, timezone
from datetime import UTC, datetime, timedelta
from typing import Any
import redis.asyncio as aioredis
from passlib.context import CryptContext
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import get_settings
from app.models.user import User, UserTenant
from app.models.role import Role
from app.models.session import Session as SessionModel
from app.models.user import User
_pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto", bcrypt__rounds=get_settings().bcrypt_rounds)
_pwd_context = CryptContext(
schemes=["bcrypt"], deprecated="auto", bcrypt__rounds=get_settings().bcrypt_rounds
)
def hash_password(password: str) -> str:
@@ -63,7 +63,7 @@ async def create_session(
settings = get_settings()
session_id = str(uuid.uuid4())
csrf_token = generate_csrf_token()
expires_at = datetime.now(timezone.utc) + timedelta(seconds=settings.session_ttl_seconds)
expires_at = datetime.now(UTC) + timedelta(seconds=settings.session_ttl_seconds)
# Redis runtime session
session_data: dict[str, Any] = {
@@ -76,6 +76,7 @@ async def create_session(
"is_active": user.is_active,
}
import json
await redis.setex(
f"session:{session_id}",
settings.session_ttl_seconds,
@@ -99,6 +100,7 @@ async def create_session(
async def get_session_data(redis: aioredis.Redis, session_id: str) -> dict[str, Any] | None:
"""Retrieve session data from Redis."""
import json
raw = await redis.get(f"session:{session_id}")
if raw is None:
return None
@@ -117,6 +119,7 @@ async def update_session_tenant(
) -> dict[str, Any] | None:
"""Update the active tenant in a Redis session."""
import json
settings = get_settings()
raw = await redis.get(f"session:{session_id}")
if raw is None:
@@ -130,7 +133,9 @@ async def update_session_tenant(
return data
def check_permission(role_name: str, module: str, action: str, permissions: dict | None = None) -> bool:
def check_permission(
role_name: str, module: str, action: str, permissions: dict | None = None
) -> bool:
"""Check if a role has permission for a module+action.
Built-in roles: admin (all), editor (read+write), viewer (read only).
Custom roles use the permissions dict.
-1
View File
@@ -9,7 +9,6 @@ import redis.asyncio as aioredis
from app.config import get_settings
_cache_redis: aioredis.Redis | None = None
+13 -15
View File
@@ -3,10 +3,14 @@
from __future__ import annotations
import contextlib
import uuid
from collections.abc import AsyncGenerator
from typing import Any
from datetime import datetime
from typing import Any # noqa: F401
from sqlalchemy import event as sa_event
from sqlalchemy import DateTime, String, func, text # noqa: F401
from sqlalchemy import event as sa_event # noqa: F401
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.ext.asyncio import (
AsyncEngine,
AsyncSession,
@@ -14,10 +18,6 @@ from sqlalchemy.ext.asyncio import (
create_async_engine,
)
from sqlalchemy.orm import DeclarativeBase, Mapped, declared_attr, mapped_column
from sqlalchemy import text, String, DateTime, func
from sqlalchemy.dialects.postgresql import UUID as PGUUID
import uuid
from datetime import datetime
from app.config import get_settings
@@ -26,8 +26,8 @@ class Base(DeclarativeBase):
"""Declarative base with shared columns and tenant-scoping support."""
@declared_attr.directive
def __tablename__(cls) -> str:
return cls.__name__.lower() + "s"
def __tablename__(self) -> str:
return self.__name__.lower() + "s"
class TimestampMixin:
@@ -44,9 +44,7 @@ class TimestampMixin:
class TenantMixin(TimestampMixin):
"""Adds tenant_id column and enables ORM-level auto-filtering."""
tenant_id: Mapped[uuid.UUID] = mapped_column(
PGUUID(as_uuid=True), nullable=False, index=True
)
tenant_id: Mapped[uuid.UUID] = mapped_column(PGUUID(as_uuid=True), nullable=False, index=True)
# Global engine and session factory
@@ -101,7 +99,9 @@ async def set_tenant_context(session: AsyncSession, tenant_id: uuid.UUID | str)
@contextlib.asynccontextmanager
async def create_db_session(tenant_id: uuid.UUID | str | None = None) -> AsyncGenerator[AsyncSession, None]:
async def create_db_session(
tenant_id: uuid.UUID | str | None = None,
) -> AsyncGenerator[AsyncSession, None]:
"""Create a standalone session outside FastAPI (e.g. for tests/workers)."""
factory = get_session_factory()
async with factory() as session:
@@ -128,7 +128,5 @@ def reset_engine_for_testing(engine: AsyncEngine) -> async_sessionmaker[AsyncSes
"""Replace the global engine with a test engine. Returns a session factory."""
global _engine, _session_factory
_engine = engine
_session_factory = async_sessionmaker(
bind=engine, expire_on_commit=False, class_=AsyncSession
)
_session_factory = async_sessionmaker(bind=engine, expire_on_commit=False, class_=AsyncSession)
return _session_factory
+3 -1
View File
@@ -4,7 +4,8 @@ from __future__ import annotations
import asyncio
from collections import defaultdict
from typing import Any, Callable, Coroutine
from collections.abc import Callable, Coroutine
from typing import Any
EventHandler = Callable[[dict[str, Any]], Coroutine[Any, Any, None]]
@@ -48,4 +49,5 @@ def register_workflow_event_handlers() -> None:
Should be called during application startup.
"""
from app.workflows.engine import register_workflow_event_handlers as _register
_register()
+20 -10
View File
@@ -3,9 +3,10 @@
from __future__ import annotations
import uuid
from datetime import UTC
from typing import Any
from sqlalchemy import select, func, update
from sqlalchemy import func, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.notification import Notification
@@ -42,9 +43,13 @@ async def list_notifications(
"""List notifications for a user, unread first, then by created_at desc."""
offset = (page - 1) * page_size
# Count total
count_q = select(func.count()).select_from(Notification).where(
Notification.tenant_id == tenant_id,
Notification.user_id == user_id,
count_q = (
select(func.count())
.select_from(Notification)
.where(
Notification.tenant_id == tenant_id,
Notification.user_id == user_id,
)
)
total = (await db.execute(count_q)).scalar() or 0
@@ -80,7 +85,8 @@ async def mark_notification_read(
notification_id: uuid.UUID,
) -> Notification | None:
"""Mark a notification as read."""
from datetime import datetime, timezone
from datetime import datetime
q = (
update(Notification)
.where(
@@ -88,7 +94,7 @@ async def mark_notification_read(
Notification.tenant_id == tenant_id,
Notification.user_id == user_id,
)
.values(read_at=datetime.now(timezone.utc))
.values(read_at=datetime.now(UTC))
.returning(Notification)
)
result = await db.execute(q)
@@ -102,10 +108,14 @@ async def get_unread_count(
user_id: uuid.UUID,
) -> int:
"""Get unread notification count for a user."""
q = select(func.count()).select_from(Notification).where(
Notification.tenant_id == tenant_id,
Notification.user_id == user_id,
Notification.read_at.is_(None),
q = (
select(func.count())
.select_from(Notification)
.where(
Notification.tenant_id == tenant_id,
Notification.user_id == user_id,
Notification.read_at.is_(None),
)
)
result = await db.execute(q)
return result.scalar() or 0
-1
View File
@@ -5,7 +5,6 @@ from __future__ import annotations
from fastapi import HTTPException, Request, status
from app.core.auth import get_redis
from app.config import get_settings
async def check_rate_limit(
+1 -3
View File
@@ -3,10 +3,7 @@
from __future__ import annotations
import uuid
from typing import Any
from sqlalchemy import event as sa_event
from sqlalchemy.orm import Session, with_loader_criteria
from sqlalchemy.sql import Select
from app.core.db import TenantMixin
@@ -21,4 +18,5 @@ def apply_tenant_filter(query: Select, tenant_id: uuid.UUID) -> Select:
async def set_rls_context(session, tenant_id: uuid.UUID | str) -> None:
"""Set PostgreSQL RLS session variable."""
from app.core.db import set_tenant_context
await set_tenant_context(session, tenant_id)