feat(backend): consolidate queue logic for scheduling
This commit is contained in:
@@ -1,6 +1,22 @@
|
||||
# database/models/__init__.py
|
||||
# backend/database/models/__init__.py
|
||||
from .classification import Classification
|
||||
from .member import Member
|
||||
from .service_type import ServiceType
|
||||
from .servicetype import ServiceType
|
||||
from .service import Service
|
||||
from .service_availability import ServiceAvailability
|
||||
from .serviceavailability import ServiceAvailability
|
||||
from .schedule import Schedule
|
||||
from .acceptedlog import AcceptedLog
|
||||
from .declinelog import DeclineLog
|
||||
from .scheduledlog import ScheduledLog
|
||||
|
||||
__all__ = [
|
||||
"Classification",
|
||||
"Member",
|
||||
"ServiceType",
|
||||
"Service",
|
||||
"ServiceAvailability",
|
||||
"Schedule",
|
||||
"AcceptedLog",
|
||||
"DeclineLog",
|
||||
"ScheduledLog",
|
||||
]
|
||||
|
||||
49
backend/database/models/_base.py
Normal file
49
backend/database/models/_base.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass, asdict, fields
|
||||
from datetime import date, datetime
|
||||
from typing import Any, Dict, Tuple, Type, TypeVar, Union
|
||||
|
||||
Row = Tuple[Any, ...] | Dict[str, Any] # what sqlite3.Row returns
|
||||
|
||||
T = TypeVar("T", bound="BaseModel")
|
||||
|
||||
|
||||
@dataclass()
|
||||
class BaseModel:
|
||||
"""A tiny helper that gives every model a common interface."""
|
||||
|
||||
@classmethod
|
||||
def from_row(cls: Type[T], row: Row) -> T:
|
||||
"""
|
||||
Build a model instance from a sqlite3.Row (or a dict‑like object).
|
||||
Column names are matched to the dataclass field names.
|
||||
"""
|
||||
if isinstance(row, dict):
|
||||
data = row
|
||||
else: # sqlite3.Row behaves like a mapping, but we guard for safety
|
||||
data = dict(row)
|
||||
|
||||
# Convert raw strings to proper Python types where we know the annotation
|
||||
converted: Dict[str, Any] = {}
|
||||
for f in fields(cls):
|
||||
value = data.get(f.name)
|
||||
if value is None:
|
||||
converted[f.name] = None
|
||||
continue
|
||||
|
||||
# datetime/date handling – sqlite returns str in ISO format
|
||||
if f.type is datetime:
|
||||
converted[f.name] = datetime.fromisoformat(value)
|
||||
elif f.type is date:
|
||||
converted[f.name] = date.fromisoformat(value)
|
||||
else:
|
||||
converted[f.name] = value
|
||||
return cls(**converted) # type: ignore[arg-type]
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Return a plain dict (useful for INSERT/UPDATE statements)."""
|
||||
return asdict(self)
|
||||
|
||||
def __repr__(self) -> str: # a nicer representation when printing
|
||||
field_vals = ", ".join(f"{f.name}={getattr(self, f.name)!r}" for f in fields(self))
|
||||
return f"{self.__class__.__name__}({field_vals})"
|
||||
11
backend/database/models/acceptedlog.py
Normal file
11
backend/database/models/acceptedlog.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from ._base import BaseModel
|
||||
|
||||
|
||||
@dataclass()
|
||||
class AcceptedLog(BaseModel):
|
||||
LogId: int
|
||||
MemberId: int
|
||||
ServiceId: int
|
||||
AcceptedAt: datetime
|
||||
@@ -1,14 +1,8 @@
|
||||
from ..connection import DatabaseConnection
|
||||
from dataclasses import dataclass
|
||||
from ._base import BaseModel
|
||||
|
||||
class Classification:
|
||||
def __init__(self, classification_name: str):
|
||||
self.classification_name = classification_name
|
||||
|
||||
def save(self, db: DatabaseConnection):
|
||||
query = "INSERT INTO Classifications (ClassificationName) VALUES (?)"
|
||||
db.execute_query(query, (self.classification_name,))
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, db: DatabaseConnection):
|
||||
query = "SELECT * FROM Classifications"
|
||||
return db.execute_query_with_return(query)
|
||||
@dataclass()
|
||||
class Classification(BaseModel):
|
||||
ClassificationId: int
|
||||
ClassificationName: str
|
||||
|
||||
14
backend/database/models/declinelog.py
Normal file
14
backend/database/models/declinelog.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, date
|
||||
from typing import Optional
|
||||
from ._base import BaseModel
|
||||
|
||||
|
||||
@dataclass()
|
||||
class DeclineLog(BaseModel):
|
||||
DeclineId: int
|
||||
MemberId: int
|
||||
ServiceId: int
|
||||
DeclinedAt: datetime
|
||||
DeclineDate: date # the service day that was declined
|
||||
Reason: Optional[str] = None
|
||||
@@ -1,19 +1,20 @@
|
||||
from ..connection import DatabaseConnection
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, date
|
||||
from typing import Optional
|
||||
from ._base import BaseModel
|
||||
|
||||
class Member:
|
||||
def __init__(self, first_name: str, last_name: str, email: str, phone_number: str, classification_id: int, notes: str = None):
|
||||
self.first_name = first_name
|
||||
self.last_name = last_name
|
||||
self.email = email
|
||||
self.phone_number = phone_number
|
||||
self.classification_id = classification_id
|
||||
self.notes = notes
|
||||
|
||||
def save(self, db: DatabaseConnection):
|
||||
query = "INSERT INTO Members (FirstName, LastName, Email, PhoneNumber, ClassificationId, Notes) VALUES (?, ?, ?, ?, ?, ?)"
|
||||
db.execute_query(query, (self.first_name, self.last_name, self.email, self.phone_number, self.classification_id, self.notes))
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, db: DatabaseConnection):
|
||||
query = "SELECT * FROM Members"
|
||||
return db.execute_query_with_return(query)
|
||||
@dataclass
|
||||
class Member(BaseModel):
|
||||
MemberId: int
|
||||
FirstName: str
|
||||
LastName: str
|
||||
Email: Optional[str] = None
|
||||
PhoneNumber: Optional[str] = None
|
||||
ClassificationId: Optional[int] = None
|
||||
Notes: Optional[str] = None
|
||||
IsActive: int = 1
|
||||
LastScheduledAt: Optional[datetime] = None
|
||||
LastAcceptedAt: Optional[datetime] = None
|
||||
LastDeclinedAt: Optional[datetime] = None
|
||||
DeclineStreak: int = 0
|
||||
|
||||
17
backend/database/models/schedule.py
Normal file
17
backend/database/models/schedule.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from ._base import BaseModel
|
||||
|
||||
|
||||
@dataclass
|
||||
class Schedule(BaseModel):
|
||||
ScheduleId: int
|
||||
ServiceId: int
|
||||
MemberId: int
|
||||
Status: str # 'pending' | 'accepted' | 'declined'
|
||||
ScheduledAt: datetime # renamed from OfferedAt
|
||||
AcceptedAt: Optional[datetime] = None
|
||||
DeclinedAt: Optional[datetime] = None
|
||||
ExpiresAt: Optional[datetime] = None
|
||||
DeclineReason: Optional[str] = None
|
||||
12
backend/database/models/scheduledlog.py
Normal file
12
backend/database/models/scheduledlog.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from ._base import BaseModel
|
||||
|
||||
|
||||
@dataclass()
|
||||
class ScheduledLog(BaseModel):
|
||||
LogId: int
|
||||
MemberId: int
|
||||
ServiceId: int
|
||||
ScheduledAt: datetime
|
||||
ExpiresAt: datetime
|
||||
@@ -1,15 +1,10 @@
|
||||
from ..connection import DatabaseConnection
|
||||
from dataclasses import dataclass
|
||||
from datetime import date
|
||||
from ._base import BaseModel
|
||||
|
||||
class Service:
|
||||
def __init__(self, service_type_id: int, service_date: str):
|
||||
self.service_type_id = service_type_id
|
||||
self.service_date = service_date
|
||||
|
||||
def save(self, db: DatabaseConnection):
|
||||
query = "INSERT INTO Services (ServiceTypeId, ServiceDate) VALUES (?, ?)"
|
||||
db.execute_query(query, (self.service_type_id, self.service_date))
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, db: DatabaseConnection):
|
||||
query = "SELECT * FROM Services"
|
||||
return db.execute_query_with_return(query)
|
||||
@dataclass()
|
||||
class Service(BaseModel):
|
||||
ServiceId: int
|
||||
ServiceTypeId: int
|
||||
ServiceDate: date
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
from ..connection import DatabaseConnection
|
||||
|
||||
class ServiceAvailability:
|
||||
def __init__(self, member_id: int, service_type_id: int):
|
||||
self.member_id = member_id
|
||||
self.service_type_id = service_type_id
|
||||
|
||||
def save(self, db: DatabaseConnection):
|
||||
query = "INSERT INTO ServiceAvailability (MemberId, ServiceTypeId) VALUES (?, ?)"
|
||||
db.execute_query(query, (self.member_id, self.service_type_id))
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, db: DatabaseConnection):
|
||||
query = "SELECT * FROM ServiceAvailability"
|
||||
return db.execute_query_with_return(query)
|
||||
@@ -1,14 +0,0 @@
|
||||
from ..connection import DatabaseConnection
|
||||
|
||||
class ServiceType:
|
||||
def __init__(self, type_name: str):
|
||||
self.type_name = type_name
|
||||
|
||||
def save(self, db: DatabaseConnection):
|
||||
query = "INSERT INTO ServiceTypes (TypeName) VALUES (?)"
|
||||
db.execute_query(query, (self.type_name,))
|
||||
|
||||
@classmethod
|
||||
def get_all(cls, db: DatabaseConnection):
|
||||
query = "SELECT * FROM ServiceTypes"
|
||||
return db.execute_query_with_return(query)
|
||||
9
backend/database/models/serviceavailability.py
Normal file
9
backend/database/models/serviceavailability.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
from ._base import BaseModel
|
||||
|
||||
|
||||
@dataclass()
|
||||
class ServiceAvailability(BaseModel):
|
||||
ServiceAvailabilityId: int
|
||||
MemberId: int
|
||||
ServiceTypeId: int
|
||||
8
backend/database/models/servicetype.py
Normal file
8
backend/database/models/servicetype.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from dataclasses import dataclass
|
||||
from ._base import BaseModel
|
||||
|
||||
|
||||
@dataclass()
|
||||
class ServiceType(BaseModel):
|
||||
ServiceTypeId: int
|
||||
TypeName: str
|
||||
Reference in New Issue
Block a user