feat: vani of the day edit
This commit is contained in:
@@ -9,7 +9,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -32,8 +34,12 @@ func NewVaniService(repo *repository.VaniRepo, logger *zap.Logger) *VaniService
|
||||
// Today returns the current day's pick, choosing (and persisting) one on the
|
||||
// first call of the day. Returns (nil, nil) only if there's genuinely no
|
||||
// content to pick.
|
||||
// ErrVaniBadKind is a kind outside verse/prose/transcript.
|
||||
var ErrVaniBadKind = errors.New("kind must be verse, prose or transcript")
|
||||
var (
|
||||
// ErrVaniBadKind is a kind outside verse/prose/transcript.
|
||||
ErrVaniBadKind = errors.New("kind must be verse, prose or transcript")
|
||||
// ErrVaniBadURL is a link that isn't a verse, prose chapter or lecture.
|
||||
ErrVaniBadURL = errors.New("that link isn't a verse, chapter or lecture")
|
||||
)
|
||||
|
||||
func (s *VaniService) Today(ctx context.Context) (*model.VaniPayload, error) {
|
||||
now := time.Now().UTC()
|
||||
@@ -79,6 +85,84 @@ func (s *VaniService) Today(ctx context.Context) (*model.VaniPayload, error) {
|
||||
|
||||
// choose tries the three content kinds in random order and returns the first
|
||||
// successful sample, so each day's pick varies across content types.
|
||||
// ResolveURL turns an app URL (or bare path) into a pinnable pick.
|
||||
//
|
||||
// The intended admin workflow: paste the link to the thing you want. Search
|
||||
// cannot serve this — slugs are not indexed, so searching "720720sb-london"
|
||||
// matches on "london" and returns unrelated lectures.
|
||||
//
|
||||
// Accepts an absolute URL or a path, and understands the three shapes the
|
||||
// vāṇī can point at:
|
||||
//
|
||||
// /transcriptions/{slug}
|
||||
// /books/{book}/{chapter}/{verse} (+ /books/{book}/{division}/{c}/{v})
|
||||
// /books/{book}/{slug} (prose chapter)
|
||||
func (s *VaniService) ResolveURL(ctx context.Context, raw string, day time.Time) (*model.VaniPick, error) {
|
||||
day = day.UTC().Truncate(24 * time.Hour)
|
||||
|
||||
path := strings.TrimSpace(raw)
|
||||
if u, err := url.Parse(path); err == nil && u.Host != "" {
|
||||
path = u.Path
|
||||
}
|
||||
path = strings.Trim(strings.SplitN(path, "?", 2)[0], "/")
|
||||
parts := strings.Split(path, "/")
|
||||
if len(parts) < 2 {
|
||||
return nil, ErrVaniBadURL
|
||||
}
|
||||
|
||||
switch parts[0] {
|
||||
case "transcriptions":
|
||||
t, err := s.repo.TranscriptBySlug(ctx, parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transcriptPick(t, day), nil
|
||||
|
||||
case "books":
|
||||
book := parts[1]
|
||||
nums := parts[2:]
|
||||
// A trailing division segment (SB cantos, CC līlās) shifts the
|
||||
// chapter/verse pair along by one.
|
||||
var division *string
|
||||
if len(nums) == 3 {
|
||||
d := nums[0]
|
||||
division, nums = &d, nums[1:]
|
||||
}
|
||||
if len(nums) == 2 {
|
||||
chapter, err1 := strconv.Atoi(nums[0])
|
||||
verse, err2 := strconv.Atoi(nums[1])
|
||||
if err1 == nil && err2 == nil {
|
||||
v, err := s.repo.VerseByRef(ctx, book, division, chapter, verse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return versePick(v, day), nil
|
||||
}
|
||||
}
|
||||
// Not numeric, so it is a prose chapter's bare slug.
|
||||
if len(nums) == 1 {
|
||||
p, err := s.repo.ProseBySlug(ctx, book, nums[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return prosePick(p, s.repo.BookTitle(ctx, p.Book), day), nil
|
||||
}
|
||||
}
|
||||
return nil, ErrVaniBadURL
|
||||
}
|
||||
|
||||
// SetByURL resolves a URL and pins the result.
|
||||
func (s *VaniService) SetByURL(ctx context.Context, raw string, day time.Time) (*model.VaniPick, error) {
|
||||
pick, err := s.ResolveURL(ctx, raw, day)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.repo.Upsert(ctx, pick); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pick, nil
|
||||
}
|
||||
|
||||
// SetForDay pins a specific piece of content as a given day's Vāṇī.
|
||||
//
|
||||
// Takes only a kind and a content id — never a payload. The display blob is
|
||||
|
||||
Reference in New Issue
Block a user