Compare commits

...

3 commits

Author SHA1 Message Date
6cb3c8710f WIP: barra de busqueda
lamentablemente drizzle-kit no está aplicando los triggers en la migracion, sino se puede subir
2023-12-28 10:48:13 -03:00
fcc7449307 escribir containerfile especificando registry 2023-12-28 10:48:08 -03:00
143c87c433 informar cuando se empieza a scrapear 2023-12-28 10:40:28 -03:00
9 changed files with 221 additions and 5 deletions

View file

@ -0,0 +1,17 @@
-- Custom SQL migration file, put you code below! --
create virtual table precios_fts using fts5(ean, url, name, content=precios, content_rowid=id);
insert into precios_fts(rowid,ean,url,name) select id,ean,url,name from precios;
-- https://sqlite.org/fts5.html#external_content_and_contentless_tables
-- Triggers to keep the FTS index up to date.
CREATE TRIGGER precios_fts_ai AFTER INSERT ON precios BEGIN
INSERT INTO precios_fts(rowid, ean, url, name) VALUES (new.id, new.ean, new.url, new.name);
END;
CREATE TRIGGER precios_fts_ad AFTER DELETE ON precios BEGIN
INSERT INTO precios_fts(precios_fts, rowid, ean, url, name) VALUES('delete', old.id, old.ean, old.url, old.name);
END;
CREATE TRIGGER precios_fts_au AFTER UPDATE ON precios BEGIN
INSERT INTO precios_fts(precios_fts, rowid, ean, url, name) VALUES('delete', old.id, old.ean, old.url, old.name);
INSERT INTO precios_fts(rowid, ean, url, name) VALUES (new.id, new.ean, new.url, new.name);
END;

View file

@ -0,0 +1,101 @@
{
"id": "bf90a1cd-ae6a-4dba-a1aa-79f14a11d958",
"prevId": "e1217fdb-6f54-44c5-a04b-c5aebf202102",
"version": "5",
"dialect": "sqlite",
"tables": {
"precios": {
"name": "precios",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"ean": {
"name": "ean",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"fetched_at": {
"name": "fetched_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"precio_centavos": {
"name": "precio_centavos",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"in_stock": {
"name": "in_stock",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"warc_record_id": {
"name": "warc_record_id",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"parser_version": {
"name": "parser_version",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"image_url": {
"name": "image_url",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"precios_ean_idx": {
"name": "precios_ean_idx",
"columns": [
"ean"
],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -29,6 +29,13 @@
"when": 1703521964385,
"tag": "0003_abandoned_landau",
"breakpoints": true
},
{
"idx": 4,
"version": "5",
"when": 1703726748364,
"tag": "0004_left_wolfsbane",
"breakpoints": true
}
]
}

View file

@ -1,17 +1,17 @@
FROM oven/bun:1-alpine AS base
FROM docker.io/oven/bun:1-alpine AS base
WORKDIR /usr/src/app
FROM base AS builder
ENV NODE_ENV=production
COPY . .
RUN bun install --frozen-lockfile \
&& bun build scraper/cli.ts --target=bun --outfile=/tmp/cli.build.js \
&& rm -rf node_modules/
&& bun build scraper/cli.ts --target=bun --outfile=/tmp/cli.build.js \
&& rm -rf node_modules/
FROM base
RUN apk add --no-cache wget zstd cronie tini
RUN printf "#!/bin/sh\nexec bun /bin/scraper auto" > /etc/periodic/daily/scraper \
&& chmod +x /etc/periodic/daily/scraper
&& chmod +x /etc/periodic/daily/scraper
COPY --from=builder /tmp/cli.build.js /bin/scraper
COPY --from=builder /usr/src/app/db-datos/drizzle /bin/drizzle

View file

@ -66,6 +66,8 @@ class Auto {
token: process.env.TELEGRAM_BOT_TOKEN,
chatId: process.env.TELEGRAM_BOT_CHAT_ID,
};
this.inform("[auto] Empezando scrap");
}
async downloadList(supermercado: Supermercado) {

View file

@ -0,0 +1,14 @@
import { count, countDistinct, eq, max, sql } from "drizzle-orm";
import type { PageServerLoad } from "./$types";
import { db, schema } from "$lib/server/db";
const { precios } = schema;
export const load: PageServerLoad = async () => {
const nProductosR = await db
.select({
count: countDistinct(precios.ean),
})
.from(precios);
const nProductos = nProductosR[0].count;
return { nProductos };
};

View file

@ -1,5 +1,43 @@
<script>
<script lang="ts">
import "../app.pcss";
import type { PageData } from "./$types";
export let data: PageData;
</script>
<!-- https://flowbite.com/docs/forms/search-input/ -->
<form method="GET" action="/search">
<div class="flex items-stretch p-4">
<input
type="search"
name="q"
class="block w-full rounded-l-lg border border-s-2 border-gray-300 border-s-gray-50 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:border-s-gray-700 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500"
placeholder={`Buscar entre ${data.nProductos} productos`}
required
/>
<button
type="submit"
class="block rounded-e-lg border border-blue-700 bg-blue-700 p-2.5 text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
<svg
class="h-4 w-4"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 20"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
/>
</svg>
<span class="sr-only">Search</span>
</button>
</div>
</form>
<slot />

View file

@ -0,0 +1,18 @@
import { error } from "@sveltejs/kit";
import { eq, max, sql } from "drizzle-orm";
import type { PageServerLoad } from "./$types";
import { db, schema } from "$lib/server/db";
const { precios } = schema;
export const load: PageServerLoad = async ({ url }) => {
const query = url.searchParams.get("q");
let results: null | { ean: string; name: string }[] = null;
if (query) {
results = db.all(
sql`select ean, name from precios_fts where name match ${query};`,
);
console.debug(results);
}
return { query, results };
};

View file

@ -0,0 +1,19 @@
<script lang="ts">
import type { PageData } from "./$types";
export let data: PageData;
</script>
{#if data.results}
<ul>
{#each data.results as result}
<li>
<a href={`/ean/${result.ean}`}>
{result.name}
</a>
</li>
{/each}
</ul>
{:else}
Probá buscando algo.
{/if}