File size: 7,784 Bytes
f871fed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
"""
Study Plan domain models.
AI-generated personalized study schedules based on content and deadlines.
"""

from datetime import datetime
from enum import Enum
from typing import Optional, List
from pydantic import BaseModel, Field


class PlanStatus(str, Enum):
    """Study plan status."""
    ACTIVE = "active"
    COMPLETED = "completed"
    PAUSED = "paused"
    CANCELLED = "cancelled"


class TopicDifficulty(str, Enum):
    """Topic difficulty level."""
    EASY = "easy"
    MEDIUM = "medium"
    HARD = "hard"


class TopicStatus(str, Enum):
    """Study topic status."""
    NOT_STARTED = "not_started"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    SKIPPED = "skipped"


class SessionType(str, Enum):
    """Study session type."""
    LEARN = "learn"
    REVIEW = "review"
    PRACTICE = "practice"
    QUIZ = "quiz"


class SessionStatus(str, Enum):
    """Study session status."""
    SCHEDULED = "scheduled"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    SKIPPED = "skipped"
    RESCHEDULED = "rescheduled"


class AdjustmentType(str, Enum):
    """Plan adjustment type."""
    RESCHEDULE = "reschedule"
    ADD_REVIEW = "add_review"
    EXTEND_DEADLINE = "extend_deadline"
    INCREASE_HOURS = "increase_hours"
    REDUCE_SCOPE = "reduce_scope"


class AdjustmentStatus(str, Enum):
    """Plan adjustment status."""
    PENDING = "pending"
    ACCEPTED = "accepted"
    REJECTED = "rejected"


# Request/Response models

class StudyPlanCreate(BaseModel):
    """Create a new study plan."""
    notebook_id: str
    title: str
    description: Optional[str] = None
    deadline: datetime
    available_hours_per_day: float = Field(default=2.0, ge=0.5, le=12.0)


class StudyPlanUpdate(BaseModel):
    """Update study plan."""
    title: Optional[str] = None
    description: Optional[str] = None
    deadline: Optional[datetime] = None
    available_hours_per_day: Optional[float] = Field(default=None, ge=0.5, le=12.0)
    status: Optional[PlanStatus] = None


class StudyPlan(BaseModel):
    """Study plan model."""
    id: str
    notebook_id: str
    title: str
    description: Optional[str] = None
    deadline: datetime
    available_hours_per_day: float = 2.0
    total_study_hours: float = 0.0
    status: PlanStatus = PlanStatus.ACTIVE
    progress_percentage: float = 0.0
    created_at: datetime
    updated_at: datetime


class StudyTopicCreate(BaseModel):
    """Create a study topic."""
    plan_id: str
    name: str
    description: Optional[str] = None
    difficulty: TopicDifficulty = TopicDifficulty.MEDIUM
    estimated_hours: float = Field(ge=0.25)
    priority: int = Field(default=5, ge=1, le=10)
    source_ids: List[str] = Field(default_factory=list)
    prerequisites: List[str] = Field(default_factory=list)


class StudyTopicUpdate(BaseModel):
    """Update study topic."""
    name: Optional[str] = None
    description: Optional[str] = None
    difficulty: Optional[TopicDifficulty] = None
    estimated_hours: Optional[float] = Field(default=None, ge=0.25)
    priority: Optional[int] = Field(default=None, ge=1, le=10)
    status: Optional[TopicStatus] = None
    mastery_level: Optional[float] = Field(default=None, ge=0.0, le=100.0)


class StudyTopic(BaseModel):
    """Study topic model."""
    id: str
    plan_id: str
    name: str
    description: Optional[str] = None
    difficulty: TopicDifficulty = TopicDifficulty.MEDIUM
    estimated_hours: float
    priority: int = 5
    source_ids: List[str] = Field(default_factory=list)
    prerequisites: List[str] = Field(default_factory=list)
    status: TopicStatus = TopicStatus.NOT_STARTED
    mastery_level: float = 0.0
    created_at: datetime


