from __future__ import annotations from collections import Counter from app.repositories.pricing import find_effective_price_rule from app.repositories.scheme_groups import list_scheme_version_groups from app.repositories.scheme_seats import list_scheme_version_seats from app.repositories.scheme_sectors import list_scheme_version_sectors async def build_scheme_validation_report( *, scheme_id: str, scheme_version_id: str, ) -> dict: sectors = await list_scheme_version_sectors(scheme_version_id) groups = await list_scheme_version_groups(scheme_version_id) seats = await list_scheme_version_seats(scheme_version_id) seat_ids = [row.seat_id for row in seats if row.seat_id] duplicate_seat_ids = sorted([seat_id for seat_id, count in Counter(seat_ids).items() if count > 1]) seats_without_price: list[str] = [] seats_without_sector_or_group: list[str] = [] seats_with_missing_contract: list[str] = [] priced_seats_count = 0 for seat in seats: if not seat.seat_id or not seat.element_id: seats_with_missing_contract.append(seat.element_id or seat.seat_id or "unknown") continue if not seat.sector_id and not seat.group_id: seats_without_sector_or_group.append(seat.seat_id) try: await find_effective_price_rule( scheme_id=scheme_id, seat_id=seat.seat_id, group_id=seat.group_id, sector_id=seat.sector_id, ) priced_seats_count += 1 except Exception: seats_without_price.append(seat.seat_id) errors: list[dict] = [] warnings: list[dict] = [] if duplicate_seat_ids: errors.append( { "code": "duplicate_seat_ids", "message": "Duplicate seat_id values found", "items": duplicate_seat_ids, } ) if seats_with_missing_contract: errors.append( { "code": "missing_seat_contract", "message": "Some seats have missing contract fields", "items": seats_with_missing_contract, } ) if seats_without_sector_or_group: warnings.append( { "code": "seats_without_sector_or_group", "message": "Some seats do not belong to sector or group", "items": sorted(seats_without_sector_or_group), } ) if seats_without_price: warnings.append( { "code": "seats_without_price", "message": "Some seats have no pricing rule", "items": sorted(seats_without_price), } ) is_publishable = len(errors) == 0 return { "summary": { "sectors_count": len(sectors), "groups_count": len(groups), "seats_count": len(seats), "priced_seats_count": priced_seats_count, "unpriced_seats_count": len(seats_without_price), "duplicate_seat_ids_count": len(duplicate_seat_ids), "seats_with_missing_contract_count": len(seats_with_missing_contract), "is_publishable": is_publishable, }, "errors": errors, "warnings": warnings, "issues": { "duplicate_seat_ids": duplicate_seat_ids, "seats_without_price": sorted(seats_without_price), "seats_without_sector_or_group": sorted(seats_without_sector_or_group), "seats_with_missing_contract": seats_with_missing_contract, }, }