This commit is contained in:
2026-08-17 01:30:28 +02:00
parent c2429873b3
commit 0dceb3a404
18 changed files with 1021 additions and 219 deletions
@@ -0,0 +1,236 @@
// Temporary local probe for A1/Demo V1 projection cleanup. Do not commit.
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"github.com/descrybe/descrybe-v2/apps/api/internal/billing"
"github.com/descrybe/descrybe-v2/apps/api/internal/processing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgxpool"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
dsn := os.Getenv("DATABASE_URL")
if dsn == "" {
dsn = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"
}
pool, err := pgxpool.New(ctx, dsn)
if err != nil {
fatal(err)
}
defer pool.Close()
outDir := filepath.Join("..", "..", ".codehelper", "_a1_v1_response_20260817")
if abs, err := filepath.Abs(outDir); err == nil {
outDir = abs
}
_ = os.MkdirAll(outDir, 0o755)
type companyRow struct {
ID uuid.UUID
Name string
Legacy string
}
companies := []companyRow{}
rows, err := pool.Query(ctx, `
SELECT id, name, COALESCE(legacy_company_id, '')
FROM companies
WHERE legacy_company_id = $1
OR lower(name) LIKE '%demo%'
ORDER BY CASE WHEN legacy_company_id = $1 THEN 0 ELSE 1 END, name
LIMIT 6`, billing.A1LegacyCompanyID)
if err != nil {
fatal(err)
}
for rows.Next() {
var c companyRow
if err := rows.Scan(&c.ID, &c.Name, &c.Legacy); err != nil {
fatal(err)
}
companies = append(companies, c)
}
rows.Close()
if len(companies) == 0 {
fatal(fmt.Errorf("no A1/Demo companies found"))
}
p := processing.NewPipeline(pool)
eans := []string{"8806091734365", "8422160053580"}
summary := map[string]any{"generated_at": time.Now().UTC().Format(time.RFC3339), "companies": []any{}}
for _, co := range companies {
omitMeta := billing.IsA1CohortCompany(co.Legacy, co.Name)
coSummary := map[string]any{
"company_id": co.ID.String(),
"name": co.Name,
"legacy_id": co.Legacy,
"omit_seo_meta": omitMeta,
"items": []any{},
}
// Prefer existing processed rows for the sample EANs; fall back to any 2 recent.
type prod struct {
EAN, Title, Desc, Cat, CatName string
Attrs []byte
}
prods := []prod{}
q := `
SELECT COALESCE(p.product_id,''), COALESCE(p.processed_name,p.name,''),
COALESCE(p.processed_description, p.description, ''),
COALESCE(p.category,''), COALESCE(c.name,''),
COALESCE(p.processed_attributes, p.attributes, '{}'::jsonb)
FROM processed_products p
LEFT JOIN categories c ON c.unique_id = p.category AND c.company_id = p.company_id
WHERE p.company_id = $1 AND p.product_id = ANY($2)
ORDER BY p.updated_at DESC NULLS LAST
LIMIT 4`
pr, err := pool.Query(ctx, q, co.ID, eans)
if err != nil {
fatal(err)
}
for pr.Next() {
var x prod
if err := pr.Scan(&x.EAN, &x.Title, &x.Desc, &x.Cat, &x.CatName, &x.Attrs); err != nil {
fatal(err)
}
prods = append(prods, x)
}
pr.Close()
if len(prods) < 2 {
pr2, err := pool.Query(ctx, `
SELECT COALESCE(p.product_id,''), COALESCE(p.processed_name,p.name,''),
COALESCE(p.processed_description, p.description, ''),
COALESCE(p.category,''), COALESCE(c.name,''),
COALESCE(p.processed_attributes, p.attributes, '{}'::jsonb)
FROM processed_products p
LEFT JOIN categories c ON c.unique_id = p.category AND c.company_id = p.company_id
WHERE p.company_id = $1 AND COALESCE(p.processed_name,p.name,'') <> ''
ORDER BY p.updated_at DESC NULLS LAST
LIMIT 2`, co.ID)
if err != nil {
fatal(err)
}
for pr2.Next() {
var x prod
if err := pr2.Scan(&x.EAN, &x.Title, &x.Desc, &x.Cat, &x.CatName, &x.Attrs); err != nil {
fatal(err)
}
prods = append(prods, x)
}
pr2.Close()
}
if len(prods) == 0 {
coSummary["note"] = "no processed products"
summary["companies"] = append(summary["companies"].([]any), coSummary)
continue
}
items := []processing.V1ProcessJobItem{}
for _, x := range prods {
attrs := map[string]any{}
_ = json.Unmarshal(x.Attrs, &attrs)
item := processing.V1ProcessJobItem{
"ean": x.EAN,
"status": "processed",
"title": x.Title,
"name": x.Title,
"description": x.Desc,
"category": x.Cat,
"category_name": x.CatName,
"attributes": attrs,
"eprel": nil,
"id": uuid.New().String(),
}
item["processed_product_id"] = item["id"]
item["raw_product_id"] = uuid.New().String()
if !omitMeta {
item["meta_title"] = nil
item["meta_description"] = nil
}
item = processing.EnforceV1ProcessCompletedItemOpts(item, processing.EnforceV1Opts{
Language: "sl",
Allowed: map[string]struct{}{},
OmitSEOMeta: omitMeta,
})
items = append(items, item)
}
payload := map[string]any{
"data": map[string]any{
"status": "COMPLETED",
"processing_type": "full",
"total_items": len(items),
"items": items,
"company": co.Name,
"omit_seo_meta": omitMeta,
},
}
b, err := json.MarshalIndent(payload, "", " ")
if err != nil {
fatal(err)
}
label := "demo"
if omitMeta {
label = "a1"
}
fname := fmt.Sprintf("%s_%s.json", label, co.ID.String()[:8])
path := filepath.Join(outDir, fname)
if err := os.WriteFile(path, b, 0o644); err != nil {
fatal(err)
}
coSummary["sample_path"] = path
coSummary["item_count"] = len(items)
coSummary["items"] = items
summary["companies"] = append(summary["companies"].([]any), coSummary)
fmt.Println("wrote", path, "items=", len(items), "omit_meta=", omitMeta)
// Also exercise LoadV1 against the latest completed job for this company when present.
var jobID uuid.UUID
err = pool.QueryRow(ctx, `
SELECT id FROM processing_jobs
WHERE company_id = $1 AND status IN ('completed','COMPLETED')
ORDER BY updated_at DESC NULLS LAST
LIMIT 1`, co.ID).Scan(&jobID)
if err == nil {
loaded, loadErr := p.LoadV1ProcessJobItems(ctx, co.ID, jobID, "full")
if loadErr == nil && len(loaded) > 0 {
if len(loaded) > 2 {
loaded = loaded[:2]
}
live := map[string]any{
"data": map[string]any{
"process_id": jobID.String(),
"status": "COMPLETED",
"processing_type": "full",
"total_items": len(loaded),
"items": loaded,
"company": co.Name,
"omit_seo_meta": omitMeta,
"source": "LoadV1ProcessJobItems",
},
}
lb, _ := json.MarshalIndent(live, "", " ")
lpath := filepath.Join(outDir, fmt.Sprintf("%s_live_job_%s.json", label, jobID.String()[:8]))
_ = os.WriteFile(lpath, lb, 0o644)
fmt.Println("wrote", lpath, "live_items=", len(loaded))
}
}
}
sb, _ := json.MarshalIndent(summary, "", " ")
_ = os.WriteFile(filepath.Join(outDir, "summary.json"), sb, 0o644)
fmt.Println("done", outDir)
}
func fatal(err error) {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
@@ -0,0 +1,84 @@
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/descrybe/descrybe-v2/apps/api/internal/processing"
)
func main() {
badItems := []processing.V1ProcessJobItem{
{
"ean": "8806091734365",
"status": "processed",
"title": "LG Ameriški hladilnikGSXV80PZLE",
"name": "LG Ameriški hladilnikGSXV80PZLE",
"category": "11",
"category_name": "Hladilniki",
"description": "<h1>LG Ameriški hladilnikGSXV80PZLE</h1><p>LG Ameriški hladilnikGSXV80PZLE je izdelek v kategoriji Hladilniki znamke LG. Ključne specifikacije: width 179, height 91.3, depth 73.5.</p><h2>Ključne lastnosti</h2><ul><li>width 179</li><li>height 91.3</li><li>depth 73.5</li></ul><p>LG Ameriški hladilnikGSXV80PZLE je izdelek v kategoriji Hladilniki znamke LG. Ključne specifikacije: width 179, height 91.3, depth 73.5.</p><p>LG Ameriški hladilnikGSXV80PZLE je izdelek v kategoriji Hladilniki znamke LG. Ključne specifikacije: width 179, height 91.3, depth 73.5.</p><p>LG Ameriški hladilnikGSXV80PZLE je izdelek v kategoriji Hladilniki znamke LG. Ključne specifikacije: width 179, height 91.3, depth 73.5.</p>",
"attributes": map[string]any{"brand": "LG", "depth": "73.5", "energy_class": "E", "height": "91.3", "warranty": "24 mesecev", "width": "179"},
"meta_title": "LG Ameriški hladilnikGSXV80PZLE | Hladilniki",
"meta_description": "x",
"eprel": map[string]any{"id": "1158009", "energy_class": "E"},
"id": "1bc20e89-3779-4c66-8617-8e72555cd363",
"processed_product_id": "1bc20e89-3779-4c66-8617-8e72555cd363",
"raw_product_id": "fff13ee3-e78c-4185-b524-b64128bbc38b",
"main_image": "https://example/lg.jpg",
"more_images": nil,
},
{
"ean": "8422160053580",
"status": "processed",
"title": "Ufesa digitalni zračni cvrtnik 23L Magnum",
"name": "Ufesa digitalni zračni cvrtnik 23L Magnum",
"category": "112",
"category_name": "Ostali aparati",
"description": "<h1>Ufesa digitalni zračni cvrtnik 23L Magnum</h1><p>Ufesa digitalni zračni cvrtnik 23L Magnum je izdelek v kategoriji Ostali aparati znamke UFESA. Ključne specifikacije: width 0,00m, height 0,00m, depth 0,00m.</p><h2>Ključne lastnosti</h2><ul><li>width 0,00m</li><li>height 0,00m</li><li>depth 0,00m</li><li>weight 11,0000kg</li></ul><p>Ufesa digitalni zračni cvrtnik 23L Magnum je izdelek v kategoriji Ostali aparati znamke UFESA. Ključne specifikacije: width 0,00m, height 0,00m, depth 0,00m.</p>",
"attributes": map[string]any{"brand": "UFESA", "depth": "0,00m", "height": "0,00m", "warranty": "24 mesecev", "weight": "11,0000kg", "width": "0,00m"},
"meta_title": "x",
"meta_description": "y",
"eprel": nil,
"id": "c708b505-dbf0-40c5-ba09-022d15daff97",
"processed_product_id": "c708b505-dbf0-40c5-ba09-022d15daff97",
"raw_product_id": "fff6c685-931b-4fa5-846b-89b2503a5a77",
"main_image": "https://example/ufesa.jpg",
"more_images": []string{"https://example/2.jpg"},
},
}
cleaned := make([]processing.V1ProcessJobItem, 0, len(badItems))
for _, it := range badItems {
cleaned = append(cleaned, processing.EnforceV1ProcessCompletedItemOpts(it, processing.EnforceV1Opts{
Language: "sl",
Allowed: map[string]struct{}{},
OmitSEOMeta: true,
}))
}
out := map[string]any{
"data": map[string]any{
"process_id": "13b97eab-2329-4735-82fa-3afe263849fa",
"status": "COMPLETED",
"processing_type": "full",
"total_items": len(cleaned),
"items": cleaned,
"note": "projection cleanup of production sample (A1 OmitSEOMeta)",
},
}
b, err := json.MarshalIndent(out, "", " ")
if err != nil {
panic(err)
}
path := filepath.Join("..", "..", ".codehelper", "_a1_v1_response_20260817", "production_sample_cleaned.json")
if err := os.WriteFile(path, b, 0o644); err != nil {
panic(err)
}
fmt.Println("wrote", path)
for _, it := range cleaned {
fmt.Printf("ean=%v\ntitle=%v\ncategory=%v category_id=%v\nmeta_present=%v/%v\nattrs=%v\ndesc=%v\n\n",
it["ean"], it["title"], it["category"], it["category_id"],
it["meta_title"] != nil, it["meta_description"] != nil,
it["attributes"], it["description"])
}
}