| | """ |
| | SQLModel database models for Silver Table Assistant. |
| | Defines the data structure for profiles, orders, donations, and menu items. |
| | """ |
| |
|
| | from datetime import datetime |
| | from typing import Optional, Dict, Any, List |
| | from uuid import UUID, uuid4 |
| |
|
| | from sqlmodel import SQLModel, Field, Column, JSON |
| | from sqlalchemy import Column as SQLColumn, DateTime |
| | from sqlalchemy.dialects.postgresql import UUID as PostgresUUID |
| | from sqlalchemy.sql import func |
| | from enum import Enum |
| |
|
| |
|
| | class OrderStatus(str, Enum): |
| | """Order status enumeration.""" |
| | PENDING = "pending" |
| | PAID = "paid" |
| | COMPLETED = "completed" |
| | CANCELLED = "cancelled" |
| |
|
| |
|
| | class DonationStatus(str, Enum): |
| | """Donation status enumeration.""" |
| | PENDING = "pending" |
| | COMPLETED = "completed" |
| | FAILED = "failed" |
| | CANCELLED = "cancelled" |
| |
|
| |
|
| | class Profile(SQLModel, table=True): |
| | """Senior profile model for storing health and dietary information.""" |
| | |
| | id: UUID = Field( |
| | default_factory=uuid4, |
| | sa_column=SQLColumn(PostgresUUID(as_uuid=True), primary_key=True) |
| | ) |
| | user_id: UUID = Field( |
| | description="Reference to the owner user", |
| | index=True |
| | ) |
| | name: str = Field( |
| | max_length=255, |
| | description="Senior's name" |
| | ) |
| | age: int = Field( |
| | description="Senior's age", |
| | ge=0, |
| | le=150 |
| | ) |
| | gender: str = Field( |
| | max_length=20, |
| | description="Senior's gender (male/female)" |
| | ) |
| | height: float = Field( |
| | description="Senior's height in cm" |
| | ) |
| | weight: float = Field( |
| | description="Senior's weight in kg" |
| | ) |
| | |
| | chronic_diseases: List[str] = Field( |
| | default_factory=list, |
| | sa_column=SQLColumn(JSON) |
| | ) |
| | dietary_restrictions: List[str] = Field( |
| | default_factory=list, |
| | sa_column=SQLColumn(JSON) |
| | ) |
| | chewing_ability: str = Field( |
| | default="normal", |
| | max_length=50, |
| | description="normal, soft, or pureed" |
| | ) |
| | avatar_url: Optional[str] = Field( |
| | default=None, |
| | max_length=500 |
| | ) |
| | created_at: datetime = Field( |
| | default_factory=func.now, |
| | sa_column=SQLColumn(DateTime(timezone=True), server_default=func.now()) |
| | ) |
| | updated_at: datetime = Field( |
| | default_factory=datetime.utcnow, |
| | sa_column=SQLColumn(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) |
| | ) |
| |
|
| |
|
| | class Order(SQLModel, table=True): |
| | """Order model for storing food orders and payment information.""" |
| | |
| | id: Optional[UUID] = Field( |
| | default_factory=uuid4, |
| | sa_column=SQLColumn(PostgresUUID(as_uuid=True), primary_key=True) |
| | ) |
| | profile_id: UUID = Field( |
| | foreign_key="profile.id", |
| | description="Reference to the profile that made this order", |
| | index=True |
| | ) |
| | items: Dict[str, Any] = Field( |
| | sa_column=Column(JSON), |
| | description="JSON object containing order items with quantities and details" |
| | ) |
| | total_amount: int = Field( |
| | description="Total order amount in cents", |
| | ge=0 |
| | ) |
| | status: str = Field( |
| | default=OrderStatus.PENDING.value, |
| | description="Order status: pending, paid, completed, cancelled", |
| | max_length=50 |
| | ) |
| | stripe_session_id: Optional[str] = Field( |
| | default=None, |
| | description="Stripe checkout session ID for payment tracking", |
| | max_length=1000 |
| | ) |
| | created_at: datetime = Field( |
| | default_factory=func.now, |
| | sa_column=SQLColumn(DateTime(timezone=True), server_default=func.now()) |
| | ) |
| | updated_at: datetime = Field( |
| | default_factory=datetime.utcnow, |
| | sa_column=SQLColumn(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) |
| | ) |
| |
|
| |
|
| | class Donation(SQLModel, table=True): |
| | """Donation model for storing donation information and payment details.""" |
| | |
| | id: Optional[UUID] = Field( |
| | default_factory=uuid4, |
| | sa_column=SQLColumn(PostgresUUID(as_uuid=True), primary_key=True) |
| | ) |
| | user_id: Optional[UUID] = Field( |
| | default=None, |
| | description="Optional reference to authenticated user", |
| | index=True |
| | ) |
| | donor_name: Optional[str] = Field( |
| | default=None, |
| | description="Donor's name (optional for anonymous donations)", |
| | max_length=255 |
| | ) |
| | amount: int = Field( |
| | description="Donation amount in cents", |
| | ge=1 |
| | ) |
| | status: str = Field( |
| | default=DonationStatus.PENDING.value, |
| | description="Donation status: pending, completed, failed, cancelled", |
| | max_length=50 |
| | ) |
| | stripe_session_id: Optional[str] = Field( |
| | default=None, |
| | description="Stripe checkout session ID for payment tracking", |
| | max_length=1000 |
| | ) |
| | created_at: datetime = Field( |
| | default_factory=func.now, |
| | sa_column=SQLColumn(DateTime(timezone=True), server_default=func.now()) |
| | ) |
| | updated_at: datetime = Field( |
| | default_factory=datetime.utcnow, |
| | sa_column=SQLColumn(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) |
| | ) |
| |
|
| |
|
| | class MenuItem(SQLModel, table=True): |
| | """Menu item model for storing available food items and their nutritional information.""" |
| | |
| | id: Optional[int] = Field( |
| | default=None, |
| | primary_key=True, |
| | description="Menu item ID" |
| | ) |
| | name: str = Field( |
| | max_length=255, |
| | description="Name of the menu item" |
| | ) |
| | description: Optional[str] = Field( |
| | default=None, |
| | description="Detailed description of the menu item", |
| | max_length=1000 |
| | ) |
| | suitable_for: List[str] = Field( |
| | default_factory=list, |
| | sa_column=Column(JSON), |
| | description="List of suitable health conditions (e.g., ['高血壓', '牙口不好'])" |
| | ) |
| | image_url: Optional[str] = Field( |
| | default=None, |
| | description="URL to the menu item image", |
| | max_length=500 |
| | ) |
| | nutrition: Dict[str, Any] = Field( |
| | sa_column=Column(JSON), |
| | description="JSON object containing nutritional information (calories, protein, carbs, etc.)" |
| | ) |
| | price: int = Field( |
| | description="Price of the menu item in cents", |
| | ge=0 |
| | ) |
| | category: Optional[str] = Field( |
| | default=None, |
| | description="Food category (e.g., 蛋白質類, 葷食類)", |
| | max_length=100 |
| | ) |
| | available: bool = Field( |
| | default=True, |
| | description="Whether this menu item is currently available" |
| | ) |
| | created_at: datetime = Field( |
| | default_factory=func.now, |
| | sa_column=SQLColumn(DateTime(timezone=True), server_default=func.now()) |
| | ) |
| |
|
| |
|
| | |
| | class ChatConversation(SQLModel, table=True): |
| | """Chat conversation model for storing AI chat history with users.""" |
| | |
| | id: Optional[int] = Field( |
| | default=None, |
| | primary_key=True, |
| | description="Chat conversation ID" |
| | ) |
| | profile_id: Optional[UUID] = Field( |
| | default=None, |
| | foreign_key="profile.id", |
| | description="Reference to the profile", |
| | index=True |
| | ) |
| | message: str = Field( |
| | description="User's message", |
| | max_length=5000 |
| | ) |
| | response: Optional[str] = Field( |
| | default=None, |
| | description="AI assistant's response", |
| | max_length=5000 |
| | ) |
| | meta_data: Optional[Dict[str, Any]] = Field( |
| | default={}, |
| | sa_column=Column(JSON), |
| | description="Additional metadata (e.g., context, recommendations)" |
| | ) |
| | created_at: datetime = Field( |
| | default_factory=func.now, |
| | sa_column=SQLColumn(DateTime(timezone=True), server_default=func.now()) |
| | ) |