|
|
""" |
|
|
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()) |
|
|
) |