From c67a657ca28f310815a92d3cba659f433e5ad084 Mon Sep 17 00:00:00 2001 From: Krispire Admin Date: Sun, 14 Jun 2026 13:28:16 +0530 Subject: [PATCH] feat: add bg front matter --- cmd/seed-bg-intro/main.go | 134 ++++++++++++++++++++++++++++++++++++++ internal/server/server.go | 9 +-- 2 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 cmd/seed-bg-intro/main.go diff --git a/cmd/seed-bg-intro/main.go b/cmd/seed-bg-intro/main.go new file mode 100644 index 0000000..b9e14f6 --- /dev/null +++ b/cmd/seed-bg-intro/main.go @@ -0,0 +1,134 @@ +// 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). +// +// The segment JSON is produced by extractor/scripts/bg_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 +// +// It is idempotent: existing bg intro-segments are removed before insert. +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + "sort" + "time" + + "go.uber.org/zap" + + "github.com/prabhupadasays/spsays-bff/internal/config" + "github.com/prabhupadasays/spsays-bff/internal/repository" +) + +type introSegment struct { + Slug string `json:"slug"` + Title string `json:"title"` + Type string `json:"type"` + Book string `json:"book"` + Volume int `json:"volume"` + Order int `json:"order"` + Paragraphs []string `json:"paragraphs"` +} + +func main() { + dir := flag.String("dir", "../extractor/jsons/books/bg/intro", + "directory containing bg intro-segment JSON files") + flag.Parse() + + cfg, err := config.Load() + if err != nil { + fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err) + os.Exit(1) + } + + logger, _ := zap.NewDevelopment() + defer logger.Sync() + sugar := logger.Sugar() + + mongoDB, err := repository.NewMongoDB(cfg.MongoURI, cfg.MongoDB, logger) + if err != nil { + sugar.Fatalf("failed to connect to MongoDB: %v", err) + } + defer mongoDB.Close() + + entries, err := os.ReadDir(*dir) + if err != nil { + sugar.Fatalf("failed to read dir %s: %v", *dir, err) + } + + var segments []introSegment + var files []string + for _, e := range entries { + if e.IsDir() || filepath.Ext(e.Name()) != ".json" { + continue + } + files = append(files, e.Name()) + } + sort.Strings(files) + + for _, name := range files { + raw, err := os.ReadFile(filepath.Join(*dir, name)) + if err != nil { + sugar.Fatalf("read %s: %v", name, err) + } + var seg introSegment + if err := json.Unmarshal(raw, &seg); err != nil { + sugar.Fatalf("parse %s: %v", name, err) + } + if seg.Book != "bg" || seg.Type != "intro-segment" { + sugar.Warnf("skipping %s (book=%q type=%q)", name, seg.Book, seg.Type) + continue + } + segments = append(segments, seg) + } + + if len(segments) == 0 { + sugar.Fatal("no bg intro-segments found to seed") + } + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + col := mongoDB.Collection("prose_chapters") + + // Idempotent: clear any previously seeded bg intro-segments. + del, err := col.DeleteMany(ctx, map[string]any{ + "book": "bg", + "type": "intro-segment", + }) + if err != nil { + sugar.Fatalf("delete existing bg intro-segments: %v", err) + } + sugar.Infof("removed %d existing bg intro-segment(s)", del.DeletedCount) + + 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). + docs = append(docs, map[string]any{ + "book": "bg", + "volume": 0, + "chapter": nil, + "title": seg.Title, + "type": "intro-segment", + "slug": seg.Slug, + "order": seg.Order, + "paragraphs": seg.Paragraphs, + }) + sugar.Infof(" %s (order %d, %d paragraphs)", seg.Slug, seg.Order, len(seg.Paragraphs)) + } + + res, err := col.InsertMany(ctx, docs) + if err != nil { + sugar.Fatalf("insert bg intro-segments: %v", err) + } + sugar.Infof("inserted %d bg intro-segment(s)", len(res.InsertedIDs)) +} diff --git a/internal/server/server.go b/internal/server/server.go index beb70b8..1d294d5 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -432,8 +432,8 @@ func (s *Server) setupRouter() { }) // --- Verse books without divisions --- - // BG, SB1962: pure verse books - for _, code := range []string{"bg", "sb1962"} { + // SB1962: pure verse book + for _, code := range []string{"sb1962"} { code := code // capture for closure r.Route("/"+code, func(r chi.Router) { r.Use(injectBookCode(code)) @@ -445,8 +445,9 @@ func (s *Server) setupRouter() { } // --- Verse + prose intro books (no division) --- - // BS, MM, NBS: have both verses and prose intro segments - for _, code := range []string{"bs", "mm", "nbs"} { + // BG, BS, MM, NBS: verses plus prose front-matter segments + // (the static /chapters routes take precedence over /{chapter}). + for _, code := range []string{"bg", "bs", "mm", "nbs"} { code := code r.Route("/"+code, func(r chi.Router) { r.Use(injectBookCode(code))