Dumped gorm and refactored database...
This commit is contained in:
parent
3ac516a822
commit
949a78da33
195
src/db.go
195
src/db.go
@ -1,99 +1,150 @@
|
|||||||
package ss13
|
package ss13
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DB struct {
|
const SCHEMA string = `
|
||||||
*gorm.DB
|
CREATE TABLE IF NOT EXISTS servers (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
last_updated DATETIME,
|
||||||
|
title TEXT UNIQUE,
|
||||||
|
game_url TEXT,
|
||||||
|
site_url TEXT,
|
||||||
|
players_current INTEGER,
|
||||||
|
players_avg INTEGER,
|
||||||
|
players_min INTEGER,
|
||||||
|
players_max INTEGER,
|
||||||
|
players_mon INTEGER,
|
||||||
|
players_tue INTEGER,
|
||||||
|
players_wed INTEGER,
|
||||||
|
players_thu INTEGER,
|
||||||
|
players_fri INTEGER,
|
||||||
|
players_sat INTEGER,
|
||||||
|
players_sun INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS server_populations (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
timestamp DATETIME,
|
||||||
|
players INTEGER,
|
||||||
|
server_id INTEGER REFERENCES servers(id) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS server_pop_index ON server_populations(server_id);
|
||||||
|
`
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
*sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenSqliteDB(args ...interface{}) (*DB, error) {
|
type TX struct {
|
||||||
db, e := gorm.Open("sqlite3", args...)
|
*sqlx.Tx
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenDatabase(path string) (*Database, error) {
|
||||||
|
db, e := sqlx.Connect("sqlite3", path)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return nil, e
|
return nil, e
|
||||||
}
|
}
|
||||||
db.AutoMigrate(&Server{})
|
_, e = db.Exec(SCHEMA)
|
||||||
db.AutoMigrate(&ServerPopulation{})
|
if e != nil {
|
||||||
return &DB{db}, nil
|
return nil, e
|
||||||
|
}
|
||||||
|
return &Database{db}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) NewTransaction() *DB {
|
func (db *Database) AllServers() ([]*Server, error) {
|
||||||
return &DB{db.Begin()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) AllServers() []*Server {
|
|
||||||
var tmp []*Server
|
var tmp []*Server
|
||||||
db.Order("last_updated desc, players_current desc, players_avg desc, title").Find(&tmp)
|
e := db.Select(&tmp, `SELECT * FROM servers ORDER BY
|
||||||
return tmp
|
last_updated DESC,
|
||||||
|
players_current DESC,
|
||||||
|
players_avg DESC,
|
||||||
|
title;`)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return tmp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) GetServer(id int) (*Server, error) {
|
func (db *Database) GetServer(id int) (*Server, error) {
|
||||||
var tmp Server
|
var tmp Server
|
||||||
if db.First(&tmp, id).RecordNotFound() {
|
e := db.Get(&tmp, "SELECT * FROM servers WHERE id = ? LIMIT 1;", id)
|
||||||
return nil, fmt.Errorf("Server not found")
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
}
|
}
|
||||||
return &tmp, nil
|
return &tmp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) GetOldServers(ts time.Time) []*Server {
|
func (db *Database) GetOldServers(ts time.Time) ([]*Server, error) {
|
||||||
var tmp []*Server
|
var tmp []*Server
|
||||||
db.Where("last_updated < ?", ts).Find(&tmp)
|
e := db.Select(&tmp, "SELECT * FROM servers WHERE last_updated < ?;", ts)
|
||||||
return tmp
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return tmp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) RemoveOldServers(ts time.Time) {
|
func (db *Database) RemoveOldServers(ts time.Time) error {
|
||||||
db.Where("last_updated < datetime(?, '-7 days')", ts).Delete(Server{})
|
_, e := db.Exec("DELETE FROM servers WHERE last_updated < datetime(?, '-7 days');", ts)
|
||||||
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) GetServerPopulation(id int, d time.Duration) []*ServerPopulation {
|
func (db *Database) GetServerPopulation(id int, d time.Duration) ([]*ServerPopulation, error) {
|
||||||
var tmp []*ServerPopulation
|
var tmp []*ServerPopulation
|
||||||
t := time.Now().Add(-d)
|
t := time.Now().Add(-d)
|
||||||
db.Order("timestamp desc, server_id").Where("server_id = ? and timestamp > ?", id, t).Find(&tmp)
|
e := db.Select(&tmp, `SELECT * FROM server_populations WHERE
|
||||||
return tmp
|
server_id = ? AND timestamp > ?
|
||||||
}
|
ORDER BY timestamp DESC, server_id;`, id, t)
|
||||||
|
if e != nil {
|
||||||
func (db *DB) InsertOrSelect(s *RawServerData) int {
|
return nil, e
|
||||||
var tmp Server
|
|
||||||
newserver := Server{
|
|
||||||
LastUpdated: s.Timestamp,
|
|
||||||
Title: s.Title,
|
|
||||||
GameUrl: s.Game_url,
|
|
||||||
SiteUrl: s.Site_url,
|
|
||||||
PlayersCurrent: s.Players,
|
|
||||||
PlayersAvg: s.Players,
|
|
||||||
PlayersMin: s.Players,
|
|
||||||
PlayersMax: s.Players,
|
|
||||||
}
|
}
|
||||||
db.Where("title = ?", s.Title).Attrs(newserver).FirstOrCreate(&tmp)
|
return tmp, nil
|
||||||
return tmp.ID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) AddServerPopulation(id int, s *RawServerData, now time.Time) {
|
func (db *TX) InsertOrSelect(s *RawServerData) (int, error) {
|
||||||
var tmp Server
|
var tmp Server
|
||||||
db.Where("id = ?", id).First(&tmp)
|
e := db.Get(&tmp, "SELECT * FROM servers WHERE title = ? LIMIT 1;", s.Title)
|
||||||
pop := ServerPopulation{
|
if e == nil {
|
||||||
Timestamp: now,
|
return tmp.ID, nil
|
||||||
Players: s.Players,
|
|
||||||
Server: tmp,
|
|
||||||
}
|
}
|
||||||
db.Create(&pop)
|
|
||||||
|
r, e := db.Exec(`INSERT INTO servers (
|
||||||
|
last_updated, title, game_url, site_url, players_current,
|
||||||
|
players_avg, players_min, players_max
|
||||||
|
) VALUES(?, ?, ?, ?, ?, ?, ?, ?);`,
|
||||||
|
s.Timestamp,
|
||||||
|
s.Title,
|
||||||
|
s.Game_url,
|
||||||
|
s.Site_url,
|
||||||
|
s.Players, s.Players, s.Players, s.Players)
|
||||||
|
if e != nil {
|
||||||
|
return -1, e
|
||||||
|
}
|
||||||
|
id, e := r.LastInsertId()
|
||||||
|
if e != nil {
|
||||||
|
return -1, e
|
||||||
|
}
|
||||||
|
return int(id), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) UpdateServerStats(id int, s *RawServerData, now time.Time) {
|
func (db *TX) AddServerPopulation(id int, s *RawServerData, now time.Time) error {
|
||||||
var tmp Server
|
_, e := db.Exec(`INSERT INTO server_populations (
|
||||||
|
timestamp, players, server_id
|
||||||
|
) VALUES (?, ?, ?);`, now, s.Players, id)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *TX) UpdateServerStats(id int, s *RawServerData, now time.Time) error {
|
||||||
period := now.Add(-time.Duration(30*24) * time.Hour)
|
period := now.Add(-time.Duration(30*24) * time.Hour)
|
||||||
db.Where("id = ?", id).First(&tmp)
|
rows, e := db.Queryx(`SELECT timestamp, players FROM server_populations
|
||||||
rows, err := db.Table("server_populations").Where("server_id = ? AND timestamp > ?", tmp.ID, period).Select("timestamp, players").Order("timestamp desc").Rows()
|
WHERE server_id = ? AND timestamp > ?
|
||||||
if err != nil {
|
ORDER BY timestamp DESC`, id, period)
|
||||||
log.Panic(err)
|
if e != nil {
|
||||||
return
|
return e
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
@ -119,20 +170,18 @@ func (db *DB) UpdateServerStats(id int, s *RawServerData, now time.Time) {
|
|||||||
day_counts[day]++
|
day_counts[day]++
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp.LastUpdated = s.Timestamp
|
_, e = db.Exec(`UPDATE servers SET last_updated = ?, players_current = ?,
|
||||||
tmp.Title = s.Title
|
players_avg = ?, players_min = ?, players_max = ?, players_mon = ?,
|
||||||
tmp.GameUrl = s.Game_url
|
players_tue = ?, players_wed = ?, players_thu = ?, players_fri = ?,
|
||||||
tmp.SiteUrl = s.Site_url
|
players_sat = ?, players_sun = ?
|
||||||
tmp.PlayersCurrent = s.Players
|
WHERE id = ?;`, s.Timestamp, s.Players, sum/count, min, max,
|
||||||
tmp.PlayersAvg = sum / count
|
day_sums[1]/day_counts[1],
|
||||||
tmp.PlayersMin = min
|
day_sums[2]/day_counts[2],
|
||||||
tmp.PlayersMax = max
|
day_sums[3]/day_counts[3],
|
||||||
tmp.PlayersMon = day_sums[1] / day_counts[1]
|
day_sums[4]/day_counts[4],
|
||||||
tmp.PlayersTue = day_sums[2] / day_counts[2]
|
day_sums[5]/day_counts[5],
|
||||||
tmp.PlayersWed = day_sums[3] / day_counts[3]
|
day_sums[6]/day_counts[6],
|
||||||
tmp.PlayersThu = day_sums[4] / day_counts[4]
|
day_sums[0]/day_counts[0],
|
||||||
tmp.PlayersFri = day_sums[5] / day_counts[5]
|
id)
|
||||||
tmp.PlayersSat = day_sums[6] / day_counts[6]
|
return e
|
||||||
tmp.PlayersSun = day_sums[0] / day_counts[0]
|
|
||||||
db.Save(&tmp)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,23 +9,23 @@ import (
|
|||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
ID int
|
ID int
|
||||||
LastUpdated time.Time
|
LastUpdated time.Time `db:"last_updated"`
|
||||||
Title string `sql:"type:varchar(64);unique"`
|
Title string
|
||||||
GameUrl string
|
GameUrl string `db:"game_url"`
|
||||||
SiteUrl string
|
SiteUrl string `db:"site_url"`
|
||||||
|
|
||||||
PlayersCurrent int
|
PlayersCurrent int `db:"players_current"`
|
||||||
PlayersAvg int
|
PlayersAvg int `db:"players_avg"`
|
||||||
PlayersMin int
|
PlayersMin int `db:"players_min"`
|
||||||
PlayersMax int
|
PlayersMax int `db:"players_max"`
|
||||||
|
|
||||||
PlayersMon int
|
PlayersMon int `db:"players_mon"`
|
||||||
PlayersTue int
|
PlayersTue int `db:"players_tue"`
|
||||||
PlayersWed int
|
PlayersWed int `db:"players_wed"`
|
||||||
PlayersThu int
|
PlayersThu int `db:"players_thu"`
|
||||||
PlayersFri int
|
PlayersFri int `db:"players_fri"`
|
||||||
PlayersSat int
|
PlayersSat int `db:"players_sat"`
|
||||||
PlayersSun int
|
PlayersSun int `db:"players_sun"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a server's last_updated time since now is greater or equal to X hours.
|
// Check if a server's last_updated time since now is greater or equal to X hours.
|
||||||
@ -63,10 +63,5 @@ type ServerPopulation struct {
|
|||||||
ID int
|
ID int
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
Players int
|
Players int
|
||||||
ServerID int `sql:"index;type:integer REFERENCES servers(id) ON DELETE CASCADE ON UPDATE CASCADE"`
|
ServerID int `db:"server_id"`
|
||||||
Server Server
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// See https://github.com/jinzhu/gorm/issues/635 for why we have to manually add
|
|
||||||
// in a raw REFERENCES statement here.
|
|
||||||
// Hint: Foreign key creation is bugged when using gorm with sqlite.
|
|
||||||
|
|||||||
@ -51,24 +51,50 @@ func (i *Instance) UpdateServers() {
|
|||||||
addServer(s)
|
addServer(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := i.db.NewTransaction()
|
// TODO: move this block into db.go
|
||||||
|
//tx, e := i.db.NewTransaction()
|
||||||
|
t, e := i.db.Beginx()
|
||||||
|
if e != nil {
|
||||||
|
panic(e) // TODO
|
||||||
|
}
|
||||||
|
tx := &TX{t}
|
||||||
|
|
||||||
for _, s := range servers {
|
for _, s := range servers {
|
||||||
fmt.Println("Updating:", s.Title)
|
fmt.Println("Updating:", s.Title)
|
||||||
// get server's db id (or create)
|
// get server's db id (or create)
|
||||||
id := tx.InsertOrSelect(s)
|
id, e := tx.InsertOrSelect(s)
|
||||||
|
if e != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// create new player history point
|
// create new player history point
|
||||||
tx.AddServerPopulation(id, s, now)
|
e = tx.AddServerPopulation(id, s, now)
|
||||||
|
if e != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// update server (urls and player stats)
|
// update server (urls and player stats)
|
||||||
tx.UpdateServerStats(id, s, now)
|
e = tx.UpdateServerStats(id, s, now)
|
||||||
|
if e != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e = tx.Commit()
|
||||||
|
if e != nil {
|
||||||
|
panic(e) // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
e = i.db.RemoveOldServers(Now())
|
||||||
|
if e != nil {
|
||||||
|
panic(e) // TODO
|
||||||
}
|
}
|
||||||
tx.RemoveOldServers(Now())
|
|
||||||
tx.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Instance) getOldServers() []*RawServerData {
|
func (i *Instance) getOldServers() []*RawServerData {
|
||||||
// TODO: there's some bug that makes this func return all servers?
|
|
||||||
var tmp []*RawServerData
|
var tmp []*RawServerData
|
||||||
for _, old := range i.db.GetOldServers(Now()) {
|
servers, e := i.db.GetOldServers(Now())
|
||||||
|
if e != nil {
|
||||||
|
panic(e) // TODO
|
||||||
|
}
|
||||||
|
for _, old := range servers {
|
||||||
s := RawServerData{
|
s := RawServerData{
|
||||||
Title: old.Title,
|
Title: old.Title,
|
||||||
Game_url: old.GameUrl,
|
Game_url: old.GameUrl,
|
||||||
|
|||||||
32
src/web.go
32
src/web.go
@ -18,7 +18,7 @@ type Instance struct {
|
|||||||
Config *Config
|
Config *Config
|
||||||
Debug bool
|
Debug bool
|
||||||
|
|
||||||
db *DB
|
db *Database
|
||||||
router *mux.Router
|
router *mux.Router
|
||||||
tmpls *template.Template
|
tmpls *template.Template
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ func New(debug bool, path string) (*Instance, error) {
|
|||||||
return nil, e
|
return nil, e
|
||||||
}
|
}
|
||||||
|
|
||||||
db, e := OpenSqliteDB(c.DatabasePath)
|
db, e := OpenDatabase(c.DatabasePath)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return nil, e
|
return nil, e
|
||||||
}
|
}
|
||||||
@ -89,6 +89,11 @@ func (i *Instance) page_404(w http.ResponseWriter, r *http.Request) {
|
|||||||
i.tmpls.ExecuteTemplate(w, "page_404.html", nil)
|
i.tmpls.ExecuteTemplate(w, "page_404.html", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Instance) page_500(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
//i.tmpls.ExecuteTemplate(w, "page_500.html", nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Instance) page_static(w http.ResponseWriter, r *http.Request) {
|
func (i *Instance) page_static(w http.ResponseWriter, r *http.Request) {
|
||||||
p := path.Join("static", mux.Vars(r)["file"])
|
p := path.Join("static", mux.Vars(r)["file"])
|
||||||
b, e := Asset(p)
|
b, e := Asset(p)
|
||||||
@ -106,7 +111,12 @@ func (i *Instance) page_static(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Instance) page_index(w http.ResponseWriter, r *http.Request) {
|
func (i *Instance) page_index(w http.ResponseWriter, r *http.Request) {
|
||||||
servers := i.db.AllServers()
|
servers, e := i.db.AllServers()
|
||||||
|
if e != nil {
|
||||||
|
Log("Error for AllServers(): %s", e)
|
||||||
|
i.page_500(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
i.tmpls.ExecuteTemplate(w, "page_index.html", D{
|
i.tmpls.ExecuteTemplate(w, "page_index.html", D{
|
||||||
"pagetitle": "Index",
|
"pagetitle": "Index",
|
||||||
"servers": servers,
|
"servers": servers,
|
||||||
@ -142,11 +152,23 @@ func (i *Instance) page_server(w http.ResponseWriter, r *http.Request) {
|
|||||||
weekday{"Saturday", s.PlayersSat},
|
weekday{"Saturday", s.PlayersSat},
|
||||||
weekday{"Sunday", s.PlayersSun},
|
weekday{"Sunday", s.PlayersSun},
|
||||||
}
|
}
|
||||||
|
weekly, e := i.db.GetServerPopulation(int(id), time.Duration(7*24+12)*time.Hour)
|
||||||
|
if e != nil {
|
||||||
|
Log("Error for GetServerPopulation(): %s", e)
|
||||||
|
i.page_500(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
monthly, e := i.db.GetServerPopulation(int(id), time.Duration(31*24)*time.Hour)
|
||||||
|
if e != nil {
|
||||||
|
Log("Error for GetServerPopulation(): %s", e)
|
||||||
|
i.page_500(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
i.tmpls.ExecuteTemplate(w, "page_server.html", D{
|
i.tmpls.ExecuteTemplate(w, "page_server.html", D{
|
||||||
"pagetitle": s.Title,
|
"pagetitle": s.Title,
|
||||||
"server": s,
|
"server": s,
|
||||||
"weekhistory": i.db.GetServerPopulation(int(id), time.Duration(7*24+12)*time.Hour),
|
"weekhistory": weekly,
|
||||||
"monthhistory": i.db.GetServerPopulation(int(id), time.Duration(31*24)*time.Hour),
|
"monthhistory": monthly,
|
||||||
"weekdayavg": weekdayavg,
|
"weekdayavg": weekdayavg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user