Skip to content

Commit f7700ca

Browse files
committed
Enhance manual entry feature: add timezone selection and improve datetime input handling
1 parent 7f0ad2f commit f7700ca

File tree

4 files changed

+79
-40
lines changed

4 files changed

+79
-40
lines changed

app.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
from datetime import datetime
66
from flask import Flask, render_template, request, send_file, session
77

8-
from core import get_flight, make_ics_from_selected_df_index, make_ics_from_manual_data
8+
from core import (
9+
get_flight,
10+
make_ics_from_selected_df_index,
11+
make_ics_from_manual_data,
12+
get_timezones_with_offsets,
13+
)
914

1015
app = Flask(__name__)
1116

@@ -61,7 +66,8 @@ def create_ical_from_selected(index):
6166

6267
@app.route("/manual_entry")
6368
def manual_entry():
64-
return render_template("manual_entry.html")
69+
timezones = get_timezones_with_offsets()
70+
return render_template("manual_entry.html", timezones=timezones)
6571

6672

6773
@app.route("/create_manual_event", methods=["POST"])
@@ -104,8 +110,19 @@ def create_manual_event():
104110
if not re.match(r"^[A-Z]{3}$", flight_data["destination_airport_code"]):
105111
raise ValueError("Destination airport code must be 3 uppercase letters")
106112

107-
# Validate datetime format (YYYY-MM-DD HH:MM)
113+
# Validate datetime format (datetime-local format: YYYY-MM-DDTHH:MM or YYYY-MM-DD HH:MM)
114+
# Convert datetime-local format to the expected format
108115
try:
116+
# Try datetime-local format first (YYYY-MM-DDTHH:MM)
117+
if "T" in flight_data["scheduled_departure"]:
118+
flight_data["scheduled_departure"] = flight_data[
119+
"scheduled_departure"
120+
].replace("T", " ")
121+
if "T" in flight_data["scheduled_arrival"]:
122+
flight_data["scheduled_arrival"] = flight_data[
123+
"scheduled_arrival"
124+
].replace("T", " ")
125+
109126
datetime.strptime(flight_data["scheduled_departure"], "%Y-%m-%d %H:%M")
110127
datetime.strptime(flight_data["scheduled_arrival"], "%Y-%m-%d %H:%M")
111128
except ValueError:
@@ -118,7 +135,10 @@ def create_manual_event():
118135
return send_file(ics_data, as_attachment=True, download_name=f"{flight}.ics")
119136
except Exception as e:
120137
error_message = str(e)
121-
return render_template("manual_entry.html", error=error_message)
138+
timezones = get_timezones_with_offsets()
139+
return render_template(
140+
"manual_entry.html", error=error_message, timezones=timezones
141+
)
122142

123143

124144
if __name__ == "__main__":

core.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,38 @@
55
import icalendar
66
import pandas as pd
77
from pyflightdata import FlightData
8-
from pytz import timezone
8+
from pytz import timezone, all_timezones, utc
99

1010
f = FlightData()
1111

12+
13+
def get_timezones_with_offsets():
14+
"""Get all timezones with their UTC offsets."""
15+
timezones_dict = {}
16+
now = datetime.now(utc)
17+
18+
for tz_name in all_timezones:
19+
try:
20+
tz = timezone(tz_name)
21+
offset = tz.utcoffset(now)
22+
if offset is not None:
23+
hours, remainder = divmod(int(offset.total_seconds()), 3600)
24+
minutes = remainder // 60
25+
if minutes:
26+
offset_str = f"UTC{hours:+03d}:{minutes:02d}"
27+
else:
28+
offset_str = f"UTC{hours:+03d}:00"
29+
timezones_dict[tz_name] = f"{tz_name} ({offset_str})"
30+
except Exception:
31+
continue
32+
33+
# Sort by offset, then by name
34+
sorted_timezones = sorted(
35+
timezones_dict.items(), key=lambda x: (timezone(x[0]).utcoffset(now), x[0])
36+
)
37+
return sorted_timezones
38+
39+
1240
# test_config
1341
# flight_number = "SQ327"
1442
# date = "2024-10-23"

templates/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ <h1>Create Flight Calendar Event</h1>
120120
<input type="text" id="flight_number" name="flight_number" required oninput="this.value = this.value.toUpperCase().replace(/[^A-Z0-9]/g, '')"><br><br>
121121

122122
<label for="flight_date">Flight Date:</label>
123-
<input type="text" id="flight_date" name="flight_date" required placeholder="yyyy-mm-dd" pattern="\d{4}-\d{2}-\d{2}" title="Date format: yyyy-mm-dd"><br><br>
123+
<input type="date" id="flight_date" name="flight_date" required><br><br>
124124

125125
<input type="submit" value="Create Event">
126126
</form>

templates/manual_entry.html

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,30 @@
107107
.required {
108108
color: red;
109109
}
110+
.error-message {
111+
color: #ff4d4d;
112+
background-color: #ffe6e6;
113+
border: 1px solid #ff9999;
114+
padding: 10px 20px;
115+
border-radius: 5px;
116+
margin-bottom: 15px;
117+
font-weight: bold;
118+
text-align: center;
119+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
120+
}
110121
</style>
111122
</head>
112123
<body>
113124
<div class="emoji">✈️</div>
114125
<h1>Manual Flight Entry</h1>
115126
<p>Enter your flight details manually</p>
116127

