Files
Autoparts-DB/pos/async_catalog.py
consultoria-as a1be8dd0ea OPCIÓN A: A2 Virtual Scroll + A3 Celery + A4 asyncpg PoC + A5 particionamiento
A2 — Virtual scroll en tablas grandes:
- Nuevo helper VirtualScroll en pos/static/js/virtual-scroll.js
- inventory.js: tabla de productos con virtual scroll
- customers.js: tabla de clientes con virtual scroll
- fleet.js: renderMaintenance() y renderHistory() con virtual scroll
- Templates envueltos en .vs-container para scroll

A3 — Celery worker queue:
- pos/celery_app.py + pos/tasks.py (warm cache, bulk import, reports)
- Blueprint tasks_bp.py con endpoints /pos/api/tasks/*
- Script scripts/start_celery.sh

A4 — asyncpg + Quart PoC:
- pos/async_catalog.py: endpoint /pos/api/catalog/async-search
- scripts/benchmark_async_catalog.py: benchmark Flask vs Quart

A5 — Particionar vehicle_parts:
- scripts/partition_vehicle_parts.py: migración segura por hash (16 particiones)
- Soporta --dry-run, --skip-swap, --skip-drop

Tests: 36/36 pasando
2026-04-27 09:53:36 +00:00

94 lines
2.5 KiB
Python

"""Async catalog search PoC using Quart + asyncpg.
Run:
hypercorn async_catalog:app --bind 0.0.0.0:5002
Endpoint:
GET /pos/api/catalog/async-search?q=filtro&limit=50&tenant_id=1
This demonstrates I/O non-blocking search using asyncpg.
"""
import os
import asyncio
import asyncpg
from quart import Quart, request, jsonify, g
app = Quart(__name__)
MASTER_DB_URL = os.environ.get('MASTER_DB_URL', 'postgresql://postgres@/nexus_autoparts')
# Shared connection pool
_pool = None
async def _get_pool():
global _pool
if _pool is None:
_pool = await asyncpg.create_pool(MASTER_DB_URL, min_size=2, max_size=10)
return _pool
@app.before_serving
async def startup():
await _get_pool()
@app.after_serving
async def shutdown():
global _pool
if _pool:
await _pool.close()
_pool = None
@app.route('/pos/api/catalog/async-search')
async def async_search():
q = request.args.get('q', '').strip()
if not q or len(q) < 2:
return jsonify({'data': []})
limit = min(request.args.get('limit', 50, type=int), 100)
pool = await _get_pool()
async with pool.acquire() as conn:
# Simple text search (PoC scope)
clean_q = q.upper().replace(' ', '')
rows = await conn.fetch("""
SELECT p.id_part, p.oem_part_number, p.name_part, p.name_es,
p.image_url, p.group_id
FROM parts p
WHERE REPLACE(UPPER(p.oem_part_number), ' ', '') LIKE $1
OR p.name_part ILIKE $2
OR p.name_es ILIKE $2
ORDER BY p.oem_part_number
LIMIT $3
""", f'%{clean_q}%', f'%{q}%', limit)
part_ids = [r['id_part'] for r in rows]
vehicle_map = {}
if part_ids:
vrows = await conn.fetch("""
SELECT part_id, name_brand, name_model, year_car
FROM part_vehicle_preview
WHERE part_id = ANY($1)
""", part_ids)
for vr in vrows:
vehicle_map[vr['part_id']] = f"{vr['name_brand']} {vr['name_model']} {vr['year_car']}"
results = []
for r in rows:
results.append({
'id': r['id_part'],
'oem_part_number': r['oem_part_number'],
'name': r['name_part'],
'name_es': r['name_es'],
'image_url': r['image_url'],
'group_id': r['group_id'],
'vehicle_info': vehicle_map.get(r['id_part'], ''),
})
return jsonify({'data': results})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5002)