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)
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
- Request arrives with
contentanduser_id - Validation ensures content is non-empty and within limits
- User lookup retrieves profile from database by
user_id - Personalization sends content + profile to OpenAI
- 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
# 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