Remove statistics and add frequent tags in memory
This commit is contained in:
parent
284b649b69
commit
18baa2938b
10 changed files with 35 additions and 256 deletions
|
@ -24,8 +24,6 @@ type Book struct {
|
||||||
Tsv string
|
Tsv string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: missing history
|
|
||||||
|
|
||||||
// AddBook to the database
|
// AddBook to the database
|
||||||
func (db *pgDB) AddBook(book Book) error {
|
func (db *pgDB) AddBook(book Book) error {
|
||||||
emptyTime := time.Time{}
|
emptyTime := time.Time{}
|
||||||
|
|
|
@ -22,22 +22,17 @@ type DB interface {
|
||||||
GetNews(num int, days int) (news []New, err error)
|
GetNews(num int, days int) (news []New, err error)
|
||||||
AddStats(stats interface{}) error
|
AddStats(stats interface{}) error
|
||||||
GetVisitedBooks() (books []Book, err error)
|
GetVisitedBooks() (books []Book, err error)
|
||||||
UpdateMostVisited() error
|
|
||||||
GetDownloadedBooks() (books []Book, err error)
|
GetDownloadedBooks() (books []Book, err error)
|
||||||
UpdateDownloadedBooks() error
|
|
||||||
GetTags() ([]string, error)
|
GetTags() ([]string, error)
|
||||||
UpdateTags() error
|
|
||||||
GetVisits(visitType VisitType) ([]Visits, error)
|
|
||||||
UpdateHourVisits() error
|
|
||||||
UpdateDayVisits() error
|
|
||||||
UpdateMonthVisits() error
|
|
||||||
UpdateHourDownloads() error
|
|
||||||
UpdateDayDownloads() error
|
|
||||||
UpdateMonthDownloads() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tagsDisplay = 50
|
||||||
|
)
|
||||||
|
|
||||||
type pgDB struct {
|
type pgDB struct {
|
||||||
sql *pg.DB
|
sql *pg.DB
|
||||||
|
tags []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options for the database
|
// Options for the database
|
||||||
|
@ -57,7 +52,11 @@ func Init(options Options) (DB, error) {
|
||||||
Database: options.Name,
|
Database: options.Name,
|
||||||
})
|
})
|
||||||
// TODO: create db
|
// TODO: create db
|
||||||
return &pgDB{sql}, nil
|
|
||||||
|
db := pgDB{sql, []string{}}
|
||||||
|
go db.tagUpdater()
|
||||||
|
|
||||||
|
return &db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the database connection
|
// Close the database connection
|
||||||
|
|
|
@ -76,50 +76,10 @@ func (db *roDB) GetVisitedBooks() (books []Book, err error) {
|
||||||
return db.db.GetVisitedBooks()
|
return db.db.GetVisitedBooks()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *roDB) UpdateMostVisited() error {
|
|
||||||
return errors.New("RO database")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *roDB) GetDownloadedBooks() (books []Book, err error) {
|
func (db *roDB) GetDownloadedBooks() (books []Book, err error) {
|
||||||
return db.db.GetDownloadedBooks()
|
return db.db.GetDownloadedBooks()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *roDB) UpdateDownloadedBooks() error {
|
|
||||||
return errors.New("RO database")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *roDB) GetTags() ([]string, error) {
|
func (db *roDB) GetTags() ([]string, error) {
|
||||||
return db.db.GetTags()
|
return db.db.GetTags()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *roDB) UpdateTags() error {
|
|
||||||
return errors.New("RO database")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *roDB) GetVisits(visitType VisitType) ([]Visits, error) {
|
|
||||||
return db.db.GetVisits(visitType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *roDB) UpdateHourVisits() error {
|
|
||||||
return errors.New("RO database")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *roDB) UpdateDayVisits() error {
|
|
||||||
return errors.New("RO database")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *roDB) UpdateMonthVisits() error {
|
|
||||||
return errors.New("RO database")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *roDB) UpdateHourDownloads() error {
|
|
||||||
return errors.New("RO database")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *roDB) UpdateDayDownloads() error {
|
|
||||||
return errors.New("RO database")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *roDB) UpdateMonthDownloads() error {
|
|
||||||
return errors.New("RO database")
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,24 +3,10 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/cihub/seelog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type VisitType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
Hourly_visits = iota
|
|
||||||
Daily_visits
|
|
||||||
Monthly_visits
|
|
||||||
Hourly_downloads
|
|
||||||
Daily_downloads
|
|
||||||
Monthly_downloads
|
|
||||||
)
|
|
||||||
|
|
||||||
type Visits struct {
|
|
||||||
Date time.Time "date"
|
|
||||||
Count int "count"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *pgDB) AddStats(stats interface{}) error {
|
func (db *pgDB) AddStats(stats interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -31,52 +17,34 @@ func (db *pgDB) GetVisitedBooks() (books []Book, err error) {
|
||||||
return []Book{}, nil
|
return []Book{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *pgDB) UpdateMostVisited() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the most downloaded books
|
/* Get the most downloaded books
|
||||||
*/
|
*/
|
||||||
func (db *pgDB) GetDownloadedBooks() (books []Book, err error) {
|
func (db *pgDB) GetDownloadedBooks() (books []Book, err error) {
|
||||||
return []Book{}, nil
|
return []Book{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *pgDB) UpdateDownloadedBooks() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *pgDB) GetTags() ([]string, error) {
|
func (db *pgDB) GetTags() ([]string, error) {
|
||||||
return []string{}, nil
|
return db.tags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *pgDB) UpdateTags() error {
|
func (db *pgDB) updateTags() {
|
||||||
return nil
|
err := db.sql.Model(&Book{}).
|
||||||
|
ColumnExpr("unnest(tags) as tag").
|
||||||
|
Where("active = true").
|
||||||
|
Group("tag").
|
||||||
|
Order("count(*) DESC").
|
||||||
|
Limit(tagsDisplay).
|
||||||
|
Select(&db.tags)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error updating tags: ", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *pgDB) GetVisits(visitType VisitType) ([]Visits, error) {
|
func (db *pgDB) tagUpdater() {
|
||||||
return []Visits{}, nil
|
periodicity := 57 * time.Minute
|
||||||
}
|
|
||||||
|
|
||||||
func (db *pgDB) UpdateHourVisits() error {
|
for true {
|
||||||
return nil
|
db.updateTags()
|
||||||
}
|
time.Sleep(periodicity)
|
||||||
|
}
|
||||||
func (db *pgDB) UpdateDayVisits() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *pgDB) UpdateMonthVisits() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *pgDB) UpdateHourDownloads() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *pgDB) UpdateDayDownloads() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *pgDB) UpdateMonthDownloads() error {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
60
lib/stats.go
60
lib/stats.go
|
@ -4,7 +4,6 @@ import (
|
||||||
log "github.com/cihub/seelog"
|
log "github.com/cihub/seelog"
|
||||||
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -93,65 +92,6 @@ func (sg StatsGatherer) worker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func statsHandler(h handler) {
|
|
||||||
data := statsData{
|
|
||||||
S: GetStatus(h),
|
|
||||||
HVisits: getVisits(hourlyLabel, h.db, database.Hourly_visits),
|
|
||||||
DVisits: getVisits(dailyLabel, h.db, database.Daily_visits),
|
|
||||||
MVisits: getVisits(monthlyLabel, h.db, database.Monthly_visits),
|
|
||||||
HDownloads: getVisits(hourlyLabel, h.db, database.Hourly_downloads),
|
|
||||||
DDownloads: getVisits(dailyLabel, h.db, database.Daily_downloads),
|
|
||||||
MDownloads: getVisits(monthlyLabel, h.db, database.Monthly_downloads),
|
|
||||||
}
|
|
||||||
data.S.Title = "Stats -- " + data.S.Title
|
|
||||||
data.S.Stats = true
|
|
||||||
h.template.load(h, "stats", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
type statsData struct {
|
|
||||||
S Status
|
|
||||||
HVisits []visitData
|
|
||||||
DVisits []visitData
|
|
||||||
MVisits []visitData
|
|
||||||
HDownloads []visitData
|
|
||||||
DDownloads []visitData
|
|
||||||
MDownloads []visitData
|
|
||||||
}
|
|
||||||
|
|
||||||
type visitData struct {
|
|
||||||
Label string
|
|
||||||
Count int
|
|
||||||
}
|
|
||||||
|
|
||||||
func hourlyLabel(date time.Time) string {
|
|
||||||
return strconv.Itoa(date.Hour() + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func dailyLabel(date time.Time) string {
|
|
||||||
return strconv.Itoa(date.Day())
|
|
||||||
}
|
|
||||||
|
|
||||||
func monthlyLabel(date time.Time) string {
|
|
||||||
return date.Month().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getVisits(funcLabel func(time.Time) string, db database.DB, visitType database.VisitType) []visitData {
|
|
||||||
var visits []visitData
|
|
||||||
|
|
||||||
visit, err := db.GetVisits(visitType)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("GetVisits error (", visitType, "): ", err)
|
|
||||||
}
|
|
||||||
for _, v := range visit {
|
|
||||||
var elem visitData
|
|
||||||
elem.Label = funcLabel(v.Date.UTC())
|
|
||||||
elem.Count = v.Count
|
|
||||||
visits = append(visits, elem)
|
|
||||||
}
|
|
||||||
|
|
||||||
return visits
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendFiles(r *http.Request, stats map[string]interface{}) {
|
func appendFiles(r *http.Request, stats map[string]interface{}) {
|
||||||
if r.Method == "POST" && r.MultipartForm != nil {
|
if r.Method == "POST" && r.MultipartForm != nil {
|
||||||
files := r.MultipartForm.File
|
files := r.MultipartForm.File
|
||||||
|
|
|
@ -9,40 +9,19 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minutesUpdateTags = 11
|
minutesUpdateLogger = 5
|
||||||
minutesUpdateVisited = 41
|
|
||||||
minutesUpdateDownloaded = 47
|
|
||||||
minutesUpdateHourlyV = 31
|
|
||||||
minutesUpdateDailyV = 60*12 + 7
|
|
||||||
minutesUpdateMonthlyV = 60*24 + 11
|
|
||||||
minutesUpdateHourlyD = 29
|
|
||||||
minutesUpdateDailyD = 60*12 + 13
|
|
||||||
minutesUpdateMontlyD = 60*24 + 17
|
|
||||||
minutesUpdateLogger = 5
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitTasks(db database.DB, loggerConfig string) {
|
func InitTasks(db database.DB, loggerConfig string) {
|
||||||
updateLogger := func() error {
|
updateLogger := func() error {
|
||||||
return UpdateLogger(loggerConfig)
|
return UpdateLogger(loggerConfig)
|
||||||
}
|
}
|
||||||
periodicTask(updateLogger, minutesUpdateLogger*time.Minute)
|
go tasker(updateLogger, minutesUpdateLogger)
|
||||||
|
|
||||||
periodicTask(db.UpdateTags, minutesUpdateTags*time.Minute)
|
|
||||||
periodicTask(db.UpdateMostVisited, minutesUpdateVisited*time.Minute)
|
|
||||||
periodicTask(db.UpdateDownloadedBooks, minutesUpdateDownloaded*time.Minute)
|
|
||||||
periodicTask(db.UpdateHourVisits, minutesUpdateHourlyV*time.Minute)
|
|
||||||
periodicTask(db.UpdateDayVisits, minutesUpdateDailyV*time.Minute)
|
|
||||||
periodicTask(db.UpdateMonthVisits, minutesUpdateMonthlyV*time.Minute)
|
|
||||||
periodicTask(db.UpdateHourDownloads, minutesUpdateHourlyD*time.Minute)
|
|
||||||
periodicTask(db.UpdateDayDownloads, minutesUpdateDailyD*time.Minute)
|
|
||||||
periodicTask(db.UpdateMonthDownloads, minutesUpdateMontlyD*time.Minute)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func periodicTask(task func() error, periodicity time.Duration) {
|
func tasker(task func() error, minutes int) {
|
||||||
go tasker(task, periodicity)
|
periodicity := time.Duration(minutes) * time.Minute
|
||||||
}
|
|
||||||
|
|
||||||
func tasker(task func() error, periodicity time.Duration) {
|
|
||||||
for true {
|
for true {
|
||||||
time.Sleep(periodicity)
|
time.Sleep(periodicity)
|
||||||
err := task()
|
err := task()
|
||||||
|
|
|
@ -73,7 +73,6 @@ func InitTemplate(assetsPath string) *Template {
|
||||||
path.Join(templatePath, "edit.html"),
|
path.Join(templatePath, "edit.html"),
|
||||||
path.Join(templatePath, "dashboard.html"),
|
path.Join(templatePath, "dashboard.html"),
|
||||||
path.Join(templatePath, "settings.html"),
|
path.Join(templatePath, "settings.html"),
|
||||||
path.Join(templatePath, "stats.html"),
|
|
||||||
path.Join(templatePath, "help.html"),
|
path.Join(templatePath, "help.html"),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,6 @@ func InitRouter(db database.DB, sg *StatsGatherer, assetsPath string) {
|
||||||
r.HandleFunc("/help/", sg.Gather(helpHandler))
|
r.HandleFunc("/help/", sg.Gather(helpHandler))
|
||||||
r.HandleFunc("/download/{id:"+idPattern+"}/{epub:.*}", sg.Gather(downloadHandler))
|
r.HandleFunc("/download/{id:"+idPattern+"}/{epub:.*}", sg.Gather(downloadHandler))
|
||||||
r.HandleFunc("/cover/{id:"+idPattern+"}/{size}/{img:.*}", sg.Gather(coverHandler))
|
r.HandleFunc("/cover/{id:"+idPattern+"}/{size}/{img:.*}", sg.Gather(coverHandler))
|
||||||
r.HandleFunc("/stats/", sg.Gather(statsHandler))
|
|
||||||
|
|
||||||
r.HandleFunc("/login/", sg.Gather(loginHandler)).Methods("GET")
|
r.HandleFunc("/login/", sg.Gather(loginHandler)).Methods("GET")
|
||||||
r.HandleFunc("/login/", sg.Gather(loginPostHandler)).Methods("POST")
|
r.HandleFunc("/login/", sg.Gather(loginPostHandler)).Methods("POST")
|
||||||
|
|
|
@ -57,7 +57,6 @@
|
||||||
<li {{if .About}}class="active"{{end}}><a href="/about/">About</a></li>
|
<li {{if .About}}class="active"{{end}}><a href="/about/">About</a></li>
|
||||||
<li {{if .News}}class="active"{{end}}><a href="/news/">News</a></li>
|
<li {{if .News}}class="active"{{end}}><a href="/news/">News</a></li>
|
||||||
<li {{if .Upload}}class="active"{{end}}><a href="/upload/">Upload</a></li>
|
<li {{if .Upload}}class="active"{{end}}><a href="/upload/">Upload</a></li>
|
||||||
<li {{if .Stats}}class="active"{{end}}><a href="/stats/">Statistics</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav pull-right">
|
<ul class="nav pull-right">
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
{{template "header.html" .S}}
|
|
||||||
|
|
||||||
<script src="/js/Chart.min.js"></script>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div id="visits" class="span6">
|
|
||||||
<h2>Visits</h2>
|
|
||||||
<h4>Hourly:</h4>
|
|
||||||
<canvas id="hvisits" height="400" width="500"></canvas>
|
|
||||||
<h4>Daily:</h4>
|
|
||||||
<canvas id="dvisits" height="400" width="500"></canvas>
|
|
||||||
<h4>Monthly:</h4>
|
|
||||||
<canvas id="mvisits" height="400" width="500"></canvas>
|
|
||||||
</div>
|
|
||||||
<div id="downloads" class="span6">
|
|
||||||
<h2>Downloads</h2>
|
|
||||||
<h4>Hourly:</h4>
|
|
||||||
<canvas id="hdownloads" height="400" width="500"></canvas>
|
|
||||||
<h4>Daily:</h4>
|
|
||||||
<canvas id="ddownloads" height="400" width="500"></canvas>
|
|
||||||
<h4>Monthly:</h4>
|
|
||||||
<canvas id="mdownlodas" height="400" width="500"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
function chart(id, labels, counts) {
|
|
||||||
var data = {
|
|
||||||
labels : labels,
|
|
||||||
datasets : [
|
|
||||||
{
|
|
||||||
fillColor : "rgba(151,187,205,0.5)",
|
|
||||||
strokeColor : "rgba(151,187,205,1)",
|
|
||||||
pointColor : "rgba(151,187,205,1)",
|
|
||||||
pointStrokeColor : "#fff",
|
|
||||||
data : counts
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
var ctx = $(id).get(0).getContext("2d");
|
|
||||||
new Chart(ctx).Line(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
chart("#hvisits", [{{range .HVisits}}"{{.Label}}",{{end}}],
|
|
||||||
[{{range .HVisits}}{{.Count}},{{end}}])
|
|
||||||
chart("#dvisits", [{{range .DVisits}}"{{.Label}}",{{end}}],
|
|
||||||
[{{range .DVisits}}{{.Count}},{{end}}])
|
|
||||||
chart("#mvisits", [{{range .MVisits}}"{{.Label}}",{{end}}],
|
|
||||||
[{{range .MVisits}}{{.Count}},{{end}}])
|
|
||||||
|
|
||||||
chart("#hdownloads", [{{range .HDownloads}}"{{.Label}}",{{end}}],
|
|
||||||
[{{range .HDownloads}}{{.Count}},{{end}}])
|
|
||||||
chart("#ddownloads", [{{range .DDownloads}}"{{.Label}}",{{end}}],
|
|
||||||
[{{range .DDownloads}}{{.Count}},{{end}}])
|
|
||||||
chart("#mdownlodas", [{{range .MDownloads}}"{{.Label}}",{{end}}],
|
|
||||||
[{{range .MDownloads}}{{.Count}},{{end}}])
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{template "footer.html"}}
|
|
Reference in a new issue