mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-22 11:06:37 +00:00
53 lines
2.5 KiB
Python
53 lines
2.5 KiB
Python
import re
|
|
import io
|
|
from typing import List, Any
|
|
from pydantic import BaseModel
|
|
from abc import ABC, abstractmethod
|
|
|
|
try:
|
|
from PIL import Image
|
|
except ImportError:
|
|
Image = None
|
|
|
|
class ValidationResult(BaseModel):
|
|
valid: bool
|
|
errors: List[str]
|
|
|
|
class IBatchValidator(ABC):
|
|
@abstractmethod
|
|
def validate_batch_size(self, batch: Any) -> ValidationResult: pass
|
|
@abstractmethod
|
|
def check_sequence_continuity(self, batch: Any, expected_start: int) -> ValidationResult: pass
|
|
@abstractmethod
|
|
def validate_naming_convention(self, filenames: List[str]) -> ValidationResult: pass
|
|
@abstractmethod
|
|
def validate_format(self, image_data: bytes) -> ValidationResult: pass
|
|
|
|
class BatchValidator(IBatchValidator):
|
|
"""H08: Validates image batch integrity, sequence continuity, and format."""
|
|
def validate_batch_size(self, batch: Any) -> ValidationResult:
|
|
if len(batch.images) < 10: return ValidationResult(valid=False, errors=[f"Batch size {len(batch.images)} below minimum 10"])
|
|
if len(batch.images) > 50: return ValidationResult(valid=False, errors=[f"Batch size {len(batch.images)} exceeds maximum 50"])
|
|
return ValidationResult(valid=True, errors=[])
|
|
|
|
def check_sequence_continuity(self, batch: Any, expected_start: int) -> ValidationResult:
|
|
try:
|
|
seqs = [int(re.match(r"AD(\d{6})\.", f, re.I).group(1)) for f in batch.filenames]
|
|
if seqs[0] != expected_start: return ValidationResult(valid=False, errors=[f"Expected start {expected_start}"])
|
|
for i in range(len(seqs) - 1):
|
|
if seqs[i+1] != seqs[i] + 1: return ValidationResult(valid=False, errors=["Gap detected"])
|
|
return ValidationResult(valid=True, errors=[])
|
|
except Exception as e:
|
|
return ValidationResult(valid=False, errors=[str(e)])
|
|
|
|
def validate_naming_convention(self, filenames: List[str]) -> ValidationResult:
|
|
ptn = re.compile(r"^AD\d{6}\.(jpg|JPG|png|PNG)$")
|
|
errs = [f"Invalid naming for {f}" for f in filenames if not ptn.match(f)]
|
|
return ValidationResult(valid=len(errs) == 0, errors=errs)
|
|
|
|
def validate_format(self, image_data: bytes) -> ValidationResult:
|
|
if len(image_data) > 10 * 1024 * 1024: return ValidationResult(valid=False, errors=["Size > 10MB"])
|
|
if not Image: return ValidationResult(valid=True, errors=[])
|
|
try: img = Image.open(io.BytesIO(image_data)); img.verify()
|
|
except Exception as e: return ValidationResult(valid=False, errors=[f"Corrupted: {e}"])
|
|
return ValidationResult(valid=True, errors=[]) |