# Data Model: Content Personalization API **Feature Branch**: `005-content-personalize` **Created**: 2025-12-14 **Status**: Complete ## Overview This feature uses the **existing User model** and introduces **request/response schemas** only. No new database tables are required. --- ## Existing Entity: User The User entity already contains all fields needed for personalization. **Location**: `app/models/user.py` ``` ┌─────────────────────────────────────────────┐ │ User │ ├─────────────────────────────────────────────┤ │ id: Integer (PK) │ │ username: String (nullable, unique) │ │ email: String(255) (unique, not null) │ │ hashed_password: String(60) (not null) │ │ software_level: String(20) (default: "beginner") │ │ hardware_level: String(20) (default: "none") │ │ learning_goals: Text (default: "") │ │ created_at: DateTime (auto) │ └─────────────────────────────────────────────┘ ``` ### Relevant Fields for Personalization | Field | Type | Values | Usage | |-------|------|--------|-------| | `software_level` | String(20) | "beginner", "intermediate", "advanced" | Determines code/software content complexity | | `hardware_level` | String(20) | "none", "basic", "experienced" | Determines hardware concept explanation depth | | `learning_goals` | Text | Free-form | Used to emphasize relevant topics | ### Enums (Reference) ```python class SoftwareLevel(str, Enum): beginner = "beginner" intermediate = "intermediate" advanced = "advanced" class HardwareLevel(str, Enum): none = "none" basic = "basic" experienced = "experienced" ``` --- ## New Schema: PersonalizeRequest **Purpose**: Validate incoming personalization requests **Location**: `app/schemas/personalize.py` ``` ┌─────────────────────────────────────────────┐ │ PersonalizeRequest │ ├─────────────────────────────────────────────┤ │ content: str (required, non-empty) │ │ user_id: int (required, positive) │ └─────────────────────────────────────────────┘ ``` ### Validation Rules | Field | Rule | Error | |-------|------|-------| | `content` | Non-empty after strip | "Content cannot be empty" | | `content` | Max 50,000 characters | "Content exceeds maximum length of 50000 characters" | | `user_id` | Positive integer | "User ID must be a positive integer" | --- ## New Schema: PersonalizeResponse **Purpose**: Structure the API response **Location**: `app/schemas/personalize.py` ``` ┌─────────────────────────────────────────────┐ │ PersonalizeResponse │ ├─────────────────────────────────────────────┤ │ personalized_content: str │ │ adjustments_made: str │ └─────────────────────────────────────────────┘ ``` ### Field Descriptions | Field | Description | Example | |-------|-------------|---------| | `personalized_content` | The content adapted for the user's level | Full adapted text | | `adjustments_made` | Human-readable description of changes | "Simplified technical terminology, added explanations for variables and loops, included beginner-friendly examples" | --- ## Entity Relationships ``` ┌─────────────────┐ ┌─────────────────────────┐ │ User │ ◄─────── │ PersonalizeRequest │ │ │ user_id │ │ │ id (PK) │ │ content │ │ software_level │ │ user_id │ │ hardware_level │ └─────────────────────────┘ │ learning_goals │ └─────────────────┘ │ │ Profile data used for ▼ ┌─────────────────────────────────────────────┐ │ OpenAI API Call │ │ │ │ System: Personalization rules + user profile│ │ User: Original content │ │ │ │ Returns: Adapted content + adjustments │ └─────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────┐ │ PersonalizeResponse │ │ │ │ personalized_content │ │ adjustments_made │ └─────────────────────────┘ ``` --- ## Data Flow 1. **Request** arrives with `content` and `user_id` 2. **Validation** ensures content is non-empty and within limits 3. **User lookup** retrieves profile from database by `user_id` 4. **Personalization** sends content + profile to OpenAI 5. **Response** returns adapted content and adjustment description --- ## No Database Migrations Required This feature: - Uses existing User table (no schema changes) - Does not persist personalized content (per spec - out of scope) - Only requires Pydantic schemas for request/response validation --- ## Schema Implementation ```python # app/schemas/personalize.py from pydantic import BaseModel, field_validator class PersonalizeRequest(BaseModel): content: str user_id: int @field_validator('content') @classmethod def content_not_empty(cls, v): if not v or not v.strip(): raise ValueError('Content cannot be empty') v = v.strip() if len(v) > 50000: raise ValueError('Content exceeds maximum length of 50000 characters') return v @field_validator('user_id') @classmethod def user_id_positive(cls, v): if v <= 0: raise ValueError('User ID must be a positive integer') return v class PersonalizeResponse(BaseModel): personalized_content: str adjustments_made: str ```