|
7 | 7 | from app.api.analytics import summary_router |
8 | 8 | from app.api.users import router as user_router |
9 | 9 | from app.database import get_session |
10 | | -from app.models import CodingProblem, Event, UserCodingProblem |
| 10 | +from app.models import ( |
| 11 | + CodingProblem, |
| 12 | + Event, |
| 13 | + Flashcard, |
| 14 | + Lesson, |
| 15 | + Quiz, |
| 16 | + UserCodingProblem, |
| 17 | + UserFlashcard, |
| 18 | + UserQuizAttempt, |
| 19 | +) |
11 | 20 |
|
12 | 21 |
|
13 | 22 | @pytest.fixture(name="app") |
@@ -235,6 +244,96 @@ def test_summary_with_problem_metrics(client, session, create_user, get_token): |
235 | 244 | assert data["category_problem_submissions"]["data-structures"] == 3 |
236 | 245 |
|
237 | 246 |
|
| 247 | +def test_per_category_breakdowns(client, session, create_user, get_token): |
| 248 | + """quiz_completions_by_category, anon_lesson_views_by_category, and |
| 249 | + flashcard_reviews_by_category are all present and correctly counted.""" |
| 250 | + admin = create_user(is_admin=True) |
| 251 | + token = get_token(client, "user", "password") |
| 252 | + |
| 253 | + # Seed two categories |
| 254 | + lesson_a = Lesson( |
| 255 | + title="Lesson A", |
| 256 | + slug="lesson-a", |
| 257 | + category="cat-a", |
| 258 | + content="Content", |
| 259 | + summary="Summary", |
| 260 | + reading_time_minutes=1, |
| 261 | + order=0, |
| 262 | + ) |
| 263 | + lesson_b = Lesson( |
| 264 | + title="Lesson B", |
| 265 | + slug="lesson-b", |
| 266 | + category="cat-b", |
| 267 | + content="Content", |
| 268 | + summary="Summary", |
| 269 | + reading_time_minutes=1, |
| 270 | + order=0, |
| 271 | + ) |
| 272 | + session.add(lesson_a) |
| 273 | + session.add(lesson_b) |
| 274 | + session.commit() |
| 275 | + session.refresh(lesson_a) |
| 276 | + session.refresh(lesson_b) |
| 277 | + |
| 278 | + quiz_a = Quiz(title="Quiz A", slug="quiz-a", category="cat-a", lesson_slug="lesson-a") |
| 279 | + quiz_b = Quiz(title="Quiz B", slug="quiz-b", category="cat-b", lesson_slug="lesson-b") |
| 280 | + session.add(quiz_a) |
| 281 | + session.add(quiz_b) |
| 282 | + session.commit() |
| 283 | + session.refresh(quiz_a) |
| 284 | + session.refresh(quiz_b) |
| 285 | + |
| 286 | + # 2 quiz completions for cat-a, 1 for cat-b |
| 287 | + session.add(UserQuizAttempt(user_id=admin.id, quiz_id=quiz_a.id, score=4, total=5)) |
| 288 | + session.commit() |
| 289 | + second_user = create_user(username="user2") |
| 290 | + session.add(UserQuizAttempt(user_id=second_user.id, quiz_id=quiz_a.id, score=3, total=5)) |
| 291 | + session.add(UserQuizAttempt(user_id=second_user.id, quiz_id=quiz_b.id, score=5, total=5)) |
| 292 | + session.commit() |
| 293 | + |
| 294 | + # Anon lesson views: 3 for cat-a, 1 for cat-b |
| 295 | + for _ in range(3): |
| 296 | + session.add(Event( |
| 297 | + session_id="anon-s", |
| 298 | + user_id=None, |
| 299 | + event_type="lesson_view", |
| 300 | + payload={"category": "cat-a", "slug": "lesson-a"}, |
| 301 | + )) |
| 302 | + session.add(Event( |
| 303 | + session_id="anon-s2", |
| 304 | + user_id=None, |
| 305 | + event_type="lesson_view", |
| 306 | + payload={"category": "cat-b", "slug": "lesson-b"}, |
| 307 | + )) |
| 308 | + session.commit() |
| 309 | + |
| 310 | + # Flashcard reviews: seed a card in cat-a with repetitions=5, cat-b with repetitions=2 |
| 311 | + card_a = Flashcard(title="Card A", front="Q", back="A", category="cat-a", tags=[]) |
| 312 | + card_b = Flashcard(title="Card B", front="Q", back="A", category="cat-b", tags=[]) |
| 313 | + session.add(card_a) |
| 314 | + session.add(card_b) |
| 315 | + session.commit() |
| 316 | + session.refresh(card_a) |
| 317 | + session.refresh(card_b) |
| 318 | + |
| 319 | + session.add(UserFlashcard(user_id=admin.id, flashcard_id=card_a.id, repetitions=5)) |
| 320 | + session.add(UserFlashcard(user_id=admin.id, flashcard_id=card_b.id, repetitions=2)) |
| 321 | + session.commit() |
| 322 | + |
| 323 | + resp = client.get("/analytics/summary", headers={"Authorization": f"Bearer {token}"}) |
| 324 | + assert resp.status_code == 200 |
| 325 | + data = resp.json() |
| 326 | + |
| 327 | + assert data["quiz_completions_by_category"]["cat-a"] == 2 |
| 328 | + assert data["quiz_completions_by_category"]["cat-b"] == 1 |
| 329 | + |
| 330 | + assert data["anon_lesson_views_by_category"]["cat-a"] == 3 |
| 331 | + assert data["anon_lesson_views_by_category"]["cat-b"] == 1 |
| 332 | + |
| 333 | + assert data["flashcard_reviews_by_category"]["cat-a"] == 5 |
| 334 | + assert data["flashcard_reviews_by_category"]["cat-b"] == 2 |
| 335 | + |
| 336 | + |
238 | 337 | def test_users_me_returns_is_admin_false(client, session, create_user, get_token): |
239 | 338 | create_user(is_admin=False) |
240 | 339 | token = get_token(client, "user", "password") |
|
0 commit comments