128+
{% if error %}
129+
<div class="error-message">
130+
{{ error }}
131+
</div>
132+
{% endif %}
133+
117134
<div class="form-container">
118135
<form action="/create_manual_event" method="post">
119136
<div class="form-row">
@@ -152,11 +169,11 @@ <h1>Manual Flight Entry</h1>
152169
<div class="form-row">
153170
<div class="form-group">
154171
<label for="scheduled_departure">Departure Time: <span class="required">*</span></label>
155-
<input type="text" id="scheduled_departure" name="scheduled_departure" required placeholder="yyyy-mm-dd hh:mm" pattern="\d{4}-\d{2}-\d{2} \d{2}:\d{2}" title="Format: yyyy-mm-dd hh:mm (24-hour)">
172+
<input type="datetime-local" id="scheduled_departure" name="scheduled_departure" required>
156173
</div>
157174
<div class="form-group">
158175
<label for="scheduled_arrival">Arrival Time: <span class="required">*</span></label>
159-
<input type="text" id="scheduled_arrival" name="scheduled_arrival" required placeholder="yyyy-mm-dd hh:mm" pattern="\d{4}-\d{2}-\d{2} \d{2}:\d{2}" title="Format: yyyy-mm-dd hh:mm (24-hour)">
176+
<input type="datetime-local" id="scheduled_arrival" name="scheduled_arrival" required>
160177
</div>
161178
</div>
162179

@@ -165,44 +182,18 @@ <h1>Manual Flight Entry</h1>
165182
<label for="origin_timezone">Origin Timezone: <span class="required">*</span></label>
166183
<select id="origin_timezone" name="origin_timezone" required>
167184
<option value="">Select timezone...</option>
168-
<option value="America/New_York">America/New_York (EST/EDT)</option>
169-
<option value="America/Chicago">America/Chicago (CST/CDT)</option>
170-
<option value="America/Denver">America/Denver (MST/MDT)</option>
171-
<option value="America/Los_Angeles">America/Los_Angeles (PST/PDT)</option>
172-
<option value="America/Anchorage">America/Anchorage (AKST/AKDT)</option>
173-
<option value="Pacific/Honolulu">Pacific/Honolulu (HST)</option>
174-
<option value="Europe/London">Europe/London (GMT/BST)</option>
175-
<option value="Europe/Paris">Europe/Paris (CET/CEST)</option>
176-
<option value="Europe/Berlin">Europe/Berlin (CET/CEST)</option>
177-
<option value="Asia/Tokyo">Asia/Tokyo (JST)</option>
178-
<option value="Asia/Shanghai">Asia/Shanghai (CST)</option>
179-
<option value="Asia/Dubai">Asia/Dubai (GST)</option>
180-
<option value="Asia/Singapore">Asia/Singapore (SGT)</option>
181-
<option value="Asia/Hong_Kong">Asia/Hong_Kong (HKT)</option>
182-
<option value="Australia/Sydney">Australia/Sydney (AEDT/AEST)</option>
183-
<option value="UTC">UTC</option>
185+
{% for tz_name, tz_display in timezones %}
186+
<option value="{{ tz_name }}">{{ tz_display }}</option>
187+
{% endfor %}
184188
</select>
185189
</div>
186190
<div class="form-group">
187191
<label for="destination_timezone">Destination Timezone: <span class="required">*</span></label>
188192
<select id="destination_timezone" name="destination_timezone" required>
189193
<option value="">Select timezone...</option>
190-
<option value="America/New_York">America/New_York (EST/EDT)</option>
191-
<option value="America/Chicago">America/Chicago (CST/CDT)</option>
192-
<option value="America/Denver">America/Denver (MST/MDT)</option>
193-
<option value="America/Los_Angeles">America/Los_Angeles (PST/PDT)</option>
194-
<option value="America/Anchorage">America/Anchorage (AKST/AKDT)</option>
195-
<option value="Pacific/Honolulu">Pacific/Honolulu (HST)</option>
196-
<option value="Europe/London">Europe/London (GMT/BST)</option>
197-
<option value="Europe/Paris">Europe/Paris (CET/CEST)</option>
198-
<option value="Europe/Berlin">Europe/Berlin (CET/CEST)</option>
199-
<option value="Asia/Tokyo">Asia/Tokyo (JST)</option>
200-
<option value="Asia/Shanghai">Asia/Shanghai (CST)</option>
201-
<option value="Asia/Dubai">Asia/Dubai (GST)</option>
202-
<option value="Asia/Singapore">Asia/Singapore (SGT)</option>
203-
<option value="Asia/Hong_Kong">Asia/Hong_Kong (HKT)</option>
204-
<option value="Australia/Sydney">Australia/Sydney (AEDT/AEST)</option>
205-
<option value="UTC">UTC</option>
194+
{% for tz_name, tz_display in timezones %}
195+
<option value="{{ tz_name }}">{{ tz_display }}</option>
196+
{% endfor %}
206197
</select>
207198
</div>
208199
</div>

0 commit comments

Comments
 (0)