Skip to content

Commit 9464219

Browse files
committed
added end_time as an option for the API
1 parent ac01a30 commit 9464219

File tree

1 file changed

+71
-27
lines changed

1 file changed

+71
-27
lines changed

events/api.py

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,19 @@ def get_duration_from_string(duration_str: str) -> timedelta | str:
9797
return "Invalid duration format, expected 'days:hours:minutes'"
9898

9999

100+
def get_datetime_from_string(date_str: str) -> datetime | str:
101+
"""Convert a date string in the format 'YYYY-MM-DD' to a datetime object."""
102+
try:
103+
return datetime.strptime(date_str, "%Y-%m-%d").replace(
104+
tzinfo=pytz.timezone("Europe/London")
105+
)
106+
except ValueError:
107+
return "Invalid date format, expected 'YYYY-MM-DD'"
108+
109+
100110
@events_api_bp.route("/create", methods=["POST"])
101111
@is_exec_wrapper
102-
def create_event_api() -> tuple[Response, int]:
112+
def create_event_api() -> tuple[Response, int]: # noqa: PLR0911
103113
"""Create a new event"""
104114
data = request.get_json()
105115
if not data:
@@ -116,9 +126,16 @@ def create_event_api() -> tuple[Response, int]:
116126
if field not in data:
117127
return jsonify({"error": f"Missing required field: {field}"}), 400
118128

119-
start_time = pytz.timezone("Europe/London").localize(
120-
datetime.fromisoformat(data["start_time"])
129+
start_time = get_datetime_from_string(data["start_time"])
130+
if isinstance(start_time, str):
131+
return jsonify({"error": start_time}), 400
132+
133+
end_time = (
134+
get_datetime_from_string(data.get("end_time")) if data.get("end_time") else None
121135
)
136+
if isinstance(end_time, str):
137+
return jsonify({"error": end_time}), 400
138+
122139
if "duration" in data:
123140
duration = get_duration_from_string(data["duration"])
124141
if isinstance(duration, str):
@@ -137,6 +154,7 @@ def create_event_api() -> tuple[Response, int]:
137154
data.get("colour"),
138155
start_time,
139156
duration,
157+
end_time,
140158
data.get("tags", []),
141159
)
142160
if isinstance(event, str):
@@ -156,12 +174,21 @@ def create_event( # noqa: PLR0913
156174
colour: str | None,
157175
start_time: datetime,
158176
duration: timedelta | None,
177+
end_time: datetime | None,
159178
tags: list[str],
160179
) -> Event | str:
161180
"""Create an event"""
162-
# convert start_time and calculate end_time
181+
# convert start_time and normalise end_time
163182
start_time = pytz.timezone("Europe/London").localize(start_time)
164-
end_time = start_time + duration if duration else None
183+
184+
if end_time is None:
185+
end_time = start_time + duration if duration else None
186+
else:
187+
end_time = pytz.timezone("Europe/London").localize(end_time)
188+
if duration is not None and end_time != start_time + duration:
189+
return "End time does not match the duration"
190+
if end_time and end_time < start_time:
191+
return "End time cannot be before start time"
165192

