Skip to content

Commit 904cc0a

Browse files
committed
added ability to delete otpional fields when editing
1 parent 4b20c31 commit 904cc0a

File tree

3 files changed

+102
-64
lines changed

3 files changed

+102
-64
lines changed

events/api.py

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,8 @@ def create_repeat_event_api() -> tuple[Response, int]: # noqa: PLR0911
417417

418418
@events_api_bp.route("/<int:event_id>", methods=["PATCH"])
419419
@valid_api_auth
420-
def edit_event_api(event_id: int) -> tuple[Response, int]:
421-
"""Edit an existing event
420+
def edit_event_api(event_id: int) -> tuple[Response, int]: # noqa: PLR0911, PLR0912
421+
"""Edit an existing event. Leave a field out to leave it unchanged, or as an empty string to clear it.
422422
---
423423
parameters:
424424
- name: event_id
@@ -504,36 +504,64 @@ def edit_event_api(event_id: int) -> tuple[Response, int]:
504504
if not data:
505505
return jsonify({"error": "No data provided"}), 400
506506

507+
event = {}
508+
509+
if "name" in data:
510+
if data["name"] == "":
511+
return jsonify({"error": "Name cannot be empty"}), 400
512+
event["name"] = data["name"]
513+
514+
if "description" in data:
515+
if data["description"] == "":
516+
return jsonify({"error": "Description cannot be empty"}), 400
517+
event["description"] = data["description"]
518+
519+
if "draft" in data:
520+
if not isinstance(data["draft"], bool):
521+
return jsonify({"error": "Draft must be a boolean"}), 400
522+
event["draft"] = data["draft"]
523+
524+
if "location" in data:
525+
if data["location"] == "":
526+
return jsonify({"error": "Location cannot be empty"}), 400
527+
event["location"] = data["location"]
528+
529+
event["location_url"] = data.get("location_url", None)
530+
event["icon"] = data.get("icon", None)
531+
event["colour"] = data.get("colour", None)
532+
507533
# convert strings to time objects
508534
if "start_time" in data:
509-
start_time = get_datetime_from_string(data["start_time"])
510-
if isinstance(start_time, str):
511-
return jsonify({"error": start_time}), 400
535+
if data["start_time"] == "":
536+
return jsonify({"error": "Start time cannot be empty"}), 400
537+
event["start_time"] = get_datetime_from_string(data["start_time"])
538+
if isinstance(event["start_time"], str):
539+
return jsonify({"error": event["start_time"]}), 400
512540

513541
if "end_time" in data:
514-
end_time = get_datetime_from_string(data["end_time"])
515-
if isinstance(end_time, str):
516-
return jsonify({"error": end_time}), 400
542+
if data["end_time"] == "":
543+
event["end_time"] = None
544+
else:
545+
event["end_time"] = get_datetime_from_string(data["end_time"])
546+
if isinstance(event["end_time"], str):
547+
return jsonify({"error": event["end_time"]}), 400
517548

518549
if "duration" in data:
519-
duration = get_timedelta_from_string(data["duration"])
520-
if isinstance(duration, str):
521-
return jsonify({"error": duration}), 400
522-
523-
event = edit_event(
524-
event_id,
525-
data.get("name"),
526-
data.get("description"),
527-
data.get("draft", False),
528-
data.get("location"),
529-
data.get("location_url"),
530-
data.get("icon"),
531-
data.get("colour"),
532-
start_time if "start_time" in data else None, # type: ignore
533-
duration if "duration" in data else None, # type: ignore
534-
end_time if "end_time" in data else None, # type: ignore
535-
data.get("tags", []),
536-
)
550+
if data["duration"] == "":
551+
event["duration"] = None
552+
else:
553+
event["duration"] = get_timedelta_from_string(data["duration"])
554+
if isinstance(event["duration"], str):
555+
return jsonify({"error": event["duration"]}), 400
556+
557+
if "tags" in data:
558+
if not isinstance(data["tags"], list):
559+
return jsonify({"error": "Tags must be a list"}), 400
560+
if any(not isinstance(tag, str) for tag in data["tags"]):
561+
return jsonify({"error": "All tags must be strings"}), 400
562+
event["tags"] = data["tags"]
563+
564+
event = edit_event(event_id, **event)
537565

538566
if isinstance(event, str):
539567
return jsonify({"error": event}), 400

events/ui.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@
2222
def parse_form_data(form_data: ImmutableMultiDict) -> dict | str:
2323
"""Parse event from form data"""
2424
# parse colour
25-
text_colour = form_data.get("text_colour", None)
26-
color_colour = form_data.get("color_colour", None)
27-
28-
text_colour = text_colour.strip().lower() if text_colour else None
29-
color_colour = color_colour.strip().lower() if color_colour else None
25+
text_colour = (
26+
form_data["text_colour"].strip().lower()
27+
if form_data["text_colour"] != ""
28+
else None
29+
)
30+
color_colour = (
31+
form_data["color_colour"].strip().lower()
32+
if form_data["color_colour"] != ""
33+
else None
34+
)
3035

