Spaces:
Sleeping
Sleeping
| """ | |
| Field validation helpers used by the graders. | |
| Each validator returns True if the value matches the expected type, | |
| False otherwise. These are intentionally simple and deterministic. | |
| """ | |
| import re | |
| from typing import Any, Dict, List, Tuple | |
| EMAIL_RE = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") | |
| PHONE_RE = re.compile(r"^\+?[1-9]\d{6,14}$") | |
| URL_RE = re.compile(r"^https?://[^\s]+$") | |
| ISO_DATETIME_RE = re.compile( | |
| r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})$" | |
| ) | |
| def validate_email(value: Any) -> bool: | |
| return isinstance(value, str) and bool(EMAIL_RE.match(value)) | |
| def validate_phone(value: Any) -> bool: | |
| if not isinstance(value, str): | |
| return False | |
| cleaned = value.replace(" ", "").replace("-", "") | |
| return bool(PHONE_RE.match(cleaned)) | |
| def validate_url(value: Any) -> bool: | |
| return isinstance(value, str) and bool(URL_RE.match(value)) | |
| def validate_datetime(value: Any) -> bool: | |
| return isinstance(value, str) and bool(ISO_DATETIME_RE.match(value)) | |
| def validate_enum(value: Any, allowed_values: List[str]) -> bool: | |
| return isinstance(value, str) and value in allowed_values | |
| def validate_field_type(value: Any, expected_type: str) -> bool: | |
| """Check if a value matches the expected type string from the spec. | |
| Supported types: string, integer, float, boolean, email, datetime, | |
| url, phone, enum:val1,val2, object, array. | |
| """ | |
| if value is None: | |
| return False | |
| if expected_type == "string": | |
| return isinstance(value, str) | |
| elif expected_type == "integer": | |
| return isinstance(value, int) and not isinstance(value, bool) | |
| elif expected_type == "float": | |
| return isinstance(value, (int, float)) and not isinstance(value, bool) | |
| elif expected_type == "boolean": | |
| return isinstance(value, bool) | |
| elif expected_type == "email": | |
| return validate_email(value) | |
| elif expected_type == "datetime": | |
| return validate_datetime(value) | |
| elif expected_type == "url": | |
| return validate_url(value) | |
| elif expected_type == "phone": | |
| return validate_phone(value) | |
| elif expected_type.startswith("enum:"): | |
| allowed = expected_type.split(":", 1)[1].split(",") | |
| return validate_enum(value, allowed) | |
| elif expected_type == "object": | |
| return isinstance(value, dict) | |
| elif expected_type == "array": | |
| return isinstance(value, list) | |
| else: | |
| # Unknown type, accept anything non-None | |
| return True | |
| def validate_request_against_spec( | |
| request: Dict[str, Any], | |
| spec: Dict[str, Any], | |
| ) -> Tuple[float, str]: | |
| """Validate a request body against its spec. | |
| Returns (score, feedback_string) where score is 0.0 to 1.0 | |
| based on how many checks pass. | |
| """ | |
| checks = [] | |
| total = 0 | |
| passed = 0 | |
| # Check required fields are present and non-null | |
| for field in spec["required_fields"]: | |
| total += 1 | |
| if field in request and request[field] is not None: | |
| passed += 1 | |
| checks.append(f" {field}: PRESENT") | |
| else: | |
| checks.append(f" {field}: MISSING") | |
| # Check field types for fields that are present | |
| for field, expected_type in spec["field_types"].items(): | |
| if field not in request or request[field] is None: | |
| continue | |
| total += 1 | |
| if validate_field_type(request[field], expected_type): | |
| passed += 1 | |
| checks.append(f" {field} type: VALID ({expected_type})") | |
| else: | |
| checks.append(f" {field} type: INVALID (expected {expected_type})") | |
| # Check no extra unknown fields | |
| all_known = set(spec["required_fields"]) | set(spec.get("optional_fields", [])) | |
| for field in request: | |
| if field not in all_known: | |
| total += 1 | |
| checks.append(f" {field}: UNKNOWN FIELD (not in spec)") | |
| score = passed / total if total > 0 else 0.0 | |
| feedback = f"Validation: {passed}/{total} checks passed.\n" + "\n".join(checks) | |
| return round(score, 4), feedback | |
| def validate_headers_against_spec( | |
| headers: Dict[str, str], | |
| spec: Dict[str, Any], | |
| ) -> Tuple[float, str]: | |
| """Validate request headers against the spec's required_headers. | |
| Returns (score, feedback_string). | |
| """ | |
| required = spec.get("required_headers", {}) | |
| if not required: | |
| return 1.0, "No required headers." | |
| total = len(required) | |
| passed = 0 | |
| checks = [] | |
| for header_name in required: | |
| if header_name in headers: | |
| passed += 1 | |
| checks.append(f" {header_name}: PRESENT") | |
| else: | |
| checks.append(f" {header_name}: MISSING") | |
| score = passed / total if total > 0 else 1.0 | |
| feedback = f"Headers: {passed}/{total} present.\n" + "\n".join(checks) | |
| return round(score, 4), feedback | |