fix
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package catalog
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/descrybe/descrybe-v2/apps/api/internal/aiprompts"
|
||||
"github.com/descrybe/descrybe-v2/apps/api/internal/company"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
// DefaultFormulaTarget is one freshly created category to seed formulas onto.
|
||||
type DefaultFormulaTarget struct {
|
||||
ID uuid.UUID
|
||||
UniqueID string
|
||||
Name string
|
||||
}
|
||||
|
||||
// pgxExecer is the subset of pgxpool.Pool / pgx.Tx used here so the helper works
|
||||
// inside the CSV import transaction as well as on the plain pool.
|
||||
type pgxExecer interface {
|
||||
Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)
|
||||
}
|
||||
|
||||
// applyDefaultCategoryFormulasSQL fills prompt / title_template / description_template
|
||||
// on newly created categories. Only empty columns are written, so a tenant that
|
||||
// already authored a formula (or an A1 category carrying its Slovenian legacy
|
||||
// prompt) is never overwritten.
|
||||
const applyDefaultCategoryFormulasSQL = `
|
||||
UPDATE categories AS c SET
|
||||
prompt = CASE
|
||||
WHEN c.prompt IS NULL OR c.prompt = '{}'::jsonb
|
||||
THEN v.prompt::jsonb ELSE c.prompt END,
|
||||
title_template = CASE
|
||||
WHEN c.title_template IS NULL OR c.title_template::text IN ('null', '{}')
|
||||
THEN NULLIF(v.title_template, '')::jsonb ELSE c.title_template END,
|
||||
description_template = CASE
|
||||
WHEN c.description_template IS NULL OR c.description_template::text IN ('null', '{}')
|
||||
THEN NULLIF(v.description_template, '')::jsonb ELSE c.description_template END,
|
||||
updated_at = now()
|
||||
FROM (
|
||||
SELECT * FROM unnest($2::uuid[], $3::text[], $4::text[], $5::text[])
|
||||
AS t(id, prompt, title_template, description_template)
|
||||
) AS v
|
||||
WHERE c.id = v.id AND c.company_id = $1`
|
||||
|
||||
// ApplyDefaultCategoryFormulas gives newly created categories the platform-default
|
||||
// formulas (English, same style as the A1 legacy templates) when they have none.
|
||||
//
|
||||
// New categories get a working title/description/meta formula out of the box
|
||||
// instead of falling through to generic "1-3 paragraphs" copy. Existing rows are
|
||||
// untouched — this only runs at create time. A1 categories keep their Slovenian
|
||||
// prompts because seed-a1 writes them and the SQL never overwrites a set column.
|
||||
func ApplyDefaultCategoryFormulas(ctx context.Context, db pgxExecer, companyID uuid.UUID, targets ...DefaultFormulaTarget) error {
|
||||
if db == nil || len(targets) == 0 {
|
||||
return nil
|
||||
}
|
||||
ids := make([]uuid.UUID, 0, len(targets))
|
||||
prompts := make([]string, 0, len(targets))
|
||||
titles := make([]string, 0, len(targets))
|
||||
descs := make([]string, 0, len(targets))
|
||||
for _, t := range targets {
|
||||
if t.ID == uuid.Nil {
|
||||
continue
|
||||
}
|
||||
def := aiprompts.DefaultCategoryFormula(t.UniqueID, t.Name)
|
||||
if def.Overlay == "" {
|
||||
continue
|
||||
}
|
||||
// "*" applies to every content language: the formula text is English while
|
||||
// {{language}} still selects the output language at render time.
|
||||
promptJSON, err := company.EncodeLangPromptMap(company.LangPromptMap{"*": def.Overlay})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ids = append(ids, t.ID)
|
||||
prompts = append(prompts, string(promptJSON))
|
||||
titles = append(titles, def.TitleTemplateJSON)
|
||||
descs = append(descs, def.DescriptionTemplateJSON)
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err := db.Exec(ctx, applyDefaultCategoryFormulasSQL, companyID, ids, prompts, titles, descs)
|
||||
return err
|
||||
}
|
||||
@@ -237,6 +237,13 @@ func (s *Service) flushCategoryBatch(ctx context.Context, companyID uuid.UUID, b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameByUID := make(map[string]string, len(insUIDs))
|
||||
for i, uid := range insUIDs {
|
||||
if i < len(insNames) {
|
||||
nameByUID[uid] = insNames[i]
|
||||
}
|
||||
}
|
||||
var seedFormulas []DefaultFormulaTarget
|
||||
for rows.Next() {
|
||||
var id uuid.UUID
|
||||
var uid, path string
|
||||
@@ -246,6 +253,7 @@ func (s *Service) flushCategoryBatch(ctx context.Context, companyID uuid.UUID, b
|
||||
return err
|
||||
}
|
||||
known[uid] = categoryPathInfo{id: id, path: path, level: level}
|
||||
seedFormulas = append(seedFormulas, DefaultFormulaTarget{ID: id, UniqueID: uid, Name: nameByUID[uid]})
|
||||
res.Created++
|
||||
}
|
||||
err = rows.Err()
|
||||
@@ -253,6 +261,11 @@ func (s *Service) flushCategoryBatch(ctx context.Context, companyID uuid.UUID, b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Imported categories get the platform-default formula too, so an imported
|
||||
// taxonomy enhances into formula-shaped copy without further setup.
|
||||
if err := ApplyDefaultCategoryFormulas(ctx, tx, companyID, seedFormulas...); err != nil {
|
||||
return err
|
||||
}
|
||||
pendingInserts = next
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/descrybe/descrybe-v2/apps/api/internal/company"
|
||||
@@ -167,6 +168,13 @@ func (s *Service) CreateCategory(ctx context.Context, companyID uuid.UUID, name,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// New categories start with the platform-default formula so enhance produces
|
||||
// formula-shaped copy instead of generic paragraphs. Non-fatal: a category
|
||||
// without a stored formula still falls back to the default at enhance time.
|
||||
if err := ApplyDefaultCategoryFormulas(ctx, s.Pool, companyID,
|
||||
DefaultFormulaTarget{ID: id, UniqueID: uniqueID, Name: name}); err != nil {
|
||||
log.Printf("catalog: default formulas company=%s category=%s err=%v", companyID, id, err)
|
||||
}
|
||||
return s.GetCategory(ctx, companyID, id)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user