303 lines
7.5 KiB
Go
303 lines
7.5 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
_ "embed"
|
|
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"github.com/maximotejeda/us_dop_bot/models"
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
type DB struct {
|
|
*sql.DB
|
|
log *slog.Logger
|
|
}
|
|
|
|
type change struct {
|
|
Before models.Institucion `json:"before"`
|
|
After models.Institucion `json:"after"`
|
|
}
|
|
|
|
type Message struct {
|
|
Message string `json:"message"`
|
|
Data change `json:"data"`
|
|
Error error `json:"error"`
|
|
}
|
|
|
|
// Dial
|
|
func Dial(path string, log *slog.Logger) *DB {
|
|
db, err := sql.Open("sqlite", path)
|
|
if err != nil {
|
|
fmt.Printf("opening database: %s", err.Error())
|
|
panic("opening database")
|
|
}
|
|
if err := db.Ping(); err != nil {
|
|
fmt.Printf("pinging database: %s", err.Error())
|
|
panic("pinging database")
|
|
}
|
|
return &DB{db, log}
|
|
}
|
|
|
|
// Inspect
|
|
// Handle behavior of the changes
|
|
// Will report errors to a nats consumer
|
|
func (db *DB) Inspect(enter models.Institucion) error {
|
|
if db == nil {
|
|
return fmt.Errorf("nil or empty database")
|
|
}
|
|
|
|
// Get last row added
|
|
inst, err := db.GetLatest(enter.Parser, enter.Name)
|
|
// if no rows are found because of first enter a name - parser ?
|
|
if errors.Is(sql.ErrNoRows, err) {
|
|
db.log.Info("adding new item to table: ", "parse", enter.Parser, "name", enter.Name)
|
|
|
|
if err != nil {
|
|
db.log.Error("marshaling struct", "error", err)
|
|
}
|
|
|
|
return db.AddNew(enter)
|
|
}
|
|
// check prices compra venta
|
|
if inst == nil {
|
|
db.log.Error("row is nil", "name", enter.Name, "parser", enter.Parser)
|
|
return fmt.Errorf("row is nil, not entering row")
|
|
}
|
|
if enter.Compra == inst.Compra && enter.Venta == inst.Venta {
|
|
return nil
|
|
} else {
|
|
// if one of them changes create a new row
|
|
db.log.Info("change registered, adding item", "parse", enter.Parser, "name", enter.Name, "compra enter", enter.Compra, "compra db", inst.Compra, "venta enter", enter.Venta, "venta db", inst.Venta)
|
|
|
|
if err != nil {
|
|
db.log.Error("marshaling struct", "error", err)
|
|
}
|
|
|
|
return db.AddNew(enter)
|
|
}
|
|
}
|
|
|
|
// GetLatest
|
|
// returns the latest row in a specific parser and name
|
|
// we are using DateTime in DB and date.Datetime in go
|
|
func (db *DB) GetLatest(parser string, name string) (inst *models.Institucion, err error) {
|
|
var parsed string
|
|
inst = &models.Institucion{}
|
|
stmt, err := db.Prepare("SELECT name, parser, compra, venta, parsed FROM dolars WHERE parser = ? AND name = ? ORDER BY parsed DESC LIMIT 1;")
|
|
if err != nil {
|
|
db.log.Error("preparing", "error", err.Error())
|
|
return nil, err
|
|
}
|
|
defer stmt.Close()
|
|
|
|
if err := stmt.QueryRow(parser, name).Scan(&inst.Name, &inst.Parser, &inst.Compra, &inst.Venta, &parsed); err != nil {
|
|
db.log.Error("getting latest", "error", err.Error(), "parser", parser, "name", name)
|
|
return nil, err
|
|
}
|
|
|
|
inst.Parsed, err = time.Parse(time.DateTime, parsed)
|
|
if err != nil {
|
|
//db.log.Error("parsed", "error", err.Error())
|
|
return nil, err
|
|
}
|
|
return inst, nil
|
|
}
|
|
|
|
// AddNew
|
|
// Add a new row in the dolar table
|
|
// Will send to nats changes on prices
|
|
func (db *DB) AddNew(row models.Institucion) error {
|
|
stmt, err := db.Prepare("INSERT INTO dolars (name, compra, venta, parser, parsed) VALUES(?,?,?,?,?);")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer stmt.Close()
|
|
parsed := row.Parsed.Format(time.DateTime)
|
|
_, err = stmt.Exec(&row.Name, &row.Compra, &row.Venta, &row.Parser, &parsed)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (db *DB) GetAll() ([]string, error) {
|
|
stmt, err := db.Prepare("SELECT DISTINCT dolars.name FROM dolars WHERE name LIKE '%ban%' OR name LIKE '%scoti%' OR name LIKE '%asociacion%'")
|
|
if err != nil {
|
|
db.log.Error("[db-GetAll]", "error", err)
|
|
return nil, err
|
|
}
|
|
rows, err := stmt.Query()
|
|
if err != nil {
|
|
db.log.Error("[db-GetAll-stmt]", "error", err)
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
insts := []string{}
|
|
for rows.Next() {
|
|
inst := ""
|
|
|
|
if err = rows.Scan(&inst); err != nil {
|
|
return nil, err
|
|
}
|
|
if inst == "" {
|
|
continue
|
|
}
|
|
insts = append(insts, inst)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return insts, err
|
|
}
|
|
return insts, nil
|
|
|
|
}
|
|
func (db *DB) GetBancos() ([]string, error) {
|
|
stmt, err := db.Prepare("SELECT DISTINCT dolars.name FROM dolars WHERE name LIKE '%ban%' OR name LIKE '%scoti%'")
|
|
if err != nil {
|
|
db.log.Error("[inst-GetAll]", "error", err)
|
|
return nil, err
|
|
}
|
|
rows, err := stmt.Query()
|
|
if err != nil {
|
|
db.log.Error("[inst-GetAll-stmt]", "error", err)
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
insts := []string{}
|
|
for rows.Next() {
|
|
inst := ""
|
|
|
|
if err = rows.Scan(&inst); err != nil {
|
|
return nil, err
|
|
}
|
|
if inst == "" {
|
|
continue
|
|
}
|
|
insts = append(insts, inst)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return insts, err
|
|
}
|
|
return insts, nil
|
|
|
|
}
|
|
func (db *DB) GetCajas() ([]string, error) {
|
|
stmt, err := db.Prepare("SELECT DISTINCT dolars.name FROM dolars WHERE name LIKE '%asociacion%'")
|
|
if err != nil {
|
|
db.log.Error("[inst-GetAll]", "error", err)
|
|
return nil, err
|
|
}
|
|
rows, err := stmt.Query()
|
|
if err != nil {
|
|
db.log.Error("[inst-GetAll-stmt]", "error", err)
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
insts := []string{}
|
|
for rows.Next() {
|
|
inst := ""
|
|
|
|
if err = rows.Scan(&inst); err != nil {
|
|
return nil, err
|
|
}
|
|
if inst == "" {
|
|
continue
|
|
}
|
|
insts = append(insts, inst)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return insts, err
|
|
}
|
|
return insts, nil
|
|
|
|
}
|
|
|
|
func (db *DB) GetAgentes() ([]string, error) {
|
|
stmt, err := db.Prepare("SELECT DISTINCT dolars.name FROM dolars WHERE name NOT LIKE '%ban%' AND name NOT LIKE '%scoti%' AND name NOT LIKE '%asociacion%'")
|
|
if err != nil {
|
|
db.log.Error("[inst-GetAll]", "error", err)
|
|
return nil, err
|
|
}
|
|
rows, err := stmt.Query()
|
|
if err != nil {
|
|
db.log.Error("[inst-GetAll-stmt]", "error", err)
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
insts := []string{}
|
|
for rows.Next() {
|
|
inst := ""
|
|
|
|
if err = rows.Scan(&inst); err != nil {
|
|
return nil, err
|
|
}
|
|
if inst == "" {
|
|
continue
|
|
}
|
|
insts = append(insts, inst)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return insts, err
|
|
}
|
|
return insts, nil
|
|
|
|
}
|
|
|
|
func (db *DB) GetLastPrice(name string) (inst *models.Institucion, err error) {
|
|
var parsed string
|
|
inst = &models.Institucion{}
|
|
stmt, err := db.Prepare("SELECT name, parser, compra, venta, parsed FROM dolars WHERE name = ? ORDER BY parsed DESC LIMIT 1;")
|
|
if err != nil {
|
|
db.log.Error("preparing", "error", err.Error())
|
|
return nil, err
|
|
}
|
|
defer stmt.Close()
|
|
|
|
if err := stmt.QueryRow(name).Scan(&inst.Name, &inst.Parser, &inst.Compra, &inst.Venta, &parsed); err != nil {
|
|
db.log.Error("getting last price", "error", err.Error(), "name", name)
|
|
return nil, err
|
|
}
|
|
|
|
inst.Parsed, err = time.Parse(time.DateTime, parsed)
|
|
if err != nil {
|
|
//db.log.Error("parsed", "error", err.Error())
|
|
return nil, err
|
|
}
|
|
return inst, nil
|
|
|
|
}
|
|
func (db *DB) GetChangeSince(name string, duration time.Duration) (insts []*models.Institucion, err error) {
|
|
date := time.Now().Add(-duration).Format(time.DateTime)
|
|
stmt, err := db.Prepare("SELECT name, parser, compra, venta, parsed FROM dolars WHERE name = ? AND parsed > ? ORDER BY parsed DESC;")
|
|
if err != nil {
|
|
db.log.Error("[GetChangeSince] preparing", "error", err.Error())
|
|
return nil, err
|
|
}
|
|
defer stmt.Close()
|
|
rows, err := stmt.Query(name, date)
|
|
if err != nil {
|
|
db.log.Error("[GetChangeSince] preparing", "error", err.Error())
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
inst := models.Institucion{}
|
|
parsed := ""
|
|
if err := rows.Scan(&inst.Name, &inst.Parser, &inst.Compra, &inst.Venta, &parsed); err != nil {
|
|
db.log.Error("[GetChangeSince] scanning", "error", err)
|
|
return nil, err
|
|
}
|
|
inst.Parsed, err = time.Parse(time.DateTime, parsed)
|
|
if err != nil {
|
|
//db.log.Error("parsed", "error", err.Error())
|
|
continue
|
|
}
|
|
insts = append(insts, &inst)
|
|
}
|
|
return insts, nil
|
|
}
|