Fred808 commited on
Commit
639b015
·
verified ·
1 Parent(s): aebea36

Update app/db/schemas.py

Browse files
Files changed (1) hide show
  1. app/db/schemas.py +291 -257
app/db/schemas.py CHANGED
@@ -1,258 +1,292 @@
1
- from sqlalchemy.orm import validates
2
- from sqlalchemy import event
3
- from datetime import datetime
4
- from typing import List, Optional, Dict
5
- from pydantic import BaseModel, EmailStr, validator
6
- from .models import User, Product, Order, Event, Notification
7
- import re
8
-
9
- class BranchBase(BaseModel):
10
- name: str
11
- address: str
12
- phone: str
13
- email: EmailStr
14
- is_active: bool = True
15
-
16
- class BranchCreate(BranchBase):
17
- pass
18
-
19
- class BranchInDB(BranchBase):
20
- id: int
21
- created_at: datetime
22
- updated_at: Optional[datetime] = None
23
-
24
- class Config:
25
- from_attributes = True
26
-
27
- class UserBase(BaseModel):
28
- email: EmailStr
29
- username: str
30
- full_name: str
31
- is_active: bool = True
32
- is_superuser: bool = False
33
- roles: List[str] = ["user"]
34
- branch_id: Optional[int] = None
35
-
36
- class UserCreate(UserBase):
37
- password: str
38
-
39
- class UserInDB(UserBase):
40
- id: int
41
- created_at: datetime
42
-
43
- class Config:
44
- from_attributes = True
45
-
46
- class ProductBase(BaseModel):
47
- name: str
48
- description: str
49
- price: float
50
- category: str
51
- inventory_count: int
52
- seller_id: int
53
- branch_id: int
54
-
55
- class ProductCreate(ProductBase):
56
- pass
57
-
58
- class ProductInDB(ProductBase):
59
- id: int
60
- created_at: datetime
61
- updated_at: Optional[datetime] = None
62
-
63
- class Config:
64
- from_attributes = True
65
-
66
- class OrderItemBase(BaseModel):
67
- product_id: int
68
- quantity: int
69
- price: float
70
-
71
- class OrderItemCreate(OrderItemBase):
72
- pass
73
-
74
- class OrderItemInDB(OrderItemBase):
75
- id: int
76
- order_id: int
77
-
78
- class Config:
79
- from_attributes = True
80
-
81
- class OrderBase(BaseModel):
82
- customer_id: int
83
- branch_id: int
84
- total_amount: float
85
- status: str = "pending"
86
- items: List[OrderItemCreate]
87
-
88
- class OrderCreate(OrderBase):
89
- pass
90
-
91
- class OrderInDB(OrderBase):
92
- id: int
93
- created_at: datetime
94
- updated_at: Optional[datetime] = None
95
- items: List[OrderItemInDB]
96
-
97
- class Config:
98
- from_attributes = True
99
-
100
- class NotificationBase(BaseModel):
101
- user_id: int
102
- title: str
103
- message: str
104
- type: str
105
- data: Optional[dict] = None
106
- read: bool = False
107
-
108
- class NotificationCreate(NotificationBase):
109
- pass
110
-
111
- class NotificationInDB(NotificationBase):
112
- id: int
113
- created_at: datetime
114
-
115
- class Config:
116
- from_attributes = True
117
-
118
- class EventBase(BaseModel):
119
- title: str
120
- description: str
121
- start_time: datetime
122
- end_time: datetime
123
- is_all_day: bool = False
124
- reminder_minutes: int = 30
125
-
126
- @validator('end_time')
127
- def end_time_after_start_time(cls, v, values):
128
- if 'start_time' in values and v <= values['start_time']:
129
- raise ValueError('end_time must be after start_time')
130
- return v
131
-
132
- @validator('reminder_minutes')
133
- def valid_reminder_minutes(cls, v):
134
- if v < 0:
135
- raise ValueError('reminder_minutes cannot be negative')
136
- return v
137
-
138
- class EventCreate(EventBase):
139
- attendees: List[str] = []
140
-
141
- class EventUpdate(BaseModel):
142
- title: Optional[str] = None
143
- description: Optional[str] = None
144
- start_time: Optional[datetime] = None
145
- end_time: Optional[datetime] = None
146
- is_all_day: Optional[bool] = None
147
- reminder_minutes: Optional[int] = None
148
- attendees: Optional[List[str]] = None
149
-
150
- @validator('reminder_minutes')
151
- def valid_reminder_minutes(cls, v):
152
- if v is not None and v < 0:
153
- raise ValueError('reminder_minutes cannot be negative')
154
- return v
155
-
156
- class EventInDB(EventBase):
157
- id: int
158
- user_id: int
159
- attendees: List[str]
160
- status: str
161
- attendee_responses: Dict[str, str]
162
- created_at: datetime
163
- updated_at: Optional[datetime] = None
164
- reminder_sent: bool = False
165
- is_recurring: bool = False
166
- recurrence_group: Optional[str] = None
167
- parent_event_id: Optional[int] = None
168
- sequence_number: Optional[int] = None
169
-
170
- class Config:
171
- from_attributes = True
172
-
173
- class RecurringEventCreate(EventCreate):
174
- recurrence_pattern: str
175
- recurrence_end_date: Optional[datetime] = None
176
-
177
- @validator('recurrence_pattern')
178
- def valid_recurrence_pattern(cls, v):
179
- valid_patterns = ['daily', 'weekly', 'monthly', 'yearly']
180
- if v not in valid_patterns:
181
- raise ValueError(f'recurrence_pattern must be one of: {", ".join(valid_patterns)}')
182
- return v
183
-
184
- @validator('recurrence_end_date')
185
- def end_date_after_start_time(cls, v, values):
186
- if v is not None and 'start_time' in values and v <= values['start_time']:
187
- raise ValueError('recurrence_end_date must be after start_time')
188
- return v
189
-
190
- class RecurringEventUpdate(EventUpdate):
191
- recurrence_pattern: Optional[str] = None
192
- recurrence_end_date: Optional[datetime] = None
193
-
194
- @validator('recurrence_pattern')
195
- def valid_recurrence_pattern(cls, v):
196
- if v is not None:
197
- valid_patterns = ['daily', 'weekly', 'monthly', 'yearly']
198
- if v not in valid_patterns:
199
- raise ValueError(f'recurrence_pattern must be one of: {", ".join(valid_patterns)}')
200
- return v
201
-
202
- @validates('email')
203
- def validate_email(self, key, email):
204
- if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
205
- raise ValueError('Invalid email address')
206
- return email
207
-
208
- @validates('username')
209
- def validate_username(self, key, username):
210
- if len(username) < 3:
211
- raise ValueError('Username must be at least 3 characters long')
212
- return username
213
-
214
- @validates('inventory_count')
215
- def validate_inventory(self, key, count):
216
- if count < 0:
217
- raise ValueError('Inventory count cannot be negative')
218
- return count
219
-
220
- @validates('price')
221
- def validate_price(self, key, price):
222
- if price < 0:
223
- raise ValueError('Price cannot be negative')
224
- return price
225
-
226
- # Event listeners for automatic timestamps
227
- @event.listens_for(Product, 'before_insert')
228
- def set_created_at(mapper, connection, target):
229
- target.created_at = datetime.utcnow()
230
- target.updated_at = datetime.utcnow()
231
-
232
- @event.listens_for(Product, 'before_update')
233
- def set_updated_at(mapper, connection, target):
234
- target.updated_at = datetime.utcnow()
235
-
236
- @event.listens_for(Order, 'before_insert')
237
- def set_order_created_at(mapper, connection, target):
238
- target.created_at = datetime.utcnow()
239
- target.updated_at = datetime.utcnow()
240
-
241
- @event.listens_for(Order, 'before_update')
242
- def set_order_updated_at(mapper, connection, target):
243
- target.updated_at = datetime.utcnow()
244
-
245
- @event.listens_for(Event, 'before_insert')
246
- def set_event_created_at(mapper, connection, target):
247
- target.created_at = datetime.utcnow()
248
- target.updated_at = datetime.utcnow()
249
-
250
- @event.listens_for(Event, 'before_update')
251
- def set_event_updated_at(mapper, connection, target):
252
- target.updated_at = datetime.utcnow()
253
-
254
- # Add validators to models
255
- User.validate_email = validate_email
256
- User.validate_username = validate_username
257
- Product.validate_inventory = validate_inventory
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  Product.validate_price = validate_price
 
