(import-macros {: map : reduce} :lib.macro) (tset package :path (.. package.path ";./vendor/lpeglj/?.lua")) (local fennel (require :vendor.fennel)) (local array (require :lib.array)) (local cache (require :lib.cache)) (local {: must} (require :lib.utils)) (local artoftea (require :parser.artoftea)) (local batatcha (require :parser.batatcha)) (local chaekshop (require :parser.chaekshop)) (local clubcha (require :parser.clubcha)) (local daochai (require :parser.daochai)) (local gorkovchay (require :parser.gorkovchay)) (local ipuer (require :parser.ipuer)) (local kolokolnikovchai (require :parser.kolokolnikovchai)) (local moychay (require :parser.moychay)) (local ozchai (require :parser.ozchai)) (local suhexuan (require :parser.suhexuan)) (local tea108 (require :parser.tea108)) (local teaworkshop (require :parser.teaworkshop)) (local yoceramics (require :parser.yoceramics)) (when _G.unpack (tset table :unpack _G.unpack)) (local db (must (luna.db.open "file:var/db.sqlite?_journal=WAL&_sync=NORMAL&_txlock=immediate"))) (must (luna.db.exec db " PRAGMA foreign_keys=ON; PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL; CREATE VIRTUAL TABLE IF NOT EXISTS search USING fts5(title, fid, `table`); CREATE TABLE IF NOT EXISTS products( url TEXT NOT NULL PRIMARY KEY, site TEXT NOT NULL, title TEXT NOT NULL, description TEXT NOT NULL, year INT NOT NULL, image TEXT NOT NULL, price REAL NOT NULL, weight REAL NOT NULL, volume REAL NOT NULL, price_per REAL NOT NULL, archived BOOL NOT NULL, update_time DATETIME NOT NULL ); CREATE TABLE IF NOT EXISTS permanent_products( url TEXT NOT NULL PRIMARY KEY, creation_time DATETIME NOT NULL ); CREATE TABLE IF NOT EXISTS product_tags( product TEXT NOT NULL REFERENCES products(url), tag DATETIME NOT NULL REFERENCES tags(title) ); CREATE UNIQUE INDEX IF NOT EXISTS product_tags_idx ON product_tags(product, tag); CREATE TABLE IF NOT EXISTS tags( title TEXT NOT NULL PRIMARY KEY, creation_time DATETIME NOT NULL ); CREATE TABLE IF NOT EXISTS metrics( fingerprint TEXT NOT NULL, url TEXT NOT NULL, creation_time DATETIME NOT NULL ); CREATE TABLE IF NOT EXISTS cache( key TEXT NOT NULL PRIMARY KEY, value TEXT );" [])) (fn now [] (os.date "%Y-%m-%d %H:%M:%S")) (fn store-tags [tx tags] (when (< 0 (# tags)) (local sql (.. "INSERT OR IGNORE INTO tags VALUES " (array.join (map (fn [_ _] "(?, ?)") tags) ", "))) (local vars (reduce (fn [_ tag rest] (array.concat rest [tag (now)])) tags [])) (must (luna.db.exec-tx tx sql vars)))) (fn store-product-tags [tx products] (when (< 0 (# products)) ;; flatten product tags (local tags (reduce (fn [_ product res] (array.concat res (map (fn [_ tag] [product.url tag]) product.tags))) products [])) (local sql (.. "INSERT OR IGNORE INTO product_tags VALUES " (array.join (map (fn [_ _] "(?, ?)") tags) ", "))) (local vars (reduce (fn [_ tags rest] (array.concat rest tags)) tags [])) (must (luna.db.exec-tx tx sql vars)))) (fn store-products [tx products] (local update-time (now)) ;; store product (let [sql (.. "INSERT OR REPLACE INTO products" "(url, site, title, description, year, image, price, weight, volume, price_per, archived, update_time)" " VALUES " (array.join (map (fn [_ _] "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") products) ", ")) vars (reduce (fn [_ product rest] (array.concat rest [(or product.url "") (or product.site "") (or product.title "") (or product.description "") (or product.year 0) (or product.image "") (or product.price 0) (or product.weight 0) (or product.volume 0) (or product.price-per 0) (or product.archived false) update-time])) products [])] (must (luna.db.exec-tx tx sql vars))) ;; store permenanent product data (let [sql (.. "INSERT OR IGNORE INTO permanent_products" "(url, creation_time)" " VALUES " (array.join (map (fn [_ _] "(?, ?)") products) ", ")) vars (reduce (fn [_ product rest] (array.concat rest [(or product.url "") update-time])) products [])] (must (luna.db.exec-tx tx sql vars))) ;; store tags (store-tags tx (array.unique (array.flatten (map (fn [_ v] v.tags) products)))) (store-product-tags tx products) ;; archive previous products (local site (. products 1 :site)) (when site (must (luna.db.exec-tx tx "UPDATE products SET archived = true WHERE site = ? AND update_time < ?" [site update-time])))) (fn populate-search-table [db] (local tx (must (luna.db.begin db))) (must (luna.db.exec-tx tx "DELETE FROM search" [])) (must (luna.db.exec-tx tx "INSERT INTO search SELECT title, url, 'products' FROM products;" [])) (must (luna.db.commit tx))) (each [_ parser (pairs [batatcha yoceramics daochai gorkovchay ozchai suhexuan ipuer artoftea clubcha chaekshop kolokolnikovchai tea108 teaworkshop moychay])] (local products (parser.products)) (when (< 0 (# products)) ;; replace with with-tx (local tx (must (luna.db.begin db))) (local (ok? err) (pcall store-products tx products)) (must (luna.db.commit tx)) (when (not ok?) (print (string.format "Error fetching %s:\n%s" parser.title err))))) (cache.clear db "page:") (populate-search-table db)