166193
# create the event object
167194
event = Event(
@@ -195,7 +222,7 @@ def create_event( # noqa: PLR0913
195222
return event
196223

197224

198-
def get_week_from_date(date: datetime) -> Week | None:
225+
def get_week_from_date(date: datetime) -> Week | None: # noqa: PLR0912
199226
"""Get the week from a given date"""
200227

201228
week = Week.query.filter(
@@ -219,12 +246,12 @@ def get_week_from_date(date: datetime) -> Week | None:
219246
).json()
220247

221248
for w in warwick_week["weeks"]:
222-
start_date = datetime.strptime(w["startDate"], "%Y-%m-%d").replace(
223-
tzinfo=pytz.timezone("Europe/London")
224-
)
225-
end_date = datetime.strptime(w["endDate"], "%Y-%m-%d").replace(
226-
tzinfo=pytz.timezone("Europe/London")
227-
)
249+
start_date = get_datetime_from_string(w["startDate"])
250+
if isinstance(start_date, str):
251+
return None
252+
end_date = get_datetime_from_string(w["endDate"])
253+
if isinstance(end_date, str):
254+
return None
228255
if start_date.date() <= date.date() <= end_date.date():
229256
name = w["name"]
230257
if "Term" in name:
@@ -250,9 +277,9 @@ def get_week_from_date(date: datetime) -> Week | None:
250277
with Path("olddates.json").open("r") as f:
251278
old_dates = load(f)
252279
for w in old_dates:
253-
start_date = datetime.strptime(w["start_date"], "%Y-%m-%d").replace(
254-
tzinfo=pytz.timezone("Europe/London")
255-
)
280+
start_date = get_datetime_from_string(w["startDate"])
281+
if isinstance(start_date, str):
282+
return None
256283
if start_date.date() <= date.date():
257284
week = Week(
258285
academic_year=year,
@@ -295,17 +322,18 @@ def create_repeat_event_api() -> tuple[Response, int]: # noqa: PLR0911
295322

296323
start_times = []
297324
for start_time_str in data["start_times"]:
298-
try:
299-
start_time = pytz.timezone("Europe/London").localize(
300-
datetime.fromisoformat(start_time_str)
301-
)
302-
except ValueError:
303-
return (
304-
jsonify({"error": f"Invalid start time format: {start_time_str}"}),
305-
400,
306-
)
325+
start_time = get_datetime_from_string(start_time_str)
326+
if isinstance(start_time, str):
327+
return jsonify({"error": start_time}), 400
307328
start_times.append(start_time)
308329

330+
end_times = []
331+
for end_time_str in data.get("end_times", []):
332+
end_time = get_datetime_from_string(end_time_str)
333+
if isinstance(end_time, str):
334+
return jsonify({"error": end_time}), 400
335+
end_times.append(end_time)
336+
309337
try:
310338
events = create_repeat_event(
311339
data["name"],
@@ -317,6 +345,7 @@ def create_repeat_event_api() -> tuple[Response, int]: # noqa: PLR0911
317345
data.get("colour"),
318346
start_times,
319347
duration, # type: ignore
348+
end_times,
320349
data.get("tags", []),
321350
)
322351
if isinstance(events, str):
@@ -336,11 +365,14 @@ def create_repeat_event( # noqa: PLR0913
336365
colour: str | None,
337366
start_times: list[datetime],
338367
duration: timedelta | None,
368+
end_times: list[datetime] | None,
339369
tags: list[str],
340370
) -> list[Event] | str:
341371
"""Create multiple events at once"""
342372
events = [] # the created events
343-
for start_time in start_times:
373+
for start_time, end_time in zip(
374+
start_times, end_times or [None] * len(start_times)
375+
):
344376
# iterate through start_times and create events
345377
event = create_event(
346378
name,
@@ -352,6 +384,7 @@ def create_repeat_event( # noqa: PLR0913
352384
colour,
353385
start_time,
354386
duration,
387+
end_time,
355388
tags,
356389
)
357390

@@ -388,7 +421,7 @@ def get_week_by_date(date_str: str) -> tuple[Response, int]:
388421

389422
@events_api_bp.route("/<int:event_id>", methods=["PATCH"])
390423
@is_exec_wrapper
391-
def edit_event(event_id: int) -> tuple[Response, int]:
424+
def edit_event(event_id: int) -> tuple[Response, int]: # noqa: PLR0911, PLR0912
392425
"""Edit an existing event"""
393426
event = Event.query.get(event_id)
394427
if not event:
@@ -408,11 +441,22 @@ def edit_event(event_id: int) -> tuple[Response, int]:
408441
event.start_time = pytz.timezone("Europe/London").localize(
409442
datetime.fromisoformat(data.get("start_time", event.start_time.isoformat()))
410443
)
444+
445+
# update end_time (with duration logic)
446+
if "end_time" in data:
447+
end_time = get_datetime_from_string(data["end_time"])
448+
if isinstance(end_time, str):
449+
return jsonify({"error": end_time}), 400
450+
event.end_time = end_time
451+
411452
if "duration" in data:
412453
duration = get_duration_from_string(data["duration"])
413454
if isinstance(duration, str):
414455
return jsonify({"error": duration}), 400
415-
event.end_time = event.start_time + duration
456+
if event.end_time is None:
457+
event.end_time = event.start_time + duration
458+
elif event.end_time != event.start_time + duration:
459+
return jsonify({"error": "End time does not match the duration"}), 400
416460

417461
# update week if start_time has changed
418462
if "start_time" in data:

0 commit comments

Comments
 (0)