1
+ from sqlalchemy.orm import validates
2
+ from sqlalchemy import event
3
+ from datetime import datetime
4
+ from typing import List, Optional, Dict
5
+ from pydantic import BaseModel, EmailStr, validator
6
+ from .models import User, Product, Order, Event, Notification
7
+ import re
8
+
9
+ # Role schemas
10
+ class RoleBase(BaseModel):
11
+ name: str
12
+ description: str
13
+ permissions: List[str] = []
14
+
15
+ class RoleCreate(RoleBase):
16
+ pass
17
+
18
+ class RoleUpdate(RoleBase):
19
+ name: Optional[str] = None
20
+ description: Optional[str] = None
21
+ permissions: Optional[List[str]] = None
22
+
23
+ class RoleInDB(RoleBase):
24
+ id: int
25
+ created_at: datetime
26
+ updated_at: Optional[datetime] = None
27
+
28
+ class Config:
29
+ from_attributes = True
30
+
31
+ class BranchBase(BaseModel):
32
+ name: str
33
+ address: str
34
+ phone: str
35
+ email: EmailStr
36
+ is_active: bool = True
37
+
38
+ class BranchCreate(BranchBase):
39
+ pass
40
+
41
+ class BranchInDB(BranchBase):
42
+ id: int
43
+ created_at: datetime
44
+ updated_at: Optional[datetime] = None
45
+
46
+ class Config:
47
+ from_attributes = True
48
+
49
+ # Update User schemas
50
+ class UserBase(BaseModel):
51
+ email: EmailStr
52
+ username: str
53
+ full_name: str
54
+ is_active: bool = True
55
+ is_superuser: bool = False
56
+ branch_id: Optional[int] = None
57
+
58
+ class UserCreate(UserBase):
59
+ password: str
60
+ role_ids: Optional[List[int]] = None # IDs of roles to assign
61
+
62
+ class UserUpdate(BaseModel):
63
+ email: Optional[EmailStr] = None
64
+ username: Optional[str] = None
65
+ full_name: Optional[str] = None
66
+ is_active: Optional[bool] = None
67
+ is_superuser: Optional[bool] = None
68
+ password: Optional[str] = None
69
+ branch_id: Optional[int] = None
70
+ role_ids: Optional[List[int]] = None
71
+
72
+ class UserInDB(UserBase):
73
+ id: int
74
+ created_at: datetime
75
+ roles: List[RoleInDB]
76
+
77
+ class Config:
78
+ from_attributes = True
79
+
80
+ class ProductBase(BaseModel):
81
+ name: str
82
+ description: str
83
+ price: float
84
+ category: str
85
+ inventory_count: int
86
+ seller_id: int
87
+ branch_id: int
88
+
89
+ class ProductCreate(ProductBase):
90
+ pass
91
+
92
+ class ProductInDB(ProductBase):
93
+ id: int
94
+ created_at: datetime
95
+ updated_at: Optional[datetime] = None
96
+
97
+ class Config:
98
+ from_attributes = True
99
+
100
+ class OrderItemBase(BaseModel):
101
+ product_id: int
102
+ quantity: int
103
+ price: float
104
+
105
+ class OrderItemCreate(OrderItemBase):
106
+ pass
107
+
108
+ class OrderItemInDB(OrderItemBase):
109
+ id: int
110
+ order_id: int
111
+
112
+ class Config:
113
+ from_attributes = True
114
+
115
+ class OrderBase(BaseModel):
116
+ customer_id: int
117
+ branch_id: int
118
+ total_amount: float
119
+ status: str = "pending"
120
+ items: List[OrderItemCreate]
121
+
122
+ class OrderCreate(OrderBase):
123
+ pass
124
+
125
+ class OrderInDB(OrderBase):
126
+ id: int
127
+ created_at: datetime
128
+ updated_at: Optional[datetime] = None
129
+ items: List[OrderItemInDB]
130
+
131
+ class Config:
132
+ from_attributes = True
133
+
134
+ class NotificationBase(BaseModel):
135
+ user_id: int
136
+ title: str
137
+ message: str
138
+ type: str
139
+ data: Optional[dict] = None
140
+ read: bool = False
141
+
142
+ class NotificationCreate(NotificationBase):
143
+ pass
144
+
145
+ class NotificationInDB(NotificationBase):
146
+ id: int
147
+ created_at: datetime
148
+
149
+ class Config:
150
+ from_attributes = True
151
+
152
+ class EventBase(BaseModel):
153
+ title: str
154
+ description: str
155
+ start_time: datetime
156
+ end_time: datetime
157
+ is_all_day: bool = False
158
+ reminder_minutes: int = 30
159
+
160
+ @validator('end_time')
161
+ def end_time_after_start_time(cls, v, values):
162
+ if 'start_time' in values and v <= values['start_time']:
163
+ raise ValueError('end_time must be after start_time')
164
+ return v
165
+
166
+ @validator('reminder_minutes')
167
+ def valid_reminder_minutes(cls, v):
168
+ if v < 0:
169
+ raise ValueError('reminder_minutes cannot be negative')
170
+ return v
171
+
172
+ class EventCreate(EventBase):
173
+ attendees: List[str] = []
174
+
175
+ class EventUpdate(BaseModel):
176
+ title: Optional[str] = None
177
+ description: Optional[str] = None
178
+ start_time: Optional[datetime] = None
179
+ end_time: Optional[datetime] = None
180
+ is_all_day: Optional[bool] = None
181
+ reminder_minutes: Optional[int] = None
182
+ attendees: Optional[List[str]] = None
183
+
184
+ @validator('reminder_minutes')
185
+ def valid_reminder_minutes(cls, v):
186
+ if v is not None and v < 0:
187
+ raise ValueError('reminder_minutes cannot be negative')
188
+ return v
189
+
190
+ class EventInDB(EventBase):
191
+ id: int
192
+ user_id: int
193
+ attendees: List[str]
194
+ status: str
195
+ attendee_responses: Dict[str, str]
196
+ created_at: datetime
197
+ updated_at: Optional[datetime] = None
198
+ reminder_sent: bool = False
199
+ is_recurring: bool = False
200
+ recurrence_group: Optional[str] = None
201
+ parent_event_id: Optional[int] = None
202
+ sequence_number: Optional[int] = None
203
+
204
+ class Config:
205
+ from_attributes = True
206
+
207
+ class RecurringEventCreate(EventCreate):
208
+ recurrence_pattern: str
209
+ recurrence_end_date: Optional[datetime] = None
210
+
211
+ @validator('recurrence_pattern')
212
+ def valid_recurrence_pattern(cls, v):
213
+ valid_patterns = ['daily', 'weekly', 'monthly', 'yearly']
214
+ if v not in valid_patterns:
215
+ raise ValueError(f'recurrence_pattern must be one of: {", ".join(valid_patterns)}')
216
+ return v
217
+
218
+ @validator('recurrence_end_date')
219
+ def end_date_after_start_time(cls, v, values):
220
+ if v is not None and 'start_time' in values and v <= values['start_time']:
221
+ raise ValueError('recurrence_end_date must be after start_time')
222
+ return v
223
+
224
+ class RecurringEventUpdate(EventUpdate):
225
+ recurrence_pattern: Optional[str] = None
226
+ recurrence_end_date: Optional[datetime] = None
227
+
228
+ @validator('recurrence_pattern')
229
+ def valid_recurrence_pattern(cls, v):
230
+ if v is not None:
231
+ valid_patterns = ['daily', 'weekly', 'monthly', 'yearly']
232
+ if v not in valid_patterns:
233
+ raise ValueError(f'recurrence_pattern must be one of: {", ".join(valid_patterns)}')
234
+ return v
235
+
236
+ @validates('email')
237
+ def validate_email(self, key, email):
238
+ if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
239
+ raise ValueError('Invalid email address')
240
+ return email
241
+
242
+ @validates('username')
243
+ def validate_username(self, key, username):
244
+ if len(username) < 3:
245
+ raise ValueError('Username must be at least 3 characters long')
246
+ return username
247
+
248
+ @validates('inventory_count')
249
+ def validate_inventory(self, key, count):
250
+ if count < 0:
251
+ raise ValueError('Inventory count cannot be negative')
252
+ return count
253
+
254
+ @validates('price')
255
+ def validate_price(self, key, price):
256
+ if price < 0:
257
+ raise ValueError('Price cannot be negative')
258
+ return price
259
+
260
+ # Event listeners for automatic timestamps
261
+ @event.listens_for(Product, 'before_insert')
262
+ def set_created_at(mapper, connection, target):
263
+ target.created_at = datetime.utcnow()
264
+ target.updated_at = datetime.utcnow()
265
+
266
+ @event.listens_for(Product, 'before_update')
267
+ def set_updated_at(mapper, connection, target):
268
+ target.updated_at = datetime.utcnow()
269
+
270
+ @event.listens_for(Order, 'before_insert')
271
+ def set_order_created_at(mapper, connection, target):
272
+ target.created_at = datetime.utcnow()
273
+ target.updated_at = datetime.utcnow()
274
+
275
+ @event.listens_for(Order, 'before_update')
276
+ def set_order_updated_at(mapper, connection, target):
277
+ target.updated_at = datetime.utcnow()
278
+
279
+ @event.listens_for(Event, 'before_insert')
280
+ def set_event_created_at(mapper, connection, target):
281
+ target.created_at = datetime.utcnow()
282
+ target.updated_at = datetime.utcnow()
283
+
284
+ @event.listens_for(Event, 'before_update')
285
+ def set_event_updated_at(mapper, connection, target):
286
+ target.updated_at = datetime.utcnow()
287
+
288
+ # Add validators to models
289
+ User.validate_email = validate_email
290
+ User.validate_username = validate_username
291
+ Product.validate_inventory = validate_inventory
292
  Product.validate_price = validate_price