Coverage for celery_tasks/validation_tasks.py: 7.53%
59 statements
« prev ^ index » next coverage.py v7.10.6, created at 2026-04-12 14:12 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2026-04-12 14:12 +0000
1"""
2Validation Tasks
4Tasks for validating match data before processing.
5These ensure data quality and prevent invalid data from entering the database.
6"""
8import logging
9from datetime import datetime
10from typing import Any
12from celery_app import app
14logger = logging.getLogger(__name__)
17@app.task(name="celery_tasks.validation_tasks.validate_match_data")
18def validate_match_data(match_data: dict[str, Any]) -> dict[str, Any]:
19 """
20 Validate match data structure and contents.
22 Checks:
23 - Required fields are present
24 - Data types are correct
25 - Values are within acceptable ranges
26 - Dates are valid
27 - Team names are not empty
29 Args:
30 match_data: Dictionary containing match information
32 Returns:
33 Dict containing:
34 - valid: bool - Whether the data is valid
35 - errors: List[str] - List of validation errors (empty if valid)
36 - warnings: List[str] - List of warnings (data is still valid)
37 """
38 errors: list[str] = []
39 warnings: list[str] = []
41 # Required fields
42 required_fields = ["home_team", "away_team", "match_date", "season"]
43 for field in required_fields:
44 if field not in match_data or not match_data[field]:
45 errors.append(f"Missing required field: {field}")
47 # If required fields are missing, return early
48 if errors:
49 return {"valid": False, "errors": errors, "warnings": warnings}
51 # Validate team names
52 home_team = match_data["home_team"]
53 away_team = match_data["away_team"]
55 if not isinstance(home_team, str) or len(home_team.strip()) == 0:
56 errors.append("home_team must be a non-empty string")
58 if not isinstance(away_team, str) or len(away_team.strip()) == 0:
59 errors.append("away_team must be a non-empty string")
61 if home_team == away_team:
62 errors.append("home_team and away_team cannot be the same")
64 # Validate date
65 try:
66 match_date = match_data["match_date"]
67 if isinstance(match_date, str):
68 # Try to parse ISO format date
69 datetime.fromisoformat(match_date.replace("Z", "+00:00"))
70 elif not isinstance(match_date, datetime):
71 errors.append("match_date must be a string in ISO format or datetime object")
72 except (ValueError, AttributeError) as e:
73 errors.append(f"Invalid match_date format: {e}")
75 # Validate scores (if provided)
76 if "home_score" in match_data and match_data["home_score"] is not None:
77 try:
78 score = int(match_data["home_score"])
79 if score < 0:
80 errors.append("home_score cannot be negative")
81 except (ValueError, TypeError):
82 errors.append("home_score must be an integer")
84 if "away_score" in match_data and match_data["away_score"] is not None:
85 try:
86 score = int(match_data["away_score"])
87 if score < 0:
88 errors.append("away_score cannot be negative")
89 except (ValueError, TypeError):
90 errors.append("away_score must be an integer")
92 # Validate match status
93 valid_statuses = ["scheduled", "tbd", "live", "completed", "cancelled", "postponed"]
94 if "match_status" in match_data:
95 status = match_data["match_status"]
96 if status and status not in valid_statuses:
97 warnings.append(f"Unusual match_status: {status}. Expected one of: {valid_statuses}")
99 # Validate season format
100 season = match_data["season"]
101 if not isinstance(season, str):
102 errors.append("season must be a string")
103 elif len(season) < 4:
104 warnings.append(f"Unusual season format: {season}")
106 # Log validation result
107 if errors:
108 logger.warning(f"Validation failed for match {home_team} vs {away_team}: {errors}")
109 elif warnings:
110 logger.info(f"Validation passed with warnings for {home_team} vs {away_team}: {warnings}")
111 else:
112 logger.debug(f"Validation passed for {home_team} vs {away_team}")
114 return {"valid": len(errors) == 0, "errors": errors, "warnings": warnings}