File size: 5,275 Bytes
71a3948
ec5cc84
 
71a3948
ec5cc84
71a3948
ec5cc84
71a3948
ec5cc84
 
71a3948
ec5cc84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71a3948
 
 
 
 
 
 
 
 
ec5cc84
 
71a3948
ec5cc84
 
 
71a3948
 
 
 
ec5cc84
 
71a3948
ec5cc84
 
 
71a3948
 
 
ec5cc84
 
 
71a3948
ec5cc84
 
 
 
 
 
 
 
71a3948
 
 
ec5cc84
 
 
71a3948
 
 
 
ec5cc84
 
 
 
 
 
71a3948
 
 
ec5cc84
 
 
 
71a3948
 
ec5cc84
71a3948
 
ec5cc84
 
71a3948
 
ec5cc84
 
 
 
 
 
 
 
 
71a3948
 
ec5cc84
71a3948
ec5cc84
 
71a3948
 
 
ec5cc84
71a3948
 
ec5cc84
 
 
 
71a3948
 
ec5cc84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71a3948
 
 
 
 
ec5cc84
 
 
71a3948
ec5cc84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from typing import List, Optional
from sqlmodel import Field, Relationship, SQLModel
from enum import Enum

# --- Enums for choices ---

class Role(str, Enum):
    ADMIN = "admin"
    STAFF = "staff"
    STUDENT = "student"

class Department(str, Enum):
    COMPUTER_SCIENCE = "Computer Science"
    ENGINEERING = "Engineering"
    BUSINESS_ADMIN = "Business Administration"
    LAW = "Law"
    MEDICINE = "Medicine"

class ClearanceDepartment(str, Enum):
    LIBRARY = "Library"
    STUDENT_AFFAIRS = "Student Affairs"
    BURSARY = "Bursary"
    ACADEMIC_AFFAIRS = "Academic Affairs"
    HEALTH_CENTER = "Health Center"

class ClearanceStatusEnum(str, Enum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"

# --- Database Table Models ---

class User(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    username: str = Field(index=True, unique=True)
    email: str = Field(index=True, unique=True)
    full_name: str
    hashed_password: str
    role: Role
    department: Optional[Department] = None # For staff members
    rfid_tag: Optional["RFIDTag"] = Relationship(back_populates="user", sa_relationship_kwargs={"cascade": "all, delete-orphan"})

class Student(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    full_name: str
    matric_no: str = Field(index=True, unique=True)
    email: str = Field(index=True, unique=True)
    department: Department
    # A student's login is handled by their associated User record, not directly here.
    rfid_tag: Optional["RFIDTag"] = Relationship(back_populates="student", sa_relationship_kwargs={"cascade": "all, delete-orphan"})
    clearance_statuses: List["ClearanceStatus"] = Relationship(back_populates="student", sa_relationship_kwargs={"cascade": "all, delete-orphan"})

class ClearanceStatus(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    department: ClearanceDepartment
    status: ClearanceStatusEnum = Field(default=ClearanceStatusEnum.PENDING)
    remarks: Optional[str] = None
    student_id: int = Field(foreign_key="student.id")
    student: "Student" = Relationship(back_populates="clearance_statuses")

class RFIDTag(SQLModel, table=True):
    tag_id: str = Field(primary_key=True, index=True)
    student_id: Optional[int] = Field(default=None, foreign_key="student.id", unique=True)
    user_id: Optional[int] = Field(default=None, foreign_key="user.id", unique=True)
    student: Optional["Student"] = Relationship(back_populates="rfid_tag")
    user: Optional["User"] = Relationship(back_populates="rfid_tag")

class Device(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    device_name: str = Field(unique=True, index=True)
    api_key: str = Field(unique=True, index=True)
    location: str
    is_active: bool = Field(default=True)

# --- Pydantic Models for API Operations ---

# Token Model
class Token(SQLModel):
    access_token: str
    token_type: str

# User Models
class UserCreate(SQLModel):
    username: str
    password: str
    email: str
    full_name: str
    role: Role
    department: Optional[Department] = None

class UserUpdate(SQLModel):
    username: Optional[str] = None
    email: Optional[str] = None
    full_name: Optional[str] = None
    role: Optional[Role] = None
    department: Optional[Department] = None
    password: Optional[str] = None

class UserRead(SQLModel):
    id: int
    username: str
    email: str
    full_name: str
    role: Role
    department: Optional[Department] = None

# Student Models
class StudentCreate(SQLModel):
    full_name: str
    matric_no: str
    email: str
    department: Department
    password: str # This will be used to create the associated User account for the student

class StudentUpdate(SQLModel):
    full_name: Optional[str] = None
    department: Optional[Department] = None
    email: Optional[str] = None

class StudentRead(SQLModel):
    id: int
    full_name: str
    matric_no: str
    department: Department

# Clearance Status Models
class ClearanceStatusRead(SQLModel):
    department: ClearanceDepartment
    status: ClearanceStatusEnum
    remarks: Optional[str] = None

class ClearanceUpdate(SQLModel):
    matric_no: str
    department: ClearanceDepartment
    status: ClearanceStatusEnum
    remarks: Optional[str] = None

# Combined Read Model
class StudentReadWithClearance(StudentRead):
    clearance_statuses: List[ClearanceStatusRead] = []
    rfid_tag: Optional["RFIDTagRead"] = None

# RFID Tag Models
class RFIDTagRead(SQLModel):
    tag_id: str
    student_id: Optional[int] = None
    user_id: Optional[int] = None

class TagLink(SQLModel):
    tag_id: str
    matric_no: Optional[str] = None
    username: Optional[str] = None

# RFID Device-Specific Models
class RFIDScanRequest(SQLModel):
    tag_id: str

class RFIDStatusResponse(SQLModel):
    status: str
    full_name: Optional[str] = None
    message: Optional[str] = None
    clearance: Optional[str] = None

class TagScan(SQLModel):
    tag_id: str

# Device Models
class DeviceCreate(SQLModel):
    device_name: str
    location: str

class DeviceRead(SQLModel):
    id: int
    device_name: str
    api_key: str
    location: str
    is_active: bool