stats.go 3.43 KB
Newer Older
1
package trantor
Las Zenow's avatar
Las Zenow committed
2 3

import (
Las Zenow's avatar
Las Zenow committed
4 5
	log "github.com/cihub/seelog"

Las Zenow's avatar
Las Zenow committed
6 7
	"net/http"
	"strings"
8
	"time"
9 10

	"github.com/gorilla/mux"
11
	"gitlab.com/trantor/trantor/lib/database"
12
	"gitlab.com/trantor/trantor/lib/instrument"
13
	"gitlab.com/trantor/trantor/lib/storage"
Las Zenow's avatar
Las Zenow committed
14 15
)

Las Zenow's avatar
Las Zenow committed
16
const (
17
	statsChanSize = 100
Las Zenow's avatar
Las Zenow committed
18 19
)

20
type handler struct {
21 22 23
	w        http.ResponseWriter
	r        *http.Request
	sess     *Session
Las Zenow's avatar
Las Zenow committed
24 25
	db       database.DB
	store    storage.Store
26 27
	template *Template
	hostname string
Las Zenow's avatar
Las Zenow committed
28
	ro       bool
29
	t1       time.Time
30 31
}

32 33
func (h handler) load(tmpl string, data interface{}) {
	var err error
34 35

	h.t1 = time.Now()
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
	fmt := h.r.FormValue("fmt")
	switch fmt {
	case "rss":
		err = h.template.rss.ExecuteTemplate(h.w, tmpl+".rss", data)
	case "opds":
		err = h.template.opds.ExecuteTemplate(h.w, tmpl+".opds", data)
	case "json":
		err = loadJson(h.w, tmpl, data)
	default:
		err = h.template.html.ExecuteTemplate(h.w, tmpl+".html", data)
	}
	if err != nil {
		h.template.html.ExecuteTemplate(h.w, "404.html", data)
		log.Warn("An error ocurred loading the template ", tmpl, ".", fmt, ": ", err)
	}
}

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
func (h handler) booksInSubmission(bookIDs []string, submissionID string) bool {
	submissions, err := h.db.GetSubmission(submissionID)
	if err != nil {
		return false
	}
	for _, bookID := range bookIDs {
		if bookID == "" {
			continue
		}

		found := false
		for _, s := range submissions {
			if s.BookID == bookID && !s.Book.Active {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	return true
}

Las Zenow's avatar
Las Zenow committed
77
type StatsGatherer struct {
78 79 80 81 82 83 84
	db         database.DB
	store      storage.Store
	template   *Template
	instrument instrument.Instrument
	hostname   string
	channel    chan statsRequest
	ro         bool
Las Zenow's avatar
Las Zenow committed
85 86
}

Las Zenow's avatar
Las Zenow committed
87
func InitStats(database database.DB, store storage.Store, hostname string, template *Template, ro bool) *StatsGatherer {
88 89
	in := instrument.Init()

90
	sg := StatsGatherer{
91 92 93 94 95 96 97
		channel:    make(chan statsRequest, statsChanSize),
		db:         database,
		store:      store,
		instrument: in,
		template:   template,
		hostname:   hostname,
		ro:         ro,
98
	}
Las Zenow's avatar
Las Zenow committed
99 100

	go sg.worker()
101
	return &sg
Las Zenow's avatar
Las Zenow committed
102 103 104
}

func (sg StatsGatherer) Gather(function func(handler)) func(http.ResponseWriter, *http.Request) {
Las Zenow's avatar
Las Zenow committed
105
	return func(w http.ResponseWriter, r *http.Request) {
Las Zenow's avatar
Las Zenow committed
106 107
		log.Info("Query ", r.Method, " ", r.RequestURI)

108 109
		h := handler{
			store:    sg.store,
Las Zenow's avatar
Las Zenow committed
110
			db:       sg.db,
111 112 113 114
			template: sg.template,
			hostname: sg.hostname,
			w:        w,
			r:        r,
Las Zenow's avatar
Las Zenow committed
115
			sess:     GetSession(r, sg.db),
Las Zenow's avatar
Las Zenow committed
116
			ro:       sg.ro,
117
		}
Las Zenow's avatar
Las Zenow committed
118

119
		t0 := time.Now()
120
		function(h)
121 122 123 124
		if h.t1.IsZero() {
			h.t1 = time.Now()
		}
		sg.channel <- statsRequest{r, h.t1.Sub(t0)}
Las Zenow's avatar
Las Zenow committed
125 126 127 128
	}
}

type statsRequest struct {
129 130
	r        *http.Request
	duration time.Duration
Las Zenow's avatar
Las Zenow committed
131 132
}

Las Zenow's avatar
Las Zenow committed
133 134
func (sg StatsGatherer) worker() {
	for req := range sg.channel {
135 136 137
		sg.save(req)
	}
}
138

139 140 141 142 143 144 145 146 147 148
func (sg StatsGatherer) save(req statsRequest) {
	id := mux.Vars(req.r)["id"]
	search := strings.Join(req.r.Form["q"], " ")
	fmt := req.r.FormValue("fmt")
	pattern := strings.Split(req.r.URL.Path, "/")

	section := "/"
	if len(pattern) > 1 && pattern[1] != "" {
		section = pattern[1]
	}
149

150 151 152 153 154 155 156
	sg.instrument.Request(instrument.RequestData{
		Section:  section,
		ID:       id,
		Search:   search,
		Fmt:      fmt,
		Duration: req.duration,
	})
157 158 159 160 161

	_, err := sg.db.GetBookID(id)
	if err != nil {
		return
	}
162 163 164 165 166 167 168 169 170 171 172
	switch section {
	case "download":
		err = sg.db.IncDownloads(id)
	case "book":
		err = sg.db.IncViews(id)
	case "read":
		err = sg.db.IncViews(id)
	}

	if err != nil {
		log.Warn("Problem incrementing visits: ", err)
Las Zenow's avatar
Las Zenow committed
173 174
	}
}