Улучшение системы оспариваний и исправления
- Оспаривания теперь требуют решения админа после 24ч голосования - Можно повторно оспаривать после разрешённых споров - Исправлены бонусные очки при перепрохождении после оспаривания - Сброс серии при невалидном пруфе - Колесо показывает только доступные игры - Rate limiting только через backend (RATE_LIMIT_ENABLED)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Dispute Scheduler for automatic dispute resolution after 24 hours.
|
||||
Dispute Scheduler - marks disputes as pending admin review after 24 hours.
|
||||
"""
|
||||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
@@ -8,16 +8,16 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.models import Dispute, DisputeStatus, Assignment, AssignmentStatus
|
||||
from app.services.disputes import dispute_service
|
||||
from app.services.telegram_notifier import telegram_notifier
|
||||
|
||||
|
||||
# Configuration
|
||||
CHECK_INTERVAL_SECONDS = 300 # Check every 5 minutes
|
||||
DISPUTE_WINDOW_HOURS = 24 # Disputes auto-resolve after 24 hours
|
||||
DISPUTE_WINDOW_HOURS = 24 # Disputes need admin decision after 24 hours
|
||||
|
||||
|
||||
class DisputeScheduler:
|
||||
"""Background scheduler for automatic dispute resolution."""
|
||||
"""Background scheduler that marks expired disputes for admin review."""
|
||||
|
||||
def __init__(self):
|
||||
self._running = False
|
||||
@@ -55,7 +55,7 @@ class DisputeScheduler:
|
||||
await asyncio.sleep(CHECK_INTERVAL_SECONDS)
|
||||
|
||||
async def _process_expired_disputes(self, db: AsyncSession) -> None:
|
||||
"""Process and resolve expired disputes."""
|
||||
"""Mark expired disputes as pending admin review."""
|
||||
cutoff_time = datetime.utcnow() - timedelta(hours=DISPUTE_WINDOW_HOURS)
|
||||
|
||||
# Find all open disputes that have expired
|
||||
@@ -63,7 +63,6 @@ class DisputeScheduler:
|
||||
select(Dispute)
|
||||
.options(
|
||||
selectinload(Dispute.votes),
|
||||
selectinload(Dispute.assignment).selectinload(Assignment.participant),
|
||||
)
|
||||
.where(
|
||||
Dispute.status == DisputeStatus.OPEN.value,
|
||||
@@ -74,15 +73,25 @@ class DisputeScheduler:
|
||||
|
||||
for dispute in expired_disputes:
|
||||
try:
|
||||
result_status, votes_valid, votes_invalid = await dispute_service.resolve_dispute(
|
||||
db, dispute.id
|
||||
)
|
||||
# Count votes for logging
|
||||
votes_valid = sum(1 for v in dispute.votes if v.vote is True)
|
||||
votes_invalid = sum(1 for v in dispute.votes if v.vote is False)
|
||||
|
||||
# Mark as pending admin decision
|
||||
dispute.status = DisputeStatus.PENDING_ADMIN.value
|
||||
|
||||
print(
|
||||
f"[DisputeScheduler] Auto-resolved dispute {dispute.id}: "
|
||||
f"{result_status} (valid: {votes_valid}, invalid: {votes_invalid})"
|
||||
f"[DisputeScheduler] Dispute {dispute.id} marked as pending admin "
|
||||
f"(recommendation: {'invalid' if votes_invalid > votes_valid else 'valid'}, "
|
||||
f"votes: {votes_valid} valid, {votes_invalid} invalid)"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"[DisputeScheduler] Failed to resolve dispute {dispute.id}: {e}")
|
||||
print(f"[DisputeScheduler] Failed to process dispute {dispute.id}: {e}")
|
||||
|
||||
if expired_disputes:
|
||||
await db.commit()
|
||||
# Notify admins about pending disputes
|
||||
await telegram_notifier.notify_admin_disputes_pending(db, len(expired_disputes))
|
||||
|
||||
|
||||
# Global scheduler instance
|
||||
|
||||
Reference in New Issue
Block a user