feat(ecommerce): release Ari Shopping Frontend v2
Build and Push Frontend / build (push) Successful in 1m57s

This commit is contained in:
2026-07-18 16:34:01 -05:00
parent 9f25b17468
commit 54d2f208ae
25 changed files with 376 additions and 566 deletions
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse,csv,hashlib,io,json,re,sys,urllib.request
from datetime import datetime,timezone
from pathlib import Path
try:
from PIL import Image,ImageOps
except ImportError:
Image=ImageOps=None
ROOT=Path(__file__).resolve().parents[1];OUT=ROOT/'data/products.json';IMG=ROOT/'assets/img/products'
def b(v,d=False): return str(v or '').strip().lower() in {'1','true','yes','si',''} if str(v or '').strip() else d
def n(v,d=0):
try:return int(float(str(v or '').strip()))
except:return d
def slug(v):return re.sub(r'[^a-zA-Z0-9_-]+','-',v.strip()).strip('-').lower() or hashlib.sha256(v.encode()).hexdigest()[:12]
def download(url):
req=urllib.request.Request(url,headers={'User-Agent':'AriShoppingCatalogSync/2.0'});data=urllib.request.urlopen(req,timeout=25).read(12*1024*1024+1)
if len(data)>12*1024*1024:raise ValueError('Imagen demasiado grande')
return data
def image(data,dst):
if Image is None:raise RuntimeError('Instala Pillow')
im=ImageOps.exif_transpose(Image.open(io.BytesIO(data))).convert('RGB');canvas=Image.new('RGB',(1200,1200),'white');im.thumbnail((1080,1080),Image.Resampling.LANCZOS);canvas.paste(im,((1200-im.width)//2,(1200-im.height)//2));canvas.save(dst,'WEBP',quality=86,method=6)
def main():
ap=argparse.ArgumentParser();ap.add_argument('--source',type=Path,default=ROOT/'catalog-source.csv');ap.add_argument('--download-images',action='store_true');a=ap.parse_args();items=[]
with a.source.open(encoding='utf-8-sig') as f:
for row in csv.DictReader(f):
pid=slug(row.get('id') or row['name']);imgs=[];urls=[u.strip() for u in row.get('image_urls','').split('|') if u.strip()]
if a.download_images:
for i,u in enumerate(urls,1):
try:dst=IMG/f'{pid}-{i}.webp';image(download(u),dst);imgs.append(str(dst.relative_to(ROOT)).replace('\\','/'))
except Exception as e:print(f'AVISO {pid}: {e}',file=sys.stderr)
if not imgs:
local=IMG/f'{pid}.svg';imgs=[str(local.relative_to(ROOT)).replace('\\','/')] if local.exists() else urls
items.append({'id':pid,'name':row['name'].strip(),'category':row.get('category','Otros').strip(),'price':n(row.get('price')),'compareAtPrice':n(row.get('compare_at_price')),'featured':b(row.get('featured')),'new':b(row.get('new')),'description':row.get('description','').strip(),'tags':[t.strip() for t in row.get('tags','').split('|') if t.strip()],'images':imgs,'stock':n(row.get('stock')),'available':b(row.get('available'),True),'source':{'type':'authorized-feed','name':row.get('source_name','Fuente autorizada'),'url':row.get('source_url','')}})
OUT.write_text(json.dumps({'version':'2.0.0','updatedAt':datetime.now(timezone.utc).isoformat(),'currency':'COP','products':items},ensure_ascii=False,indent=2)+'\n');print(f'OK: {len(items)} productos')
if __name__=='__main__':raise SystemExit(main())
@@ -0,0 +1 @@
Pillow==11.3.0