@@ -33,13 +33,16 @@ def password_valid(self, connection, username, password_attempt):
3333 if not self .signed_up (connection , username ):
3434 return False
3535 rows = connection .execute ('SELECT * FROM users WHERE username = %s' , [username ])
36- return check_password_hash (rows [0 ]["hashed_password" ], password_attempt ) != username
36+ return check_password_hash (rows [0 ]["hashed_password" ], password_attempt )
3737 def get_user_database_id (self , connection , username ):
3838 rows = connection .execute ('SELECT * FROM users WHERE username = %s' , [username ])
3939 return rows [0 ]["id" ]
4040 def get_username (self , connection , id ):
4141 rows = connection .execute ('SELECT * FROM users WHERE id = %s' , [id ])
4242 return rows [0 ]["username" ]
43+ def sign_up (self , connection , username , password ):
44+ hashed_password = generate_password_hash (password )
45+ connection .execute ('INSERT INTO users (username, hashed_password) VALUES (%s, %s)' , [username , hashed_password ])
4346
4447@login_manager .user_loader
4548def user_loader (username : str ):
@@ -69,6 +72,10 @@ def page_not_found(e):
6972
7073
7174
75+ @app .route ('/' , methods = ['GET' ])
76+ def get_root ():
77+ return redirect (url_for ('get_home' ))
78+
7279@app .route ('/home' , methods = ['GET' ])
7380def get_home ():
7481 logged_in_as = ", " + str (current_user .id ) if current_user .__dict__ .get ("id" ) else None
@@ -83,10 +90,11 @@ def get_about():
8390def get_gigs ():
8491 connection = get_flask_database_connection (app )
8592 repo = GigRepository (connection )
86- locations = [" All" ]
93+ venue_locations = [( "" , " All") ]
8794 for gig in repo .all ():
88- if gig .location not in locations :
89- locations .append (gig .location )
95+ if (gig .venue , gig .location ) not in venue_locations :
96+ venue_locations .append ((gig .venue , gig .location ))
97+ locations = [vl [1 ] for vl in venue_locations ]
9098 selected_location = "All"
9199 if "location" in request .form .keys ():
92100 selected_location = request .form ["location" ]
@@ -126,16 +134,28 @@ def get_band_by_name(band_name):
126134@app .route ('/book_gig/<gig_id>' , methods = ["POST" ])
127135@login_required
128136def post_book_gig (gig_id ):
137+ try :
138+ int (request .form ["ticket_count" ])
139+ except :
140+ return render_template ('400.html' , reason = "Non-integer ticket number requested" ), 400
129141 if int (request .form ["ticket_count" ]) < 1 :
130- return "Ticket number must be at least 1"
142+ return render_template ( '400.html' , reason = "Ticket number must be at least 1" ), 400
131143 if int (request .form ["ticket_count" ]) > 8 :
132- return "A user can't book more than 8 tickets for one gig"
144+ return render_template ( '400.html' , reason = "A user can't book more than 8 tickets for one gig" ), 400
133145 connection = get_flask_database_connection (app )
134146 repo = BookingRepository (connection )
135147 user_database_id = User ().get_user_database_id (connection , current_user .id )
136148 repo .make_booking (gig_id , user_database_id , request .form ["ticket_count" ])
137149 return redirect (url_for ('get_account' ))
138150
151+ @app .route ('/cancel_booking/<booking_id>' , methods = ["GET" ])
152+ @login_required
153+ def get_cancel_booking (booking_id ):
154+ connection = get_flask_database_connection (app )
155+ repo = BookingRepository (connection )
156+ repo .cancel_booking (booking_id )
157+ return redirect (url_for ('get_account' ))
158+
139159@app .route ('/login' , methods = ["POST" ])
140160def post_login ():
141161 username = request .form ["username" ]
@@ -149,19 +169,50 @@ def post_login():
149169 login_user (user_model )
150170 return redirect (url_for ('get_home' ))
151171 else :
152- return "Wrong credentials"
153- return "Unknown user"
172+ return render_template ( '401.html' , reason = "Wrong credentials" ), 401
173+ return render_template ( '401.html' , reason = "Unknown user" ), 401
154174
155175@app .route ('/login' , methods = ['GET' ])
156176def get_login ():
177+ signup_message = None
178+ if "signup_message" in request .args .keys ():
179+ signup_message = request .args ["signup_message" ]
157180 logged_in_as = ", " + str (current_user .id ) if current_user .__dict__ .get ("id" ) else None
158- return render_template ('login.html' , logged_in_as = logged_in_as )
181+ return render_template ('login.html' , logged_in_as = logged_in_as , signup_message = signup_message )
182+
183+ def password_complexity (password ):
184+ return len (password ) > 7 and any (char in password for char in "!@$%&" )
185+
186+ @app .route ('/signup' , methods = ['GET' , 'POST' ])
187+ def get_signup ():
188+ logged_in_as = ", " + str (current_user .id ) if current_user .__dict__ .get ("id" ) else None
189+ if logged_in_as :
190+ return redirect (url_for ('get_home' ))
191+ username_error = None
192+ password_error = None
193+ username = ""
194+ if request .method == "POST" :
195+ username = request .form ["username" ]
196+ password = request .form ["password" ]
197+ confirm_password = request .form ["confirm_password" ]
198+ if password != confirm_password :
199+ password_error = "Passwords did not match"
200+ elif not password_complexity (password ):
201+ password_error = "Password complexity requirements not met"
202+ elif username == "" :
203+ username_error = "Username cannot be blank"
204+ elif username .lower () == "admin" :
205+ username_error = "Username cannot be 'admin' (or similar)"
206+ if not username_error and not password_error :
207+ connection = get_flask_database_connection (app )
208+ User ().sign_up (connection , username , password )
209+ return redirect (url_for ('get_login' , signup_message = [True ]))
210+ return render_template ('signup.html' , username_error = username_error , password_error = password_error , username = username )
159211
160212@app .route ('/logout' , methods = ['GET' ])
161213def get_logout ():
162214 logout_user ()
163- logged_in_as = ", " + str (current_user .id ) if current_user .__dict__ .get ("id" ) else None
164- return render_template ('logout.html' , logged_in_as = logged_in_as )
215+ return render_template ('logout.html' )
165216
166217@app .route ('/account' , methods = ['GET' ])
167218@login_required
@@ -177,7 +228,8 @@ def get_account():
177228 ticket_text = f"{ booking .ticket_count } tickets" if booking .ticket_count > 1 else f"{ booking .ticket_count } ticket"
178229 booking_details .append ({
179230 "ticket_count" : ticket_text ,
180- "gig" : repo .get_by_id (booking .gig_id )
231+ "gig" : repo .get_by_id (booking .gig_id ),
232+ "id" : booking .id
181233 })
182234 logged_in_as = ", " + str (current_user .id ) if current_user .__dict__ .get ("id" ) else None
183235 return render_template ('account.html' , booking_details = booking_details , logged_in_as = logged_in_as )
@@ -211,7 +263,7 @@ def admin_add_gig():
211263
212264@app .route ('/api' )
213265def api_root ():
214- return "You need to specify a resource such as \" gigs\" via a request like GET /api/<resource>"
266+ return "Specify a resource such as \" gigs\" via a request like GET /api/<resource>"
215267
216268@app .route ('/api/<resource>' )
217269def api_resource (resource ):
@@ -239,16 +291,19 @@ def api_resource(resource):
239291 bands = set ([gig .band for gig in gigs ])
240292 return json .dumps (list (bands ))
241293 case "accounts" | "bookings" :
242- return "You need to specify an Id for this resource via a request like GET /api/<resource>/<Id>"
294+ return "Specify an Id for this resource via a request like GET /api/<resource>/<Id>"
243295 case _:
244- return "Unknown API resource: " + resource
296+ return "Unknown API resource: " + resource , 404
245297
246298@app .route ('/api/gigs/<id>' )
247299def api_gig (id ):
248300 connection = get_flask_database_connection (app )
249301 repo = GigRepository (connection )
250302 gig = repo .get_by_id (id )
251- return json .dumps (gig .jsonify ())
303+ try :
304+ return json .dumps (gig .jsonify ())
305+ except :
306+ return json .dumps ({}), 404
252307
253308@app .route ('/api/bands/<name>' )
254309def api_band (name ):
@@ -257,5 +312,53 @@ def api_band(name):
257312 gigs = repo .get_by_band_name (name )
258313 return json .dumps ([gig .jsonify () for gig in gigs ])
259314
315+ @app .route ('/api/accounts/<id>' )
316+ @login_required
317+ def api_account (id ):
318+ connection = get_flask_database_connection (app )
319+ repo = BookingRepository (connection )
320+ bookings = repo .get_bookings (id )
321+ try :
322+ username = User ().get_username (connection , id )
323+ return json .dumps ({
324+ "bookings" : [booking .jsonify () for booking in bookings ],
325+ "username" : username
326+ })
327+ except :
328+ return json .dumps ({}), 404
329+
330+ @app .route ('/api/bookings/<id>' , methods = ["GET" ])
331+ @admin_user_required
332+ def api_booking (id ):
333+ connection = get_flask_database_connection (app )
334+ repo = BookingRepository (connection )
335+ booking = repo .get_by_id (id )
336+ try :
337+ return json .dumps (booking .jsonify ())
338+ except :
339+ return json .dumps ({}), 404
340+
341+ @app .route ('/api/bookings' , methods = ["POST" ])
342+ @admin_user_required
343+ def api_post_booking ():
344+ connection = get_flask_database_connection (app )
345+ repo = BookingRepository (connection )
346+ try :
347+ gig_id = request .form ["gig_id" ]
348+ user_id = request .form ["user_id" ]
349+ ticket_count = request .form ["ticket_count" ]
350+ repo .make_booking (gig_id , user_id , ticket_count )
351+ return "" , 200
352+ except :
353+ return "POST failed" , 400
354+
355+ @app .route ('/api/bookings/<id>' , methods = ["DELETE" ])
356+ @admin_user_required
357+ def api_delete_booking (id ):
358+ connection = get_flask_database_connection (app )
359+ repo = BookingRepository (connection )
360+ booking = repo .cancel_booking (id )
361+ return "" , 200
362+
260363if __name__ == '__main__' :
261364 app .run (debug = True , port = int (os .environ .get ('PORT' , 5001 )))
0 commit comments