diff --git a/cmd/seed-bg-intro/main.go b/cmd/seed-book-intro/main.go similarity index 56% rename from cmd/seed-bg-intro/main.go rename to cmd/seed-book-intro/main.go index b9e14f6..a704528 100644 --- a/cmd/seed-bg-intro/main.go +++ b/cmd/seed-book-intro/main.go @@ -1,15 +1,16 @@ -// Command seed-bg-intro seeds the Bhagavad-gītā front-matter (Dedication, -// Foreword, Preface, Introduction, The Disciplic Succession) into the -// `prose_chapters` MongoDB collection as intro-segments (book="bg", volume=0). +// Command seed-book-intro seeds a book's front-matter (intro-segments such as +// Dedication, Front Flap, Preface, Introduction, Disciplic Succession) into the +// `prose_chapters` MongoDB collection (volume=0, type="intro-segment"). // -// The segment JSON is produced by extractor/scripts/bg_intro_extract.py. +// The segment JSON is produced by the per-book extractor scripts, e.g. +// extractor/scripts/bg_intro_extract.py, cc_intro_extract.py. // // Usage (from the spsays-bff directory, so .env is picked up): // -// go run ./cmd/seed-bg-intro -// go run ./cmd/seed-bg-intro --dir ../extractor/jsons/books/bg/intro +// go run ./cmd/seed-book-intro --book cc +// go run ./cmd/seed-book-intro --book bg --dir ../extractor/jsons/books/bg/intro // -// It is idempotent: existing bg intro-segments are removed before insert. +// It is idempotent: a book's existing intro-segments are removed before insert. package main import ( @@ -39,10 +40,20 @@ type introSegment struct { } func main() { - dir := flag.String("dir", "../extractor/jsons/books/bg/intro", - "directory containing bg intro-segment JSON files") + book := flag.String("book", "", "book code (e.g. bg, cc) — required") + dir := flag.String("dir", "", "directory of intro-segment JSON files "+ + "(default: ../extractor/jsons/books//intro)") flag.Parse() + if *book == "" { + fmt.Fprintln(os.Stderr, "--book is required") + os.Exit(1) + } + segDir := *dir + if segDir == "" { + segDir = filepath.Join("..", "extractor", "jsons", "books", *book, "intro") + } + cfg, err := config.Load() if err != nil { fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err) @@ -59,23 +70,22 @@ func main() { } defer mongoDB.Close() - entries, err := os.ReadDir(*dir) + entries, err := os.ReadDir(segDir) if err != nil { - sugar.Fatalf("failed to read dir %s: %v", *dir, err) + sugar.Fatalf("failed to read dir %s: %v", segDir, err) } - var segments []introSegment var files []string for _, e := range entries { - if e.IsDir() || filepath.Ext(e.Name()) != ".json" { - continue + if !e.IsDir() && filepath.Ext(e.Name()) == ".json" { + files = append(files, e.Name()) } - files = append(files, e.Name()) } sort.Strings(files) + var segments []introSegment for _, name := range files { - raw, err := os.ReadFile(filepath.Join(*dir, name)) + raw, err := os.ReadFile(filepath.Join(segDir, name)) if err != nil { sugar.Fatalf("read %s: %v", name, err) } @@ -83,7 +93,7 @@ func main() { if err := json.Unmarshal(raw, &seg); err != nil { sugar.Fatalf("parse %s: %v", name, err) } - if seg.Book != "bg" || seg.Type != "intro-segment" { + if seg.Book != *book || seg.Type != "intro-segment" { sugar.Warnf("skipping %s (book=%q type=%q)", name, seg.Book, seg.Type) continue } @@ -91,7 +101,7 @@ func main() { } if len(segments) == 0 { - sugar.Fatal("no bg intro-segments found to seed") + sugar.Fatalf("no %s intro-segments found in %s", *book, segDir) } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) @@ -99,22 +109,20 @@ func main() { col := mongoDB.Collection("prose_chapters") - // Idempotent: clear any previously seeded bg intro-segments. del, err := col.DeleteMany(ctx, map[string]any{ - "book": "bg", + "book": *book, "type": "intro-segment", }) if err != nil { - sugar.Fatalf("delete existing bg intro-segments: %v", err) + sugar.Fatalf("delete existing %s intro-segments: %v", *book, err) } - sugar.Infof("removed %d existing bg intro-segment(s)", del.DeletedCount) + sugar.Infof("removed %d existing %s intro-segment(s)", del.DeletedCount, *book) docs := make([]any, 0, len(segments)) for _, seg := range segments { - // chapter is intentionally null for intro-segments (matches the - // ProseChapterDoc shape used by KB/NoD front matter). + // chapter is null for intro-segments (matches the ProseChapterDoc shape). docs = append(docs, map[string]any{ - "book": "bg", + "book": *book, "volume": 0, "chapter": nil, "title": seg.Title, @@ -128,7 +136,7 @@ func main() { res, err := col.InsertMany(ctx, docs) if err != nil { - sugar.Fatalf("insert bg intro-segments: %v", err) + sugar.Fatalf("insert %s intro-segments: %v", *book, err) } - sugar.Infof("inserted %d bg intro-segment(s)", len(res.InsertedIDs)) + sugar.Infof("inserted %d %s intro-segment(s)", len(res.InsertedIDs), *book) } diff --git a/internal/server/server.go b/internal/server/server.go index 1d294d5..f8018c0 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -421,10 +421,13 @@ func (s *Server) setupRouter() { r.Get("/{division}/{chapter}/{verse}", s.bookH.GetDivisionVerse) }) - // CC — līlā divisions + // CC — līlā divisions (plus prose front-matter segments; + // the static /chapters routes take precedence over /{division}). r.Route("/cc", func(r chi.Router) { r.Use(injectBookCode("cc")) r.Get("/", s.bookH.GetBook) + r.Get("/chapters", s.bookH.ListProseChapters) + r.Get("/chapters/{slug}", s.bookH.GetProseChapter) r.Get("/{division}", s.bookH.GetDivisionInfo) r.Get("/{division}/{chapter}", s.bookH.GetDivisionChapterVerseIndex) r.Get("/{division}/{chapter}/verses", s.bookH.GetDivisionChapterVerses)