chualinwei3 commited on
Commit
ecd52b4
·
verified ·
1 Parent(s): 1a17009

Update models.py

Browse files
Files changed (1) hide show
  1. models.py +195 -195
models.py CHANGED
@@ -1,195 +1,195 @@
1
- from flask_sqlalchemy import SQLAlchemy
2
- from datetime import datetime
3
- import json
4
- from werkzeug.security import generate_password_hash, check_password_hash
5
-
6
- db = SQLAlchemy()
7
-
8
- class User(db.Model):
9
- """User model for authentication - supports both students and lecturers"""
10
- __tablename__ = 'users'
11
-
12
- id = db.Column(db.Integer, primary_key=True)
13
- matric_no = db.Column(db.String(50), unique=True, nullable=False) # A/B for students, L for lecturers
14
- name = db.Column(db.String(100), nullable=False)
15
- email = db.Column(db.String(100), unique=True)
16
- password_hash = db.Column(db.String(255), nullable=False)
17
- role = db.Column(db.String(20), nullable=False) # 'student' or 'lecturer'
18
- is_active = db.Column(db.Boolean, default=True)
19
- created_at = db.Column(db.DateTime, default=datetime.utcnow)
20
-
21
- def set_password(self, password):
22
- """Hash and set password"""
23
- self.password_hash = generate_password_hash(password)
24
-
25
- def check_password(self, password):
26
- """Verify password"""
27
- return check_password_hash(self.password_hash, password)
28
-
29
- @staticmethod
30
- def validate_matric_no(matric_no, role):
31
- """Validate matric number format based on role"""
32
- if not matric_no:
33
- return False, "Matric number is required"
34
-
35
- matric_no = matric_no.upper().strip()
36
-
37
- if role == 'student':
38
- # Students must start with A or B
39
- if not (matric_no.startswith('A') or matric_no.startswith('B')):
40
- return False, "Student matric number must start with 'A' or 'B'"
41
- elif role == 'lecturer':
42
- # Lecturers must start with L
43
- if not matric_no.startswith('L'):
44
- return False, "Lecturer matric number must start with 'L'"
45
- else:
46
- return False, "Invalid role"
47
-
48
- return True, None
49
-
50
- def to_dict(self):
51
- return {
52
- 'id': self.id,
53
- 'matric_no': self.matric_no,
54
- 'name': self.name,
55
- 'email': self.email,
56
- 'role': self.role,
57
- 'is_active': self.is_active,
58
- 'created_at': self.created_at.isoformat() if self.created_at else None
59
- }
60
-
61
-
62
- class Student(db.Model):
63
- """Student model - stores student information and face encodings"""
64
- __tablename__ = 'students'
65
-
66
- id = db.Column(db.Integer, primary_key=True)
67
- user_id = db.Column(db.Integer, db.ForeignKey('users.id'), unique=True) # Link to User
68
- student_id = db.Column(db.String(50), unique=True, nullable=False)
69
- name = db.Column(db.String(100), nullable=False)
70
- email = db.Column(db.String(100), unique=True)
71
- face_encoding = db.Column(db.Text) # Stored as JSON string
72
- registered_at = db.Column(db.DateTime, default=datetime.utcnow)
73
- is_active = db.Column(db.Boolean, default=True)
74
-
75
- # Relationships
76
- attendance_records = db.relationship('AttendanceRecord', backref='student', lazy=True)
77
- user = db.relationship('User', backref='student_profile', uselist=False)
78
-
79
- def get_face_encoding(self):
80
- """Deserialize face encoding from JSON"""
81
- if self.face_encoding:
82
- return json.loads(self.face_encoding)
83
- return None
84
-
85
- def set_face_encoding(self, encoding):
86
- """Serialize face encoding to JSON"""
87
- if encoding is not None:
88
- self.face_encoding = json.dumps(encoding.tolist())
89
-
90
- def to_dict(self):
91
- return {
92
- 'id': self.id,
93
- 'student_id': self.student_id,
94
- 'name': self.name,
95
- 'email': self.email,
96
- 'registered_at': self.registered_at.isoformat() if self.registered_at else None,
97
- 'is_active': self.is_active
98
- }
99
-
100
-
101
- class Classroom(db.Model):
102
- """Classroom model - stores classroom locations with GPS coordinates"""
103
- __tablename__ = 'classrooms'
104
-
105
- id = db.Column(db.Integer, primary_key=True)
106
- name = db.Column(db.String(100), nullable=False)
107
- building = db.Column(db.String(100))
108
- latitude = db.Column(db.Float, nullable=False)
109
- longitude = db.Column(db.Float, nullable=False)
110
- radius_meters = db.Column(db.Integer, default=50) # Geofencing radius
111
-
112
- # Relationships
113
- sessions = db.relationship('AttendanceSession', backref='classroom', lazy=True)
114
-
115
- def to_dict(self):
116
- return {
117
- 'id': self.id,
118
- 'name': self.name,
119
- 'building': self.building,
120
- 'latitude': self.latitude,
121
- 'longitude': self.longitude,
122
- 'radius_meters': self.radius_meters
123
- }
124
-
125
-
126
- class AttendanceSession(db.Model):
127
- """Attendance session - represents a class session with auto-refresh codes"""
128
- __tablename__ = 'attendance_sessions'
129
-
130
- id = db.Column(db.Integer, primary_key=True)
131
- course_name = db.Column(db.String(100), nullable=False)
132
- classroom_id = db.Column(db.Integer, db.ForeignKey('classrooms.id'), nullable=False)
133
- lecturer_name = db.Column(db.String(100), nullable=False)
134
-
135
- # Session timing
136
- start_time = db.Column(db.DateTime, default=datetime.utcnow)
137
- end_time = db.Column(db.DateTime)
138
- is_active = db.Column(db.Boolean, default=True)
139
-
140
- # Auto-refresh attendance code
141
- current_code = db.Column(db.String(10))
142
- code_generated_at = db.Column(db.DateTime)
143
-
144
- # Relationships
145
- attendance_records = db.relationship('AttendanceRecord', backref='session', lazy=True)
146
-
147
- def to_dict(self):
148
- return {
149
- 'id': self.id,
150
- 'course_name': self.course_name,
151
- 'classroom_id': self.classroom_id,
152
- 'classroom': self.classroom.to_dict() if self.classroom else None,
153
- 'lecturer_name': self.lecturer_name,
154
- 'start_time': self.start_time.isoformat() if self.start_time else None,
155
- 'end_time': self.end_time.isoformat() if self.end_time else None,
156
- 'is_active': self.is_active,
157
- 'current_code': self.current_code,
158
- 'code_generated_at': self.code_generated_at.isoformat() if self.code_generated_at else None
159
- }
160
-
161
-
162
- class AttendanceRecord(db.Model):
163
- """Attendance record - individual student attendance for a session"""
164
- __tablename__ = 'attendance_records'
165
-
166
- id = db.Column(db.Integer, primary_key=True)
167
- session_id = db.Column(db.Integer, db.ForeignKey('attendance_sessions.id'), nullable=False)
168
- student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
169
-
170
- # Verification details
171
- marked_at = db.Column(db.DateTime, default=datetime.utcnow)
172
- face_verified = db.Column(db.Boolean, default=False)
173
- location_verified = db.Column(db.Boolean, default=False)
174
- code_verified = db.Column(db.Boolean, default=False)
175
-
176
- # Location data
177
- student_latitude = db.Column(db.Float)
178
- student_longitude = db.Column(db.Float)
179
- distance_from_classroom = db.Column(db.Float) # In meters
180
-
181
- # Status
182
- status = db.Column(db.String(20), default='present') # present, late, absent
183
-
184
- def to_dict(self):
185
- return {
186
- 'id': self.id,
187
- 'session_id': self.session_id,
188
- 'student': self.student.to_dict() if self.student else None,
189
- 'marked_at': self.marked_at.isoformat() if self.marked_at else None,
190
- 'face_verified': self.face_verified,
191
- 'location_verified': self.location_verified,
192
- 'code_verified': self.code_verified,
193
- 'distance_from_classroom': self.distance_from_classroom,
194
- 'status': self.status
195
- }
 
