Hide compliance status on none-Schengen trips.
This commit is contained in:
		
							parent
							
								
									7e3f9a9b1e
								
							
						
					
					
						commit
						5d5ce61da4
					
				| 
						 | 
				
			
			@ -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 = []
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue