Spaces:
Configuration error
Configuration error
| # 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 | |
| ``` | |