Keep count of downladed/viewed books

This commit is contained in:
Las Zenow 2017-05-30 22:10:21 +00:00
parent 68ffe6cf5b
commit 71d975c2f7
7 changed files with 109 additions and 91 deletions

View file

@ -105,7 +105,6 @@ SELECT
$$ LANGUAGE sql IMMUTABLE; $$ LANGUAGE sql IMMUTABLE;
CREATE TABLE news ( CREATE TABLE news (
id serial unique, id serial unique,
date timestamp, date timestamp,
@ -120,3 +119,13 @@ CREATE TABLE users (
salt bytea, salt bytea,
role varchar(255) 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);

View file

@ -24,6 +24,7 @@ type Book struct {
Active bool `sql:",notnull"` Active bool `sql:",notnull"`
UploadDate time.Time UploadDate time.Time
Tsv string Tsv string
Visit *Visit
} }
// AddBook to the database // AddBook to the database

View file

@ -24,9 +24,10 @@ type DB interface {
AddNews(text string) error AddNews(text string) error
AddRawNews(text string, date time.Time) error AddRawNews(text string, date time.Time) error
GetNews(num int, days int) (news []New, err error) GetNews(num int, days int) (news []New, err error)
AddStats(stats interface{}) error IncViews(ID string) error
GetVisitedBooks() (books []Book, err error) IncDownloads(ID string) error
GetDownloadedBooks() (books []Book, err error) GetVisitedBooks(num int) (books []Book, err error)
GetDownloadedBooks(num int) (books []Book, err error)
GetTags() ([]string, error) GetTags() ([]string, error)
} }

View file

@ -77,16 +77,20 @@ func (db *roDB) GetNews(num int, days int) (news []New, err error) {
return db.db.GetNews(num, days) return db.db.GetNews(num, days)
} }
func (db *roDB) AddStats(stats interface{}) error { func (db *roDB) IncViews(ID string) error {
return nil return nil
} }
func (db *roDB) GetVisitedBooks() (books []Book, err error) { func (db *roDB) IncDownloads(ID string) error {
return db.db.GetVisitedBooks() return nil
} }
func (db *roDB) GetDownloadedBooks() (books []Book, err error) { func (db *roDB) GetVisitedBooks(num int) (books []Book, err error) {
return db.db.GetDownloadedBooks() 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) { func (db *roDB) GetTags() ([]string, error) {

View file

@ -1,4 +1,3 @@
// TODO
package database package database
import ( import (
@ -7,20 +6,60 @@ import (
log "github.com/cihub/seelog" log "github.com/cihub/seelog"
) )
func (db *pgDB) AddStats(stats interface{}) error { type Visit struct {
return nil ID int
Downloads int `sql:",notnull"`
Views int `sql:",notnull"`
BookID string
Book *Book
} }
/* Get the most visited books func (db *pgDB) IncViews(ID string) error {
*/ visit := &Visit{
func (db *pgDB) GetVisitedBooks() (books []Book, err error) { Downloads: 0,
return []Book{}, nil 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) { func (db *pgDB) GetVisitedBooks(num int) (books []Book, err error) {
return []Book{}, nil 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) { func (db *pgDB) GetTags() ([]string, error) {

View file

@ -5,7 +5,6 @@ import (
"net/http" "net/http"
"strings" "strings"
"time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"gitlab.com/trantor/trantor/lib/database" "gitlab.com/trantor/trantor/lib/database"
@ -67,82 +66,34 @@ func (sg StatsGatherer) Gather(function func(handler)) func(http.ResponseWriter,
} }
function(h) function(h)
sg.channel <- statsRequest{time.Now(), mux.Vars(r), h.sess, r} sg.channel <- statsRequest{r}
} }
} }
type statsRequest struct { type statsRequest struct {
date time.Time r *http.Request
vars map[string]string
sess *Session
r *http.Request
} }
func (sg StatsGatherer) worker() { func (sg StatsGatherer) worker() {
for req := range sg.channel { for req := range sg.channel {
stats := make(map[string]interface{}) var err error
appendFiles(req.r, stats) pattern := strings.Split(req.r.URL.Path, "/")
appendMuxVars(req.vars, stats) id := mux.Vars(req.r)["id"]
appendUrl(req.r, stats) if len(pattern) > 1 && pattern[1] != "" && id != "" {
appendSession(req.sess, stats) switch pattern[1] {
stats["version"] = stats_version case "download":
stats["method"] = req.r.Method id := mux.Vars(req.r)["id"]
stats["date"] = req.date err = sg.db.IncDownloads(id)
sg.db.AddStats(stats) case "book":
} id := mux.Vars(req.r)["id"]
} err = sg.db.IncViews(id)
case "read":
func appendFiles(r *http.Request, stats map[string]interface{}) { id := mux.Vars(req.r)["id"]
if r.Method == "POST" && r.MultipartForm != nil { err = sg.db.IncViews(id)
files := r.MultipartForm.File
for key := range files {
list := make([]string, len(files[key]))
for i, f := range files[key] {
list[i] = f.Filename
} }
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
}
}

View file

@ -110,13 +110,26 @@ type indexData struct {
func indexHandler(h handler) { func indexHandler(h handler) {
var data indexData 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 = GetStatus(h)
data.S.Home = true data.S.Home = true
data.Books, data.Count, _ = h.db.GetBooks("", booksFrontPage, 0) data.Books, data.Count, err = h.db.GetBooks("", booksFrontPage, 0)
data.VisitedBooks, _ = h.db.GetVisitedBooks() if err != nil {
data.DownloadedBooks, _ = h.db.GetDownloadedBooks() 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) data.News = getNews(1, daysNewsIndexpage, h.db)
h.template.load(h, "index", data) h.template.load(h, "index", data)
} }