Merge branch 'master' into user
This commit is contained in:
commit
4695977bd8
8 changed files with 302 additions and 435 deletions
4
README
4
README
|
@ -13,11 +13,9 @@ https://gitorious.org/trantor/
|
|||
In order to run Trantor, you need to install the following packages:
|
||||
|
||||
* Go language
|
||||
* Epub development library
|
||||
* Mongodb
|
||||
* Bazaar
|
||||
* Mercurial
|
||||
* Git (necessary only if you want to deal with the repository)
|
||||
* Git
|
||||
|
||||
Under Debian Wheezy you can simply run:
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ const (
|
|||
MINUTES_UPDATE_MONTHLY_D = 60*24 + 17
|
||||
MINUTES_UPDATE_LOGGER = 5
|
||||
TAGS_DISPLAY = 50
|
||||
BOOKS_FRONT_PAGE = 6
|
||||
SEARCH_ITEMS_PAGE = 20
|
||||
NEW_ITEMS_PAGE = 50
|
||||
NUM_NEWS = 10
|
||||
|
|
142
database.go
142
database.go
|
@ -4,6 +4,7 @@ import log "github.com/cihub/seelog"
|
|||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"labix.org/v2/mgo"
|
||||
"labix.org/v2/mgo/bson"
|
||||
"os"
|
||||
|
@ -182,14 +183,14 @@ func (d *DB) GetBooks(query bson.M, r ...int) (books []Book, num int, err error)
|
|||
|
||||
/* Get the most visited books
|
||||
*/
|
||||
func (d *DB) GetVisitedBooks(num int) (books []Book, err error) {
|
||||
func (d *DB) GetVisitedBooks() (books []Book, err error) {
|
||||
visitedColl := d.session.DB(DB_NAME).C(VISITED_COLL)
|
||||
bookId, err := GetBooksVisited(num, visitedColl)
|
||||
bookId, err := GetBooksVisited(visitedColl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books = make([]Book, num)
|
||||
books = make([]Book, len(bookId))
|
||||
for i, id := range bookId {
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
booksColl.Find(bson.M{"_id": id}).One(&books[i])
|
||||
|
@ -199,21 +200,22 @@ func (d *DB) GetVisitedBooks(num int) (books []Book, err error) {
|
|||
}
|
||||
|
||||
func (d *DB) UpdateMostVisited() error {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.UpdateMostVisited(statsColl)
|
||||
var u DBUpdate
|
||||
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
u.dst = d.session.DB(DB_NAME).C(VISITED_COLL)
|
||||
return u.UpdateMostBooks("book")
|
||||
}
|
||||
|
||||
/* Get the most downloaded books
|
||||
*/
|
||||
func (d *DB) GetDownloadedBooks(num int) (books []Book, err error) {
|
||||
func (d *DB) GetDownloadedBooks() (books []Book, err error) {
|
||||
downloadedColl := d.session.DB(DB_NAME).C(DOWNLOADED_COLL)
|
||||
bookId, err := GetBooksVisited(num, downloadedColl)
|
||||
bookId, err := GetBooksVisited(downloadedColl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books = make([]Book, num)
|
||||
books = make([]Book, len(bookId))
|
||||
for i, id := range bookId {
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
booksColl.Find(bson.M{"_id": id}).One(&books[i])
|
||||
|
@ -223,9 +225,10 @@ func (d *DB) GetDownloadedBooks(num int) (books []Book, err error) {
|
|||
}
|
||||
|
||||
func (d *DB) UpdateDownloadedBooks() error {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.UpdateMostDownloaded(statsColl)
|
||||
var u DBUpdate
|
||||
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
u.dst = d.session.DB(DB_NAME).C(DOWNLOADED_COLL)
|
||||
return u.UpdateMostBooks("download")
|
||||
}
|
||||
|
||||
/* optional parameters: length and start index
|
||||
|
@ -250,84 +253,93 @@ func (d *DB) GetFS(prefix string) *mgo.GridFS {
|
|||
return d.session.DB(DB_NAME).GridFS(prefix)
|
||||
}
|
||||
|
||||
func (d *DB) GetTags(numTags int) ([]string, error) {
|
||||
func (d *DB) GetTags() ([]string, error) {
|
||||
tagsColl := d.session.DB(DB_NAME).C(TAGS_COLL)
|
||||
return GetTags(numTags, tagsColl)
|
||||
return GetTags(tagsColl)
|
||||
}
|
||||
|
||||
func (d *DB) UpdateTags() error {
|
||||
booksColl := d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.UpdateTags(booksColl)
|
||||
var u DBUpdate
|
||||
u.src = d.session.DB(DB_NAME).C(BOOKS_COLL)
|
||||
u.dst = d.session.DB(DB_NAME).C(TAGS_COLL)
|
||||
return u.UpdateTags()
|
||||
}
|
||||
|
||||
type VisitType int
|
||||
|
||||
const (
|
||||
hourly_visits = iota
|
||||
daily_visits
|
||||
monthly_visits
|
||||
hourly_downloads
|
||||
daily_downloads
|
||||
monthly_downloads
|
||||
)
|
||||
|
||||
type Visits struct {
|
||||
Date int64 "_id"
|
||||
Count int "value"
|
||||
Date time.Time "date"
|
||||
Count int "count"
|
||||
}
|
||||
|
||||
func (d *DB) GetHourVisits() ([]Visits, error) {
|
||||
hourlyColl := d.session.DB(DB_NAME).C(HOURLY_VISITS_COLL)
|
||||
return GetVisits(hourlyColl)
|
||||
func (d *DB) GetVisits(visitType VisitType) ([]Visits, error) {
|
||||
var coll *mgo.Collection
|
||||
switch visitType {
|
||||
case hourly_visits:
|
||||
coll = d.session.DB(DB_NAME).C(HOURLY_VISITS_COLL)
|
||||
case daily_visits:
|
||||
coll = d.session.DB(DB_NAME).C(DAILY_VISITS_COLL)
|
||||
case monthly_visits:
|
||||
coll = d.session.DB(DB_NAME).C(MONTHLY_VISITS_COLL)
|
||||
case hourly_downloads:
|
||||
coll = d.session.DB(DB_NAME).C(HOURLY_DOWNLOADS_COLL)
|
||||
case daily_downloads:
|
||||
coll = d.session.DB(DB_NAME).C(DAILY_DOWNLOADS_COLL)
|
||||
case monthly_downloads:
|
||||
coll = d.session.DB(DB_NAME).C(MONTHLY_DOWNLOADS_COLL)
|
||||
default:
|
||||
return nil, errors.New("Not valid VisitType")
|
||||
}
|
||||
return GetVisits(coll)
|
||||
}
|
||||
|
||||
func (d *DB) UpdateHourVisits() error {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.UpdateHourVisits(statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetDayVisits() ([]Visits, error) {
|
||||
dailyColl := d.session.DB(DB_NAME).C(DAILY_VISITS_COLL)
|
||||
return GetVisits(dailyColl)
|
||||
var u DBUpdate
|
||||
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
u.dst = d.session.DB(DB_NAME).C(HOURLY_VISITS_COLL)
|
||||
return u.UpdateHourVisits(false)
|
||||
}
|
||||
|
||||
func (d *DB) UpdateDayVisits() error {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.UpdateDayVisits(statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetMonthVisits() ([]Visits, error) {
|
||||
monthlyColl := d.session.DB(DB_NAME).C(MONTHLY_VISITS_COLL)
|
||||
return GetVisits(monthlyColl)
|
||||
var u DBUpdate
|
||||
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
u.dst = d.session.DB(DB_NAME).C(DAILY_VISITS_COLL)
|
||||
return u.UpdateDayVisits(false)
|
||||
}
|
||||
|
||||
func (d *DB) UpdateMonthVisits() error {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.UpdateMonthVisits(statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetHourDownloads() ([]Visits, error) {
|
||||
hourlyColl := d.session.DB(DB_NAME).C(HOURLY_DOWNLOADS_COLL)
|
||||
return GetVisits(hourlyColl)
|
||||
var u DBUpdate
|
||||
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
u.dst = d.session.DB(DB_NAME).C(MONTHLY_VISITS_COLL)
|
||||
return u.UpdateMonthVisits(false)
|
||||
}
|
||||
|
||||
func (d *DB) UpdateHourDownloads() error {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.UpdateHourDownloads(statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetDayDownloads() ([]Visits, error) {
|
||||
dailyColl := d.session.DB(DB_NAME).C(DAILY_DOWNLOADS_COLL)
|
||||
return GetVisits(dailyColl)
|
||||
var u DBUpdate
|
||||
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
u.dst = d.session.DB(DB_NAME).C(HOURLY_DOWNLOADS_COLL)
|
||||
return u.UpdateHourVisits(true)
|
||||
}
|
||||
|
||||
func (d *DB) UpdateDayDownloads() error {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.UpdateDayDownloads(statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetMonthDownloads() ([]Visits, error) {
|
||||
monthlyColl := d.session.DB(DB_NAME).C(MONTHLY_DOWNLOADS_COLL)
|
||||
return GetVisits(monthlyColl)
|
||||
var u DBUpdate
|
||||
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
u.dst = d.session.DB(DB_NAME).C(DAILY_DOWNLOADS_COLL)
|
||||
return u.UpdateDayVisits(true)
|
||||
}
|
||||
|
||||
func (d *DB) UpdateMonthDownloads() error {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.UpdateMonthDownloads(statsColl)
|
||||
var u DBUpdate
|
||||
u.src = d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
u.dst = d.session.DB(DB_NAME).C(MONTHLY_DOWNLOADS_COLL)
|
||||
return u.UpdateMonthVisits(true)
|
||||
}
|
||||
|
|
200
db_stats.go
Normal file
200
db_stats.go
Normal file
|
@ -0,0 +1,200 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"labix.org/v2/mgo"
|
||||
"labix.org/v2/mgo/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DBUpdate struct {
|
||||
src *mgo.Collection
|
||||
dst *mgo.Collection
|
||||
}
|
||||
|
||||
func GetTags(tagsColl *mgo.Collection) ([]string, error) {
|
||||
var result []struct {
|
||||
Tag string "_id"
|
||||
}
|
||||
err := tagsColl.Find(nil).Sort("-count").All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tags := make([]string, len(result))
|
||||
for i, r := range result {
|
||||
tags[i] = r.Tag
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func GetBooksVisited(visitedColl *mgo.Collection) ([]bson.ObjectId, error) {
|
||||
var result []struct {
|
||||
Book bson.ObjectId "_id"
|
||||
}
|
||||
err := visitedColl.Find(nil).Sort("-count").All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books := make([]bson.ObjectId, len(result))
|
||||
for i, r := range result {
|
||||
books[i] = r.Book
|
||||
}
|
||||
return books, nil
|
||||
}
|
||||
|
||||
func GetVisits(visitsColl *mgo.Collection) ([]Visits, error) {
|
||||
var result []Visits
|
||||
err := visitsColl.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (u *DBUpdate) UpdateTags() error {
|
||||
var tags []struct {
|
||||
Tag string "_id"
|
||||
Count int "count"
|
||||
}
|
||||
err := u.src.Pipe([]bson.M{
|
||||
{"$project": bson.M{"subject": 1}},
|
||||
{"$unwind": "$subject"},
|
||||
{"$group": bson.M{"_id": "$subject", "count": bson.M{"$sum": 1}}},
|
||||
{"$sort": bson.M{"count": -1}},
|
||||
{"$limit": TAGS_DISPLAY},
|
||||
}).All(&tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.dst.DropCollection()
|
||||
for _, tag := range tags {
|
||||
err = u.dst.Insert(tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DBUpdate) UpdateMostBooks(section string) error {
|
||||
const numDays = 30
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour)
|
||||
|
||||
var books []struct {
|
||||
Book string "_id"
|
||||
Count int "count"
|
||||
}
|
||||
err := u.src.Pipe([]bson.M{
|
||||
{"$match": bson.M{"date": bson.M{"$gt": start}, "section": section}},
|
||||
{"$project": bson.M{"id": 1}},
|
||||
{"$group": bson.M{"_id": "$id", "count": bson.M{"$sum": 1}}},
|
||||
{"$sort": bson.M{"count": -1}},
|
||||
{"$limit": BOOKS_FRONT_PAGE},
|
||||
}).All(&books)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.dst.DropCollection()
|
||||
for _, book := range books {
|
||||
err = u.dst.Insert(book)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *DBUpdate) UpdateHourVisits(isDownloads bool) error {
|
||||
const numDays = 2
|
||||
spanStore := numDays * 24 * time.Hour
|
||||
return u.updateVisits(hourInc, spanStore, isDownloads)
|
||||
}
|
||||
|
||||
func (u *DBUpdate) UpdateDayVisits(isDownloads bool) error {
|
||||
const numDays = 30
|
||||
spanStore := numDays * 24 * time.Hour
|
||||
return u.updateVisits(dayInc, spanStore, isDownloads)
|
||||
}
|
||||
|
||||
func (u *DBUpdate) UpdateMonthVisits(isDownloads bool) error {
|
||||
const numDays = 365
|
||||
spanStore := numDays * 24 * time.Hour
|
||||
return u.updateVisits(monthInc, spanStore, isDownloads)
|
||||
}
|
||||
|
||||
func hourInc(date time.Time) time.Time {
|
||||
const span = time.Hour
|
||||
return date.Add(span).Truncate(span)
|
||||
}
|
||||
|
||||
func dayInc(date time.Time) time.Time {
|
||||
const span = 24 * time.Hour
|
||||
return date.Add(span).Truncate(span)
|
||||
}
|
||||
|
||||
func monthInc(date time.Time) time.Time {
|
||||
const span = 24 * time.Hour
|
||||
return date.AddDate(0, 1, 1-date.Day()).Truncate(span)
|
||||
}
|
||||
|
||||
func (u *DBUpdate) updateVisits(incTime func(time.Time) time.Time, spanStore time.Duration, isDownloads bool) error {
|
||||
start := u.calculateStart(spanStore)
|
||||
for start.Before(time.Now().UTC()) {
|
||||
stop := incTime(start)
|
||||
|
||||
var count int
|
||||
var err error
|
||||
if isDownloads {
|
||||
count, err = u.countDownloads(start, stop)
|
||||
} else {
|
||||
count = u.countVisits(start, stop)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = u.dst.Insert(bson.M{"date": start, "count": count})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
start = stop
|
||||
}
|
||||
|
||||
_, err := u.dst.RemoveAll(bson.M{"date": bson.M{"$lt": time.Now().UTC().Add(-spanStore)}})
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *DBUpdate) calculateStart(spanStore time.Duration) time.Time {
|
||||
var date struct {
|
||||
Id bson.ObjectId `bson:"_id"`
|
||||
Date time.Time `bson:"date"`
|
||||
}
|
||||
err := u.dst.Find(bson.M{}).Sort("-date").One(&date)
|
||||
if err == nil {
|
||||
u.dst.RemoveId(date.Id)
|
||||
return date.Date
|
||||
}
|
||||
return time.Now().UTC().Add(-spanStore).Truncate(time.Hour)
|
||||
}
|
||||
|
||||
func (u *DBUpdate) countVisits(start time.Time, stop time.Time) int {
|
||||
var result struct {
|
||||
Count int "count"
|
||||
}
|
||||
err := u.src.Pipe([]bson.M{
|
||||
{"$match": bson.M{"date": bson.M{"$gte": start, "$lt": stop}}},
|
||||
{"$group": bson.M{"_id": "$session"}},
|
||||
{"$group": bson.M{"_id": 1, "count": bson.M{"$sum": 1}}},
|
||||
}).One(&result)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return result.Count
|
||||
}
|
||||
|
||||
func (u *DBUpdate) countDownloads(start time.Time, stop time.Time) (int, error) {
|
||||
query := bson.M{"date": bson.M{"$gte": start, "$lt": stop}, "section": "download"}
|
||||
return u.src.Find(query).Count()
|
||||
}
|
283
mapreduce.go
283
mapreduce.go
|
@ -1,283 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"labix.org/v2/mgo"
|
||||
"labix.org/v2/mgo/bson"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetTags(numTags int, tagsColl *mgo.Collection) ([]string, error) {
|
||||
var result []struct {
|
||||
Tag string "_id"
|
||||
}
|
||||
err := tagsColl.Find(nil).Sort("-value").Limit(numTags).All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tags := make([]string, len(result))
|
||||
for i, r := range result {
|
||||
tags[i] = r.Tag
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func GetBooksVisited(num int, visitedColl *mgo.Collection) ([]bson.ObjectId, error) {
|
||||
var result []struct {
|
||||
Book bson.ObjectId "_id"
|
||||
}
|
||||
err := visitedColl.Find(nil).Sort("-value").Limit(num).All(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
books := make([]bson.ObjectId, len(result))
|
||||
for i, r := range result {
|
||||
books[i] = r.Book
|
||||
}
|
||||
return books, nil
|
||||
}
|
||||
|
||||
func GetVisits(visitsColl *mgo.Collection) ([]Visits, error) {
|
||||
var result []Visits
|
||||
err := visitsColl.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
type MR struct {
|
||||
database *mgo.Database
|
||||
}
|
||||
|
||||
func NewMR(database *mgo.Database) *MR {
|
||||
m := new(MR)
|
||||
m.database = database
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MR) UpdateTags(booksColl *mgo.Collection) error {
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
if (this.subject) {
|
||||
this.subject.forEach(function(s) { emit(s, 1); });
|
||||
}
|
||||
}`
|
||||
mr.Reduce = `function(tag, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function() { count += 1; });
|
||||
return count;
|
||||
}`
|
||||
return m.update(&mr, bson.M{"active": true}, booksColl, TAGS_COLL)
|
||||
}
|
||||
|
||||
func (m *MR) UpdateMostVisited(statsColl *mgo.Collection) error {
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
if (this.id) {
|
||||
emit(this.id, 1);
|
||||
}
|
||||
}`
|
||||
mr.Reduce = `function(tag, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function() { count += 1; });
|
||||
return count;
|
||||
}`
|
||||
return m.update(&mr, bson.M{"section": "book"}, statsColl, VISITED_COLL)
|
||||
}
|
||||
|
||||
func (m *MR) UpdateMostDownloaded(statsColl *mgo.Collection) error {
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
emit(this.id, 1);
|
||||
}`
|
||||
mr.Reduce = `function(tag, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function() { count += 1; });
|
||||
return count;
|
||||
}`
|
||||
return m.update(&mr, bson.M{"section": "download"}, statsColl, DOWNLOADED_COLL)
|
||||
}
|
||||
|
||||
func (m *MR) UpdateHourVisits(statsColl *mgo.Collection) error {
|
||||
const numDays = 2
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour)
|
||||
|
||||
const reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth(),
|
||||
this.date.getUTCDate(),
|
||||
this.date.getUTCHours());
|
||||
emit({date: date, session: this.session}, 1);
|
||||
}`
|
||||
mr.Reduce = reduce
|
||||
err := m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, HOURLY_VISITS_COLL+"_raw")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var mr2 mgo.MapReduce
|
||||
mr2.Map = `function() {
|
||||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
hourly_raw := m.database.C(HOURLY_VISITS_COLL + "_raw")
|
||||
return m.update(&mr2, bson.M{}, hourly_raw, HOURLY_VISITS_COLL)
|
||||
}
|
||||
|
||||
func (m *MR) UpdateDayVisits(statsColl *mgo.Collection) error {
|
||||
const numDays = 30
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||
|
||||
const reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth(),
|
||||
this.date.getUTCDate());
|
||||
emit({date: date, session: this.session}, 1);
|
||||
}`
|
||||
mr.Reduce = reduce
|
||||
err := m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, DAILY_VISITS_COLL+"_raw")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var mr2 mgo.MapReduce
|
||||
mr2.Map = `function() {
|
||||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
daily_raw := m.database.C(DAILY_VISITS_COLL + "_raw")
|
||||
return m.update(&mr2, bson.M{}, daily_raw, DAILY_VISITS_COLL)
|
||||
}
|
||||
|
||||
func (m *MR) UpdateMonthVisits(statsColl *mgo.Collection) error {
|
||||
const numDays = 365
|
||||
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||
|
||||
const reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth());
|
||||
emit({date: date, session: this.session}, 1);
|
||||
}`
|
||||
mr.Reduce = reduce
|
||||
err := m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, MONTHLY_VISITS_COLL+"_raw")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var mr2 mgo.MapReduce
|
||||
mr2.Map = `function() {
|
||||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
monthly_raw := m.database.C(MONTHLY_VISITS_COLL + "_raw")
|
||||
return m.update(&mr2, bson.M{}, monthly_raw, MONTHLY_VISITS_COLL)
|
||||
}
|
||||
|
||||
func (m *MR) UpdateHourDownloads(statsColl *mgo.Collection) error {
|
||||
const numDays = 2
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour)
|
||||
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
if (this.section == "download") {
|
||||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth(),
|
||||
this.date.getUTCDate(),
|
||||
this.date.getUTCHours());
|
||||
emit(date, 1);
|
||||
}
|
||||
}`
|
||||
mr.Reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
return m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, HOURLY_DOWNLOADS_COLL)
|
||||
}
|
||||
|
||||
func (m *MR) UpdateDayDownloads(statsColl *mgo.Collection) error {
|
||||
const numDays = 30
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
if (this.section == "download") {
|
||||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth(),
|
||||
this.date.getUTCDate());
|
||||
emit(date, 1);
|
||||
}
|
||||
}`
|
||||
mr.Reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
return m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, DAILY_DOWNLOADS_COLL)
|
||||
}
|
||||
|
||||
func (m *MR) UpdateMonthDownloads(statsColl *mgo.Collection) error {
|
||||
const numDays = 365
|
||||
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
if (this.section == "download") {
|
||||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth());
|
||||
emit(date, 1);
|
||||
}
|
||||
}`
|
||||
mr.Reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
return m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, MONTHLY_DOWNLOADS_COLL)
|
||||
}
|
||||
|
||||
func (m *MR) update(mr *mgo.MapReduce, query bson.M, queryColl *mgo.Collection, storeColl string) error {
|
||||
metaColl := m.database.C(META_COLL)
|
||||
_, err := metaColl.RemoveAll(bson.M{"type": storeColl})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mr.Out = bson.M{"replace": storeColl}
|
||||
_, err = queryColl.Find(query).MapReduce(mr, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return metaColl.Insert(bson.M{"type": storeColl})
|
||||
}
|
||||
|
||||
func (m *MR) isOutdated(coll string, minutes float64) bool {
|
||||
var result struct {
|
||||
Id bson.ObjectId `bson:"_id"`
|
||||
}
|
||||
metaColl := m.database.C(META_COLL)
|
||||
err := metaColl.Find(bson.M{"type": coll}).One(&result)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
lastUpdate := result.Id.Time()
|
||||
return time.Since(lastUpdate).Minutes() > minutes
|
||||
}
|
95
stats.go
95
stats.go
|
@ -69,12 +69,12 @@ func statsHandler(h handler) {
|
|||
var data statsData
|
||||
data.S = GetStatus(h)
|
||||
data.S.Stats = true
|
||||
data.HVisits = getHourlyVisits(h.db)
|
||||
data.DVisits = getDailyVisits(h.db)
|
||||
data.MVisits = getMonthlyVisits(h.db)
|
||||
data.HDownloads = getHourlyDownloads(h.db)
|
||||
data.DDownloads = getDailyDownloads(h.db)
|
||||
data.MDownloads = getMonthlyDownloads(h.db)
|
||||
data.HVisits = getVisits(hourlyLabel, h.db, hourly_visits)
|
||||
data.DVisits = getVisits(dailyLabel, h.db, daily_visits)
|
||||
data.MVisits = getVisits(monthlyLabel, h.db, monthly_visits)
|
||||
data.HDownloads = getVisits(hourlyLabel, h.db, hourly_downloads)
|
||||
data.DDownloads = getVisits(dailyLabel, h.db, daily_downloads)
|
||||
data.MDownloads = getVisits(monthlyLabel, h.db, monthly_downloads)
|
||||
|
||||
loadTemplate(h.w, "stats", data)
|
||||
}
|
||||
|
@ -94,89 +94,28 @@ type visitData struct {
|
|||
Count int
|
||||
}
|
||||
|
||||
func getHourlyVisits(db *DB) []visitData {
|
||||
var visits []visitData
|
||||
|
||||
visit, _ := db.GetHourVisits()
|
||||
for _, v := range visit {
|
||||
var elem visitData
|
||||
hour := time.Unix(v.Date/1000, 0).UTC().Hour()
|
||||
elem.Label = strconv.Itoa(hour + 1)
|
||||
elem.Count = v.Count
|
||||
visits = append(visits, elem)
|
||||
}
|
||||
|
||||
return visits
|
||||
func hourlyLabel(date time.Time) string {
|
||||
return strconv.Itoa(date.Hour() + 1)
|
||||
}
|
||||
|
||||
func getDailyVisits(db *DB) []visitData {
|
||||
var visits []visitData
|
||||
|
||||
visit, _ := db.GetDayVisits()
|
||||
for _, v := range visit {
|
||||
var elem visitData
|
||||
day := time.Unix(v.Date/1000, 0).UTC().Day()
|
||||
elem.Label = strconv.Itoa(day)
|
||||
elem.Count = v.Count
|
||||
visits = append(visits, elem)
|
||||
}
|
||||
|
||||
return visits
|
||||
func dailyLabel(date time.Time) string {
|
||||
return strconv.Itoa(date.Day())
|
||||
}
|
||||
|
||||
func getMonthlyVisits(db *DB) []visitData {
|
||||
var visits []visitData
|
||||
|
||||
visit, _ := db.GetMonthVisits()
|
||||
for _, v := range visit {
|
||||
var elem visitData
|
||||
month := time.Unix(v.Date/1000, 0).UTC().Month()
|
||||
elem.Label = month.String()
|
||||
elem.Count = v.Count
|
||||
visits = append(visits, elem)
|
||||
}
|
||||
|
||||
return visits
|
||||
func monthlyLabel(date time.Time) string {
|
||||
return date.Month().String()
|
||||
}
|
||||
|
||||
func getHourlyDownloads(db *DB) []visitData {
|
||||
func getVisits(funcLabel func(time.Time) string, db *DB, visitType VisitType) []visitData {
|
||||
var visits []visitData
|
||||
|
||||
visit, _ := db.GetHourDownloads()
|
||||
for _, v := range visit {
|
||||
var elem visitData
|
||||
hour := time.Unix(v.Date/1000, 0).UTC().Hour()
|
||||
elem.Label = strconv.Itoa(hour + 1)
|
||||
elem.Count = v.Count
|
||||
visits = append(visits, elem)
|
||||
visit, err := db.GetVisits(visitType)
|
||||
if err != nil {
|
||||
log.Warn("GetVisits error (", visitType, "): ", err)
|
||||
}
|
||||
|
||||
return visits
|
||||
}
|
||||
|
||||
func getDailyDownloads(db *DB) []visitData {
|
||||
var visits []visitData
|
||||
|
||||
visit, _ := db.GetDayDownloads()
|
||||
for _, v := range visit {
|
||||
var elem visitData
|
||||
day := time.Unix(v.Date/1000, 0).UTC().Day()
|
||||
elem.Label = strconv.Itoa(day)
|
||||
elem.Count = v.Count
|
||||
visits = append(visits, elem)
|
||||
}
|
||||
|
||||
return visits
|
||||
}
|
||||
|
||||
func getMonthlyDownloads(db *DB) []visitData {
|
||||
var visits []visitData
|
||||
|
||||
visit, _ := db.GetMonthDownloads()
|
||||
for _, v := range visit {
|
||||
var elem visitData
|
||||
month := time.Unix(v.Date/1000, 0).UTC().Month()
|
||||
elem.Label = month.String()
|
||||
elem.Label = funcLabel(v.Date.UTC())
|
||||
elem.Count = v.Count
|
||||
visits = append(visits, elem)
|
||||
}
|
||||
|
|
|
@ -40,8 +40,8 @@ func uploadEpub(filename string, db *DB) {
|
|||
return
|
||||
}
|
||||
|
||||
book["filename"] = id
|
||||
book["filenamesize"] = size
|
||||
book["file"] = id
|
||||
book["filesize"] = size
|
||||
err = db.InsertBook(book)
|
||||
if err != nil {
|
||||
log.Error("Error storing metadata (", title, "): ", err)
|
||||
|
|
|
@ -112,12 +112,12 @@ type indexData struct {
|
|||
func indexHandler(h handler) {
|
||||
var data indexData
|
||||
|
||||
data.Tags, _ = h.db.GetTags(TAGS_DISPLAY)
|
||||
data.Tags, _ = h.db.GetTags()
|
||||
data.S = GetStatus(h)
|
||||
data.S.Home = true
|
||||
data.Books, data.Count, _ = h.db.GetBooks(bson.M{"active": true}, 6)
|
||||
data.VisitedBooks, _ = h.db.GetVisitedBooks(6)
|
||||
data.DownloadedBooks, _ = h.db.GetDownloadedBooks(6)
|
||||
data.Books, data.Count, _ = h.db.GetBooks(bson.M{"active": true}, BOOKS_FRONT_PAGE)
|
||||
data.VisitedBooks, _ = h.db.GetVisitedBooks()
|
||||
data.DownloadedBooks, _ = h.db.GetDownloadedBooks()
|
||||
data.News = getNews(1, DAYS_NEWS_INDEXPAGE, h.db)
|
||||
loadTemplate(h.w, "index", data)
|
||||
}
|
||||
|
|
Reference in a new issue