1- from flask import Blueprint , flash , redirect , render_template , request , url_for
1+ from flask import Blueprint , abort , flash , redirect , render_template , request , url_for
2+ from werkzeug .datastructures import ImmutableMultiDict
23from werkzeug .wrappers import Response
34
45from auth .auth import is_exec_wrapper
5- from config import colours , icons
6+ from config import colours , custom_icons
67from events .utils import (
78 create_event ,
9+ edit_event ,
810 get_all_tags ,
911 get_datetime_from_string ,
1012 get_event_by_slug ,
1517events_ui_bp = Blueprint ("events_ui" , __name__ , url_prefix = "/events" )
1618
1719
20+ def parse_form_data (form_data : ImmutableMultiDict ) -> dict | str :
21+ """Parse event from form data"""
22+ # parse colour
23+ text_colour = form_data .get ("text_colour" , None )
24+ color_colour = form_data .get ("color_colour" , None )
25+
26+ text_colour = text_colour .strip ().lower () if text_colour else None
27+ color_colour = color_colour .strip ().lower () if color_colour else None
28+
29+ if (error := validate_colour (text_colour , color_colour )) is not None :
30+ return error
31+
32+ # prefer text_colour if both are provided (in case these colours change)
33+ colour = text_colour if text_colour else color_colour
34+
35+ # parse dates and duration
36+ start_time = get_datetime_from_string (form_data ["start_time" ])
37+ if isinstance (start_time , str ):
38+ return start_time
39+
40+ duration = (
41+ get_timedelta_from_string (form_data ["duration" ])
42+ if form_data ["duration" ]
43+ else None
44+ )
45+ if isinstance (duration , str ):
46+ return duration
47+
48+ end_time = (
49+ get_datetime_from_string (form_data ["end_time" ])
50+ if form_data ["end_time" ]
51+ else None
52+ )
53+ if isinstance (end_time , str ):
54+ return end_time
55+
56+ # parse tags
57+ tags = form_data .getlist ("tags[]" )
58+ tags = [tag .strip ().lower () for tag in tags if tag .strip ()]
59+
60+ return {
61+ "name" : form_data ["name" ],
62+ "description" : form_data ["description" ],
63+ "draft" : "draft" in form_data ,
64+ "location" : form_data ["location" ],
65+ "location_url" : form_data .get ("location_url" , None ),
66+ "icon" : form_data .get ("icon" , None ),
67+ "colour" : colour ,
68+ "start_time" : start_time ,
69+ "duration" : duration ,
70+ "end_time" : end_time ,
71+ "tags" : tags ,
72+ }
73+
74+
1875@events_ui_bp .route ("/create" , methods = ["GET" , "POST" ])
1976@is_exec_wrapper
20- def create (error : str | None = None ) -> str | Response : # noqa: PLR0911
77+ def create () -> str | Response :
2178 """Create a new event."""
2279
2380 tags = [tag .name for tag in get_all_tags ()]
@@ -26,11 +83,9 @@ def create(error: str | None = None) -> str | Response: # noqa: PLR0911
2683 if request .method == "GET" :
2784 return render_template (
2885 "events/form.html" ,
29- error = error ,
3086 action = "events_ui.create" ,
3187 method = "POST" ,
32- event = None ,
33- icons = icons ,
88+ icons = custom_icons ,
3489 colours = colours ,
3590 tags = tags ,
3691 )
@@ -39,67 +94,92 @@ def create(error: str | None = None) -> str | Response: # noqa: PLR0911
3994
4095 print ("Creating event with data:" , request .form )
4196
42- # parse colour
43- text_colour = request .form .get ("text_colour" , None )
44- color_colour = request .form .get ("color_colour" , None )
45-
46- text_colour = text_colour .strip ().lower () if text_colour else None
47- color_colour = color_colour .strip ().lower () if color_colour else None
48-
49- if (colour := validate_colour (text_colour , color_colour )) is not None :
50- flash (colour , "error" )
97+ # parse form data
98+ data = parse_form_data (request .form )
99+ if isinstance (data , str ):
100+ flash (data , "error" )
51101 return redirect (url_for ("events_ui.create" ))
52102
53- # prefer text_colour if both are provided (in case these colours change)
54- colour = text_colour if text_colour else color_colour
103+ # attempt to create the event
104+ event = create_event ( ** data )
55105
56- # parse dates and duration
57- start_time = get_datetime_from_string (request .form ["start_time" ])
58- if isinstance (start_time , str ):
59- flash (start_time , "error" )
106+ # if failed, redirect to the create page with an error
107+ if isinstance (event , str ):
108+ flash (event , "error" )
60109 return redirect (url_for ("events_ui.create" ))
61110
62- duration = (
63- get_timedelta_from_string (request .form ["duration" ])
64- if request .form ["duration" ]
65- else None
111+ # if successful, redirect to the event page
112+ return redirect (
113+ url_for (
114+ "events_ui.view" ,
115+ year = event .date .academic_year ,
116+ term = event .date .term ,
117+ week = event .date .week ,
118+ slug = event .slug ,
119+ )
66120 )
67- if isinstance (duration , str ):
68- flash (duration , "error" )
69- return redirect (url_for ("events_ui.create" ))
70121
71- end_time = (
72- get_datetime_from_string (request .form ["end_time" ])
73- if request .form ["end_time" ]
74- else None
75- )
76- if isinstance (end_time , str ):
77- flash (end_time , "error" )
78- return redirect (url_for ("events_ui.create" ))
79122
80- # parse tags
81- tags = request .form .getlist ("tags[]" )
82- tags = [tag .strip ().lower () for tag in tags if tag .strip ()] if tags else []
123+ @events_ui_bp .route (
124+ "/<int:year>/<int:term>/<int:week>/<string:slug>/edit" , methods = ["GET" , "POST" ]
125+ )
126+ @is_exec_wrapper
127+ def edit (
128+ year : int , term : int , week : int , slug : str , error : str | None = None
129+ ) -> str | Response :
130+ """Edit an existing event by its year, term, week, and slug."""
83131
84- # attempt to create the event
85- event = create_event (
86- request .form ["name" ],
87- request .form ["description" ],
88- "draft" in request .form ,
89- request .form ["location" ],
90- request .form .get ("location_url" , None ),
91- request .form .get ("icon" , None ),
92- colour ,
93- start_time ,
94- duration ,
95- end_time ,
96- tags ,
97- )
132+ event = get_event_by_slug (year , term , week , slug )
98133
99- # if failed, redirect to the create page with an error
134+ if event is None :
135+ return abort (404 , description = "Event not found" )
136+
137+ tags = [tag .name for tag in get_all_tags ()]
138+
139+ # if getting, return the ui for editing the event
140+ if request .method == "GET" :
141+ return render_template (
142+ "events/form.html" ,
143+ error = error ,
144+ action = "events_ui.edit" ,
145+ method = "POST" ,
146+ event = event ,
147+ icons = custom_icons ,
148+ colours = colours ,
149+ tags = tags ,
150+ )
151+
152+ # if posting, update the event
153+
154+ # parse form data
155+ data = parse_form_data (request .form )
156+ if isinstance (data , str ):
157+ flash (data , "error" )
158+ return redirect (
159+ url_for (
160+ "events_ui.edit" ,
161+ year = year ,
162+ term = term ,
163+ week = week ,
164+ slug = slug ,
165+ )
166+ )
167+
168+ # attempt to edit the event
169+ event = edit_event (event .id , ** data )
170+
171+ # if failed, redirect to the edit page with an error
100172 if isinstance (event , str ):
101173 flash (event , "error" )
102- return redirect (url_for ("events_ui.create" , error = event ))
174+ return redirect (
175+ url_for (
176+ "events_ui.edit" ,
177+ year = year ,
178+ term = term ,
179+ week = week ,
180+ slug = slug ,
181+ )
182+ )
103183
104184 # if successful, redirect to the event page
105185 return redirect (
@@ -113,16 +193,16 @@ def create(error: str | None = None) -> str | Response: # noqa: PLR0911
113193 )
114194
115195
116- # TODO: other event management UI
117-
118-
119196@events_ui_bp .route ("/<int:year>/<int:term>/<int:week>/<string:slug>" )
120197def view (year : int , term : int , week : int , slug : str ) -> str :
121198 """View an event by its year, term, week, and slug."""
122199
123200 event = get_event_by_slug (year , term , week , slug )
124201
125202 if event is None :
126- return "Event not found"
203+ return abort ( 404 , description = "Event not found" )
127204
128205 return str (event .to_dict ())
206+
207+
208+ # TODO: combos of events
0 commit comments