generated from Code-Institute-Org/gitpod-full-template
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathhelpers.py
1669 lines (1339 loc) · 58.4 KB
/
helpers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from flask import (
flash, render_template,
redirect, request, session, url_for,
abort)
from flask_paginate import Pagination, get_page_args
import imghdr
import os
from datetime import datetime
from datetime import timedelta
import random
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename
# Constant Variables
competitions = [
{
"category": "portraiture",
"instructions": "Enter your portraits now! \
These can be of animals or humans and can be \
close up or full length.They should communicate\
something substantial about the subject."
},
{
"category": "landscape",
"instructions": "Enter your lanscapes now!\
These should be primarily focused on the natural world.\
No city-scapes. Focus on delivering images with great\
lighting in interesting locations."
},
{
"category": "architecture",
"instructions": "Enter your architectural photos now!\
Interesting angles and great composition is key here."
},
{
"category": "wildlife",
"instructions": "Enter your wildlife and nature photos now!\
Flora OR fauna are acceptable. Capture amazing images of\
the natural world at its most spectacular."
},
{
"category": "street",
"instructions": "Enter your street photography now!\
Encounters and imagery from urban jungles."
},
{
"category": "monochrome",
"instructions": "Enter your monochrome photography now.\
Any subject, any place, black and white imagery only.\
PLEASE no sepia tones!"
},
{
"category": "event",
"instructions": "Enter your event photography now!\
Weddings, baptisms, concerts, theatre etc.. If it\
has guests, it's an event!"
}
]
# Development Testing Functions
# 1. clear_user_points(database_var)
# 2. clear_all_awards(database_var)
# 3. clear_all_photo_votes(database_var)
# 4. delete_collection()
def clear_user_points(database_var):
'''
* This function brings all the user points to 0.
It is only used in development.
\n Args:
1. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* Resets all the users' user_points fields to 0 in the db.
'''
all_users = list(database_var.db.users.find())
for user in all_users:
database_var.db.users.update_one(
{"username": user["username"]},
{'$set': {"user_points": 0}})
def clear_all_awards(database_var):
'''
* This function brings all the photo awards to null.
It is only used in development for testing.
\n Args:
1. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* Set all the photo documents as having "awards": null
'''
all_photos = list(database_var.db.photos.find())
for photo in all_photos:
database_var.db.photos.update_one(
{"filename": photo["filename"]},
{'$set': {"awards": None}})
def clear_all_photo_votes(database_var):
'''
* This removes all photo vote records from all photos in the db.
\n Args:
1. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* Deletes all values in the photo_votes field on all photo docs in the db.
'''
all_photos = list(database_var.db.photos.find())
for photo in all_photos:
database_var.db.photos.update_one(
{"filename": photo["filename"]},
{'$set': {"photo_votes": 0}})
def delete_collection(database_var):
'''
* This function deletes the entire mongoDB collection.
It is only used for testing in development.
\n Args:
1. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* Deletes the entire db collection.
'''
database_var.db.fs.chunks.remove({})
database_var.db.fs.files.remove({})
database_var.db.photos.remove({})
database_var.db.users.remove({})
# Scheduled/Timed Functions:
# 1. new_comp(database_var)
def new_comp(database_var):
'''
* This function allows all users to enter a new competition.
It is run automatically using APScheduler at 0:00 Monday morning.
\n Args:
1. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* Changes the 'can_enter' field to True for every user in the db.
'''
all_users = list(database_var.db.users.find())
for user in all_users:
database_var.db.users.update_one(
{"username": user["username"]},
{'$set': {"can_enter": True}})
# awards() Helper Functions:
# 1. get_this_weeks_comp_users(*args)
# 2. filter_users_and_exclude_non_voters(*args)
# 3. get_range_of_scores(*args)
# 4. awards_score_requirements(*args)
# 5. determine_winners(*args)
# 6. add_points_to_winning_users(*args)
# 7. add_points_to_users_who_voted_well(*args)
def get_this_weeks_comp_users(entries, database_var):
'''
* Creates a list of the users who entered a particular competition.
\n Args:
1. entries (arr): All the photo objects entered into this
competition.
2. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* An array of users who entered this week's competition.
'''
this_weeks_usernames = []
this_weeks_users = []
for img in entries:
this_weeks_usernames.append(img["created_by"])
for username in this_weeks_usernames:
this_weeks_users.append(
database_var.db.users.find_one({"username": username}))
return this_weeks_users
def filter_users_and_exclude_non_voters(
array_of_users, database_var, comp_week_and_year):
'''
* This divides the arr of users who entered the competition into users
who voted and users who did not vote. It then penalises users who entered
the competition but failed to vote, by reducing their entry's points
to 0.
\n Args:
1. array_of_users (arr): The users who entered the specific competition.
2. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
3. comp_week_and_year (str): A datetime formatted string of type: "052021"
representing the week & year the competition took place.
\n Returns:
* Alters the user db for those photos by users who failed to vote. Resets
the photo's "photo_votes" to 0.
* Returns an array of valid_users.
'''
valid_users = []
non_voters = []
for user in array_of_users:
if user["votes_to_use"] > 0:
non_voters.append(user)
else:
valid_users.append(user)
for user in non_voters:
database_var.db.photos.update_one(
{"created_by": user["username"],
"week_and_year": comp_week_and_year},
{'$set': {"photo_votes": 0}})
database_var.db.users.update_one(
{"username": user["username"]},
{'$set': {"votes_to_use": 0}})
return valid_users
def get_range_of_scores(comp_week_and_year, database_var):
'''
* This calculates the range of scores or votes in the competition.
\n Args:
1. comp_week_and_year (str): A datetime formatted string of type: "052021"
representing the week & year the competition took place.
2. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* An array of the range of votes / points from photos in the
competition.
'''
this_weeks_entries_ordered = list(database_var.db.photos.find(
{'$query': {"week_and_year": comp_week_and_year},
'$orderby': {'photo_votes': -1}}))
range_of_votes = []
for photo in this_weeks_entries_ordered:
if photo["photo_votes"] > 0:
range_of_votes.append(photo["photo_votes"])
return range_of_votes
def awards_score_requirements(array_of_scores):
'''
* This calculates the number of votes needed for a photo to receive a
1st, 2nd & 3rd place award, given an array of scores.
\n Args:
1. array_of_scores (arr): Specifically an array of the numbers of total
votes all photos have received in the competition.
\n Returns:
* 3 values: the votes needed for 1st place, 2nd place & 3rd place.
'''
second_place_vote_array = []
third_place_vote_array = []
first_place_vote_count = max(array_of_scores) if array_of_scores else None
if first_place_vote_count:
second_place_vote_array = \
[n for n in array_of_scores if n != first_place_vote_count]
second_place_vote_count = \
max(second_place_vote_array) if second_place_vote_array else None
if second_place_vote_count:
third_place_vote_array = \
[n for n in second_place_vote_array
if n != second_place_vote_count]
third_place_vote_count = \
max(third_place_vote_array) if third_place_vote_array else None
return first_place_vote_count, \
second_place_vote_count, third_place_vote_count
def get_valid_entries(this_weeks_entries, valid_users):
'''
* This returns only the valid entries from the list of
that week's entries.
\n Args:
1. this_weeks_entries (arr): All photos entered into this
week's competition.
2. valid_users(arr): The array of users who have voted and
entered the competition.
\n Returns:
* valid_entries (arr): An array of entries minus any created by
users who failed to vote.
'''
valid_entries = []
for entry in this_weeks_entries:
for user in valid_users:
if entry["created_by"] == user["username"]:
valid_entries.append(entry)
return valid_entries
def determine_winners(
first_place_votes_needed, second_place_votes_needed,
third_place_votes_needed, photo_arr, database_var):
'''
* This uses the score requirements for winning awards, to determine which
photos and users won 1st, 2nd and 3rd place. It updates the "awards"
field in the db for the winning images, and it returns 3 arrays of
winning users.
\n Args:
1. first_place_votes_needed, second_place_votes_needed &
third_place_votes_needed (int): The required number of votes a photo
needs to place 1st, 2nd or 3rd.
2. photo_arr (arr): The array of photo objs in this specific competition.
3. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* 3 arrays: Users winning 1st, 2nd & 3rd place for this competition.
They are arrays because ties are possible.
'''
first_place_users = []
second_place_users = []
third_place_users = []
for entry in photo_arr:
if entry["photo_votes"] == \
first_place_votes_needed and first_place_votes_needed > 0:
database_var.db.photos.update_one(
{"filename": entry["filename"]},
{'$set': {"awards": 1}})
user = database_var.db.users.find_one(
{"username": entry["created_by"]})
if user not in first_place_users:
first_place_users.append(user)
elif entry["photo_votes"] == \
second_place_votes_needed and second_place_votes_needed > 0:
database_var.db.photos.update_one(
{"filename": entry["filename"]},
{'$set': {"awards": 2}})
user = database_var.db.users.find_one(
{"username": entry["created_by"]})
if user not in second_place_users:
second_place_users.append(user)
elif entry["photo_votes"] == \
third_place_votes_needed and second_place_votes_needed > 0:
database_var.db.photos.update_one(
{"filename": entry["filename"]},
{'$set': {"awards": 3}})
user = database_var.db.users.find_one(
{"username": entry["created_by"]})
if user not in third_place_users:
third_place_users.append(user)
return first_place_users, second_place_users, third_place_users
def add_points_to_winning_users(
first_place_user_arr, second_place_user_arr,
third_place_user_arr, database_var):
'''
* Takes in arrays of winning users and increases their "user_points"
fields in the db by their respective scores.
\n Args:
1. first_place_user_arr, second_place_user_arr, third_place_user_arr (arr):
Arrays of users who have won 1st, 2nd & 3rd place.
2. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* Updates the db with the scores for 1st, 2nd & 3rd place (7, 5 & 3).
'''
for user in first_place_user_arr:
database_var.db.users.update_one(
{"username": user["username"]}, {'$inc': {"user_points": 7}})
for user in second_place_user_arr:
database_var.db.users.update_one(
{"username": user["username"]}, {'$inc': {"user_points": 5}})
for user in third_place_user_arr:
database_var.db.users.update_one(
{"username": user["username"]}, {'$inc': {"user_points": 3}})
def add_points_to_users_who_voted_well(
user_arr, comp_week_and_year, database_var):
'''
* Adds points to users who voted for the 1st, 2nd & 3rd placed images.
\n Args:
1. user_arr (arr): An array of users who entered the competition.
2. comp_week_and_year (str): A datetime formatted string of type: "052021"
representing the week & year the competition took place.
3. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* Increments the users' "user_points" field in the db if they
voted for the 1st, 2nd & 3rd placed images by 3, 2 & 1 points
respectively.
'''
for user in user_arr:
for photo in user["photos_voted_for"]:
photo_as_obj = list(database_var.db.photos.find(
{"_id": photo, "week_and_year": comp_week_and_year}))
if photo_as_obj:
photo_as_obj = photo_as_obj[0]
if photo_as_obj["awards"] == 1:
database_var.db.users.update_one(
{"username": user["username"]},
{'$inc': {"user_points": 3}})
if photo_as_obj["awards"] == 2:
database_var.db.users.update_one(
{"username": user["username"]},
{'$inc': {"user_points": 2}})
if photo_as_obj["awards"] == 3:
database_var.db.users.update_one(
{"username": user["username"]},
{'$inc': {"user_points": 1}})
# Winners Helper Functions
# 1. get_images_by_week_and_year(*args)
# 2. get_last_monday_and_images(*args)
# 3. first_second_third_place_compcat_users(*args)
def get_images_by_week_and_year(w_a_y, database_var):
'''
* Returns a list of images entered into a competition in a
particular week.
\n Args:
1. w_a_y (str): A datetime formatted string of type: "052021"
representing the week & year the photo was entered into competition.
2. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* A list of photos that were entered into the competition that
specific week.
'''
entries_that_week = list(
database_var.db.photos.find({"week_and_year": w_a_y}))
return entries_that_week
def get_last_monday_and_images(database_var, func_to_get_images):
'''
* This gets the datetime object of the 'last monday' i.e. the date of
commencement of the 'last' referenced competition. It also gets the
collection of entries for that particular competition.
\n Args:
1. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
2. func_to_get_images (func): A function that takes 2 args:
1. The week & year in question.
2. A database obj (as per #1 above)
This func returns an array of images from a particular competition.
\n Returns:
* The datetime object of the 'last monday' - if we access this Mon-Sun
before 22:00PM, this will reference the previous week's Monday. If we
access this on Sunday after 22:00PM this will reference the current
week's Monday.
* An array of 'images_to_display'-i.e. competition entries from that
particular week.
'''
today = datetime.now()
day_of_week = today.weekday()
hour_of_day = today.time().hour
this_week_and_year = datetime.now().strftime("%V%G")
week_before = today - timedelta(weeks=1)
last_week_and_year = week_before.strftime("%V%G")
if day_of_week in range(0, 6) or day_of_week == 6 and hour_of_day < 22:
images_to_display = \
func_to_get_images(last_week_and_year, database_var)
last_mon = week_before
while last_mon.weekday() != 0:
last_mon = last_mon - timedelta(days=1)
else:
images_to_display = \
func_to_get_images(this_week_and_year, database_var)
last_mon = today
while last_mon.weekday() != 0:
last_mon = last_mon - timedelta(days=1)
return images_to_display, last_mon
def first_second_third_place_compcat_users(photo_array, database_var):
'''
* This divides a given array of photos into 3 arrays, for 1st,
2nd & 3rd placed award-winning photos. It also creates an arr
of the users who have created those award-winning photos. Finally
it returns the category of the competition in question.
\n Args:
1. photo_array (arr): An array of photo objects.
2. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* first_place, second_place, third_place (arrs): Arrays of competition
winning photo objects.
* list_of_users (arr): An array of the users who created those images.
* competition_category (str): The theme of the competition in question.
'''
first_place = []
second_place = []
third_place = []
list_of_users = []
competition_category = None
for img in photo_array:
if img["awards"] == 1:
first_place.append(img)
list_of_users.append(database_var.db.users.find_one(
{"username": img["created_by"]}))
competition_category = img["competition_category"]
elif img["awards"] == 2:
second_place.append(img)
list_of_users.append(database_var.db.users.find_one(
{"username": img["created_by"]}))
competition_category = img["competition_category"]
elif img["awards"] == 3:
third_place.append(img)
list_of_users.append(database_var.db.users.find_one(
{"username": img["created_by"]}))
competition_category = img["competition_category"]
return first_place, second_place, \
third_place, list_of_users, competition_category
def paginated_and_pagination_args(
objs_arr, PER_PAGE, page_param, per_page_param):
'''
* Uses the flask_paginate extension to return the specific
pagination options for a particular template.
\n Args:
1. objs_arr (arr): The array of photos for the template to
be paginated.
2. PER_PAGE (int): The number of images to display per
paginated page.
3. page_param (str): The reference of a GET param that holds the
page index. It displays in the URL.
4. per_page_param (str): The reference to refer to 'per_page'.
\n Returns:
* pagination_args (obj): An instance of the Pagination object with
all the inputed specs.
* objs_to_display (arr): The array of objects that were passed in to
the function split using the offset & PER_PAGE variables.
'''
page, _, _, = get_page_args(
page_parameter=page_param, per_page_parameter=per_page_param)
offset = page * PER_PAGE - PER_PAGE
total = len(objs_arr)
pagination_args = Pagination(page=page,
per_page=PER_PAGE,
total=total,
page_parameter=page_param,
per_page_parameter=per_page_param)
objs_to_display = objs_arr[offset: offset + PER_PAGE]
return pagination_args, objs_to_display
# Filter Search Functions
# 1. filter_user_search(*args)
# 2. filter_admin_search(*args)
def filter_user_search(
select_search, keyword_search, checkbox_search, database_var):
'''
* Filters a user's search by their inputed criteria.
\n Args:
1. select_search (str): One of a finite number of pre-populated
dropdown menu search categories.
2. keyword_search (str): A user's text input search.
3. checkbox_search (arr): An array of integers representing
checkbox options on the search page.
4. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* An array of photo objects from the db filtered to match the search
inputs.
'''
full_search = keyword_search
full_query = {}
if keyword_search:
full_query["$text"] = {"$search": full_search}
if checkbox_search:
full_query["awards"] = {"$in": checkbox_search}
if select_search:
full_query["competition_category"] = select_search
filtered_photos = list(database_var.db.photos.find(full_query))
return filtered_photos
def filter_admin_search(
keyword_search, database_var):
'''
* Filters an admin's search for a user by their keyword search.
\n Args:
1. keyword_search (str): An admin's text input search.
2. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* An array of user objects from the db filtered to match the search
inputs.
'''
full_search = keyword_search
full_query = {}
if keyword_search:
full_query["$text"] = {"$search": full_search}
filtered_users = list(database_var.db.users.find(full_query))
return filtered_users
# Upload Image Functions
# 1. validate_image_type(arg)
# 2. check_file_size(*args)
# 3. save_photo(*args)
def validate_image_type(stream):
'''
* This checks what type of file is being uploaded.
\n Args:
1. stream(data): A particular stream of data.
\n Returns:
* If the data read matches one of a selection of image file types
then the function returns the file extension.
* If the data does not match a selection of image file types then
the function returns None.
'''
header = stream.read(512)
stream.seek(0)
format = imghdr.what(None, header)
if not format:
return None
return '.' + (format if format != 'jpeg' else 'jpg')
def check_file_size(file, size_limit_in_bytes):
'''
* This checks the size of the uploaded file against a set limit.
\n Args:
1. file (obj): A file of some form of data.
2. size_limit_in_bytes (int): The maximum fize size limit in bytes.
\n Returns:
* If the file size is greater than the limit set, this function returns
a 413 error: "Payload too large".
* If the file size is under the limit this function returns True.
'''
file.seek(0, os.SEEK_END)
file_length = file.tell()
file.seek(0, 0)
if file_length > size_limit_in_bytes:
return abort(413)
else:
return True
def save_photo(request, database_var, name_of_image_from_form, app):
'''
* This saves an image from a form to the mongo DB.
\n Args:
1. request (obj): The POST request object send via the form
by the user to the server.
2. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
3. name_of_image_from_form (str): The string assigned to form's file
upload "name" field.
4. app (obj): The WSGI application as an object of the Flask class.
'''
photo = request.files[name_of_image_from_form]
file_extension = os.path.splitext(photo.filename)[1]
if file_extension not in app.config['UPLOAD_EXTENSIONS'] or \
file_extension != validate_image_type(photo.stream):
abort(415)
check_file_size(photo, 560000)
photo.filename = secure_filename(photo.filename)
file_id = database_var.save_file(photo.filename, photo)
# This makes the filename unique
new_filename = str(file_id) + file_extension
# Update the gridFS "Filename" attribute to be equal to the file_id
database_var.db.fs.files.update_one(
{"_id": file_id},
{'$set': {"filename": new_filename}})
return file_id, new_filename
def register_new_user(database_var, request, app):
'''
* This registers a new user and saves their data to the db.
\n Args:
1. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
2. request (obj): The request object the user POSTs to the server
containing the data from the register form: username, email &
password.
\n Returns:
* If successful this will save the new user's data to the db and
render the new user's profile page.
* It also logs the user in and starts a new session with that
user's username as the session variable's value.
* If unsuccessful this will flash a message detailing the issue and
reload the register template.
'''
existing_email = database_var.db.users.find_one(
{"email": request.form.get("email").lower()})
existing_username = database_var.db.users.find_one(
{"username": request.form.get("username").lower()})
if existing_email:
flash("Email is already registered.")
url = redirect(url_for('register'))
return url
if existing_username:
flash("Username is already in use, please choose a different one.")
url = redirect(url_for('register'))
return url
password1 = request.form.get("password")
password2 = request.form.get("password-confirmation")
if password1 != password2:
flash("Passwords do not match, please try again.")
url = redirect(url_for('register'))
return url
photo_filename_to_add_to_user = None
if request.files['profile-pic']:
file_id, new_filename = save_photo(
request, database_var, "profile-pic", app)
new_entry = {
"file_id": file_id,
"filename": new_filename,
"type": "profile-pic",
"user": request.form.get("username").lower()
}
database_var.db.photos.insert_one(new_entry)
photo_to_add_to_user = database_var.db.photos.find_one(
{"file_id": file_id})
photo_filename_to_add_to_user = photo_to_add_to_user["filename"]
register_user = {
"username": request.form.get("username").lower(),
"email": request.form.get("email").lower(),
"profile_photo": photo_filename_to_add_to_user
if photo_filename_to_add_to_user else None,
"password": generate_password_hash(request.form.get("password")),
"user_points": 0,
"photos": [],
"photos_voted_for": [],
"votes_to_use": 0,
"can_enter": True
}
database_var.db.users.insert_one(register_user)
session["user"] = request.form.get("username").lower()
flash("Registration successful!")
username = session["user"]
url = redirect(url_for("profile", username=username))
return url
def login_user(email, password, database_var):
'''
* This logs the user in, or tells them why their login
attempt was unsuccessful.
\n Args:
1. email (str): user input from login form of their email.
Must be unique and match the password they input.
2. password (str): user input from login form, must match
hashed password in db connected to user email from arg 1.
3. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* A url variable that holds the template to redirect to. For a
successful login, this will be the user's profile page. Otherwise
it will reload the login page with a flash message telling the user
why their login attempt failed.
'''
existing_user = database_var.db.users.find_one(
{"email": email})
if existing_user:
if check_password_hash(
existing_user["password"], password):
username = existing_user["username"]
session["user"] = username
flash(f"Welcome, {username}!")
if existing_user["username"] == "admin":
url = redirect(url_for("admin"))
else:
url = redirect(url_for("profile", username=username))
return url
else:
flash("Incorrect username and/or password!")
url = redirect(url_for("login"))
return url
else:
flash("Incorrect username and/or password!")
url = redirect(url_for("login"))
return url
def get_profile_page_photos(username, database_var):
'''
* This gets the three categories of photos to show on
a user's profile page, based on the username passed in.
\n Args:
1. username (str): A user's username to find in the db.
2. database_var (obj): A variable holding the PyMongo Object that
accesses the MongoDB Server.
\n Returns:
* Three arrays of photo objects associated with the username passed
in:
* 1. The user's profile photo if there is one.
* 2. All photos the user has entered into competition.
* 3. All photos the user has voted for in competition.
* 4. All the user's award-winning photos.
'''
user = database_var.db.users.find_one({"username": username})
user_profile_photo = None
user_profile_photo = database_var.db.photos.find_one({"user": username})
try:
user_photos = list(
database_var.db.photos.find({"created_by": user["username"]}))
except TypeError:
flash("I'm sorry but that user could not be found.")
abort(404)
user_entry_this_comp = database_var.db.photos.find_one(
{"created_by": user["username"],
"week_and_year": datetime.now().strftime("%V%G")})
photos_voted_for_array = user["photos_voted_for"]
photos_voted_for_objs = []
if photos_voted_for_array != []:
for img in photos_voted_for_array:
photo_obj = list(database_var.db.photos.find({"_id": img}))
for photo in photo_obj:
if photo["week_and_year"] != \
datetime.now().strftime("%V%G"):
photos_voted_for_objs.append(photo)
award_winners = []
for img in user_photos:
if img["awards"] is not None:
award_winners.append(img)
return user_profile_photo, user_photos,\
photos_voted_for_objs,\
award_winners, user_entry_this_comp
# Code from Emmanuel's Stack Overflow answer (attributed in README.md)
def get_next_weekday(startdate, weekday):
"""
* This calculates the next occuring weekday of any passed in day
of the week.
\n Args:
1. startdate (str): given date, in format '2013-05-25'
2. weekday (int): week day between 0 (Monday) to 6 (Sunday)
\n Returns:
* A datetime object representing the date of the next day of the
week passed into the function. I.e. The date of next Monday.
"""
date = datetime.strptime(startdate, '%Y-%m-%d')
time = timedelta((7 + weekday - date.weekday()) % 7)
final_date = date + time
return final_date
def get_time_remaining_string(timedelta):
'''
* This returns a string describing an amount of time in days,
hours and minutes.
\n Args:
1. timedelta (datetime.timedelta): Describes an amount of time.
\n Returns:
* A string in English made from the timedelta describing the
time in the format: 'x' days, 'y' hours and 'z' minutes.
'''
days = timedelta.days
timedelta_string = str(timedelta)
time_array = timedelta_string.split(",").pop().split(":")
hours = time_array[0]
minutes = time_array[1]
days_plural = "days, "
days_singular = "day, "
hours_singular = 'hour'
hours_plural = 'hours'
mins_singular = 'minute'
mins_plural = 'minutes'
fd = f"{days} {days_singular if int(days)==1 else days_plural}"
fh = f"{hours} {hours_singular if int(hours)==1 else hours_plural}"
fm = f" and {minutes} {mins_singular if int(minutes)==1 else mins_plural}"
final_time_string = fd + fh + fm
return final_time_string
def time_strings_for_template(
comp_end_date, next_comp_start_date, vote_end_date, get_time_func):
'''
* This forms human readable strings referring to how much time