class StudySessionCreate(BaseModel):
    """Create a study session."""
    plan_id: str
    topic_id: str
    scheduled_date: datetime
    scheduled_duration_minutes: int = Field(ge=15, le=480)
    session_type: SessionType = SessionType.LEARN


class StudySessionUpdate(BaseModel):
    """Update study session."""
    scheduled_date: Optional[datetime] = None
    scheduled_duration_minutes: Optional[int] = Field(default=None, ge=15, le=480)
    session_type: Optional[SessionType] = None
    actual_start: Optional[datetime] = None
    actual_end: Optional[datetime] = None
    status: Optional[SessionStatus] = None
    notes: Optional[str] = None
    rating: Optional[int] = Field(default=None, ge=1, le=5)


class StudySession(BaseModel):
    """Study session model."""
    id: str
    plan_id: str
    topic_id: str
    scheduled_date: datetime
    scheduled_duration_minutes: int
    actual_start: Optional[datetime] = None
    actual_end: Optional[datetime] = None
    session_type: SessionType = SessionType.LEARN
    status: SessionStatus = SessionStatus.SCHEDULED
    notes: Optional[str] = None
    rating: Optional[int] = None
    created_at: datetime
    # Populated when joined
    topic_name: Optional[str] = None


class PlanAdjustment(BaseModel):
    """Plan adjustment suggestion."""
    id: str
    plan_id: str
    adjustment_type: AdjustmentType
    reason: str
    original_value: Optional[str] = None
    suggested_value: Optional[str] = None
    status: AdjustmentStatus = AdjustmentStatus.PENDING
    created_at: datetime


class PlanAdjustmentResponse(BaseModel):
    """Response to a plan adjustment."""
    adjustment_id: str
    accepted: bool


# Response models with nested data

class StudyPlanWithTopics(StudyPlan):
    """Study plan with topics."""
    topics: List[StudyTopic] = Field(default_factory=list)


class StudyPlanFull(StudyPlanWithTopics):
    """Complete study plan with all data."""
    sessions: List[StudySession] = Field(default_factory=list)
    adjustments: List[PlanAdjustment] = Field(default_factory=list)
    days_remaining: int = 0
    completed_hours: float = 0.0
    upcoming_sessions: List[StudySession] = Field(default_factory=list)


class DailySchedule(BaseModel):
    """Schedule for a single day."""
    date: datetime
    sessions: List[StudySession] = Field(default_factory=list)
    total_hours: float = 0.0
    is_today: bool = False


class WeeklySchedule(BaseModel):
    """Weekly schedule view."""
    plan_id: str
    week_start: datetime
    week_end: datetime
    days: List[DailySchedule] = Field(default_factory=list)
    total_planned_hours: float = 0.0
    total_completed_hours: float = 0.0


class StudyPlanStats(BaseModel):
    """Statistics for study plan."""
    plan_id: str
    total_topics: int = 0
    completed_topics: int = 0
    total_sessions: int = 0
    completed_sessions: int = 0
    total_planned_hours: float = 0.0
    total_completed_hours: float = 0.0
    average_rating: Optional[float] = None
    on_track: bool = True
    days_remaining: int = 0
    hours_per_day_needed: float = 0.0


class TopicAnalysis(BaseModel):
    """AI-extracted topic from sources."""
    name: str
    description: str
    difficulty: TopicDifficulty
    estimated_hours: float
    priority: int
    source_ids: List[str]
    prerequisites: List[str] = Field(default_factory=list)
    key_concepts: List[str] = Field(default_factory=list)


class PlanGenerationRequest(BaseModel):
    """Request to generate a study plan."""
    notebook_id: str
    title: str
    description: Optional[str] = None
    deadline: datetime
    available_hours_per_day: float = Field(default=2.0, ge=0.5, le=12.0)
    include_reviews: bool = True
    include_practice: bool = True
    focus_areas: Optional[List[str]] = None  # Optional list of topics to prioritize


class PlanGenerationResult(BaseModel):
    """Result of plan generation."""
    plan: StudyPlan
    topics: List[StudyTopic]
    sessions: List[StudySession]
    total_hours: float
    days_with_sessions: int
    warnings: List[str] = Field(default_factory=list)