Backend scores updates on products

This commit is contained in:
sriram
2026-09-05 11:53:13 +05:30
parent 0c36628d4b
commit 7ac3571b59
10 changed files with 984 additions and 2 deletions

View File

@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""
Copy `nutrition_insights` scores onto the brand tables and the seed catalogs.
WHY THIS EXISTS
---------------
`nutrition_score` and `health_score` are stored on `nutrition_insights`, keyed
by (brand, image_id). Nothing joined that table to `brand_*`, so anyone reading
product rows straight out of Postgres - which is how the merchant console does
it - could not see a score at all. The two columns now exist on every brand
table; this is what fills them the first time.
Afterwards it maintains itself: `enrich_all_products` and the `/upload/nutrition`
endpoint both call `nutrition_score_sync.sync_after_write` when they finish, so
the mirror stays within one run of the source. Run this by hand after anything
that edits `nutrition_insights` outside those two paths - notably
`purge_non_consumable_nutrition`, which deletes rows and would otherwise leave
stale scores behind on the brand tables.
SAFE TO RE-RUN
--------------
Every statement is guarded with `IS DISTINCT FROM`, so a second run changes
nothing and reports 0. That makes "0 rows changed" a real assertion that the
mirror is current, not an artefact of the script doing nothing.
USAGE
-----
python -m scripts.sync_nutrition_scores # dry run
python -m scripts.sync_nutrition_scores --apply
python -m scripts.sync_nutrition_scores --apply --brands "Cadbury,Amul"
python -m scripts.sync_nutrition_scores --apply --db-only
python -m scripts.sync_nutrition_scores --apply --json-only
Dry run by default. `--apply` writes.
"""
from __future__ import annotations
import argparse
import logging
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from app.infrastructure.settings import DB_HOST, DB_NAME # noqa: E402
from app.services.nutrition_score_sync import ( # noqa: E402
sync_scores_to_brand_tables,
sync_scores_to_seed_files,
)
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)
def main() -> int:
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--apply", action="store_true",
help="commit the changes (default is a dry run)")
parser.add_argument("--dry-run", action="store_true",
help="explicit no-op; this is already the default")
parser.add_argument("--brands", default=None,
help="comma-separated brand names to limit the run to")
parser.add_argument("--db-only", action="store_true",
help="brand tables only, leave the seed JSON alone")
parser.add_argument("--json-only", action="store_true",
help="seed JSON only, leave the database alone")
args = parser.parse_args()
if args.db_only and args.json_only:
parser.error("--db-only and --json-only are mutually exclusive")
apply = args.apply and not args.dry_run
brands = [b.strip() for b in (args.brands or "").split(",") if b.strip()] or None
logger.info("Target database: %s / %s", DB_HOST, DB_NAME)
logger.info("Mode: %s", "APPLY - this writes" if apply
else "DRY RUN - nothing is written")
if brands:
logger.info("Limited to: %s", ", ".join(brands))
logger.info("")
total = 0
if not args.json_only:
logger.info("Brand tables")
logger.info("-" * 52)
changed = sync_scores_to_brand_tables(brands, dry_run=not apply)
for brand, count in sorted(changed.items(), key=lambda kv: -kv[1]):
logger.info(" %-38s %5d", brand, count)
if not changed:
logger.info(" nothing to change - the mirror is current")
total += sum(changed.values())
logger.info("")
if not args.db_only:
logger.info("Seed catalogs")
logger.info("-" * 52)
changed = sync_scores_to_seed_files(brands, dry_run=not apply)
for name, count in sorted(changed.items(), key=lambda kv: -kv[1]):
logger.info(" %-38s %5d", name, count)
if not changed:
logger.info(" nothing to change - the files are current")
total += sum(changed.values())
logger.info("")
if not apply and total:
logger.info("Re-run with --apply to write %d change(s).", total)
elif apply:
logger.info("Done. %d change(s) written.", total)
return 0
if __name__ == "__main__":
raise SystemExit(main())