Add a downloads collumn to the statistics
This commit is contained in:
parent
173ee04592
commit
a23264ec8d
5 changed files with 251 additions and 35 deletions
33
config.go
33
config.go
|
@ -3,21 +3,24 @@ package main
|
|||
const (
|
||||
PORT = "8080"
|
||||
|
||||
DB_IP = "127.0.0.1"
|
||||
DB_NAME = "trantor"
|
||||
META_COLL = "meta"
|
||||
BOOKS_COLL = "books"
|
||||
TAGS_COLL = "tags"
|
||||
VISITED_COLL = "visited"
|
||||
DOWNLOADED_COLL = "downloaded"
|
||||
HOURLY_VISITS_COLL = "visits.hourly"
|
||||
DAILY_VISITS_COLL = "visits.daily"
|
||||
MONTHLY_VISITS_COLL = "visits.monthly"
|
||||
USERS_COLL = "users"
|
||||
NEWS_COLL = "news"
|
||||
STATS_COLL = "statistics"
|
||||
FS_BOOKS = "fs_books"
|
||||
FS_IMGS = "fs_imgs"
|
||||
DB_IP = "127.0.0.1"
|
||||
DB_NAME = "trantor"
|
||||
META_COLL = "meta"
|
||||
BOOKS_COLL = "books"
|
||||
TAGS_COLL = "tags"
|
||||
VISITED_COLL = "visited"
|
||||
DOWNLOADED_COLL = "downloaded"
|
||||
HOURLY_VISITS_COLL = "visits.hourly"
|
||||
DAILY_VISITS_COLL = "visits.daily"
|
||||
MONTHLY_VISITS_COLL = "visits.monthly"
|
||||
HOURLY_DOWNLOADS_COLL = "downloads.hourly"
|
||||
DAILY_DOWNLOADS_COLL = "downloads.daily"
|
||||
MONTHLY_DOWNLOADS_COLL = "downloads.monthly"
|
||||
USERS_COLL = "users"
|
||||
NEWS_COLL = "news"
|
||||
STATS_COLL = "statistics"
|
||||
FS_BOOKS = "fs_books"
|
||||
FS_IMGS = "fs_imgs"
|
||||
|
||||
PASS_SALT = "ImperialLibSalt"
|
||||
MINUTES_UPDATE_TAGS = 11
|
||||
|
|
18
database.go
18
database.go
|
@ -258,3 +258,21 @@ func (d *DB) GetMonthVisits(start time.Time) ([]Visits, error) {
|
|||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.GetMonthVisits(start, statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetHourDownloads(start time.Time) ([]Visits, error) {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.GetHourDownloads(start, statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetDayDownloads(start time.Time) ([]Visits, error) {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.GetDayDowloads(start, statsColl)
|
||||
}
|
||||
|
||||
func (d *DB) GetMonthDownloads(start time.Time) ([]Visits, error) {
|
||||
statsColl := d.session.DB(DB_NAME).C(STATS_COLL)
|
||||
mr := NewMR(d.session.DB(DB_NAME))
|
||||
return mr.GetMonthDowloads(start, statsColl)
|
||||
}
|
||||
|
|
119
mapreduce.go
119
mapreduce.go
|
@ -129,7 +129,7 @@ func (m *MR) GetHourVisits(start time.Time, statsColl *mgo.Collection) ([]Visits
|
|||
var date = Date.UTC(this.date.getUTCFullYear(),
|
||||
this.date.getUTCMonth(),
|
||||
this.date.getUTCDate(),
|
||||
this.date.getUTCHours());
|
||||
this.date.getUTCHours());
|
||||
emit({date: date, session: this.session}, 1);
|
||||
}`
|
||||
mr.Reduce = reduce
|
||||
|
@ -228,6 +228,123 @@ func (m *MR) GetMonthVisits(start time.Time, statsColl *mgo.Collection) ([]Visit
|
|||
return result, err
|
||||
}
|
||||
|
||||
func (m *MR) GetHourDownloads(start time.Time, statsColl *mgo.Collection) ([]Visits, error) {
|
||||
if m.isOutdated(HOURLY_DOWNLOADS_COLL, MINUTES_UPDATE_HOURLY) {
|
||||
const reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
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: date}, 1);
|
||||
}
|
||||
}`
|
||||
mr.Reduce = reduce
|
||||
err := m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, HOURLY_DOWNLOADS_COLL+"_raw")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mr2 mgo.MapReduce
|
||||
mr2.Map = `function() {
|
||||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
hourly_raw := m.database.C(HOURLY_DOWNLOADS_COLL + "_raw")
|
||||
err = m.update(&mr2, bson.M{}, hourly_raw, HOURLY_DOWNLOADS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []Visits
|
||||
hourlyColl := m.database.C(HOURLY_DOWNLOADS_COLL)
|
||||
err := hourlyColl.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (m *MR) GetDayDowloads(start time.Time, statsColl *mgo.Collection) ([]Visits, error) {
|
||||
if m.isOutdated(DAILY_DOWNLOADS_COLL, MINUTES_UPDATE_DAILY) {
|
||||
const reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
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: date, session: this.session}, 1);
|
||||
}
|
||||
}`
|
||||
mr.Reduce = reduce
|
||||
err := m.update(&mr, bson.M{"date": bson.M{"$gte": start}}, statsColl, DAILY_DOWNLOADS_COLL+"_raw")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mr2 mgo.MapReduce
|
||||
mr2.Map = `function() {
|
||||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
daily_raw := m.database.C(DAILY_DOWNLOADS_COLL + "_raw")
|
||||
err = m.update(&mr2, bson.M{}, daily_raw, DAILY_DOWNLOADS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []Visits
|
||||
dailyColl := m.database.C(DAILY_DOWNLOADS_COLL)
|
||||
err := dailyColl.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (m *MR) GetMonthDowloads(start time.Time, statsColl *mgo.Collection) ([]Visits, error) {
|
||||
if m.isOutdated(MONTHLY_DOWNLOADS_COLL, MINUTES_UPDATE_MONTHLY) {
|
||||
const reduce = `function(date, vals) {
|
||||
var count = 0;
|
||||
vals.forEach(function(v) { count += v; });
|
||||
return count;
|
||||
}`
|
||||
var mr mgo.MapReduce
|
||||
mr.Map = `function() {
|
||||
if (this.section == "download") {
|
||||
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_DOWNLOADS_COLL+"_raw")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mr2 mgo.MapReduce
|
||||
mr2.Map = `function() {
|
||||
emit(this['_id']['date'], 1);
|
||||
}`
|
||||
mr2.Reduce = reduce
|
||||
monthly_raw := m.database.C(MONTHLY_DOWNLOADS_COLL + "_raw")
|
||||
err = m.update(&mr2, bson.M{}, monthly_raw, MONTHLY_DOWNLOADS_COLL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var result []Visits
|
||||
monthlyColl := m.database.C(MONTHLY_DOWNLOADS_COLL)
|
||||
err := monthlyColl.Find(nil).All(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
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})
|
||||
|
|
71
stats.go
71
stats.go
|
@ -65,18 +65,24 @@ func statsHandler(h handler) {
|
|||
var data statsData
|
||||
data.S = GetStatus(h)
|
||||
data.S.Stats = true
|
||||
data.Hourly = getHourlyVisits(h.db)
|
||||
data.Daily = getDailyVisits(h.db)
|
||||
data.Monthly = getMonthlyVisits(h.db)
|
||||
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)
|
||||
|
||||
loadTemplate(h.w, "stats", data)
|
||||
}
|
||||
|
||||
type statsData struct {
|
||||
S Status
|
||||
Hourly []visitData
|
||||
Daily []visitData
|
||||
Monthly []visitData
|
||||
S Status
|
||||
HVisits []visitData
|
||||
DVisits []visitData
|
||||
MVisits []visitData
|
||||
HDownloads []visitData
|
||||
DDownloads []visitData
|
||||
MDownloads []visitData
|
||||
}
|
||||
|
||||
type visitData struct {
|
||||
|
@ -135,6 +141,57 @@ func getMonthlyVisits(db *DB) []visitData {
|
|||
return visits
|
||||
}
|
||||
|
||||
func getHourlyDownloads(db *DB) []visitData {
|
||||
const numDays = 2
|
||||
var visits []visitData
|
||||
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour)
|
||||
visit, _ := db.GetHourDownloads(start)
|
||||
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 getDailyDownloads(db *DB) []visitData {
|
||||
const numDays = 30
|
||||
var visits []visitData
|
||||
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||
visit, _ := db.GetDayDownloads(start)
|
||||
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 {
|
||||
const numDays = 365
|
||||
var visits []visitData
|
||||
|
||||
start := time.Now().UTC().Add(-numDays * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||
visit, _ := db.GetMonthDownloads(start)
|
||||
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 appendFiles(r *http.Request, stats map[string]interface{}) {
|
||||
if r.Method == "POST" && r.MultipartForm != nil {
|
||||
files := r.MultipartForm.File
|
||||
|
|
|
@ -3,12 +3,24 @@
|
|||
<script src="/js/Chart.min.js"></script>
|
||||
|
||||
<div class="row">
|
||||
<h4>Hourly visits:</h4>
|
||||
<canvas id="hourly" height="400" width="800"></canvas>
|
||||
<h4>Daily visits:</h4>
|
||||
<canvas id="daily" height="400" width="800"></canvas>
|
||||
<h4>Monthly visits:</h4>
|
||||
<canvas id="monthly" height="400" width="800"></canvas>
|
||||
<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">
|
||||
|
@ -30,12 +42,21 @@
|
|||
new Chart(ctx).Line(data);
|
||||
}
|
||||
|
||||
chart("#hourly", [{{range .Hourly}}"{{.Label}}",{{end}}],
|
||||
[{{range .Hourly}}{{.Count}},{{end}}])
|
||||
chart("#daily", [{{range .Daily}}"{{.Label}}",{{end}}],
|
||||
[{{range .Daily}}{{.Count}},{{end}}])
|
||||
chart("#monthly", [{{range .Monthly}}"{{.Label}}",{{end}}],
|
||||
[{{range .Monthly}}{{.Count}},{{end}}])
|
||||
$(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