"""GET/POST /upload - photograph or pick a boarding pass image, decode it, and queue it for the daily FlightAware job.""" import uuid from datetime import datetime, timedelta, timezone from flask import Blueprint, redirect, render_template, request, url_for import bcbp import config import decode import models import reference_data bp = Blueprint("upload", __name__) @bp.route("/upload", methods=["GET", "POST"]) def upload(): if request.method == "GET": return render_template("upload.html") file = request.files.get("photo") if not file or not file.filename: return render_template("upload.html", error="No photo selected."), 400 image_bytes = file.read() upload_dt = datetime.now(timezone.utc) image_name = f"{uuid.uuid4().hex}.jpg" image_path = config.UPLOADS_DIR / image_name config.ensure_dirs() with open(image_path, "wb") as f: f.write(image_bytes) raw_bcbp = decode.decode_boarding_pass(image_bytes) if not raw_bcbp: flight_id = models.create_pending_flight( image_path=image_name, status="decode_failed", error_message="Could not detect a PDF417 barcode in the photo.", ) return redirect(url_for("review.detail", flight_id=flight_id)) try: parsed = bcbp.parse_bcbp(raw_bcbp, upload_dt=upload_dt) except bcbp.BCBPParseError as e: flight_id = models.create_pending_flight( image_path=image_name, raw_bcbp=raw_bcbp, status="decode_failed", error_message=str(e), ) return redirect(url_for("review.detail", flight_id=flight_id)) if parsed.julian_day is None: flight_id = models.create_pending_flight( image_path=image_name, raw_bcbp=raw_bcbp, passenger_name=parsed.passenger_name, pnr=parsed.pnr, from_iata=parsed.from_iata, to_iata=parsed.to_iata, operating_carrier_iata=parsed.operating_carrier, flight_number=parsed.flight_number, compartment=parsed.compartment, seat=parsed.seat, parse_warnings=parsed.warnings, status="decode_failed", error_message="Could not determine the flight date from the barcode.", ) return redirect(url_for("review.detail", flight_id=flight_id)) flight_date = bcbp.resolve_flight_date(parsed.julian_day, upload_dt) process_after = datetime.combine( flight_date + timedelta(days=1), datetime.min.time(), tzinfo=timezone.utc ).isoformat() carrier_icao = reference_data.iata_to_icao_airline(parsed.operating_carrier) from_icao = reference_data.iata_to_icao_airport(parsed.from_iata) to_icao = reference_data.iata_to_icao_airport(parsed.to_iata) flight_id = models.create_pending_flight( image_path=image_name, raw_bcbp=raw_bcbp, passenger_name=parsed.passenger_name, pnr=parsed.pnr, from_iata=parsed.from_iata, to_iata=parsed.to_iata, from_icao=from_icao, to_icao=to_icao, operating_carrier_iata=parsed.operating_carrier, operating_carrier_icao=carrier_icao, flight_number=parsed.flight_number, julian_day=parsed.julian_day, flight_date=flight_date.isoformat(), compartment=parsed.compartment, seat=parsed.seat, process_after=process_after, parse_warnings=parsed.warnings, status="queued", ) return redirect(url_for("review.detail", flight_id=flight_id))