Compare commits
	
		
			3 commits
		
	
	
		
			8cd2335630
			...
			f04aaf12a2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							
							
								
									
								
								 | 
						f04aaf12a2 | ||
| 
							
							
								
									
								
								 | 
						9a5035d9fd | ||
| 
							
							
								
									
								
								 | 
						503d39e6b8 | 
| 
						 | 
				
			
			@ -16,7 +16,7 @@ def travel_legs(trip: Trip, stats: StrDict) -> None:
 | 
			
		|||
            stats.setdefault("flight_count", 0)
 | 
			
		||||
            stats.setdefault("airlines", Counter())
 | 
			
		||||
            stats["flight_count"] += 1
 | 
			
		||||
            stats["airlines"][leg["airline_name"]] += 1
 | 
			
		||||
            stats["airlines"][leg["airline_detail"]["name"]] += 1
 | 
			
		||||
        if leg["type"] == "train":
 | 
			
		||||
            stats.setdefault("train_count", 0)
 | 
			
		||||
            stats["train_count"] += 1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,11 +60,18 @@ def parse_yaml(travel_type: str, data_dir: str) -> TravelList:
 | 
			
		|||
def get_flights(data_dir: str) -> list[Event]:
 | 
			
		||||
    """Get travel events."""
 | 
			
		||||
    bookings = parse_yaml("flights", data_dir)
 | 
			
		||||
    airlines = parse_yaml("airlines", data_dir)
 | 
			
		||||
    by_iata = {a["iata"]: a for a in airlines}
 | 
			
		||||
    events = []
 | 
			
		||||
    for booking in bookings:
 | 
			
		||||
        for item in booking["flights"]:
 | 
			
		||||
            if not item["depart"].date():
 | 
			
		||||
                continue
 | 
			
		||||
            airline = by_iata[item["airline"]]
 | 
			
		||||
            item["airline_code"] = airline[
 | 
			
		||||
                "iata" if not airline.get("flight_number_prefer_icao") else "icao"
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
            e = Event(
 | 
			
		||||
                date=item["depart"],
 | 
			
		||||
                end_date=item.get("arrive"),
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +102,7 @@ def get_trains(data_dir: str) -> list[Event]:
 | 
			
		|||
 | 
			
		||||
def flight_number(flight: Leg) -> str:
 | 
			
		||||
    """Flight number."""
 | 
			
		||||
    airline_code = flight["airline"]
 | 
			
		||||
    airline_code = flight["airline_code"]
 | 
			
		||||
    # make sure this is the airline code, not the airline name
 | 
			
		||||
    assert " " not in airline_code and not any(c.islower() for c in airline_code)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,14 @@ from agenda import travel
 | 
			
		|||
from agenda.types import StrDict, Trip
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Airline(typing.TypedDict, total=False):
 | 
			
		||||
    """Airline."""
 | 
			
		||||
 | 
			
		||||
    iata: str
 | 
			
		||||
    icao: str
 | 
			
		||||
    name: str
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_travel(travel_type: str, plural: str, data_dir: str) -> list[StrDict]:
 | 
			
		||||
    """Read flight and train journeys."""
 | 
			
		||||
    items = travel.parse_yaml(plural, data_dir)
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +93,7 @@ def depart_datetime(item: StrDict) -> datetime:
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def process_flight(
 | 
			
		||||
    flight: StrDict, iata: dict[str, str], airports: list[StrDict]
 | 
			
		||||
    flight: StrDict, by_iata: dict[str, Airline], airports: list[StrDict]
 | 
			
		||||
) -> None:
 | 
			
		||||
    """Add airport detail, airline name and distance to flight."""
 | 
			
		||||
    if flight["from"] in airports:
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +101,11 @@ def process_flight(
 | 
			
		|||
    if flight["to"] in airports:
 | 
			
		||||
        flight["to_airport"] = airports[flight["to"]]
 | 
			
		||||
    if "airline" in flight:
 | 
			
		||||
        flight["airline_name"] = iata.get(flight["airline"], "[unknown]")
 | 
			
		||||
        airline = by_iata[flight["airline"]]
 | 
			
		||||
        flight["airline_detail"] = airline
 | 
			
		||||
        flight["airline_code"] = airline[
 | 
			
		||||
            "iata" if not airline.get("flight_number_prefer_icao") else "icao"
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    flight["distance"] = travel.flight_distance(flight)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -102,11 +114,11 @@ def load_flight_bookings(data_dir: str) -> list[StrDict]:
 | 
			
		|||
    """Load flight bookings."""
 | 
			
		||||
    bookings = load_travel("flight", "flights", data_dir)
 | 
			
		||||
    airlines = yaml.safe_load(open(os.path.join(data_dir, "airlines.yaml")))
 | 
			
		||||
    iata = {a["iata"]: a["name"] for a in airlines}
 | 
			
		||||
    by_iata = {a["iata"]: a for a in airlines}
 | 
			
		||||
    airports = travel.parse_yaml("airports", data_dir)
 | 
			
		||||
    for booking in bookings:
 | 
			
		||||
        for flight in booking["flights"]:
 | 
			
		||||
            process_flight(flight, iata, airports)
 | 
			
		||||
            process_flight(flight, by_iata, airports)
 | 
			
		||||
    return bookings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -118,7 +118,7 @@
 | 
			
		|||
  {% endfor %}
 | 
			
		||||
 | 
			
		||||
  {% for item in booking.flights %}
 | 
			
		||||
    {% set full_flight_number = item.airline + item.flight_number %}
 | 
			
		||||
    {% set full_flight_number = item.airline_code + item.flight_number %}
 | 
			
		||||
    {% set radarbox_url = "https://www.radarbox.com/data/flights/" + full_flight_number %}
 | 
			
		||||
    <div class="grid-item"></div>
 | 
			
		||||
    <div class="grid-item"></div>
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +147,7 @@
 | 
			
		|||
{% endmacro %}
 | 
			
		||||
 | 
			
		||||
{% macro flight_row(item) %}
 | 
			
		||||
  {% set full_flight_number = item.airline + item.flight_number %}
 | 
			
		||||
  {% set full_flight_number = item.airline_code + item.flight_number %}
 | 
			
		||||
  {% set radarbox_url = "https://www.radarbox.com/data/flights/" + full_flight_number %}
 | 
			
		||||
  <div class="grid-item text-end">{{ item.depart.strftime("%a, %d %b %Y") }}</div>
 | 
			
		||||
  <div class="grid-item">{{ item.from }} → {{ item.to }}</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -373,10 +373,10 @@
 | 
			
		|||
              –
 | 
			
		||||
              {{ e.end_loc }} {{ flag(trip, e.end_country.flag) }}
 | 
			
		||||
              {% if e.element_type == "flight" %}
 | 
			
		||||
                {% set full_flight_number = e.detail.airline + e.detail.flight_number %}
 | 
			
		||||
                {% set full_flight_number = e.detail.airline_code + e.detail.flight_number %}
 | 
			
		||||
                {% set radarbox_url = "https://www.radarbox.com/data/flights/" + full_flight_number %}
 | 
			
		||||
                <span class="text-nowrap"><strong>airline:</strong> {{ e.detail.airline_name }}</span>
 | 
			
		||||
                <span class="text-nowrap"><strong>flight number:</strong> {{ e.detail.airline }}{{ e.detail.flight_number }}</span>
 | 
			
		||||
                <span class="text-nowrap"><strong>flight number:</strong> {{ full_flight_number }}</span>
 | 
			
		||||
                {% if e.detail.duration %}
 | 
			
		||||
                  <span class="text-nowrap"><strong>duration:</strong> {{ e.detail.duration }}</span>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
| 
						 | 
				
			
			@ -387,7 +387,7 @@
 | 
			
		|||
              {% endif %}
 | 
			
		||||
              {% if e.element_type == "flight" %}
 | 
			
		||||
                <a href="https://www.flightradar24.com/data/flights/{{ full_flight_number | lower }}">flightradar24</a>
 | 
			
		||||
                | <a href="https://uk.flightaware.com/live/flight/{{ full_flight_number | replace("U2", "EZY") }}">FlightAware</a>
 | 
			
		||||
                | <a href="https://uk.flightaware.com/live/flight/{{ full_flight_number }}">FlightAware</a>
 | 
			
		||||
                | <a href="{{ radarbox_url }}">radarbox</a>
 | 
			
		||||
              {% endif %}
 | 
			
		||||
            </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -252,7 +252,7 @@
 | 
			
		|||
                <span>🕒{{ ((item.arrive - item.depart).total_seconds() // 60) | int }} mins</span>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                ✨
 | 
			
		||||
                <span>{{ item.airline }}{{ item.flight_number }}</span>
 | 
			
		||||
                <span>{{ item.airline_code }}{{ item.flight_number }}</span>
 | 
			
		||||
 | 
			
		||||
                {% if item.distance %}
 | 
			
		||||
                  <span>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,9 +162,19 @@ def check_airlines() -> list[agenda.types.StrDict]:
 | 
			
		|||
    airlines = agenda.travel.parse_yaml("airlines", data_dir)
 | 
			
		||||
    print(len(airlines), "airlines")
 | 
			
		||||
    for airline in airlines:
 | 
			
		||||
        assert airline.keys() == {"icao", "iata", "name"}
 | 
			
		||||
        assert len(airline["icao"]) == 3
 | 
			
		||||
        assert len(airline["iata"]) == 2
 | 
			
		||||
        try:
 | 
			
		||||
            keys = set(airline.keys())
 | 
			
		||||
            keys.discard("flight_number_prefer_icao")
 | 
			
		||||
            assert keys == {"icao", "iata", "name"}
 | 
			
		||||
            iata, icao = airline["iata"], airline["icao"]
 | 
			
		||||
            assert iata[0].isupper() and iata[1].isupper() or iata[1].isdigit()
 | 
			
		||||
            assert icao.isupper()
 | 
			
		||||
            assert len(iata) == 2 and len(icao) == 3
 | 
			
		||||
            if "flight_number_prefer_icao" in airline:
 | 
			
		||||
                assert isinstance(airline["flight_number_prefer_icao"], bool)
 | 
			
		||||
        except AssertionError:
 | 
			
		||||
            print(yaml.dump([airline]))
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    return airlines
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue