Implement: US #33 Reading Pace Notifications #38

Closed
marko wants to merge 1 commits from feature/us33-reading-pace-notifications into main
Owner

Summary

Implements reading pace notifications (US #33) — "You should be on Section X by Friday" feature.

Backend

  • New groups app: ReadingGroup, GroupMeeting, GroupMembership models
  • NotificationPreference: Per-user settings (enable/disable, frequency daily/weekly, reminders)
  • PaceNotification: Track sent/dismissed notifications to avoid duplicates
  • Pace calculation service: Compares current reading progress vs meeting target sections
  • Ahead/behind/on_track/completed status determination
  • API endpoints:
    • GET /api/groups/notifications/pace/ — pace status for upcoming meetings
    • GET/PATCH /api/groups/notifications/preferences/ — notification preferences CRUD
    • POST /api/groups/notifications/dismiss/ — dismiss a notification
    • GET /api/groups/notifications/reminders/ — pre-meeting reminders (24h)
    • GET /api/groups/groups/ — user reading groups
    • GET /api/groups/meetings/upcoming/ — upcoming meetings

Frontend

  • PaceNotification component: In-app alert showing behind/ahead/on_track status with dismiss button
  • ReadingPaceBanner: Persistent sticky banner in reader view when near target section
  • Notification settings: Integrated into Settings page (enable/disable, reminders toggle, frequency)
  • groupsApi module: API client for all notification/group endpoints

Acceptance Criteria

  • Notification showing target section when upcoming meeting within 3 days
  • Behind status with catch-up section count
  • Ahead/on_track congratulatory messages
  • Persistent banner when approaching target section
  • Status updates when target completed
  • Pre-meeting reminder endpoint (24h before)
  • Notification settings toggle with frequency options
## Summary Implements reading pace notifications (US #33) — "You should be on Section X by Friday" feature. ### Backend - **New `groups` app**: ReadingGroup, GroupMeeting, GroupMembership models - **NotificationPreference**: Per-user settings (enable/disable, frequency daily/weekly, reminders) - **PaceNotification**: Track sent/dismissed notifications to avoid duplicates - **Pace calculation service**: Compares current reading progress vs meeting target sections - **Ahead/behind/on_track/completed** status determination - **API endpoints**: - `GET /api/groups/notifications/pace/` — pace status for upcoming meetings - `GET/PATCH /api/groups/notifications/preferences/` — notification preferences CRUD - `POST /api/groups/notifications/dismiss/` — dismiss a notification - `GET /api/groups/notifications/reminders/` — pre-meeting reminders (24h) - `GET /api/groups/groups/` — user reading groups - `GET /api/groups/meetings/upcoming/` — upcoming meetings ### Frontend - **PaceNotification component**: In-app alert showing behind/ahead/on_track status with dismiss button - **ReadingPaceBanner**: Persistent sticky banner in reader view when near target section - **Notification settings**: Integrated into Settings page (enable/disable, reminders toggle, frequency) - **groupsApi module**: API client for all notification/group endpoints ### Acceptance Criteria - ✅ Notification showing target section when upcoming meeting within 3 days - ✅ Behind status with catch-up section count - ✅ Ahead/on_track congratulatory messages - ✅ Persistent banner when approaching target section - ✅ Status updates when target completed - ✅ Pre-meeting reminder endpoint (24h before) - ✅ Notification settings toggle with frequency options
marko added 1 commit 2026-06-20 19:35:00 +00:00
Backend:
- New hermes app with ReadingGroup, GroupMeeting, GroupMembership models
- NotificationPreference (per-user: enable/disable, frequency daily/weekly)
- PaceNotification model for tracking sent/dismissed notifications
- Pace calculation service comparing current progress vs meeting targets
- API endpoints: pace status, notification preferences CRUD, reminders, dismiss
- Ahead/behind/on_track/completed status determination

Frontend:
- PaceNotification component for in-app alerts (behind/ahead/on_track statuses)
- ReadingPaceBanner — persistent banner in reader view
- Notification settings in Settings page (enable/disable, frequency, reminders)
- API client module (groupsApi) for all notification endpoints
Owner

Reid's Review — PR #38

Verdict: 🔴 Changes Required


🔴 Blocking Issues

These MUST be resolved before this PR can be merged.

  • [File: frontend/src/components/notifications/PaceNotification.tsx | Line ~14] inline styles
    The component uses inline styles extensively (style prop on multiple elements). Per project conventions, inline styles must use CSS modules, styled-components, or a consistent styling solution. This entire component is built with inline styles and needs to be refactored.

  • [File: frontend/src/components/notifications/ReadingPaceBanner.tsx | Line ~6] inline styles
    Same issue - all styling is inline. This component must be refactored to use a proper styling solution.

  • [File: backend/apps/groups/views.py | Line ~180] hacky import pattern
    Uses __import__("django").utils.timezone.now() which is an improper way to handle imports. This should be from django.utils import timezone at module level. Also, the import inside upcoming() method (line ~132) should be moved to the top of the file.

  • [File: backend/apps/groups/views.py | Line ~160-162] N+1 query risk
    In reminder_view, the fallback queries EBook.objects.filter(reading_groups=mtg.group)... inside a loop over meetings. This causes N+1 queries. Should prefetch ebooks in the initial queryset.


🟡 Suggestions (Non-Blocking)

These are recommendations for improvement. Not required for merge.

  • [File: frontend/src/components/notifications/ReadingPaceBanner.tsx] The currentSection is in the useEffect dependency array but the API call does not use it - remove from dependencies to avoid unnecessary re-fetches.

  • [File: backend/apps/groups/services/pace.py] The PaceStatus TypedDict return type annotation could use more specific typing for the status field.


📋 AC Coverage

Based on linked issue #33

  • Notification preference settings per user (NotificationPreference model & views)
  • Pace calculation (services/pace.py with calculate_pace)
  • Notification trigger logic (3 days before meeting in views.py upcoming action)
  • In-app notification UI component - uses inline styles (blocking issue)
  • Ahead/behind status calculation - implemented in PaceStatus TypedDict
  • Persistent reading banner in reader view - ReadingPaceBanner.tsx exists but needs style refactor
  • Pre-meeting reminder (24h before) - reminder_view implemented
  • Notification frequency settings - frequency field in NotificationPreference model
## Reid's Review — PR #38 **Verdict:** 🔴 Changes Required --- ### 🔴 Blocking Issues > These MUST be resolved before this PR can be merged. - **[File: frontend/src/components/notifications/PaceNotification.tsx | Line ~14]** `inline styles` The component uses inline styles extensively (style prop on multiple elements). Per project conventions, inline styles must use CSS modules, styled-components, or a consistent styling solution. This entire component is built with inline styles and needs to be refactored. - **[File: frontend/src/components/notifications/ReadingPaceBanner.tsx | Line ~6]** `inline styles` Same issue - all styling is inline. This component must be refactored to use a proper styling solution. - **[File: backend/apps/groups/views.py | Line ~180]** `hacky import pattern` Uses `__import__("django").utils.timezone.now()` which is an improper way to handle imports. This should be `from django.utils import timezone` at module level. Also, the import inside `upcoming()` method (line ~132) should be moved to the top of the file. - **[File: backend/apps/groups/views.py | Line ~160-162]** `N+1 query risk` In `reminder_view`, the fallback queries `EBook.objects.filter(reading_groups=mtg.group)...` inside a loop over meetings. This causes N+1 queries. Should prefetch ebooks in the initial queryset. --- ### 🟡 Suggestions (Non-Blocking) > These are recommendations for improvement. Not required for merge. - **[File: frontend/src/components/notifications/ReadingPaceBanner.tsx]** The `currentSection` is in the useEffect dependency array but the API call does not use it - remove from dependencies to avoid unnecessary re-fetches. - **[File: backend/apps/groups/services/pace.py]** The PaceStatus TypedDict return type annotation could use more specific typing for the `status` field. --- ### 📋 AC Coverage > Based on linked issue #33 - [x] Notification preference settings per user (NotificationPreference model & views) - [x] Pace calculation (services/pace.py with calculate_pace) - [x] Notification trigger logic (3 days before meeting in views.py upcoming action) - [ ] In-app notification UI component - uses inline styles (blocking issue) - [ ] Ahead/behind status calculation - implemented in PaceStatus TypedDict - [ ] Persistent reading banner in reader view - ReadingPaceBanner.tsx exists but needs style refactor - [x] Pre-meeting reminder (24h before) - reminder_view implemented - [x] Notification frequency settings - frequency field in NotificationPreference model
max closed this pull request 2026-07-21 22:15:56 +00:00
This repo is archived. You cannot comment on pull requests.
No Reviewers
No labels
2 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: HermesFactory/cloud-reader#38