renderizar best selling en sitio!

This commit is contained in:
Cat /dev/Nulo 2024-02-06 19:24:37 -03:00
parent fb0d5cd7d5
commit ca5d0e81dc
5 changed files with 86 additions and 110 deletions

View file

@ -42,6 +42,9 @@ importers:
drizzle-orm: drizzle-orm:
specifier: ^0.29.1 specifier: ^0.29.1
version: 0.29.3(@types/better-sqlite3@7.6.8)(better-sqlite3@9.2.2) version: 0.29.3(@types/better-sqlite3@7.6.8)(better-sqlite3@9.2.2)
zod:
specifier: ^3.22.4
version: 3.22.4
devDependencies: devDependencies:
'@sveltejs/adapter-node': '@sveltejs/adapter-node':
specifier: ^2.0.2 specifier: ^2.0.2
@ -2859,4 +2862,3 @@ packages:
/zod@3.22.4: /zod@3.22.4:
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
dev: true

View file

@ -14,8 +14,11 @@
"format": "prettier --write ." "format": "prettier --write ."
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-node": "^2.0.2",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/better-sqlite3": "^7.6.8",
"@types/node": "^20.10.6",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"db-datos": "workspace:^", "db-datos": "workspace:^",
"postcss": "^8.4.32", "postcss": "^8.4.32",
@ -28,10 +31,7 @@
"tailwindcss": "^3.3.6", "tailwindcss": "^3.3.6",
"tslib": "^2.4.1", "tslib": "^2.4.1",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"vite": "^5.0.3", "vite": "^5.0.3"
"@sveltejs/adapter-node": "^2.0.2",
"@types/better-sqlite3": "^7.6.8",
"@types/node": "^20.10.6"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
@ -39,6 +39,7 @@
"chart.js": "^4.4.1", "chart.js": "^4.4.1",
"chartjs-adapter-dayjs-4": "^1.0.4", "chartjs-adapter-dayjs-4": "^1.0.4",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"drizzle-orm": "^0.29.1" "drizzle-orm": "^0.29.1",
"zod": "^3.22.4"
} }
} }

View file

