UKVI Travel History Calculator

This Python script is designed to calculate the total number of days spent outside the UK from a flight history log, which is particularly useful for UK Visa and Immigration (UKVI) applications like Indefinite Leave to Remain (ILR) that have limits on absences.

Note

Generated with Google Gemini

Out:

--- Processing Flight Log ---
⚠️ Warning: Ignoring Inbound flight on 2019-12-02 06:15:00 as there was no preceding Outbound flight.
⚠️ Warning: Consecutive Outbound flight on 2022-08-06 05:50:00. Using this as the new departure.
⚠️ Warning: Consecutive Outbound flight on 2023-09-04 08:50:00. Using this as the new departure.
⚠️ Warning: Consecutive Outbound flight on 2024-04-24 16:30:00. Using this as the new departure.

⚠️ Note: Log ends while abroad. The last trip starting 2024-07-20 19:15:00 is not included.

--- Final Summary ---
✅ Total time spent abroad: 543 days (exact duration)
✅ Total time spent abroad: 535 days (full days only)

--- Valid Round Trips ---
         Outbound Date        Inbound Date  Duration (days)                           Route
0  2019-12-04 20:05:00 2020-01-13 00:20:00               39  Out: STN → MAD | In: SGN → LHR
1  2020-09-15 10:00:00 2021-02-08 15:35:00              146  Out: STN → RMI | In: MAD → LHR
2  2021-06-17 08:25:00 2021-10-05 07:05:00              109  Out: STN → MAD | In: MAD → STN
3  2021-11-19 08:05:00 2021-12-02 20:45:00               13  Out: STN → BLO | In: RMU → LGW
4  2022-01-20 17:20:00 2022-02-15 12:35:00               25  Out: LGW → MAD | In: MAD → STN
5  2022-03-24 09:50:00 2022-03-30 14:50:00                6  Out: STN → TFS | In: TFS → LGW
6  2022-04-22 11:40:00 2022-04-27 15:10:00                5  Out: STN → LIS | In: LIS → STN
7  2022-06-03 12:55:00 2022-06-14 15:35:00               11  Out: STN → MAD | In: MAD → STN
8  2022-08-06 05:50:00 2022-09-21 06:45:00               46  Out: LTN → CDT | In: MAD → STN
9  2022-10-26 13:05:00 2022-11-15 15:50:00               20  Out: STN → MAD | In: MAD → LHR
10 2022-11-16 21:25:00 2022-12-26 09:05:00               39  Out: LHR → KUL | In: KUL → LHR
11 2023-02-04 06:45:00 2023-02-06 14:10:00                2  Out: LTN → ATH | In: ATH → LGW
12 2023-02-21 16:05:00 2023-03-01 10:15:00                7  Out: LTN → BLQ | In: PEG → STN
13 2023-04-28 14:45:00 2023-05-09 12:20:00               10  Out: STN → LPA | In: LPA → STN
14 2023-09-04 08:50:00 2023-09-11 07:10:00                6  Out: LGW → MXP | In: MXP → LGW
15 2023-10-04 18:15:00 2023-10-17 15:35:00               12  Out: STN → MAD | In: MAD → STN
16 2023-12-15 06:15:00 2024-01-10 10:45:00               26  Out: STN → PEG | In: MAD → STN
17 2024-04-24 16:30:00 2024-05-02 20:50:00                8  Out: LHR → MAD | In: MAD → LHR
18 2024-06-26 15:50:00 2024-07-01 18:40:00                5  Out: LHR → MAD | In: MAD → LGW

 14 # Libraries
 15 import pandas as pd
 16
 17
 18 def calculate_time_abroad_from_df(
 19         df: pd.DataFrame,
 20         date_col: str,
 21         status_col: str,
 22         dep_port_col: str,
 23         arr_port_col: str
 24 ) -> tuple[pd.Timedelta, pd.DataFrame]:
 25     """
 26     Calculates total time spent abroad from a flight log in a pandas DataFrame,
 27     handling multi-airport routes.
 28
 29     Args:
 30         df: The DataFrame containing the flight log.
 31         date_col: The name of the column with the flight timestamps.
 32         status_col: The name of the column with the 'Inbound'/'Outbound' status.
 33         dep_port_col: The name of the departure port column.
 34         arr_port_col: The name of the arrival port column.
 35
 36     Returns:
 37         A tuple containing:
 38         - The total duration abroad (pandas.Timedelta).
 39         - A new DataFrame with details of the valid round trips, including routes.
 40     """
 41
 42     # --- 1. Prepare the DataFrame ---
 43     df_processed = df.copy()
 44     df_processed[date_col] = pd.to_datetime(
 45         df_processed[date_col], dayfirst=True, errors='coerce')
 46     df_processed = df_processed.dropna(subset=[date_col]) \
 47         .sort_values(by=date_col).reset_index(drop=True)
 48
 49     # --- 2. Calculate Time Abroad with State Machine Logic ---
 50     total_duration_abroad = pd.Timedelta(0)
 51     last_outbound_leg = None  # Stores the entire row of the last outbound flight
 52     valid_trips = []
 53
 54     print("--- Processing Flight Log ---")
 55
 56     for index, row in df_processed.iterrows():
 57         if row[status_col] == 'Outbound':
 58             if last_outbound_leg is not None:
 59                 print(f"⚠️ Warning: Consecutive Outbound flight on {row[date_col]}. Using this as the new departure.")
 60             last_outbound_leg = row
 61
 62         elif row[status_col] == 'Inbound':
 63             if last_outbound_leg is not None:
 64                 # A valid return trip is found
 65                 duration = row[date_col] - last_outbound_leg[date_col]
 66                 total_duration_abroad += duration
 67
 68
 69                 # Capture the outbound and inbound journeys separately
 70                 outbound_route = f"{last_outbound_leg[dep_port_col]}{last_outbound_leg[arr_port_col]}"
 71                 inbound_route = f"{row[dep_port_col]}{row[arr_port_col]}"
 72
 73                 # Combine them for a full description
 74                 full_route_str = f"Out: {outbound_route} | In: {inbound_route}"
 75
 76                 valid_trips.append({
 77                     'Outbound Date': last_outbound_leg[date_col],
 78                     'Inbound Date': row[date_col],
 79                     'Duration (days)': duration.days,
 80                     'Route': full_route_str
 81                 })
 82
 83                 # Reset state to "Home"
 84                 last_outbound_leg = None
 85             else:
 86                 print(
 87                     f"⚠️ Warning: Ignoring Inbound flight on {row[date_col]} as there was no preceding Outbound flight.")
 88
 89     if last_outbound_leg is not None:
 90         print(
 91             f"\n⚠️ Note: Log ends while abroad. The last trip starting {last_outbound_leg[date_col]} is not included.")
 92
 93     trips_df = pd.DataFrame(valid_trips)
 94
 95     return total_duration_abroad, trips_df
 96
 97
 98
 99
100
101
102
103 # ------------------------------------------
104 # Main
105 # ------------------------------------------
106 # Libraries
107 from pathlib import Path
108
109 # Configuration
110 id = '775243'
111 id = '1085721'
112 out_path = Path(f'./outputs/{id}')
113
114 # Load data
115 flight_df = pd.read_json(out_path / 'flights.json')
116
117 # 2. Call the function with all the required column names
118 total_time, valid_trips_df = calculate_time_abroad_from_df(
119     df=flight_df,
120     date_col='Departure Date/Time',
121     status_col='In/Out',
122     dep_port_col='Dep Port',
123     arr_port_col='Arrival Port'
124 )
125
126 # 3. Print the final results
127 print("\n--- Final Summary ---")
128 print(f"✅ Total time spent abroad: {total_time.days} days (exact duration)")
129 print(f"✅ Total time spent abroad: %s days (full days only)" % \
130     valid_trips_df['Duration (days)'].sum())
131
132 print("\n--- Valid Round Trips ---")
133 print(valid_trips_df)

Total running time of the script: ( 0 minutes 0.015 seconds)

Gallery generated by Sphinx-Gallery