From 71d975c2f7463ca752b226e447777b6b2c5bcf9d Mon Sep 17 00:00:00 2001 From: Las Zenow Date: Tue, 30 May 2017 22:10:21 +0000 Subject: [PATCH] Keep count of downladed/viewed books --- createdb.sql | 11 ++++- lib/database/books.go | 1 + lib/database/database.go | 7 ++-- lib/database/ro.go | 14 ++++--- lib/database/stats.go | 59 ++++++++++++++++++++++----- lib/stats.go | 87 +++++++++------------------------------- lib/trantor.go | 21 ++++++++-- 7 files changed, 109 insertions(+), 91 deletions(-) diff --git a/createdb.sql b/createdb.sql index 262169f..50f018a 100644 --- a/createdb.sql +++ b/createdb.sql @@ -105,7 +105,6 @@ SELECT $$ LANGUAGE sql IMMUTABLE; - CREATE TABLE news ( id serial unique, date timestamp, @@ -120,3 +119,13 @@ CREATE TABLE users ( salt bytea, role varchar(255) ); + + +CREATE TABLE visits ( + id serial unique, + downloads integer, + views integer, + book_id varchar(16) unique REFERENCES books(id) ON DELETE CASCADE +); +CREATE INDEX CONCURRENTLY visits_downloads_idx on visits(downloads DESC NULLS LAST); +CREATE INDEX CONCURRENTLY visits_views_idx on visits(views DESC NULLS LAST); diff --git a/lib/database/books.go b/lib/database/books.go index 8f19406..b64b5d9 100644 --- a/lib/database/books.go +++ b/lib/database/books.go @@ -24,6 +24,7 @@ type Book struct { Active bool `sql:",notnull"` UploadDate time.Time Tsv string + Visit *Visit } // AddBook to the database diff --git a/lib/database/database.go b/lib/database/database.go index 3aa017d..094539e 100644 --- a/lib/database/database.go +++ b/lib/database/database.go @@ -24,9 +24,10 @@ type DB interface { AddNews(text string) error AddRawNews(text string, date time.Time) error GetNews(num int, days int) (news []New, err error) - AddStats(stats interface{}) error - GetVisitedBooks() (books []Book, err error) - GetDownloadedBooks() (books []Book, err error) + IncViews(ID string) error + IncDownloads(ID string) error + GetVisitedBooks(num int) (books []Book, err error) + GetDownloadedBooks(num int) (books []Book, err error) GetTags() ([]string, error) } diff --git a/lib/database/ro.go b/lib/database/ro.go index f5b111d..fec283c 100644 --- a/lib/database/ro.go +++ b/lib/database/ro.go @@ -77,16 +77,20 @@ func (db *roDB) GetNews(num int, days int) (news []New, err error) { return db.db.GetNews(num, days) } -func (db *roDB) AddStats(stats interface{}) error { +func (db *roDB) IncViews(ID string) error { return nil } -func (db *roDB) GetVisitedBooks() (books []Book, err error) { - return db.db.GetVisitedBooks() +func (db *roDB) IncDownloads(ID string) error { + return nil } -func (db *roDB) GetDownloadedBooks() (books []Book, err error) { - return db.db.GetDownloadedBooks() +func (db *roDB) GetVisitedBooks(num int) (books []Book, err error) { + return db.db.GetVisitedBooks(num) +} + +func (db *roDB) GetDownloadedBooks(num int) (books []Book, err error) { + return db.db.GetDownloadedBooks(num) } func (db *roDB) GetTags() ([]string, error) { diff --git a/lib/database/stats.go b/lib/database/stats.go index 62106fd..c9b3236 100644 --- a/lib/database/stats.go +++ b/lib/database/stats.go @@ -1,4 +1,3 @@ -// TODO package database import ( @@ -7,20 +6,60 @@ import ( log "github.com/cihub/seelog" ) -func (db *pgDB) AddStats(stats interface{}) error { - return nil +type Visit struct { + ID int + Downloads int `sql:",notnull"` + Views int `sql:",notnull"` + BookID string + Book *Book } -/* Get the most visited books - */ -func (db *pgDB) GetVisitedBooks() (books []Book, err error) { - return []Book{}, nil +func (db *pgDB) IncViews(ID string) error { + visit := &Visit{ + Downloads: 0, + Views: 1, + BookID: ID, + } + _, err := db.sql.Model(visit). + OnConflict("(book_id) DO UPDATE"). + Set("views = Visit.views + 1"). + Insert() + return err } -/* Get the most downloaded books +func (db *pgDB) IncDownloads(ID string) error { + visit := &Visit{ + Downloads: 1, + Views: 0, + BookID: ID, + } + _, err := db.sql.Model(visit). + OnConflict("(book_id) DO UPDATE"). + Set("downloads = Visit.downloads + 1"). + Insert() + return err +} + +/* GetVisitedBooks get the most visited books */ -func (db *pgDB) GetDownloadedBooks() (books []Book, err error) { - return []Book{}, nil +func (db *pgDB) GetVisitedBooks(num int) (books []Book, err error) { + err = db.sql.Model(&books). + Column("Visit"). + Order("views DESC NULLS LAST"). + Limit(num). + Select() + return +} + +/* GetDownloadedBooks the most downloaded books + */ +func (db *pgDB) GetDownloadedBooks(num int) (books []Book, err error) { + err = db.sql.Model(&books). + Column("Visit"). + Order("downloads DESC NULLS LAST"). + Limit(num). + Select() + return } func (db *pgDB) GetTags() ([]string, error) { diff --git a/lib/stats.go b/lib/stats.go index c04afac..302c7d7 100644 --- a/lib/stats.go +++ b/lib/stats.go @@ -5,7 +5,6 @@ import ( "net/http" "strings" - "time" "github.com/gorilla/mux" "gitlab.com/trantor/trantor/lib/database" @@ -67,82 +66,34 @@ func (sg StatsGatherer) Gather(function func(handler)) func(http.ResponseWriter, } function(h) - sg.channel <- statsRequest{time.Now(), mux.Vars(r), h.sess, r} + sg.channel <- statsRequest{r} } } type statsRequest struct { - date time.Time - vars map[string]string - sess *Session - r *http.Request + r *http.Request } func (sg StatsGatherer) worker() { for req := range sg.channel { - stats := make(map[string]interface{}) - appendFiles(req.r, stats) - appendMuxVars(req.vars, stats) - appendUrl(req.r, stats) - appendSession(req.sess, stats) - stats["version"] = stats_version - stats["method"] = req.r.Method - stats["date"] = req.date - sg.db.AddStats(stats) - } -} - -func appendFiles(r *http.Request, stats map[string]interface{}) { - if r.Method == "POST" && r.MultipartForm != nil { - files := r.MultipartForm.File - for key := range files { - list := make([]string, len(files[key])) - for i, f := range files[key] { - list[i] = f.Filename + var err error + pattern := strings.Split(req.r.URL.Path, "/") + id := mux.Vars(req.r)["id"] + if len(pattern) > 1 && pattern[1] != "" && id != "" { + switch pattern[1] { + case "download": + id := mux.Vars(req.r)["id"] + err = sg.db.IncDownloads(id) + case "book": + id := mux.Vars(req.r)["id"] + err = sg.db.IncViews(id) + case "read": + id := mux.Vars(req.r)["id"] + err = sg.db.IncViews(id) } - stats[key] = list + } + if err != nil { + log.Warn("Problem incrementing visits: ", err) } } } - -func appendMuxVars(vars map[string]string, stats map[string]interface{}) { - for key, value := range vars { - switch { - case key == "id": - stats["id"] = value - case key == "ids": - var objectIds []string - ids := strings.Split(value, "/") - for _, id := range ids { - objectIds = append(objectIds, id) - } - if len(objectIds) > 0 { - stats["ids"] = objectIds - stats["id"] = objectIds[0] - } - default: - stats[key] = value - } - } -} - -func appendUrl(r *http.Request, stats map[string]interface{}) { - for key, value := range r.URL.Query() { - stats[key] = value - } - stats["host"] = r.Host - stats["path"] = r.URL.Path - pattern := strings.Split(r.URL.Path, "/") - if len(pattern) > 1 && pattern[1] != "" { - stats["section"] = pattern[1] - } else { - stats["section"] = "/" - } -} - -func appendSession(sess *Session, stats map[string]interface{}) { - stats["session"] = sess.Id() - if sess.User != "" { - stats["user"] = sess.User - } -} diff --git a/lib/trantor.go b/lib/trantor.go index 1bbf4a1..dc96aa0 100644 --- a/lib/trantor.go +++ b/lib/trantor.go @@ -110,13 +110,26 @@ type indexData struct { func indexHandler(h handler) { var data indexData + var err error - data.Tags, _ = h.db.GetTags() + data.Tags, err = h.db.GetTags() + if err != nil { + log.Warn("Problem getting tags: ", err) + } data.S = GetStatus(h) data.S.Home = true - data.Books, data.Count, _ = h.db.GetBooks("", booksFrontPage, 0) - data.VisitedBooks, _ = h.db.GetVisitedBooks() - data.DownloadedBooks, _ = h.db.GetDownloadedBooks() + data.Books, data.Count, err = h.db.GetBooks("", booksFrontPage, 0) + if err != nil { + log.Warn("Problem getting front books: ", err) + } + data.VisitedBooks, err = h.db.GetVisitedBooks(booksFrontPage) + if err != nil { + log.Warn("Problem getting visited books: ", err) + } + data.DownloadedBooks, err = h.db.GetDownloadedBooks(booksFrontPage) + if err != nil { + log.Warn("Problem getting downloaded books: ", err) + } data.News = getNews(1, daysNewsIndexpage, h.db) h.template.load(h, "index", data) }