@ -1,10 +1,19 @@
<script lang="ts"> <script lang="ts" context="module">
export let product: { ean: string; name: string; imageUrl?: string | null }; export type Product = { ean: string; name: string; imageUrl: string | null };
</script> </script>
<a href={`/ean/${product.ean}`} class="flex"> <script lang="ts">
export let product: Product;
</script>
<a href={`/ean/${product.ean}`} class="flex gap-2">
{#if product.imageUrl} {#if product.imageUrl}
<img src={product.imageUrl} alt={product.name} class="max-h-48" /> <img
src={product.imageUrl}
alt={product.name}
class="max-h-48"
loading="lazy"
/>
{/if} {/if}
<p class="text-xl">{product.name}</p> <p class="text-xl">{product.name}</p>
</a> </a>

View file

@ -1,64 +1,60 @@
import type { PageData, PageServerLoad } from "./$types"; import type { PageData, PageServerLoad } from "./$types";
import { getDb, schema } from "$lib/server/db"; import { getDb, schema } from "$lib/server/db";
const { precios } = schema; const { precios, bestSelling } = schema;
import { desc, sql } from "drizzle-orm"; import { desc, max, sql } from "drizzle-orm";
import { import {
Supermercado, Supermercado,
hostBySupermercado, hostBySupermercado,
supermercados, supermercados,
} from "db-datos/supermercado"; } from "db-datos/supermercado";
import z from "zod";
import type { Product } from "$lib/ProductPreview.svelte";
let cache: Promise<{ key: Date; data: { precios: Precios } }> = doQuery(); type Data = {
category: string;
products: Product[];
}[];
let cache: Promise<{ key: Date; data: Data }> = doQuery();
async function doQuery() { async function doQuery() {
const db = await getDb(); const db = await getDb();
console.time("ean");
const eans = await db const categories = await db
.select({ .select({
ean: precios.ean, fetchedAt: bestSelling.fetchedAt,
category: bestSelling.category,
eansJson: bestSelling.eansJson,
}) })
.from(precios) .from(bestSelling)
.groupBy(precios.ean) .groupBy(bestSelling.category)
.orderBy(sql`random()`) .having(max(bestSelling.fetchedAt));
.limit(50);
console.timeEnd("ean");
return; const categoriesWithProducts = await Promise.all(
categories.map(async (category) => {
const eans = z.array(z.string()).parse(JSON.parse(category.eansJson));
const precioss = await Promise.all( const products = await db
supermercados.map( .select({
async ( ean: precios.ean,
supermercado, name: precios.name,
): Promise< imageUrl: precios.imageUrl,
[ })
Supermercado, .from(precios)
{ ean: string; name: string | null; imageUrl: string | null }[], .where(sql`${precios.ean} in ${eans}`)
] .groupBy(precios.ean)
> => { .having(max(precios.fetchedAt));
const host = hostBySupermercado[supermercado];
console.time(supermercado); return {
const q = db category: category.category,
.select({ products: eans
ean: precios.ean, .map((ean) => products.find((p) => p.ean === ean))
name: precios.name, .filter((x): x is Product => !!x && !!x.name),
imageUrl: precios.imageUrl, };
}) }),
.from(precios)
.groupBy(precios.ean)
.having(sql`max(fetched_at)`)
.where(
sql`ean in ${eans.map((x) => x.ean)} and in_stock and url like ${`%${host}%`}`,
);
// console.debug(q.toSQL());
const res = await q;
console.timeEnd(supermercado);
return [supermercado, res];
},
),
); );
const data = { precios: precioss.flatMap(([_, r]) => r) };
return { key: new Date(), data }; return { key: new Date(), data: categoriesWithProducts };
} }
setInterval( setInterval(
@ -69,14 +65,8 @@ setInterval(
4 * 60 * 60 * 1000, 4 * 60 * 60 * 1000,
); );
type Precios = {
ean: string;
name: string | null;
imageUrl: string | null;
}[];
export const load: PageServerLoad = async ({ export const load: PageServerLoad = async ({
params, params,
}): Promise<{ precios: Precios }> => { }): Promise<{ data: Data }> => {
return (await cache).data; return { data: (await cache).data };
}; };

View file

@ -3,53 +3,27 @@
import type { PageData } from "./$types"; import type { PageData } from "./$types";
export let data: PageData; export let data: PageData;
$: precios = data.precios.filter(
(d): d is { ean: string; name: string; imageUrl: string | null } => const categoryLabels: { [key in string]: string } = {
!!d.name, almacen: "Almacen",
); bebidas: "Bebidas",
$: productos = precios.reduce( "frutas-y-verduras": "Frutas y Verduras",
(prev, curr) => [ };
...prev,
...(prev.find((p) => p.ean === curr.ean) ? [] : [curr]),
],
[] as { ean: string; name: string; imageUrl: string | null }[],
);
</script> </script>
<h1 class="text-xl">WIP</h1> {#each data.data as { category, products }}
<section class="my-6">
<section> <h2 class="text-2xl font-bold">
<h2 class="text-lg font-bold">Ejemplos</h2> {categoryLabels[category] ?? category}
<ul> </h2>
<li> <ul
<a href="/ean/7790070410795"> class="grid max-w-full grid-flow-col grid-rows-2 gap-x-8 gap-y-4 overflow-x-auto"
Cookies Sabor Vainilla Con Chips De Chocolate Exquisita Paq 300 Grm >
</a> {#each products as product}
</li> <li class="w-96">
<li> <ProductPreview {product} />
<a href="/ean/7794000006911"> </li>
Sopa Instantánea KNORR QUICK Zapallo Romero Sobres 5 Un. {/each}
</a> </ul>
</li> </section>
<li> {/each}
<a href="/ean/7798062540253">Agua Saborizada Levité Pera 1,5 Lts.</a>
</li>
<li>
<a href="/ean/7790895000430">Gaseosa Coca-Cola Sabor Original 1,5 Lts.</a>
</li>
<li>
<a href="/ean/7792200000128">Bizcochos Agridulc 9 De Oro Paq 200 Grm</a>
</li>
</ul>
</section>
<section>
<h2 class="text-lg font-bold">Random</h2>
<ul class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{#each productos as product}
<li>
<ProductPreview {product} />
</li>
{/each}
</ul>
</section>