128 lines
4.8 KiB
Go
128 lines
4.8 KiB
Go
package processing
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
|
||
|
|
"github.com/descrybe/descrybe-v2/apps/api/internal/aiprompts"
|
||
|
|
"github.com/descrybe/descrybe-v2/apps/api/internal/catalog"
|
||
|
|
"github.com/google/uuid"
|
||
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
||
|
|
)
|
||
|
|
|
||
|
|
// FixCompanyCatalogOpts controls optional Fix A1 / catalog hygiene steps.
|
||
|
|
type FixCompanyCatalogOpts struct {
|
||
|
|
// BackfillCategories copies mapped_data category unique_ids onto empty
|
||
|
|
// processed_products.category.
|
||
|
|
BackfillCategories bool
|
||
|
|
// ReprocessSampleLimit caps recommended raw_product_ids in the response
|
||
|
|
// (0 = counts only, no sample ids).
|
||
|
|
ReprocessSampleLimit int
|
||
|
|
// AIPrompts optionally re-applies BuiltInDefaults product_enhance per content language.
|
||
|
|
AIPrompts *aiprompts.Service
|
||
|
|
}
|
||
|
|
|
||
|
|
// FixCompanyCatalogResult is the idempotent admin Fix A1 report.
|
||
|
|
type FixCompanyCatalogResult struct {
|
||
|
|
CompanyID uuid.UUID `json:"company_id"`
|
||
|
|
CompanyName string `json:"company_name"`
|
||
|
|
A1Cohort bool `json:"a1_cohort"`
|
||
|
|
|
||
|
|
CategoryAttributeOrphansRemoved int `json:"category_attribute_orphans_removed"`
|
||
|
|
CategoryAttributeLinks int `json:"category_attribute_links"`
|
||
|
|
|
||
|
|
CategoryPromptsUpdated int `json:"category_prompts_updated"`
|
||
|
|
CategoryPromptsAlreadyOK int `json:"category_prompts_already_ok"`
|
||
|
|
CategoryPromptsEmpty int `json:"category_prompts_empty_skipped"`
|
||
|
|
// Prompts aliases category_prompts_updated for flash.admin.fixA1Success {prompts}.
|
||
|
|
Prompts int `json:"prompts"`
|
||
|
|
|
||
|
|
ProductEnhanceLanguages int `json:"product_enhance_languages"`
|
||
|
|
|
||
|
|
WeakHashesCleared int `json:"weak_hashes_cleared"`
|
||
|
|
CategoriesBackfilled int `json:"categories_backfilled"`
|
||
|
|
// Hashes / Categories alias the long names for flash.admin.fixA1Success.
|
||
|
|
Hashes int `json:"hashes"`
|
||
|
|
Categories int `json:"categories"`
|
||
|
|
|
||
|
|
DescriptionsNormalized int `json:"descriptions_normalized"`
|
||
|
|
DescriptionsBackfilled int `json:"descriptions_backfilled"`
|
||
|
|
MappedDescriptionsFlat int `json:"mapped_descriptions_flattened"`
|
||
|
|
MetaBackfilled int `json:"meta_backfilled"`
|
||
|
|
AttributesSanitized int64 `json:"attributes_sanitized"`
|
||
|
|
ProductsScanned int `json:"products_scanned"`
|
||
|
|
|
||
|
|
ReprocessNeededCount int `json:"reprocess_needed_count"`
|
||
|
|
ReprocessSampleRawProductIDs []uuid.UUID `json:"reprocess_sample_raw_product_ids,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// FixCompanyCatalog runs the admin Fix A1 / catalog hygiene sequence in place.
|
||
|
|
// Never clears the catalog or uses A1 as a clone destination.
|
||
|
|
// Step 2 applies RepairCompanyCategoryEnhancePrompts (same repaired map as
|
||
|
|
// RepairA1DemoCategoryEnhancePrompts / cmd/repair-category-prompts -apply).
|
||
|
|
// Legacy converters + weak-hash clear live in FixCatalogHygieneWithIDs.
|
||
|
|
func FixCompanyCatalog(ctx context.Context, pool *pgxpool.Pool, companyID uuid.UUID, companyName string, a1Cohort bool, opts FixCompanyCatalogOpts) (FixCompanyCatalogResult, error) {
|
||
|
|
out := FixCompanyCatalogResult{
|
||
|
|
CompanyID: companyID,
|
||
|
|
CompanyName: companyName,
|
||
|
|
A1Cohort: a1Cohort,
|
||
|
|
}
|
||
|
|
if pool == nil {
|
||
|
|
return out, fmt.Errorf("nil pool")
|
||
|
|
}
|
||
|
|
|
||
|
|
orphans, links, err := catalog.EnsureCategoryAttributeLinks(ctx, pool, companyID)
|
||
|
|
if err != nil {
|
||
|
|
return out, err
|
||
|
|
}
|
||
|
|
out.CategoryAttributeOrphansRemoved = orphans
|
||
|
|
out.CategoryAttributeLinks = links
|
||
|
|
|
||
|
|
updated, alreadyOK, emptySkipped, err := catalog.RepairCompanyCategoryEnhancePrompts(ctx, pool, companyID)
|
||
|
|
if err != nil {
|
||
|
|
return out, err
|
||
|
|
}
|
||
|
|
out.CategoryPromptsUpdated = updated
|
||
|
|
out.CategoryPromptsAlreadyOK = alreadyOK
|
||
|
|
out.CategoryPromptsEmpty = emptySkipped
|
||
|
|
|
||
|
|
if opts.AIPrompts != nil {
|
||
|
|
n, err := opts.AIPrompts.ApplyBuiltInProductEnhance(ctx, companyID)
|
||
|
|
if err != nil {
|
||
|
|
return out, fmt.Errorf("product_enhance templates: %w", err)
|
||
|
|
}
|
||
|
|
out.ProductEnhanceLanguages = n
|
||
|
|
}
|
||
|
|
|
||
|
|
hygiene, clearedIDs, err := FixCatalogHygieneWithIDs(ctx, pool, companyID, opts.BackfillCategories)
|
||
|
|
if err != nil {
|
||
|
|
return out, err
|
||
|
|
}
|
||
|
|
out.WeakHashesCleared = hygiene.WeakHashesCleared
|
||
|
|
out.CategoriesBackfilled = hygiene.CategoriesBackfilled
|
||
|
|
out.DescriptionsNormalized = hygiene.DescriptionsNormalized
|
||
|
|
out.DescriptionsBackfilled = hygiene.DescriptionsBackfilled
|
||
|
|
out.MappedDescriptionsFlat = hygiene.MappedDescriptionsFlat
|
||
|
|
out.MetaBackfilled = hygiene.MetaBackfilled
|
||
|
|
out.ProductsScanned = hygiene.ProductsScanned
|
||
|
|
|
||
|
|
sanitized, err := BackfillCompanyProcessedAttributes(ctx, pool, companyID)
|
||
|
|
if err != nil {
|
||
|
|
return out, fmt.Errorf("sanitize attributes: %w", err)
|
||
|
|
}
|
||
|
|
out.AttributesSanitized = sanitized
|
||
|
|
|
||
|
|
needed, sample, err := RecommendReprocessRawProductIDs(ctx, pool, companyID, clearedIDs, opts.ReprocessSampleLimit)
|
||
|
|
if err != nil {
|
||
|
|
return out, err
|
||
|
|
}
|
||
|
|
out.ReprocessNeededCount = needed
|
||
|
|
out.ReprocessSampleRawProductIDs = sample
|
||
|
|
|
||
|
|
// Short keys for flash.admin.fixA1Success {prompts,hashes,categories}.
|
||
|
|
out.Prompts = out.CategoryPromptsUpdated
|
||
|
|
out.Hashes = out.WeakHashesCleared
|
||
|
|
out.Categories = out.CategoriesBackfilled
|
||
|
|
return out, nil
|
||
|
|
}
|