feat: Implement MFA authentication, add ride statistics model, and update various services, APIs, and tests across the application.

This commit is contained in:
pacnpal
2025-12-28 17:32:53 -05:00
parent aa56c46c27
commit c95f99ca10
452 changed files with 7948 additions and 6073 deletions

View File

@@ -5,18 +5,19 @@ This service handles the creation, delivery, and management of notifications
for various events including submission approvals/rejections.
"""
from django.utils import timezone
from django.contrib.contenttypes.models import ContentType
from django.template.loader import render_to_string
from django.conf import settings
from django.db import models
from typing import Optional, Dict, Any, List
from datetime import datetime, timedelta
import logging
from datetime import datetime, timedelta
from typing import Any
from apps.accounts.models import User, UserNotification, NotificationPreference
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.template.loader import render_to_string
from django.utils import timezone
from django_forwardemail.services import EmailService
from apps.accounts.models import NotificationPreference, User, UserNotification
logger = logging.getLogger(__name__)
@@ -29,10 +30,10 @@ class NotificationService:
notification_type: str,
title: str,
message: str,
related_object: Optional[Any] = None,
related_object: Any | None = None,
priority: str = UserNotification.Priority.NORMAL,
extra_data: Optional[Dict[str, Any]] = None,
expires_at: Optional[datetime] = None,
extra_data: dict[str, Any] | None = None,
expires_at: datetime | None = None,
) -> UserNotification:
"""
Create a new notification for a user.
@@ -273,9 +274,9 @@ class NotificationService:
def get_user_notifications(
user: User,
unread_only: bool = False,
notification_types: Optional[List[str]] = None,
limit: Optional[int] = None,
) -> List[UserNotification]:
notification_types: list[str] | None = None,
limit: int | None = None,
) -> list[UserNotification]:
"""
Get notifications for a user.
@@ -308,7 +309,7 @@ class NotificationService:
@staticmethod
def mark_notifications_read(
user: User, notification_ids: Optional[List[int]] = None
user: User, notification_ids: list[int] | None = None
) -> int:
"""
Mark notifications as read for a user.

View File

@@ -6,13 +6,14 @@ social authentication providers while ensuring users never lock themselves
out of their accounts.
"""
from typing import Dict, List, Tuple, TYPE_CHECKING
from django.contrib.auth import get_user_model
import logging
from typing import TYPE_CHECKING
from allauth.socialaccount.models import SocialApp
from allauth.socialaccount.providers import registry
from django.contrib.auth import get_user_model
from django.contrib.sites.shortcuts import get_current_site
from django.http import HttpRequest
import logging
if TYPE_CHECKING:
from apps.accounts.models import User
@@ -26,7 +27,7 @@ class SocialProviderService:
"""Service for managing social provider connections."""
@staticmethod
def can_disconnect_provider(user: User, provider: str) -> Tuple[bool, str]:
def can_disconnect_provider(user: User, provider: str) -> tuple[bool, str]:
"""
Check if a user can safely disconnect a social provider.
@@ -69,7 +70,7 @@ class SocialProviderService:
return False, "Unable to verify disconnection safety. Please try again."
@staticmethod
def get_connected_providers(user: "User") -> List[Dict]:
def get_connected_providers(user: "User") -> list[dict]:
"""
Get all social providers connected to a user's account.
@@ -106,7 +107,7 @@ class SocialProviderService:
return []
@staticmethod
def get_available_providers(request: HttpRequest) -> List[Dict]:
def get_available_providers(request: HttpRequest) -> list[dict]:
"""
Get all available social providers for the current site.
@@ -152,7 +153,7 @@ class SocialProviderService:
return []
@staticmethod
def disconnect_provider(user: "User", provider: str) -> Tuple[bool, str]:
def disconnect_provider(user: "User", provider: str) -> tuple[bool, str]:
"""
Disconnect a social provider from a user's account.
@@ -191,7 +192,7 @@ class SocialProviderService:
return False, f"Failed to disconnect {provider} account. Please try again."
@staticmethod
def get_auth_status(user: "User") -> Dict:
def get_auth_status(user: "User") -> dict:
"""
Get comprehensive authentication status for a user.
@@ -231,7 +232,7 @@ class SocialProviderService:
}
@staticmethod
def validate_provider_exists(provider: str) -> Tuple[bool, str]:
def validate_provider_exists(provider: str) -> tuple[bool, str]:
"""
Validate that a social provider is configured and available.

View File

@@ -5,19 +5,18 @@ This service handles user account deletion while preserving submissions
and maintaining data integrity across the platform.
"""
from django.utils import timezone
from django.db import transaction
from django.contrib.auth import get_user_model
from django.core.mail import send_mail
from django.conf import settings
from django.template.loader import render_to_string
from typing import Dict, Any, Tuple, Optional
import logging
import secrets
import string
from datetime import datetime
from typing import Any
from apps.accounts.models import User
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.mail import send_mail
from django.db import transaction
from django.template.loader import render_to_string
from django.utils import timezone
logger = logging.getLogger(__name__)
@@ -41,7 +40,7 @@ class UserDeletionService:
_deletion_requests = {}
@staticmethod
def can_delete_user(user: User) -> Tuple[bool, Optional[str]]:
def can_delete_user(user: User) -> tuple[bool, str | None]:
"""
Check if a user can be safely deleted.
@@ -104,7 +103,7 @@ class UserDeletionService:
return deletion_request
@staticmethod
def verify_and_delete_user(verification_code: str) -> Dict[str, Any]:
def verify_and_delete_user(verification_code: str) -> dict[str, Any]:
"""
Verify deletion code and delete user account.
@@ -169,7 +168,7 @@ class UserDeletionService:
@staticmethod
@transaction.atomic
def delete_user_preserve_submissions(user: User) -> Dict[str, Any]:
def delete_user_preserve_submissions(user: User) -> dict[str, Any]:
"""
Delete a user account while preserving all their submissions.
@@ -217,7 +216,7 @@ class UserDeletionService:
}
@staticmethod
def _count_user_submissions(user: User) -> Dict[str, int]:
def _count_user_submissions(user: User) -> dict[str, int]:
"""Count all submissions for a user."""
counts = {}