Repository navigation
Expand file tree
/
Copy pathmodels.py
More file actions
878 lines (696 loc) · 33.2 KB
/
Copy pathmodels.py
File metadata and controls
878 lines (696 loc) · 33.2 KB
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
"""
File: models.py
Purpose: Database models for GenCon SG application using SQLAlchemy ORM
Author: to be assigned
Date: December 2025
Description: Defines all database tables and relationships:
- User accounts (seniors, youth, admins)
- Stories and reactions/comments
- Messages and translations
- Events and participants
- Communities and posts
- Buddy pairs
- Streaks and badges
- Chat reports
- Check-ins
"""
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
import json
# Initialize SQLAlchemy database object
# This will be initialized in app.py with db.init_app(app)
db = SQLAlchemy()
# ==================== USER MODEL ====================
class User(db.Model):
"""
User model representing all user accounts (seniors, youth, admins).
Attributes:
id (int): Primary key, auto-incrementing user ID
username (str): Unique username for login
password_hash (str): Hashed password (never store plain text!)
email (str): User email address
full_name (str): User's full name
phone (str): Contact phone number
age (int): User's age (60+ for seniors, 13+ for youth)
role (str): User role - 'senior', 'youth', or 'admin'
profile_picture (str): Path to profile picture file
interests_json (str): JSON string of user interests
languages_json (str): JSON string of languages spoken
accessibility_settings_json (str): JSON string of accessibility preferences
created_at (datetime): Account creation timestamp
last_active (datetime): Last login/activity timestamp
is_active (bool): Whether account is active
Relationships:
stories: One-to-many with Story (stories created by this user)
sent_messages: One-to-many with Message (messages sent)
received_messages: One-to-many with Message (messages received)
senior_pairs: One-to-many with Pair (if user is senior)
youth_pairs: One-to-many with Pair (if user is youth)
event_participants: One-to-many with EventParticipant
community_members: One-to-many with CommunityMember
streak: One-to-one with Streak
badges: One-to-many with Badge
checkins: One-to-many with Checkin
story_reactions: One-to-many with StoryReaction
story_comments: One-to-many with StoryComment
"""
__tablename__ = 'users'
# Primary key
id = db.Column(db.Integer, primary_key=True)
# Authentication fields
username = db.Column(db.String(80), unique=True, nullable=False, index=True)
password_hash = db.Column(db.String(255), nullable=False)
# Personal information
email = db.Column(db.String(120), unique=True, nullable=False, index=True)
full_name = db.Column(db.String(120), nullable=False)
phone = db.Column(db.String(20))
age = db.Column(db.Integer, nullable=False)
bio = db.Column(db.Text) # User biography
school = db.Column(db.String(255)) # School or organization for youth
# Role-based access control
role = db.Column(db.String(20), nullable=False) # 'senior', 'youth', or 'admin'
# Game statistics
elo = db.Column(db.Integer, default=1200) # Default ELO rating
# Profile customization
profile_picture = db.Column(db.String(255), default='images/default-avatar.png')
# Disabkle Account #
disable_reason = db.Column(db.Text)
# JSON fields for flexible data storage
# Using JSON allows storing arrays/objects without additional tables
interests_json = db.Column(db.Text) # Stores interests as JSON array
languages_json = db.Column(db.Text) # Stores languages as JSON array
accessibility_settings_json = db.Column(db.Text) # Stores settings as JSON object
# Timestamps and status
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
last_active = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
is_active = db.Column(db.Boolean, default=True)
# Relationships (defined with back_populates for bidirectional access)
stories = db.relationship('Story', back_populates='user', lazy='dynamic')
sent_messages = db.relationship('Message', foreign_keys='Message.sender_id',
back_populates='sender', lazy='dynamic')
received_messages = db.relationship('Message', foreign_keys='Message.recipient_id',
back_populates='recipient', lazy='dynamic')
senior_pairs = db.relationship('Pair', foreign_keys='Pair.senior_id',
back_populates='senior', lazy='dynamic')
youth_pairs = db.relationship('Pair', foreign_keys='Pair.youth_id',
back_populates='youth', lazy='dynamic')
streak = db.relationship('Streak', back_populates='user', uselist=False)
badges = db.relationship('Badge', back_populates='user', lazy='dynamic')
checkins = db.relationship('Checkin', back_populates='user', lazy='dynamic')
def set_password(self, password):
"""
Hash and set user password using Werkzeug security.
Args:
password (str): Plain text password
Security: Uses pbkdf2:sha256 algorithm for secure hashing
"""
self.password_hash = generate_password_hash(password)
def check_password(self, password):
"""
Verify password against stored hash.
Args:
password (str): Plain text password to check
Returns:
bool: True if password matches, False otherwise
"""
return check_password_hash(self.password_hash, password)
@property
def interests(self):
"""Get interests as Python list from JSON string."""
if self.interests_json:
return json.loads(self.interests_json)
return []
@interests.setter
def interests(self, value):
"""Set interests from Python list, store as JSON string."""
self.interests_json = json.dumps(value)
@property
def languages(self):
"""Get languages as Python list from JSON string."""
if self.languages_json:
return json.loads(self.languages_json)
return []
@languages.setter
def languages(self, value):
"""Set languages from Python list, store as JSON string."""
self.languages_json = json.dumps(value)
@property
def accessibility_settings(self):
"""Get accessibility settings as Python dict from JSON string."""
if self.accessibility_settings_json:
return json.loads(self.accessibility_settings_json)
return {}
@accessibility_settings.setter
def accessibility_settings(self, value):
"""Set accessibility settings from Python dict, store as JSON string."""
self.accessibility_settings_json = json.dumps(value)
def __repr__(self):
"""String representation for debugging."""
return f'<User {self.username} ({self.role})>'
# ==================== STORY MODEL ====================
class Story(db.Model):
"""
Story model for senior life stories.
Attributes:
id (int): Primary key
user_id (int): Foreign key to User who created the story
title (str): Story title
content (text): Full story content
category (str): Category - 'Childhood', 'Work Life', 'Family', 'Hobbies', 'Other'
photo_url (str): Path to uploaded photo (optional)
audio_url (str): Path to voice recording (optional)
created_at (datetime): Creation timestamp
updated_at (datetime): Last edit timestamp
Relationships:
user: Many-to-one with User
reactions: One-to-many with StoryReaction
comments: One-to-many with StoryComment
"""
__tablename__ = 'stories'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
category = db.Column(db.String(50), nullable=False) # 5 categories
photo_url = db.Column(db.String(255)) # Optional photo
audio_url = db.Column(db.String(255)) # Optional voice recording
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, index=True)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# Relationships
user = db.relationship('User', back_populates='stories')
reactions = db.relationship('StoryReaction', back_populates='story',
lazy='dynamic', cascade='all, delete-orphan')
comments = db.relationship('StoryComment', back_populates='story',
lazy='dynamic', cascade='all, delete-orphan')
def __repr__(self):
return f'<Story {self.id}: {self.title[:30]}>'
# ==================== STORY REACTION MODEL ====================
class StoryReaction(db.Model):
"""
Story reaction model (Heart, Smile, Clap, Hug).
Attributes:
id (int): Primary key
story_id (int): Foreign key to Story
user_id (int): Foreign key to User who reacted
reaction_type (str): Type of reaction - 'heart', 'smile', 'clap', 'hug'
created_at (datetime): Reaction timestamp
Relationships:
story: Many-to-one with Story
user: Many-to-one with User
"""
__tablename__ = 'story_reactions'
id = db.Column(db.Integer, primary_key=True)
story_id = db.Column(db.Integer, db.ForeignKey('stories.id'), nullable=False, index=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
reaction_type = db.Column(db.String(20), nullable=False) # heart, smile, clap, hug
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
# Relationships
story = db.relationship('Story', back_populates='reactions')
user = db.relationship('User')
# Unique constraint: one user can only react once per story with same type
__table_args__ = (db.UniqueConstraint('story_id', 'user_id', 'reaction_type',
name='unique_story_user_reaction'),)
def __repr__(self):
return f'<Reaction {self.reaction_type} on Story {self.story_id}>'
# ==================== STORY COMMENT MODEL ====================
class StoryComment(db.Model):
"""
Comments on stories.
Attributes:
id (int): Primary key
story_id (int): Foreign key to Story
user_id (int): Foreign key to User who commented
content (text): Comment text
created_at (datetime): Comment timestamp
Relationships:
story: Many-to-one with Story
user: Many-to-one with User
"""
__tablename__ = 'story_comments'
id = db.Column(db.Integer, primary_key=True)
story_id = db.Column(db.Integer, db.ForeignKey('stories.id'), nullable=False, index=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
content = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
# Relationships
story = db.relationship('Story', back_populates='comments')
user = db.relationship('User')
def __repr__(self):
return f'<Comment {self.id} on Story {self.story_id}>'
# ==================== MESSAGE MODEL ====================
class Message(db.Model):
"""
Chat messages between buddies (Lingo Bridge).
Attributes:
id (int): Primary key
sender_id (int): Foreign key to User who sent message
recipient_id (int): Foreign key to User who receives message
content (text): Message content (original text)
original_language (str): Language code of original message
translated_content (text): Translated message text (optional)
is_voice (bool): Whether message is voice message
voice_url (str): Path to voice recording file (optional)
is_flagged (bool): Whether message contains inappropriate content
created_at (datetime): Message timestamp
Relationships:
sender: Many-to-one with User
recipient: Many-to-one with User
reports: One-to-many with ChatReport
"""
__tablename__ = 'messages'
id = db.Column(db.Integer, primary_key=True)
sender_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
recipient_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
content = db.Column(db.Text, nullable=False)
original_language = db.Column(db.String(10), default='en')
translated_content = db.Column(db.Text) # Translated version
is_voice = db.Column(db.Boolean, default=False)
voice_url = db.Column(db.String(255))
is_flagged = db.Column(db.Boolean, default=False) # Flagged by safety system
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, index=True)
# Relationships
sender = db.relationship('User', foreign_keys=[sender_id], back_populates='sent_messages')
recipient = db.relationship('User', foreign_keys=[recipient_id], back_populates='received_messages')
reports = db.relationship('ChatReport', back_populates='message', lazy='dynamic')
def __repr__(self):
return f'<Message {self.id} from User {self.sender_id} to {self.recipient_id}>'
# ==================== PAIR MODEL ====================
class Pair(db.Model):
"""
Buddy pairs between seniors and youth volunteers.
Feature owner: Tian An
Attributes:
id (int): Primary key
senior_id (int): Foreign key to senior User
youth_id (int): Foreign key to youth User
program (str): Program name/type
status (str): Pair status - 'active', 'inactive', 'paused'
paired_date (datetime): When pair was created
last_interaction (datetime): Last message/activity timestamp
Relationships:
senior: Many-to-one with User (senior)
youth: Many-to-one with User (youth)
"""
__tablename__ = 'pairs'
id = db.Column(db.Integer, primary_key=True)
senior_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
youth_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
program = db.Column(db.String(100)) # Program/initiative name
status = db.Column(db.String(20), default='active') # active, inactive, paused
paired_date = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
last_interaction = db.Column(db.DateTime, default=datetime.utcnow)
# Relationships
senior = db.relationship('User', foreign_keys=[senior_id], back_populates='senior_pairs')
youth = db.relationship('User', foreign_keys=[youth_id], back_populates='youth_pairs')
# Unique constraint: one senior can only be paired with one youth at a time
__table_args__ = (db.UniqueConstraint('senior_id', 'youth_id', name='unique_senior_youth_pair'),)
def __repr__(self):
return f'<Pair {self.id}: Senior {self.senior_id} - Youth {self.youth_id}>'
# ==================== EVENT MODEL ====================
class Event(db.Model):
"""
Events and activities.
Feature owner: Asher
Attributes:
id (int): Primary key
title (str): Event title
description (text): Event description
event_type (str): Type - 'online' or 'in-person'
location (str): Location (physical address or online link)
date (datetime): Event date and time
capacity (int): Maximum participants
created_by (int): Foreign key to admin User who created event
created_at (datetime): Creation timestamp
Relationships:
creator: Many-to-one with User
participants: One-to-many with EventParticipant
"""
__tablename__ = 'events'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
description = db.Column(db.Text)
event_type = db.Column(db.String(20), nullable=False) # 'online' or 'in-person'
location = db.Column(db.String(255))
date = db.Column(db.DateTime, nullable=False, index=True)
capacity = db.Column(db.Integer)
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
# Relationships
creator = db.relationship('User')
participants = db.relationship('EventParticipant', back_populates='event',
lazy='dynamic', cascade='all, delete-orphan')
def __repr__(self):
return f'<Event {self.id}: {self.title}>'
# ==================== EVENT PARTICIPANT MODEL ====================
class EventParticipant(db.Model):
"""
Event registrations (junction table).
Attributes:
id (int): Primary key
event_id (int): Foreign key to Event
user_id (int): Foreign key to User
registered_at (datetime): Registration timestamp
Relationships:
event: Many-to-one with Event
user: Many-to-one with User
"""
__tablename__ = 'event_participants'
id = db.Column(db.Integer, primary_key=True)
event_id = db.Column(db.Integer, db.ForeignKey('events.id'), nullable=False, index=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
registered_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
# Relationships
event = db.relationship('Event', back_populates='participants')
user = db.relationship('User')
# Unique constraint: one user can only register once per event
__table_args__ = (db.UniqueConstraint('event_id', 'user_id', name='unique_event_user'),)
def __repr__(self):
return f'<EventParticipant User {self.user_id} in Event {self.event_id}>'
# ==================== COMMUNITY MODEL ====================
class Community(db.Model):
"""
Interest-based communities.
Feature owner: Hong You
Attributes:
id (int): Primary key
name (str): Community name
type (str): Community type - 'Story', 'Hobby', 'Learning'
description (text): Community description
rules (text): Community rules
member_count (int): Number of members
created_by (int): Foreign key to User who created community
created_at (datetime): Creation timestamp
Relationships:
creator: Many-to-one with User
members: One-to-many with CommunityMember
posts: One-to-many with CommunityPost
"""
__tablename__ = 'communities'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False, unique=True)
type = db.Column(db.String(20), nullable=False) # Story, Hobby, Learning
description = db.Column(db.Text)
rules = db.Column(db.Text)
# Visual customization
icon = db.Column(db.String(50), default='fas fa-users')
banner_class = db.Column(db.String(50), default='default')
tags = db.Column(db.String(255)) # Comma separated tags
photo_url = db.Column(db.String(255))
member_count = db.Column(db.Integer, default=0)
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
# Relationships
creator = db.relationship('User')
members = db.relationship('CommunityMember', back_populates='community',
lazy='dynamic', cascade='all, delete-orphan')
posts = db.relationship('CommunityPost', back_populates='community',
lazy='dynamic', cascade='all, delete-orphan')
def __repr__(self):
return f'<Community {self.id}: {self.name}>'
# ==================== COMMUNITY MEMBER MODEL ====================
class CommunityMember(db.Model):
"""
Community memberships (junction table).
Attributes:
id (int): Primary key
community_id (int): Foreign key to Community
user_id (int): Foreign key to User
joined_at (datetime): Join timestamp
Relationships:
community: Many-to-one with Community
user: Many-to-one with User
"""
__tablename__ = 'community_members'
id = db.Column(db.Integer, primary_key=True)
community_id = db.Column(db.Integer, db.ForeignKey('communities.id'), nullable=False, index=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
joined_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
last_viewed_at = db.Column(db.DateTime, default=datetime.utcnow)
# Relationships
community = db.relationship('Community', back_populates='members')
user = db.relationship('User')
# Unique constraint: one user can only join a community once
__table_args__ = (db.UniqueConstraint('community_id', 'user_id', name='unique_community_user'),)
def __repr__(self):
return f'<CommunityMember User {self.user_id} in Community {self.community_id}>'
# ==================== COMMUNITY POST MODEL ====================
class CommunityPost(db.Model):
"""
Posts within communities.
Attributes:
id (int): Primary key
community_id (int): Foreign key to Community
user_id (int): Foreign key to User who posted
content (text): Post content
created_at (datetime): Post timestamp
Relationships:
community: Many-to-one with Community
user: Many-to-one with User
"""
__tablename__ = 'community_posts'
id = db.Column(db.Integer, primary_key=True)
community_id = db.Column(db.Integer, db.ForeignKey('communities.id'), nullable=False, index=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
content = db.Column(db.Text, nullable=True)
photo_url = db.Column(db.String(255))
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
# Relationships
community = db.relationship('Community', back_populates='posts')
user = db.relationship('User')
def __repr__(self):
return f'<CommunityPost {self.id} in Community {self.community_id}>'
# ==================== STREAK MODEL ====================
class Streak(db.Model):
"""
User engagement streaks.
Feature owner: Tian An
Attributes:
id (int): Primary key
user_id (int): Foreign key to User (one-to-one relationship)
current_streak (int): Current consecutive days
longest_streak (int): Record longest streak
points (int): Total points accumulated
games_played (int): Total games completed
games_won (int): Total games won
last_login (date): Last login date for streak tracking
Relationships:
user: One-to-one with User
"""
__tablename__ = 'streaks'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, unique=True)
current_streak = db.Column(db.Integer, default=0)
longest_streak = db.Column(db.Integer, default=0)
points = db.Column(db.Integer, default=0)
games_played = db.Column(db.Integer, default=0)
games_won = db.Column(db.Integer, default=0)
last_login = db.Column(db.Date, default=datetime.utcnow().date)
# Relationships
user = db.relationship('User', back_populates='streak')
def __repr__(self):
return f'<Streak User {self.user_id}: {self.current_streak} days>'
# ==================== BADGE MODEL ====================
class Badge(db.Model):
"""
Achievement badges earned by users.
Feature owner: Tian An
Attributes:
id (int): Primary key
user_id (int): Foreign key to User
badge_type (str): Badge type/name
earned_at (datetime): When badge was earned
Relationships:
user: Many-to-one with User
"""
__tablename__ = 'badges'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
badge_type = db.Column(db.String(50), nullable=False) # e.g., 'Week Warrior', 'Month Master'
earned_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
# Relationships
user = db.relationship('User', back_populates='badges')
def __repr__(self):
return f'<Badge {self.badge_type} for User {self.user_id}>'
# ==================== CHAT REPORT MODEL ====================
class ChatReport(db.Model):
"""
Reported chat messages for moderation.
Feature owner: Rai (Smart Chat Safety)
Attributes:
id (int): Primary key
message_id (int): Foreign key to reported Message
reported_by (int): Foreign key to User who reported
reported_user_id (int): Foreign key to User who sent the message
reason (str): Report reason
description (text): Additional details (optional)
status (str): Report status - 'pending', 'under_review', 'resolved', 'dismissed'
admin_notes (text): Admin notes on the report
created_at (datetime): Report timestamp
Relationships:
message: Many-to-one with Message
reporter: Many-to-one with User
reported_user: Many-to-one with User
"""
__tablename__ = 'chat_reports'
id = db.Column(db.Integer, primary_key=True)
message_id = db.Column(db.Integer, db.ForeignKey('messages.id'), nullable=True, index=True)
community_post_id = db.Column(db.Integer, db.ForeignKey('community_posts.id'), nullable=True, index=True)
reported_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
reported_user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
reason = db.Column(db.String(100), nullable=False) # Harassment, Inappropriate, Spam, etc.
description = db.Column(db.Text)
status = db.Column(db.String(20), default='pending') # pending, under_review, resolved, dismissed
admin_notes = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, index=True)
# Relationships
message = db.relationship('Message', back_populates='reports')
community_post = db.relationship('CommunityPost', backref='reports')
reporter = db.relationship('User', foreign_keys=[reported_by])
reported_user = db.relationship('User', foreign_keys=[reported_user_id])
def __repr__(self):
return f'<ChatReport {self.id}: {self.status}>'
# ==================== CHECKIN MODEL ====================
class Checkin(db.Model):
"""
Weekly mood check-ins for seniors.
Feature owner: Rai
Attributes:
id (int): Primary key
user_id (int): Foreign key to senior User
mood (str): Mood selection - 'Great', 'Good', 'Okay', 'Not Good'
notes (text): Optional written response
created_at (datetime): Check-in timestamp
Relationships:
user: Many-to-one with User
"""
__tablename__ = 'checkins'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
mood = db.Column(db.String(20), nullable=False) # Great, Good, Okay, Not Good
notes = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, index=True)
# Relationships
user = db.relationship('User', back_populates='checkins')
def __repr__(self):
return f'<Checkin {self.id}: User {self.user_id} - {self.mood}>'
# ==================== NOTIFICATION MODEL ====================
class Notification(db.Model):
"""
User notifications for events, games, etc.
"""
__tablename__ = 'notifications'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
title = db.Column(db.String(100), nullable=False)
message = db.Column(db.Text, nullable=False)
type = db.Column(db.String(20)) # 'event', 'game', 'message'
link = db.Column(db.String(255))
is_read = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
# Relationship
user = db.relationship('User', backref=db.backref('notifications_list', lazy='dynamic'))
def __repr__(self):
return f'<Notification {self.id}: {self.title}>'
# ==================== REGISTRATION CODE MODEL ====================
class RegistrationCode(db.Model):
"""
Registration codes required for sign up.
"""
__tablename__ = 'registration_codes'
id = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(20), unique=True, nullable=False)
is_used = db.Column(db.Boolean, default=False)
used_by_id = db.Column(db.Integer, db.ForeignKey('users.id'))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
# Relationship
used_by = db.relationship('User', backref=db.backref('registration_code', uselist=False))
def __repr__(self):
return f'<RegistrationCode {self.code}: {"Used" if self.is_used else "Active"}>'
# ==================== GAME MODELS ====================
class Game(db.Model):
"""
Game definitions for the Games Arcade.
"""
__tablename__ = 'games'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text)
icon = db.Column(db.String(50)) # e.g., 'fas fa-chess'
# Badge info (e.g., "Traditional", "Strategy")
badge_label = db.Column(db.String(50))
badge_class = db.Column(db.String(50)) # css class
badge_icon = db.Column(db.String(50))
# Meta info
players_text = db.Column(db.String(50)) # "2 Players"
duration_text = db.Column(db.String(50)) # "20-40 min"
# Type info
type_label = db.Column(db.String(50)) # "Strategy", "Logic"
type_icon = db.Column(db.String(50))
# Visuals
bg_gradient = db.Column(db.String(255)) # CSS gradient string
sessions = db.relationship('GameSession', back_populates='game', lazy='dynamic')
def __repr__(self):
return f'<Game {self.title}>'
class GameSession(db.Model):
"""
Active game sessions between users.
"""
__tablename__ = 'game_sessions'
id = db.Column(db.Integer, primary_key=True)
game_id = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=False)
player1_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
player2_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
current_turn_id = db.Column(db.Integer, db.ForeignKey('users.id'))
status = db.Column(db.String(20), default='waiting') # active, completed, abandoned, waiting
winner_id = db.Column(db.Integer, db.ForeignKey('users.id'))
game_state = db.Column(db.Text) # Stores board state/FEN/PGN
player1_ready = db.Column(db.Boolean, default=False)
player2_ready = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
# Relationships
game = db.relationship('Game', back_populates='sessions')
player1 = db.relationship('User', foreign_keys=[player1_id])
player2 = db.relationship('User', foreign_keys=[player2_id])
winner = db.relationship('User', foreign_keys=[winner_id])
class GameHistory(db.Model):
"""
History of completed games for stats and ELO tracking.
"""
__tablename__ = 'game_history'
id = db.Column(db.Integer, primary_key=True)
game_id = db.Column(db.Integer, db.ForeignKey('games.id'), nullable=False)
player1_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
player2_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
winner_id = db.Column(db.Integer, db.ForeignKey('users.id')) # NULL if draw
player1_elo_before = db.Column(db.Integer)
player1_elo_after = db.Column(db.Integer)
player2_elo_before = db.Column(db.Integer)
player2_elo_after = db.Column(db.Integer)
completed_at = db.Column(db.DateTime, default=datetime.utcnow)
# Relationships
game = db.relationship('Game')
player1 = db.relationship('User', foreign_keys=[player1_id])
player2 = db.relationship('User', foreign_keys=[player2_id])
winner = db.relationship('User', foreign_keys=[winner_id])
class TicTacToeSession(db.Model):
"""
Tic Tac Toe game sessions.
"""
__tablename__ = 'tictactoe_sessions'
id = db.Column(db.Integer, primary_key=True)
player1_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
player2_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
current_turn_id = db.Column(db.Integer, db.ForeignKey('users.id'))
status = db.Column(db.String(20), default='waiting') # waiting, active, completed
player1_ready = db.Column(db.Boolean, default=False)
player2_ready = db.Column(db.Boolean, default=False)
board_state = db.Column(db.String(9), default=' ') # 9 spaces for empty board
winner_id = db.Column(db.Integer, db.ForeignKey('users.id'))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
# Relationships
player1 = db.relationship('User', foreign_keys=[player1_id])
player2 = db.relationship('User', foreign_keys=[player2_id])
winner = db.relationship('User', foreign_keys=[winner_id])
def __repr__(self):
return f'<TicTacToeSession {self.id}: {self.status}>'