1
+ from flask_sqlalchemy import SQLAlchemy
2
+ from datetime import datetime
3
+ import json
4
+ from werkzeug.security import generate_password_hash, check_password_hash
5
+
6
+ db = SQLAlchemy()
7
+
8
+ class User(db.Model):
9
+ """User model for authentication - supports both students and lecturers"""
10
+ __tablename__ = 'users'
11
+
12
+ id = db.Column(db.Integer, primary_key=True)
13
+ matric_no = db.Column(db.String(50), unique=True, nullable=False) # A/B for students, L for lecturers
14
+ name = db.Column(db.String(100), nullable=False)
15
+ email = db.Column(db.String(100), unique=True)
16
+ password_hash = db.Column(db.String(255), nullable=False)
17
+ role = db.Column(db.String(20), nullable=False) # 'student' or 'lecturer'
18
+ is_active = db.Column(db.Boolean, default=True)
19
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
20
+
21
+ def set_password(self, password):
22
+ """Hash and set password"""
23
+ self.password_hash = generate_password_hash(password)
24
+
25
+ def check_password(self, password):
26
+ """Verify password"""
27
+ return check_password_hash(self.password_hash, password)
28
+
29
+ @staticmethod
30
+ def validate_matric_no(matric_no, role):
31
+ """Validate matric number format based on role"""
32
+ if not matric_no:
33
+ return False, "Matric number is required"
34
+
35
+ matric_no = matric_no.upper().strip()
36
+
37
+ if role == 'student':
38
+ # Students must start with A or B
39
+ if not (matric_no.startswith('A') or matric_no.startswith('B')):
40
+ return False, "Student matric number must start with 'A' or 'B'"
41
+ elif role == 'lecturer':
42
+ # Lecturers must start with L
43
+ if not matric_no.startswith('L'):
44
+ return False, "Lecturer matric number must start with 'L'"
45
+ else:
46
+ return False, "Invalid role"
47
+
48
+ return True, None
49
+
50
+ def to_dict(self):
51
+ return {
52
+ 'id': self.id,
53
+ 'matric_no': self.matric_no,
54
+ 'name': self.name,
55
+ 'email': self.email,
56
+ 'role': self.role,
57
+ 'is_active': self.is_active,
58
+ 'created_at': self.created_at.isoformat() + 'Z' if self.created_at else None
59
+ }
60
+
61
+
62
+ class Student(db.Model):
63
+ """Student model - stores student information and face encodings"""
64
+ __tablename__ = 'students'
65
+
66
+ id = db.Column(db.Integer, primary_key=True)
67
+ user_id = db.Column(db.Integer, db.ForeignKey('users.id'), unique=True) # Link to User
68
+ student_id = db.Column(db.String(50), unique=True, nullable=False)
69
+ name = db.Column(db.String(100), nullable=False)
70
+ email = db.Column(db.String(100), unique=True)
71
+ face_encoding = db.Column(db.Text) # Stored as JSON string
72
+ registered_at = db.Column(db.DateTime, default=datetime.utcnow)
73
+ is_active = db.Column(db.Boolean, default=True)
74
+
75
+ # Relationships
76
+ attendance_records = db.relationship('AttendanceRecord', backref='student', lazy=True)
77
+ user = db.relationship('User', backref='student_profile', uselist=False)
78
+
79
+ def get_face_encoding(self):
80
+ """Deserialize face encoding from JSON"""
81
+ if self.face_encoding:
82
+ return json.loads(self.face_encoding)
83
+ return None
84
+
85
+ def set_face_encoding(self, encoding):
86
+ """Serialize face encoding to JSON"""
87
+ if encoding is not None:
88
+ self.face_encoding = json.dumps(encoding.tolist())
89
+
90
+ def to_dict(self):
91
+ return {
92
+ 'id': self.id,
93
+ 'student_id': self.student_id,
94
+ 'name': self.name,
95
+ 'email': self.email,
96
+ 'registered_at': self.registered_at.isoformat() + 'Z' if self.registered_at else None,
97
+ 'is_active': self.is_active
98
+ }
99
+
100
+
101
+ class Classroom(db.Model):
102
+ """Classroom model - stores classroom locations with GPS coordinates"""
103
+ __tablename__ = 'classrooms'
104
+
105
+ id = db.Column(db.Integer, primary_key=True)
106
+ name = db.Column(db.String(100), nullable=False)
107
+ building = db.Column(db.String(100))
108
+ latitude = db.Column(db.Float, nullable=False)
109
+ longitude = db.Column(db.Float, nullable=False)
110
+ radius_meters = db.Column(db.Integer, default=50) # Geofencing radius
111
+
112
+ # Relationships
113
+ sessions = db.relationship('AttendanceSession', backref='classroom', lazy=True)
114
+
115
+ def to_dict(self):
116
+ return {
117
+ 'id': self.id,
118
+ 'name': self.name,
119
+ 'building': self.building,
120
+ 'latitude': self.latitude,
121
+ 'longitude': self.longitude,
122
+ 'radius_meters': self.radius_meters
123
+ }
124
+
125
+
126
+ class AttendanceSession(db.Model):
127
+ """Attendance session - represents a class session with auto-refresh codes"""
128
+ __tablename__ = 'attendance_sessions'
129
+
130
+ id = db.Column(db.Integer, primary_key=True)
131
+ course_name = db.Column(db.String(100), nullable=False)
132
+ classroom_id = db.Column(db.Integer, db.ForeignKey('classrooms.id'), nullable=False)
133
+ lecturer_name = db.Column(db.String(100), nullable=False)
134
+
135
+ # Session timing
136
+ start_time = db.Column(db.DateTime, default=datetime.utcnow)
137
+ end_time = db.Column(db.DateTime)
138
+ is_active = db.Column(db.Boolean, default=True)
139
+
140
+ # Auto-refresh attendance code
141
+ current_code = db.Column(db.String(10))
142
+ code_generated_at = db.Column(db.DateTime)
143
+
144
+ # Relationships
145
+ attendance_records = db.relationship('AttendanceRecord', backref='session', lazy=True)
146
+
147
+ def to_dict(self):
148
+ return {
149
+ 'id': self.id,
150
+ 'course_name': self.course_name,
151
+ 'classroom_id': self.classroom_id,
152
+ 'classroom': self.classroom.to_dict() if self.classroom else None,
153
+ 'lecturer_name': self.lecturer_name,
154
+ 'start_time': self.start_time.isoformat() + 'Z' if self.start_time else None,
155
+ 'end_time': self.end_time.isoformat() + 'Z' if self.end_time else None,
156
+ 'is_active': self.is_active,
157
+ 'current_code': self.current_code,
158
+ 'code_generated_at': self.code_generated_at.isoformat() + 'Z' if self.code_generated_at else None
159
+ }
160
+
161
+
162
+ class AttendanceRecord(db.Model):
163
+ """Attendance record - individual student attendance for a session"""
164
+ __tablename__ = 'attendance_records'
165
+
166
+ id = db.Column(db.Integer, primary_key=True)
167
+ session_id = db.Column(db.Integer, db.ForeignKey('attendance_sessions.id'), nullable=False)
168
+ student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
169
+
170
+ # Verification details
171
+ marked_at = db.Column(db.DateTime, default=datetime.utcnow)
172
+ face_verified = db.Column(db.Boolean, default=False)
173
+ location_verified = db.Column(db.Boolean, default=False)
174
+ code_verified = db.Column(db.Boolean, default=False)
175
+
176
+ # Location data
177
+ student_latitude = db.Column(db.Float)
178
+ student_longitude = db.Column(db.Float)
179
+ distance_from_classroom = db.Column(db.Float) # In meters
180
+
181
+ # Status
182
+ status = db.Column(db.String(20), default='present') # present, late, absent
183
+
184
+ def to_dict(self):
185
+ return {
186
+ 'id': self.id,
187
+ 'session_id': self.session_id,
188
+ 'student': self.student.to_dict() if self.student else None,
189
+ 'marked_at': self.marked_at.isoformat() + 'Z' if self.marked_at else None,
190
+ 'face_verified': self.face_verified,
191
+ 'location_verified': self.location_verified,
192
+ 'code_verified': self.code_verified,
193
+ 'distance_from_classroom': self.distance_from_classroom,
194
+ 'status': self.status
195
+ }