3136
if (error := validate_colour(text_colour, color_colour)) is not None:
3237
return error
@@ -44,7 +49,7 @@ def parse_form_data(form_data: ImmutableMultiDict) -> dict | str:
4449

4550
duration = (
4651
get_timedelta_from_string(form_data["duration"])
47-
if form_data["duration"]
52+
if form_data["duration"] != ""
4853
else None
4954
)
5055
if isinstance(duration, str):
@@ -68,8 +73,10 @@ def parse_form_data(form_data: ImmutableMultiDict) -> dict | str:
6873
"description": form_data["description"],
6974
"draft": "draft" in form_data,
7075
"location": form_data["location"],
71-
"location_url": form_data.get("location_url", None),
72-
"icon": form_data.get("icon", None),
76+
"location_url": (
77+
form_data["location_url"] if form_data["location_url"] != "" else None
78+
),
79+
"icon": form_data["icon"] if form_data["icon"] != "" else None,
7380
"colour": colour,
7481
"duration": duration,
7582
"tags": tags,
@@ -105,8 +112,6 @@ def create() -> str | Response:
105112

106113
# if posting, create the event
107114

108-
print("Creating event with data:", request.form)
109-
110115
# parse form data
111116
data = parse_form_data(request.form)
112117
if isinstance(data, str):

events/utils.py

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -307,19 +307,22 @@ def get_events_by_time(
307307
return query.order_by(Event.start_time, Event.end_time, Event.name).all() # type: ignore
308308

309309

310+
_KEEP = object() # placeholder to leave the field unchanged
311+
312+
310313
def edit_event( # noqa: PLR0913
311314
id: int,
312-
name: str | None = None,
313-
description: str | None = None,
314-
draft: bool | None = None,
315-
location: str | None = None,
316-
location_url: str | None = None,
317-
icon: str | None = None,
318-
colour: str | None = None,
319-
start_time: datetime | None = None,
320-
duration: timedelta | None = None,
321-
end_time: datetime | None = None,
322-
tags: list[str] | None = None,
315+
name: str | object = _KEEP,
316+
description: str | object = _KEEP,
317+
draft: bool | object = _KEEP,
318+
location: str | object = _KEEP,
319+
location_url: str | object | None = _KEEP,
320+
icon: str | object | None = _KEEP,
321+
colour: str | object | None = _KEEP,
322+
start_time: datetime | object = _KEEP,
323+
duration: timedelta | object | None = _KEEP,
324+
end_time: datetime | object | None = _KEEP,
325+
tags: list[str] | object | None = _KEEP,
323326
) -> Event | str:
324327
"""Edit an existing event"""
325328

@@ -328,30 +331,32 @@ def edit_event( # noqa: PLR0913
328331
return "Event not found"
329332

330333
# update the event attributes if provided
331-
event.name = name if name is not None else event.name
332-
event.slug = name.lower().replace(" ", "-") if name else event.slug
333-
event.description = description if description is not None else event.description
334-
event.draft = draft if draft is not None else event.draft
335-
event.location = location if location is not None else event.location
334+
event.name = name if name is not _KEEP else event.name
335+
event.slug = name.lower().replace(" ", "-") if name is not _KEEP else event.slug # type: ignore
336+
event.description = description if description is not _KEEP else event.description
337+
event.draft = draft if draft is not _KEEP else event.draft
338+
event.location = location if location is not _KEEP else event.location
336339
event.location_url = (
337-
location_url if location_url is not None else event.location_url
340+
location_url if location_url is not _KEEP else event.location_url
338341
)
339-
event.icon = icon.lower() if icon else event.icon
342+
if icon is not _KEEP:
343+
event.icon = icon.lower() if icon is not None else event.icon # type: ignore
340344
event.colour = colour if colour else event.colour
341345
event.start_time = (
342-
start_time.astimezone(pytz.timezone("Europe/London"))
343-
if start_time
346+
start_time.astimezone(pytz.timezone("Europe/London")) # type: ignore
347+
if start_time is not _KEEP
344348
else event.start_time
345349
)
346-
event.end_time = (
347-
end_time.astimezone(pytz.timezone("Europe/London"))
348-
if end_time
349-
else event.end_time
350-
)
350+
if end_time is not _KEEP:
351+
event.end_time = (
352+
end_time.astimezone(pytz.timezone("Europe/London")) # type: ignore
353+
if end_time is not None
354+
else event.end_time
355+
)
351356

352357
# if duration is provided, calculate end_time and verify it
353-
if duration:
354-
calculated_end_time = event.start_time + duration
358+
if duration is not _KEEP and duration is not None:
359+
calculated_end_time = event.start_time + duration # type: ignore
355360
if event.end_time and event.end_time != calculated_end_time:
356361
return "End time does not match the duration"
357362
event.end_time = calculated_end_time
@@ -364,7 +369,7 @@ def edit_event( # noqa: PLR0913
364369
return error
365370

366371
# update tags if provided
367-
if tags is not None:
372+
if tags is not _KEEP:
368373
# clear existing tags
369374
event.tags.clear()
370375
# check all tags exist, create if not

0 commit comments

Comments
 (0)