feat: add book categorisation in transcripts
This commit is contained in:
@@ -3,6 +3,7 @@ package handler
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/prabhupadasays/spsays-bff/internal/repository"
|
||||
@@ -70,6 +71,29 @@ func (h *TranscriptionHandler) GetByType(w http.ResponseWriter, r *http.Request)
|
||||
response.JSON(w, http.StatusOK, docs)
|
||||
}
|
||||
|
||||
func (h *TranscriptionHandler) GetBooks(w http.ResponseWriter, r *http.Request) {
|
||||
docs, err := h.repo.GetBooks(r.Context())
|
||||
if err != nil {
|
||||
response.Error(w, http.StatusInternalServerError, "failed to fetch books")
|
||||
return
|
||||
}
|
||||
response.JSON(w, http.StatusOK, docs)
|
||||
}
|
||||
|
||||
func (h *TranscriptionHandler) GetByBook(w http.ResponseWriter, r *http.Request) {
|
||||
code := strings.ToUpper(chi.URLParam(r, "book"))
|
||||
if h.repo.BookTitle(code) == "" {
|
||||
response.Error(w, http.StatusNotFound, "unknown book")
|
||||
return
|
||||
}
|
||||
docs, err := h.repo.GetByBook(r.Context(), code)
|
||||
if err != nil {
|
||||
response.Error(w, http.StatusInternalServerError, "failed to fetch transcriptions")
|
||||
return
|
||||
}
|
||||
response.JSON(w, http.StatusOK, docs)
|
||||
}
|
||||
|
||||
func (h *TranscriptionHandler) GetYears(w http.ResponseWriter, r *http.Request) {
|
||||
docs, err := h.repo.GetYears(r.Context())
|
||||
if err != nil {
|
||||
|
||||
@@ -180,6 +180,80 @@ func (r *TranscriptionRepo) GetPlaces(ctx context.Context) ([]bson.M, error) {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// TranscriptionBooks is the canonical set of scriptures that appear in the
|
||||
// free-text `reference` field of transcriptions (Prabhupāda's lectures are
|
||||
// commentaries on specific verses). Browsing by "book" is derived from the
|
||||
// reference string — there are no structured book/chapter columns. Ordered for
|
||||
// display fallbacks; the listing endpoints filter out books with no transcripts.
|
||||
var TranscriptionBooks = []struct{ Code, Title string }{
|
||||
{"SB", "Śrīmad-Bhāgavatam"},
|
||||
{"BG", "Bhagavad-gītā"},
|
||||
{"CC", "Caitanya-caritāmṛta"},
|
||||
{"BS", "Brahma-saṁhitā"},
|
||||
{"NOD", "The Nectar of Devotion"},
|
||||
{"ISO", "Śrī Īśopaniṣad"},
|
||||
}
|
||||
|
||||
// bookTitle returns the display title for a known book code, or "" if unknown.
|
||||
func bookTitle(code string) string {
|
||||
for _, b := range TranscriptionBooks {
|
||||
if b.Code == code {
|
||||
return b.Title
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// bookRefFilter matches published transcriptions whose reference begins with the
|
||||
// given book code. The trailing "([^A-Za-z]|$)" is a boundary so e.g. "BS" can't
|
||||
// match "BG"; no code is a prefix of another, so this is unambiguous.
|
||||
func bookRefFilter(code string) bson.M {
|
||||
return bson.M{
|
||||
"isPublished": true,
|
||||
"reference": bson.M{"$regex": "^" + code + "([^A-Za-z]|$)"},
|
||||
}
|
||||
}
|
||||
|
||||
// GetBooks returns the books that have at least one transcript, as
|
||||
// {_id, title, count} — mirroring the {_id, count} shape the frontend already
|
||||
// consumes for years/places. One count query per book (like GetTypeDistribution).
|
||||
func (r *TranscriptionRepo) GetBooks(ctx context.Context) ([]bson.M, error) {
|
||||
results := make([]bson.M, 0, len(TranscriptionBooks))
|
||||
for _, b := range TranscriptionBooks {
|
||||
count, err := r.col.CountDocuments(ctx, bookRefFilter(b.Code))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
results = append(results, bson.M{"_id": b.Code, "title": b.Title, "count": count})
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetByBook lists the transcript summaries for a book code, sorted
|
||||
// chronologically (same projection/sort as GetByType).
|
||||
func (r *TranscriptionRepo) GetByBook(ctx context.Context, code string) ([]TranscriptionSummaryDoc, error) {
|
||||
opts := options.Find().
|
||||
SetSort(bson.D{{Key: "year", Value: 1}, {Key: "month", Value: 1}, {Key: "day", Value: 1}}).
|
||||
SetProjection(transcriptionSummaryProjection)
|
||||
cursor, err := r.col.Find(ctx, bookRefFilter(code), opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
var results []TranscriptionSummaryDoc
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// BookTitle exposes the registry lookup to the handler for validation.
|
||||
func (r *TranscriptionRepo) BookTitle(code string) string {
|
||||
return bookTitle(code)
|
||||
}
|
||||
|
||||
func (r *TranscriptionRepo) GetTypeDistribution(ctx context.Context) (int64, int64, error) {
|
||||
lectureCount, err := r.col.CountDocuments(ctx, bson.M{"type": "lecture", "isPublished": true})
|
||||
if err != nil {
|
||||
|
||||
@@ -626,9 +626,11 @@ func (s *Server) setupRouter() {
|
||||
r.Get("/years", s.transcriptionH.GetYears)
|
||||
r.Get("/places", s.transcriptionH.GetPlaces)
|
||||
r.Get("/types", s.transcriptionH.GetTypeDistribution)
|
||||
r.Get("/books", s.transcriptionH.GetBooks)
|
||||
r.Get("/year/{year}", s.transcriptionH.GetByYear)
|
||||
r.Get("/place/{place}", s.transcriptionH.GetByPlace)
|
||||
r.Get("/type/{type}", s.transcriptionH.GetByType)
|
||||
r.Get("/book/{book}", s.transcriptionH.GetByBook)
|
||||
r.Get("/adjacent/{slug}", s.transcriptionH.GetAdjacent)
|
||||
r.Get("/{slug}", s.transcriptionH.GetBySlug)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user