Hide compliance status on none-Schengen trips.

This commit is contained in:
Edward Betts 2025-11-03 12:52:24 +00:00
parent 7e3f9a9b1e
commit 5d5ce61da4
2 changed files with 59 additions and 45 deletions

View file

@ -7,26 +7,37 @@ from datetime import date, timedelta
import flask
from . import get_country, trip
from .schengen import calculate_schengen_time, extract_schengen_stays_from_travel
from .schengen import (
SCHENGEN_COUNTRIES,
calculate_schengen_time,
extract_schengen_stays_from_travel,
)
from .types import SchengenCalculation, SchengenStay, StrDict, Trip
def add_schengen_compliance_to_trip(trip_obj: Trip) -> Trip:
def trip_includes_schengen(trip: Trip) -> bool:
return bool({c.alpha_2.lower() for c in trip.countries} & SCHENGEN_COUNTRIES)
def add_schengen_compliance_to_trip(trip: Trip) -> Trip:
"""Add Schengen compliance information to a trip object."""
if not trip_includes_schengen(trip):
return trip
try:
# Calculate Schengen compliance for the trip
calculation = calculate_schengen_time(trip_obj.travel)
calculation = calculate_schengen_time(trip.travel)
# Add the calculation to the trip object
trip_obj.schengen_compliance = calculation
trip.schengen_compliance = calculation
except Exception as e:
# Log the error but don't fail the trip loading
logging.warning(
f"Failed to calculate Schengen compliance for trip {trip_obj.start}: {e}"
f"Failed to calculate Schengen compliance for trip {trip.start}: {e}"
)
trip_obj.schengen_compliance = None
trip.schengen_compliance = None
return trip_obj
return trip
def get_schengen_compliance_for_all_trips(
@ -127,7 +138,9 @@ def schengen_dashboard_data(data_dir: str | None = None) -> dict[str, typing.Any
data_dir = flask.current_app.config["PERSONAL_DATA"]
# Load all trips
trip_list = trip.build_trip_list(data_dir)
trip_list = [
trip for trip in trip.build_trip_list(data_dir) if trip_includes_schengen(trip)
]
# Calculate current compliance with trip information
all_travel_items = []

View file

@ -55,6 +55,43 @@ def airport_label(airport: StrDict) -> str:
return f"{name} ({airport['iata']})"
@dataclass
class SchengenStay:
"""Represents a stay in the Schengen area."""
entry_date: date
exit_date: date | None # None if currently in Schengen
country: str
days: int
trip_date: date | None = None # Trip start date for linking
trip_name: str | None = None # Trip name for display
def __post_init__(self) -> None:
"""Post init."""
if self.exit_date is None:
# Currently in Schengen, calculate days up to today
self.days = (date.today() - self.entry_date).days + 1
else:
self.days = (self.exit_date - self.entry_date).days + 1
@dataclass
class SchengenCalculation:
"""Result of Schengen time calculation."""
total_days_used: int
days_remaining: int
is_compliant: bool
current_180_day_period: tuple[date, date] # (start, end)
stays_in_period: SchengenStay
next_reset_date: typing.Optional[date] # When the 180-day window resets
@property
def days_over_limit(self) -> int:
"""Days over the 90-day limit."""
return max(0, self.total_days_used - 90)
@dataclass
class Trip:
"""Trip."""
@ -67,7 +104,7 @@ class Trip:
flight_bookings: list[StrDict] = field(default_factory=list)
name: str | None = None
private: bool = False
schengen_compliance: typing.Optional["SchengenCalculation"] = None
schengen_compliance: SchengenCalculation | None = None
@property
def title(self) -> str:
@ -409,39 +446,3 @@ class Holiday:
if self.local_name and self.local_name != self.name
else self.name
)
@dataclass
class SchengenStay:
"""Represents a stay in the Schengen area."""
entry_date: date
exit_date: typing.Optional[date] # None if currently in Schengen
country: str
days: int
trip_date: typing.Optional[date] = None # Trip start date for linking
trip_name: typing.Optional[str] = None # Trip name for display
def __post_init__(self) -> None:
if self.exit_date is None:
# Currently in Schengen, calculate days up to today
self.days = (date.today() - self.entry_date).days + 1
else:
self.days = (self.exit_date - self.entry_date).days + 1
@dataclass
class SchengenCalculation:
"""Result of Schengen time calculation."""
total_days_used: int
days_remaining: int
is_compliant: bool
current_180_day_period: tuple[date, date] # (start, end)
stays_in_period: list["SchengenStay"]
next_reset_date: typing.Optional[date] # When the 180-day window resets
@property
def days_over_limit(self) -> int:
"""Days over the 90-day limit."""
return max(0, self.total_days_used - 90)