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;
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);

View file

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

View file

@ -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)
}

View file

@ -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) {

View file

@ -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) {

View file

@ -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
}
}

View file

@ -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)
}