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

@@ -7,23 +7,21 @@ Rankings are determined by winning percentage in these comparisons.
"""
import logging
from typing import Dict, List, Optional
from decimal import Decimal
from datetime import date
from decimal import Decimal
from django.db import transaction
from django.db.models import Avg, Count, Q
from django.utils import timezone
from apps.rides.models import (
Ride,
RideReview,
RideRanking,
RidePairComparison,
RankingSnapshot,
Ride,
RidePairComparison,
RideRanking,
RideReview,
)
logger = logging.getLogger(__name__)
@@ -43,7 +41,7 @@ class RideRankingService:
self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
self.calculation_version = "1.0"
def update_all_rankings(self, category: Optional[str] = None) -> Dict[str, any]:
def update_all_rankings(self, category: str | None = None) -> dict[str, any]:
"""
Main entry point to update all ride rankings.
@@ -105,7 +103,7 @@ class RideRankingService:
self.logger.error(f"Error updating rankings: {e}", exc_info=True)
raise
def _get_eligible_rides(self, category: Optional[str] = None) -> List[Ride]:
def _get_eligible_rides(self, category: str | None = None) -> list[Ride]:
"""
Get rides that are eligible for ranking.
@@ -127,8 +125,8 @@ class RideRankingService:
return list(queryset.distinct())
def _calculate_all_comparisons(
self, rides: List[Ride]
) -> Dict[tuple[int, int], RidePairComparison]:
self, rides: list[Ride]
) -> dict[tuple[int, int], RidePairComparison]:
"""
Calculate pairwise comparisons for all ride pairs.
@@ -156,7 +154,7 @@ class RideRankingService:
def _calculate_pairwise_comparison(
self, ride_a: Ride, ride_b: Ride
) -> Optional[RidePairComparison]:
) -> RidePairComparison | None:
"""
Calculate the pairwise comparison between two rides.
@@ -246,8 +244,8 @@ class RideRankingService:
return comparison
def _calculate_rankings_from_comparisons(
self, rides: List[Ride], comparisons: Dict[tuple[int, int], RidePairComparison]
) -> List[Dict]:
self, rides: list[Ride], comparisons: dict[tuple[int, int], RidePairComparison]
) -> list[dict]:
"""
Calculate final rankings from pairwise comparisons.
@@ -343,9 +341,9 @@ class RideRankingService:
def _apply_tiebreakers(
self,
rankings: List[Dict],
comparisons: Dict[tuple[int, int], RidePairComparison],
) -> List[Dict]:
rankings: list[dict],
comparisons: dict[tuple[int, int], RidePairComparison],
) -> list[dict]:
"""
Apply head-to-head tiebreaker for rides with identical winning percentages.
@@ -379,9 +377,9 @@ class RideRankingService:
def _sort_tied_group(
self,
tied_group: List[Dict],
comparisons: Dict[tuple[int, int], RidePairComparison],
) -> List[Dict]:
tied_group: list[dict],
comparisons: dict[tuple[int, int], RidePairComparison],
) -> list[dict]:
"""
Sort a group of tied rides using head-to-head comparisons.
"""
@@ -426,7 +424,7 @@ class RideRankingService:
return tied_group
def _save_rankings(self, rankings: List[Dict]):
def _save_rankings(self, rankings: list[dict]):
"""Save calculated rankings to the database."""
for ranking_data in rankings:
RideRanking.objects.update_or_create(
@@ -445,7 +443,7 @@ class RideRankingService:
},
)
def _save_ranking_snapshots(self, rankings: List[Dict]):
def _save_ranking_snapshots(self, rankings: list[dict]):
"""Save ranking snapshots for historical tracking."""
today = date.today()
@@ -471,7 +469,7 @@ class RideRankingService:
if deleted_snapshots[0] > 0:
self.logger.info(f"Deleted {deleted_snapshots[0]} old ranking snapshots")
def get_ride_ranking_details(self, ride: Ride) -> Optional[Dict]:
def get_ride_ranking_details(self, ride: Ride) -> dict | None:
"""
Get detailed ranking information for a specific ride.