Files
descrybe/apps/api/cmd/tmp-scan-cats/main.go
T
2026-08-16 23:07:32 +02:00

141 lines
4.4 KiB
Go

package main
import (
"context"
"fmt"
"os"
"strings"
"time"
"github.com/descrybe/descrybe-v2/apps/api/internal/billing"
"github.com/descrybe/descrybe-v2/apps/api/internal/config"
"github.com/descrybe/descrybe-v2/apps/api/internal/processing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgxpool"
)
func main() {
config.LoadDotEnv()
dumpPath := strings.TrimSpace(os.Getenv("SEED_A1_MYSQL_DUMP"))
if dumpPath == "" && len(os.Args) > 1 {
dumpPath = os.Args[1]
}
if dumpPath == "" {
dumpPath = processing.ResolveMySQLDumpPath("")
}
legacy := billing.A1LegacyCompanyID
companyID := uuid.MustParse("604f23a8-b66e-4b21-8b45-0d72b68f4790")
fmt.Printf("dump=%s\n", dumpPath)
fmt.Printf("legacy_company=%s\n", legacy)
if dumpPath != "" {
f, err := os.Open(dumpPath)
if err != nil {
fmt.Printf("dump_open_err=%v\n", err)
} else {
defer f.Close()
byGTIN, err := processing.ScanA1ProcessedCategories(f, legacy)
if err != nil {
fmt.Printf("scan_err=%v\n", err)
} else {
withCat := 0
for _, c := range byGTIN {
if strings.TrimSpace(c) != "" && !strings.EqualFold(c, "NULL") && !strings.EqualFold(c, "none") {
withCat++
}
}
fmt.Printf("dump_pairs=%d with_nonempty_cat=%d\n", len(byGTIN), withCat)
}
}
} else {
fmt.Println("dump=NONE")
}
pgURL := strings.TrimSpace(os.Getenv("DATABASE_URL"))
if pgURL == "" {
fmt.Println("postgres=NO_DATABASE_URL")
return
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
pg, err := pgxpool.New(ctx, pgURL)
if err != nil {
fmt.Printf("postgres_err=%v\n", err)
return
}
defer pg.Close()
var legacyDB, name string
_ = pg.QueryRow(ctx, `SELECT COALESCE(legacy_company_id::text,''), name FROM companies WHERE id=$1`, companyID).Scan(&legacyDB, &name)
fmt.Printf("pg_company name=%q legacy=%s\n", name, legacyDB)
var rawTotal, rawWith, rawWithout int
err = pg.QueryRow(ctx, `
SELECT COUNT(*),
COUNT(*) FILTER (WHERE COALESCE(NULLIF(trim(mapped_data->>'category'), ''), '') <> ''),
COUNT(*) FILTER (WHERE COALESCE(NULLIF(trim(mapped_data->>'category'), ''), '') = '')
FROM raw_products WHERE company_id=$1`, companyID).Scan(&rawTotal, &rawWith, &rawWithout)
if err != nil {
fmt.Printf("raw_count_err=%v\n", err)
} else {
pct := 0.0
if rawTotal > 0 {
pct = 100.0 * float64(rawWith) / float64(rawTotal)
}
fmt.Printf("pg_raw total=%d with_cat=%d without_cat=%d pct_with=%.1f\n", rawTotal, rawWith, rawWithout, pct)
}
var ppTotal, ppWith, ppWithout int
err = pg.QueryRow(ctx, `
SELECT COUNT(*),
COUNT(*) FILTER (WHERE COALESCE(NULLIF(trim(category), ''), '') <> '' AND lower(trim(category)) <> 'none'),
COUNT(*) FILTER (WHERE COALESCE(NULLIF(trim(category), ''), '') = '' OR lower(trim(category)) = 'none')
FROM processed_products WHERE company_id=$1`, companyID).Scan(&ppTotal, &ppWith, &ppWithout)
if err != nil {
fmt.Printf("pp_count_err=%v\n", err)
} else {
fmt.Printf("pg_processed total=%d with_cat=%d without_cat=%d\n", ppTotal, ppWith, ppWithout)
}
// Dry-run overlap: how many dump GTINs would fill empty mapped categories
if dumpPath != "" {
f2, err := os.Open(dumpPath)
if err == nil {
byGTIN, err := processing.ScanA1ProcessedCategories(f2, legacy)
_ = f2.Close()
if err == nil && len(byGTIN) > 0 {
gtins := make([]string, 0, len(byGTIN))
cats := make([]string, 0, len(byGTIN))
for g, c := range byGTIN {
gtins = append(gtins, g)
cats = append(cats, c)
}
var wouldUpdate, alreadyOk, noRawMatch int
err = pg.QueryRow(ctx, `
WITH dump(gtin, category) AS (
SELECT * FROM unnest($2::text[], $3::text[])
),
joined AS (
SELECT r.gtin,
COALESCE(NULLIF(trim(r.mapped_data->>'category'), ''), '') AS cur,
COALESCE(NULLIF(trim(d.category), ''), '') AS want
FROM dump d
LEFT JOIN raw_products r ON r.company_id=$1 AND r.gtin=d.gtin
)
SELECT
COUNT(*) FILTER (WHERE gtin IS NOT NULL AND want <> '' AND (cur = '' OR cur IS DISTINCT FROM want)),
COUNT(*) FILTER (WHERE gtin IS NOT NULL AND want <> '' AND cur <> '' AND cur = want),
COUNT(*) FILTER (WHERE gtin IS NULL)
FROM joined
`, companyID, gtins, cats).Scan(&wouldUpdate, &alreadyOk, &noRawMatch)
if err != nil {
fmt.Printf("dry_run_err=%v\n", err)
} else {
fmt.Printf("dry_run would_update_mapped=%d already_ok=%d dump_gtin_no_raw_row=%d\n", wouldUpdate, alreadyOk, noRawMatch)
}
}
}
}
}