1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
package main
import (
"database/sql"
"fmt"
"strings"
)
func (app *App) createTables() error {
query1 := `CREATE TABLE IF NOT EXISTS shop(
id INTEGER PRIMARY KEY AUTO_INCREMENT,
name varchar(255) UNIQUE NOT NULL,
url varchar(255) UNIQUE NOT NULL,
short_url varchar(255) UNIQUE,
logo_url text,
shipping_costs int,
free_shipping text
)`
_, err := app.DB.Exec(query1)
if err != nil {
return err
}
query2 := `CREATE TABLE IF NOT EXISTS angebot (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
shop INT NOT NULL,
name VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
short_url TEXT,
abv DECIMAL(10,2) NOT NULL,
volume DECIMAL(10,2) NOT NULL,
original_price INT NOT NULL,
discounted_price INT NOT NULL,
base_price INT NOT NULL,
image_url TEXT,
spirit_type TEXT NOT NULL,
website TEXT,
website_raw TEXT NOT NULL,
valid_until INT NOT NULL DEFAULT 0,
created_at INT,
FOREIGN KEY(shop) REFERENCES shop(id),
UNIQUE(name,shop,volume,abv,original_price,discounted_price,valid_until),
UNIQUE(name,shop,url,created_at)
)`
_, err = app.DB.Exec(query2)
if err != nil {
return err
}
views := []string{"whisky", "wodka", "rum", "gin", "misc", "all"}
for _, v := range views {
var spirit_type string
if v == "misc" {
spirit_type = `spirit_type NOT IN ("` + strings.Join(views, `", "`) + `") AND`
} else if v == "all" {
spirit_type = "1 = 1 AND "
} else {
spirit_type = `spirit_type = "` + v + `" AND`
}
view_query := `CREATE OR REPLACE VIEW ` + v + `_view AS
SELECT
angebot.id, angebot.name, angebot.abv, angebot.volume, angebot.url as long_url, angebot.short_url as url, spirit_type,
original_price/100 as original_price, discounted_price/100 as discounted_price, angebot.base_price/100 as base_price, image_url,
shop.name as shop, shop.id as shop_id, shop.short_url as shop_url, shop.shipping_costs/100 as shipping_costs, shop.free_shipping,
ROUND(100-((discounted_price/original_price)*100)) AS procent, created_at
FROM angebot
JOIN shop ON angebot.shop = shop.id
WHERE
` + spirit_type + ` (valid_until = 0 OR valid_until > (SELECT UNIX_TIMESTAMP()))`
_, err = app.DB.Exec(view_query)
if err != nil {
return err
}
}
return err
}
func (app *App) save_offer(W []Angebot) error {
query := `INSERT INTO angebot (shop, name, url, abv, volume, original_price, discounted_price, base_price, valid_until, image_url, website_raw, spirit_type, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
stmt, err := app.DB.Prepare(query)
if err != nil {
Debug(err, "Save Offer: Preparing query failed")
return err
}
defer stmt.Close()
for _, o := range W {
var found int
if o.Discounted_price == 0 || o.Original_price == 0 {
continue
}
// resembles UNIQUE constraint
detect_duplicate_query := `SELECT 1 FROM all_view WHERE name = ? AND shop_id = ? AND
volume = ? AND abv = ? AND original_price = ? AND discounted_price = ? AND valid_until = ?`
err := app.DB.QueryRow(detect_duplicate_query, o.Name, o.Shop, o.Volume, o.Abv, o.Original_price, o.Discounted_price, o.Valid_until).Scan(&found)
if err == sql.ErrNoRows {
_, err = stmt.Exec(o.Shop, o.Name, o.Url, o.Abv, o.Volume, o.Original_price, o.Discounted_price, o.Base_price, o.Valid_until, o.Image_url, o.Website, o.Spirit_type, app.Now)
if err != nil {
o.error_msg = err.Error()
o.error_ctx = "Save Offer: Inserting offer failed"
WarnOffer(o, "Save Offer: Inserting offer failed")
return err
}
}
}
//return app.remove_expired(W)
return nil
}
func (app *App) remove_expired(W []Angebot, shop Shop) error {
query := `SELECT id, name, shop, volume, abv, original_price, discounted_price FROM angebot WHERE shop = ? AND created_at < ?
AND (valid_until = 0 OR valid_until > ?)`
rows, err := app.DB.Queryx(query, shop.Id, app.Now, app.Now)
if err != nil {
Debug(err, "Remove expired: Query failed")
return err
}
defer rows.Close()
for rows.Next() {
var offer_db Angebot
err = rows.StructScan(&offer_db)
if err != nil {
Debug(err, "Remove expired: Struct scan failed")
return err
}
if !app.offer_contains(W, offer_db) {
DebugOffer(offer_db, "Contains not - Set to expire")
expire_query := `UPDATE angebot SET valid_until = ? WHERE id = ?`
_, err = app.DB.Exec(expire_query, app.Now, offer_db.Id)
if err != nil {
offer_db.error_msg = err.Error()
offer_db.error_ctx = fmt.Sprintf("UPDATE angebot SET valid_until = %d WHERE id = %d", app.Now, offer_db.Id)
WarnOffer(offer_db, "Remove expired: Update failed")
return err
}
}
DebugOffer(offer_db, "Contains! DOES NOT EXPIRE!")
}
return nil
}
func (app *App) offer_contains(W []Angebot, offer_db Angebot) bool {
if len(W) < 1 {
return false
}
for _, v := range W {
// resembles UNIQUE constraint
if (v.Name == offer_db.Name) && (v.Shop == offer_db.Shop) && (v.Volume == offer_db.Volume) && (v.Abv == offer_db.Abv) && (v.Original_price == offer_db.Original_price) && (v.Discounted_price == offer_db.Discounted_price) && (v.Valid_until == offer_db.Valid_until) {
return true
}
}
return false
}
|