From 74f881ac9cc53ed2b410856d1bc4ad5a75f85ba8 Mon Sep 17 00:00:00 2001 From: maximo tejeda Date: Sun, 25 Feb 2024 21:22:48 -0400 Subject: [PATCH] ADD external definitions --- edb/db.go | 302 +++++++++++++++++++++++++++++++++++++++++++++++ models/models.go | 13 ++ 2 files changed, 315 insertions(+) create mode 100644 edb/db.go create mode 100644 models/models.go diff --git a/edb/db.go b/edb/db.go new file mode 100644 index 0000000..7c56137 --- /dev/null +++ b/edb/db.go @@ -0,0 +1,302 @@ +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 +} diff --git a/models/models.go b/models/models.go new file mode 100644 index 0000000..f6008d1 --- /dev/null +++ b/models/models.go @@ -0,0 +1,13 @@ +package models + +import ( + "time" +) + +type Institucion struct { + Name string `json:"name"` + Compra float64 `json:"compra"` + Venta float64 `json:"venta"` + Parser string `json:"parser"` + Parsed time.Time `json:"parsed"` +}