feat: implement group EPUB upload and section splitting (US #29)

Backend:
- Create hermes Django app with models: ReadingGroup, GroupBook, Section, ReadingSchedule, MemberProgress
- EPUB section splitting service with automatic detection and reading time estimation
- Section recommendation engine for 4-week meeting schedule
- REST API endpoints for groups, books, sections, schedule, and member progress
- Manual section adjustment (merge/split) support

Frontend:
- GroupsPage: list/create reading groups
- GroupDetailPage: manage members, upload EPUB to group, view group books
- GroupBookPage: section breakdown with merge/split controls, reading schedule, member progress
- API client and TypeScript types for all group operations
- i18n keys for English and Spanish

Shared:
- Group-related types and API endpoint constants in packages/shared
This commit is contained in:
Marko
2026-06-20 19:30:11 +00:00
parent 26c5f6f06b
commit 1b3b709ba5
23 changed files with 2492 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from apps.groups.views import GroupBookViewSet, ReadingGroupViewSet
# ReadingGroup routes (standard ViewSet)
router = DefaultRouter()
router.register(r"", ReadingGroupViewSet, basename="group")
urlpatterns = [
path("", include(router.urls)),
# Nested GroupBook routes under a group
path(
"<int:group_pk>/books/",
GroupBookViewSet.as_view({"get": "list", "post": "create"}),
name="group-book-list",
),
path(
"<int:group_pk>/books/<int:pk>/",
GroupBookViewSet.as_view({"get": "retrieve", "delete": "destroy"}),
name="group-book-detail",
),
path(
"<int:group_pk>/books/<int:pk>/detect-sections/",
GroupBookViewSet.as_view({"post": "detect_sections_action"}),
name="group-book-detect-sections",
),
path(
"<int:group_pk>/books/<int:pk>/adjust-sections/",
GroupBookViewSet.as_view({"post": "adjust_sections"}),
name="group-book-adjust-sections",
),
path(
"<int:group_pk>/books/<int:pk>/schedule/",
GroupBookViewSet.as_view({"get": "schedule", "post": "schedule", "delete": "schedule"}),
name="group-book-schedule",
),
path(
"<int:group_pk>/books/<int:pk>/progress/",
GroupBookViewSet.as_view({"get": "progress", "patch": "progress"}),
name="group-book-progress",
),
]