modify all routes & queries to take a site ID parameter

This commit is contained in:
Danny van Kooten 2018-10-05 13:09:58 +02:00
parent dc8bf7fdc3
commit c1367325e6
16 changed files with 155 additions and 124 deletions

View File

@ -215,7 +215,7 @@ class Chart extends Component {
fetchData(before, after) { fetchData(before, after) {
this.setState({ loading: true }) this.setState({ loading: true })
Client.request(`/stats/site/groupby/day?before=${before}&after=${after}`) Client.request(`/sites/${this.props.site.id}/stats/site/groupby/day?before=${before}&after=${after}`)
.then((d) => { .then((d) => {
// request finished; check if timestamp range is still the one user wants to see // request finished; check if timestamp range is still the one user wants to see
if( this.props.before != before || this.props.after != after ) { if( this.props.before != before || this.props.after != after ) {

View File

@ -33,7 +33,7 @@ class Realtime extends Component {
@bind @bind
fetchData() { fetchData() {
Client.request(`stats/site/realtime`) Client.request(`/sites/${this.props.site.id}/stats/site/realtime`)
.then((d) => { .then((d) => {
this.setState({ count: d }) this.setState({ count: d })
this.setDocumentTitle(); this.setDocumentTitle();

View File

@ -29,7 +29,7 @@ class Sidebar extends Component {
fetchData(before, after) { fetchData(before, after) {
this.setState({ loading: true }) this.setState({ loading: true })
Client.request(`stats/site?before=${before}&after=${after}`) Client.request(`/sites/${this.props.site.id}/stats/site?before=${before}&after=${after}`)
.then((data) => { .then((data) => {
// request finished; check if timestamp range is still the one user wants to see // request finished; check if timestamp range is still the one user wants to see
if( this.props.before != before || this.props.after != after ) { if( this.props.before != before || this.props.after != after ) {

View File

@ -33,7 +33,7 @@ class Table extends Component {
fetchRecords(before, after) { fetchRecords(before, after) {
this.setState({ loading: true }); this.setState({ loading: true });
Client.request(`${this.props.endpoint}?before=${before}&after=${after}&limit=${this.state.limit}`) Client.request(`/sites/${this.props.site.id}/${this.props.endpoint}?before=${before}&after=${after}&limit=${this.state.limit}`)
.then((d) => { .then((d) => {
// request finished; check if timestamp range is still the one user wants to see // request finished; check if timestamp range is still the one user wants to see
if( this.props.before != before || this.props.after != after ) { if( this.props.before != before || this.props.after != after ) {
@ -47,7 +47,7 @@ class Table extends Component {
}); });
// fetch totals too // fetch totals too
Client.request(`${this.props.endpoint}/pageviews?before=${before}&after=${after}`) Client.request(`/sites/${this.props.site.id}/${this.props.endpoint}/pageviews?before=${before}&after=${after}`)
.then((d) => { .then((d) => {
this.setState({ this.setState({
total: d total: d

View File

@ -111,7 +111,7 @@ class Dashboard extends Component {
<li class="logo"><a href="/">Fathom</a></li> <li class="logo"><a href="/">Fathom</a></li>
<SiteSwitcher sites={state.sites} selectedSite={state.site} onChange={this.changeSelectedSite} onAdd={this.openSiteSettings} showAdd={!state.isPublic}/> <SiteSwitcher sites={state.sites} selectedSite={state.site} onChange={this.changeSelectedSite} onAdd={this.openSiteSettings} showAdd={!state.isPublic}/>
<Gearwheel onClick={this.openSiteSettings} visible={!state.isPublic} /> <Gearwheel onClick={this.openSiteSettings} visible={!state.isPublic} />
<li class="visitors"><Realtime /></li> <li class="visitors"><Realtime site={state.site} /></li>
</ul> </ul>
</nav> </nav>
</header> </header>

View File

@ -4,9 +4,10 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/urfave/cli"
"os" "os"
"time" "time"
"github.com/urfave/cli"
) )
var statsCmd = cli.Command{ var statsCmd = cli.Command{
@ -40,7 +41,10 @@ func stats(c *cli.Context) error {
return errors.New("Invalid argument: supply a valid --end-date") return errors.New("Invalid argument: supply a valid --end-date")
} }
result, err := app.database.GetAggregatedSiteStats(start, end) // TODO: add flag for this
// TODO: add method for getting total sum of pageviews across sites
var siteID int64 = 0
result, err := app.database.GetAggregatedSiteStats(siteID, start, end)
if err != nil { if err != nil {
return err return err
} }

View File

@ -17,7 +17,7 @@ func (agg *aggregator) getSiteStats(r *results, t time.Time) (*models.SiteStats,
} }
// get from db // get from db
stats, err := agg.database.GetSiteStats(t) stats, err := agg.database.GetSiteStats(0, t)
if err != nil && err != datastore.ErrNoResults { if err != nil && err != datastore.ErrNoResults {
return nil, err return nil, err
} }
@ -44,7 +44,7 @@ func (agg *aggregator) getPageStats(r *results, t time.Time, hostname string, pa
return stats, nil return stats, nil
} }
stats, err := agg.database.GetPageStats(t, hostname, pathname) stats, err := agg.database.GetPageStats(0, t, hostname, pathname)
if err != nil && err != datastore.ErrNoResults { if err != nil && err != datastore.ErrNoResults {
return nil, err return nil, err
} }
@ -72,7 +72,7 @@ func (agg *aggregator) getReferrerStats(r *results, t time.Time, hostname string
} }
// get from db // get from db
stats, err := agg.database.GetReferrerStats(t, hostname, pathname) stats, err := agg.database.GetReferrerStats(0, t, hostname, pathname)
if err != nil && err != datastore.ErrNoResults { if err != nil && err != datastore.ErrNoResults {
return nil, err return nil, err
} }

View File

@ -7,7 +7,7 @@ import (
// URL: /api/stats/page // URL: /api/stats/page
func (api *API) GetPageStatsHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetPageStatsHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetAggregatedPageStats(params.StartDate, params.EndDate, params.Limit) result, err := api.database.GetAggregatedPageStats(params.SiteID, params.StartDate, params.EndDate, params.Limit)
if err != nil { if err != nil {
return err return err
} }
@ -16,7 +16,7 @@ func (api *API) GetPageStatsHandler(w http.ResponseWriter, r *http.Request) erro
func (api *API) GetPageStatsPageviewsHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetPageStatsPageviewsHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetAggregatedPageStatsPageviews(params.StartDate, params.EndDate) result, err := api.database.GetAggregatedPageStatsPageviews(params.SiteID, params.StartDate, params.EndDate)
if err != nil { if err != nil {
return err return err
} }

View File

@ -5,11 +5,14 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/gorilla/mux"
) )
// Params defines the commonly used API parameters // Params defines the commonly used API parameters
type Params struct { type Params struct {
Limit int SiteID int64
Limit int64
StartDate time.Time StartDate time.Time
EndDate time.Time EndDate time.Time
} }
@ -17,23 +20,37 @@ type Params struct {
// GetRequestParams parses the query parameters and returns commonly used API parameters, with defaults // GetRequestParams parses the query parameters and returns commonly used API parameters, with defaults
func GetRequestParams(r *http.Request) *Params { func GetRequestParams(r *http.Request) *Params {
params := &Params{ params := &Params{
SiteID: 0,
Limit: 20, Limit: 20,
StartDate: time.Now(), StartDate: time.Now(),
EndDate: time.Now().AddDate(0, 0, -7), EndDate: time.Now().AddDate(0, 0, -7),
} }
vars := mux.Vars(r)
if _, ok := vars["id"]; ok {
if siteID, err := strconv.ParseInt(vars["id"], 10, 64); err == nil {
params.SiteID = siteID
}
}
q := r.URL.Query() q := r.URL.Query()
if q.Get("after") != "" {
if after, err := strconv.ParseInt(q.Get("after"), 10, 64); err == nil && after > 0 { if after, err := strconv.ParseInt(q.Get("after"), 10, 64); err == nil && after > 0 {
params.StartDate = time.Unix(after, 0) params.StartDate = time.Unix(after, 0)
} }
}
if q.Get("before") != "" {
if before, err := strconv.ParseInt(q.Get("before"), 10, 64); err == nil && before > 0 { if before, err := strconv.ParseInt(q.Get("before"), 10, 64); err == nil && before > 0 {
params.EndDate = time.Unix(before, 0) params.EndDate = time.Unix(before, 0)
} }
}
if limit, err := strconv.Atoi(q.Get("limit")); err == nil && limit > 0 { if q.Get("limit") != "" {
if limit, err := strconv.ParseInt(q.Get("limit"), 10, 64); err == nil && limit > 0 {
params.Limit = limit params.Limit = limit
} }
}
return params return params
} }

View File

@ -7,7 +7,7 @@ import (
// URL: /api/stats/referrer // URL: /api/stats/referrer
func (api *API) GetReferrerStatsHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetReferrerStatsHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetAggregatedReferrerStats(params.StartDate, params.EndDate, params.Limit) result, err := api.database.GetAggregatedReferrerStats(params.SiteID, params.StartDate, params.EndDate, params.Limit)
if err != nil { if err != nil {
return err return err
} }
@ -17,7 +17,7 @@ func (api *API) GetReferrerStatsHandler(w http.ResponseWriter, r *http.Request)
// URL: /api/stats/referrer/pageviews // URL: /api/stats/referrer/pageviews
func (api *API) GetReferrerStatsPageviewsHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetReferrerStatsPageviewsHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetAggregatedReferrerStatsPageviews(params.StartDate, params.EndDate) result, err := api.database.GetAggregatedReferrerStatsPageviews(params.SiteID, params.StartDate, params.EndDate)
if err != nil { if err != nil {
return err return err
} }

View File

@ -12,28 +12,28 @@ func (api *API) Routes() *mux.Router {
r := mux.NewRouter() r := mux.NewRouter()
r.Handle("/collect", NewCollector(api.database)).Methods(http.MethodGet) r.Handle("/collect", NewCollector(api.database)).Methods(http.MethodGet)
r.Handle("/api/session", HandlerFunc(api.GetSession)).Methods(http.MethodGet)
r.Handle("/api/session", HandlerFunc(api.CreateSession)).Methods(http.MethodPost)
r.Handle("/api/session", HandlerFunc(api.DeleteSession)).Methods(http.MethodDelete)
r.Handle("/api/sites", api.Authorize(HandlerFunc(api.GetSitesHandler))).Methods(http.MethodGet) r.Handle("/api/sites", api.Authorize(HandlerFunc(api.GetSitesHandler))).Methods(http.MethodGet)
r.Handle("/api/sites", api.Authorize(HandlerFunc(api.SaveSiteHandler))).Methods(http.MethodPost) r.Handle("/api/sites", api.Authorize(HandlerFunc(api.SaveSiteHandler))).Methods(http.MethodPost)
r.Handle("/api/sites/{id:[0-9]+}", api.Authorize(HandlerFunc(api.SaveSiteHandler))).Methods(http.MethodPost) r.Handle("/api/sites/{id:[0-9]+}", api.Authorize(HandlerFunc(api.SaveSiteHandler))).Methods(http.MethodPost)
r.Handle("/api/sites/{id:[0-9]+}", api.Authorize(HandlerFunc(api.DeleteSiteHandler))).Methods(http.MethodDelete) r.Handle("/api/sites/{id:[0-9]+}", api.Authorize(HandlerFunc(api.DeleteSiteHandler))).Methods(http.MethodDelete)
r.Handle("/api/session", HandlerFunc(api.GetSession)).Methods(http.MethodGet) r.Handle("/api/sites/{id:[0-9]+}/stats/site", api.Authorize(HandlerFunc(api.GetSiteStatsHandler))).Methods(http.MethodGet)
r.Handle("/api/session", HandlerFunc(api.CreateSession)).Methods(http.MethodPost) r.Handle("/api/sites/{id:[0-9]+}/stats/site/groupby/day", api.Authorize(HandlerFunc(api.GetSiteStatsPerDayHandler))).Methods(http.MethodGet)
r.Handle("/api/session", HandlerFunc(api.DeleteSession)).Methods(http.MethodDelete) r.Handle("/api/sites/{id:[0-9]+}/stats/site/pageviews", api.Authorize(HandlerFunc(api.GetSiteStatsPageviewsHandler))).Methods(http.MethodGet)
r.Handle("/api/sites/{id:[0-9]+}/stats/site/visitors", api.Authorize(HandlerFunc(api.GetSiteStatsVisitorsHandler))).Methods(http.MethodGet)
r.Handle("/api/sites/{id:[0-9]+}/stats/site/duration", api.Authorize(HandlerFunc(api.GetSiteStatsDurationHandler))).Methods(http.MethodGet)
r.Handle("/api/sites/{id:[0-9]+}/stats/site/bounces", api.Authorize(HandlerFunc(api.GetSiteStatsBouncesHandler))).Methods(http.MethodGet)
r.Handle("/api/sites/{id:[0-9]+}/stats/site/realtime", api.Authorize(HandlerFunc(api.GetSiteStatsRealtimeHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/site", api.Authorize(HandlerFunc(api.GetSiteStatsHandler))).Methods(http.MethodGet) r.Handle("/api/sites/{id:[0-9]+}/stats/pages", api.Authorize(HandlerFunc(api.GetPageStatsHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/site/groupby/day", api.Authorize(HandlerFunc(api.GetSiteStatsPerDayHandler))).Methods(http.MethodGet) r.Handle("/api/sites/{id:[0-9]+}/stats/pages/pageviews", api.Authorize(HandlerFunc(api.GetPageStatsPageviewsHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/site/pageviews", api.Authorize(HandlerFunc(api.GetSiteStatsPageviewsHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/site/visitors", api.Authorize(HandlerFunc(api.GetSiteStatsVisitorsHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/site/duration", api.Authorize(HandlerFunc(api.GetSiteStatsDurationHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/site/bounces", api.Authorize(HandlerFunc(api.GetSiteStatsBouncesHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/site/realtime", api.Authorize(HandlerFunc(api.GetSiteStatsRealtimeHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/pages", api.Authorize(HandlerFunc(api.GetPageStatsHandler))).Methods(http.MethodGet) r.Handle("/api/sites/{id:[0-9]+}/stats/referrers", api.Authorize(HandlerFunc(api.GetReferrerStatsHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/pages/pageviews", api.Authorize(HandlerFunc(api.GetPageStatsPageviewsHandler))).Methods(http.MethodGet) r.Handle("/api/sites/{id:[0-9]+}/stats/referrers/pageviews", api.Authorize(HandlerFunc(api.GetReferrerStatsPageviewsHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/referrers", api.Authorize(HandlerFunc(api.GetReferrerStatsHandler))).Methods(http.MethodGet)
r.Handle("/api/stats/referrers/pageviews", api.Authorize(HandlerFunc(api.GetReferrerStatsPageviewsHandler))).Methods(http.MethodGet)
r.Handle("/health", HandlerFunc(api.Health)).Methods(http.MethodGet) r.Handle("/health", HandlerFunc(api.Health)).Methods(http.MethodGet)

View File

@ -7,7 +7,7 @@ import (
// URL: /api/stats/site // URL: /api/stats/site
func (api *API) GetSiteStatsHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetSiteStatsHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetAggregatedSiteStats(params.StartDate, params.EndDate) result, err := api.database.GetAggregatedSiteStats(params.SiteID, params.StartDate, params.EndDate)
if err != nil { if err != nil {
return err return err
} }
@ -17,7 +17,7 @@ func (api *API) GetSiteStatsHandler(w http.ResponseWriter, r *http.Request) erro
// URL: /api/stats/site/pageviews // URL: /api/stats/site/pageviews
func (api *API) GetSiteStatsPageviewsHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetSiteStatsPageviewsHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetTotalSiteViews(params.StartDate, params.EndDate) result, err := api.database.GetTotalSiteViews(params.SiteID, params.StartDate, params.EndDate)
if err != nil { if err != nil {
return err return err
} }
@ -27,7 +27,7 @@ func (api *API) GetSiteStatsPageviewsHandler(w http.ResponseWriter, r *http.Requ
// URL: /api/stats/site/visitors // URL: /api/stats/site/visitors
func (api *API) GetSiteStatsVisitorsHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetSiteStatsVisitorsHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetTotalSiteVisitors(params.StartDate, params.EndDate) result, err := api.database.GetTotalSiteVisitors(params.SiteID, params.StartDate, params.EndDate)
if err != nil { if err != nil {
return err return err
} }
@ -37,7 +37,7 @@ func (api *API) GetSiteStatsVisitorsHandler(w http.ResponseWriter, r *http.Reque
// URL: /api/stats/site/duration // URL: /api/stats/site/duration
func (api *API) GetSiteStatsDurationHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetSiteStatsDurationHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetAverageSiteDuration(params.StartDate, params.EndDate) result, err := api.database.GetAverageSiteDuration(params.SiteID, params.StartDate, params.EndDate)
if err != nil { if err != nil {
return err return err
} }
@ -47,7 +47,7 @@ func (api *API) GetSiteStatsDurationHandler(w http.ResponseWriter, r *http.Reque
// URL: /api/stats/site/bounces // URL: /api/stats/site/bounces
func (api *API) GetSiteStatsBouncesHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetSiteStatsBouncesHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetAverageSiteBounceRate(params.StartDate, params.EndDate) result, err := api.database.GetAverageSiteBounceRate(params.SiteID, params.StartDate, params.EndDate)
if err != nil { if err != nil {
return err return err
} }
@ -56,7 +56,8 @@ func (api *API) GetSiteStatsBouncesHandler(w http.ResponseWriter, r *http.Reques
// URL: /api/stats/site/realtime // URL: /api/stats/site/realtime
func (api *API) GetSiteStatsRealtimeHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetSiteStatsRealtimeHandler(w http.ResponseWriter, r *http.Request) error {
result, err := api.database.GetRealtimeVisitorCount() params := GetRequestParams(r)
result, err := api.database.GetRealtimeVisitorCount(params.SiteID)
if err != nil { if err != nil {
return err return err
} }
@ -66,7 +67,7 @@ func (api *API) GetSiteStatsRealtimeHandler(w http.ResponseWriter, r *http.Reque
// URL: /api/stats/site/groupby/day // URL: /api/stats/site/groupby/day
func (api *API) GetSiteStatsPerDayHandler(w http.ResponseWriter, r *http.Request) error { func (api *API) GetSiteStatsPerDayHandler(w http.ResponseWriter, r *http.Request) error {
params := GetRequestParams(r) params := GetRequestParams(r)
result, err := api.database.GetSiteStatsPerDay(params.StartDate, params.EndDate) result, err := api.database.GetSiteStatsPerDay(params.SiteID, params.StartDate, params.EndDate)
if err != nil { if err != nil {
return err return err
} }

View File

@ -25,17 +25,17 @@ type Datastore interface {
DeleteSite(s *models.Site) error DeleteSite(s *models.Site) error
// site stats // site stats
GetSiteStats(time.Time) (*models.SiteStats, error) GetSiteStats(int64, time.Time) (*models.SiteStats, error)
GetSiteStatsPerDay(time.Time, time.Time) ([]*models.SiteStats, error) GetSiteStatsPerDay(int64, time.Time, time.Time) ([]*models.SiteStats, error)
InsertSiteStats(*models.SiteStats) error InsertSiteStats(*models.SiteStats) error
UpdateSiteStats(*models.SiteStats) error UpdateSiteStats(*models.SiteStats) error
GetAggregatedSiteStats(time.Time, time.Time) (*models.SiteStats, error) GetAggregatedSiteStats(int64, time.Time, time.Time) (*models.SiteStats, error)
GetTotalSiteViews(time.Time, time.Time) (int, error) GetTotalSiteViews(int64, time.Time, time.Time) (int64, error)
GetTotalSiteVisitors(time.Time, time.Time) (int, error) GetTotalSiteVisitors(int64, time.Time, time.Time) (int64, error)
GetTotalSiteSessions(time.Time, time.Time) (int, error) GetTotalSiteSessions(int64, time.Time, time.Time) (int64, error)
GetAverageSiteDuration(time.Time, time.Time) (float64, error) GetAverageSiteDuration(int64, time.Time, time.Time) (float64, error)
GetAverageSiteBounceRate(time.Time, time.Time) (float64, error) GetAverageSiteBounceRate(int64, time.Time, time.Time) (float64, error)
GetRealtimeVisitorCount() (int, error) GetRealtimeVisitorCount(int64) (int64, error)
// pageviews // pageviews
InsertPageviews([]*models.Pageview) error InsertPageviews([]*models.Pageview) error
@ -45,18 +45,18 @@ type Datastore interface {
DeletePageviews([]*models.Pageview) error DeletePageviews([]*models.Pageview) error
// page stats // page stats
GetPageStats(time.Time, string, string) (*models.PageStats, error) GetPageStats(int64, time.Time, string, string) (*models.PageStats, error)
InsertPageStats(*models.PageStats) error InsertPageStats(*models.PageStats) error
UpdatePageStats(*models.PageStats) error UpdatePageStats(*models.PageStats) error
GetAggregatedPageStats(time.Time, time.Time, int) ([]*models.PageStats, error) GetAggregatedPageStats(int64, time.Time, time.Time, int64) ([]*models.PageStats, error)
GetAggregatedPageStatsPageviews(time.Time, time.Time) (int, error) GetAggregatedPageStatsPageviews(int64, time.Time, time.Time) (int64, error)
// referrer stats // referrer stats
GetReferrerStats(time.Time, string, string) (*models.ReferrerStats, error) GetReferrerStats(int64, time.Time, string, string) (*models.ReferrerStats, error)
InsertReferrerStats(*models.ReferrerStats) error InsertReferrerStats(*models.ReferrerStats) error
UpdateReferrerStats(*models.ReferrerStats) error UpdateReferrerStats(*models.ReferrerStats) error
GetAggregatedReferrerStats(time.Time, time.Time, int) ([]*models.ReferrerStats, error) GetAggregatedReferrerStats(int64, time.Time, time.Time, int64) ([]*models.ReferrerStats, error)
GetAggregatedReferrerStatsPageviews(time.Time, time.Time) (int, error) GetAggregatedReferrerStatsPageviews(int64, time.Time, time.Time) (int64, error)
// misc // misc
Health() error Health() error

View File

@ -2,14 +2,15 @@ package sqlstore
import ( import (
"database/sql" "database/sql"
"github.com/usefathom/fathom/pkg/models"
"time" "time"
"github.com/usefathom/fathom/pkg/models"
) )
func (db *sqlstore) GetPageStats(date time.Time, hostname string, pathname string) (*models.PageStats, error) { func (db *sqlstore) GetPageStats(siteID int64, date time.Time, hostname string, pathname string) (*models.PageStats, error) {
stats := &models.PageStats{} stats := &models.PageStats{}
query := db.Rebind(`SELECT * FROM daily_page_stats WHERE hostname = ? AND pathname = ? AND date = ? LIMIT 1`) query := db.Rebind(`SELECT * FROM daily_page_stats WHERE site_id = ? AND hostname = ? AND pathname = ? AND date = ? LIMIT 1`)
err := db.Get(stats, query, hostname, pathname, date.Format("2006-01-02")) err := db.Get(stats, query, siteID, hostname, pathname, date.Format("2006-01-02"))
if err != nil && err == sql.ErrNoRows { if err != nil && err == sql.ErrNoRows {
return nil, ErrNoResults return nil, ErrNoResults
} }
@ -17,18 +18,18 @@ func (db *sqlstore) GetPageStats(date time.Time, hostname string, pathname strin
} }
func (db *sqlstore) InsertPageStats(s *models.PageStats) error { func (db *sqlstore) InsertPageStats(s *models.PageStats) error {
query := db.Rebind(`INSERT INTO daily_page_stats(pageviews, visitors, entries, bounce_rate, avg_duration, known_durations, hostname, pathname, date) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)`) query := db.Rebind(`INSERT INTO daily_page_stats(pageviews, visitors, entries, bounce_rate, avg_duration, known_durations, site_id, hostname, pathname, date) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
_, err := db.Exec(query, s.Pageviews, s.Visitors, s.Entries, s.BounceRate, s.AvgDuration, s.KnownDurations, s.Hostname, s.Pathname, s.Date.Format("2006-01-02")) _, err := db.Exec(query, s.Pageviews, s.Visitors, s.Entries, s.BounceRate, s.AvgDuration, s.KnownDurations, s.SiteID, s.Hostname, s.Pathname, s.Date.Format("2006-01-02"))
return err return err
} }
func (db *sqlstore) UpdatePageStats(s *models.PageStats) error { func (db *sqlstore) UpdatePageStats(s *models.PageStats) error {
query := db.Rebind(`UPDATE daily_page_stats SET pageviews = ?, visitors = ?, entries = ?, bounce_rate = ROUND(?, 4), avg_duration = ROUND(?, 4), known_durations = ? WHERE hostname = ? AND pathname = ? AND date = ?`) query := db.Rebind(`UPDATE daily_page_stats SET pageviews = ?, visitors = ?, entries = ?, bounce_rate = ROUND(?, 4), avg_duration = ROUND(?, 4), known_durations = ? WHERE site_id = ? AND hostname = ? AND pathname = ? AND date = ?`)
_, err := db.Exec(query, s.Pageviews, s.Visitors, s.Entries, s.BounceRate, s.AvgDuration, s.KnownDurations, s.Hostname, s.Pathname, s.Date.Format("2006-01-02")) _, err := db.Exec(query, s.Pageviews, s.Visitors, s.Entries, s.BounceRate, s.AvgDuration, s.KnownDurations, s.SiteID, s.Hostname, s.Pathname, s.Date.Format("2006-01-02"))
return err return err
} }
func (db *sqlstore) GetAggregatedPageStats(startDate time.Time, endDate time.Time, limit int) ([]*models.PageStats, error) { func (db *sqlstore) GetAggregatedPageStats(siteID int64, startDate time.Time, endDate time.Time, limit int64) ([]*models.PageStats, error) {
var result []*models.PageStats var result []*models.PageStats
query := db.Rebind(`SELECT query := db.Rebind(`SELECT
hostname, hostname,
@ -38,16 +39,16 @@ func (db *sqlstore) GetAggregatedPageStats(startDate time.Time, endDate time.Tim
SUM(entries) AS entries, SUM(entries) AS entries,
COALESCE(ROUND(SUM(entries*bounce_rate) / NULLIF(SUM(entries), 0), 4), 0.00) AS bounce_rate, COALESCE(ROUND(SUM(entries*bounce_rate) / NULLIF(SUM(entries), 0), 4), 0.00) AS bounce_rate,
COALESCE(ROUND(SUM(avg_duration*pageviews) / SUM(pageviews), 4), 0.00) AS avg_duration COALESCE(ROUND(SUM(avg_duration*pageviews) / SUM(pageviews), 4), 0.00) AS avg_duration
FROM daily_page_stats WHERE date >= ? AND date <= ? FROM daily_page_stats WHERE site_id = ? AND date >= ? AND date <= ?
GROUP BY hostname, pathname GROUP BY hostname, pathname
ORDER BY pageviews DESC LIMIT ?`) ORDER BY pageviews DESC LIMIT ?`)
err := db.Select(&result, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"), limit) err := db.Select(&result, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"), limit)
return result, err return result, err
} }
func (db *sqlstore) GetAggregatedPageStatsPageviews(startDate time.Time, endDate time.Time) (int, error) { func (db *sqlstore) GetAggregatedPageStatsPageviews(siteID int64, startDate time.Time, endDate time.Time) (int64, error) {
var result int var result int64
query := db.Rebind(`SELECT COALESCE(SUM(pageviews), 0) FROM daily_page_stats WHERE date >= ? AND date <= ?`) query := db.Rebind(`SELECT COALESCE(SUM(pageviews), 0) FROM daily_page_stats WHERE site_id = ? AND date >= ? AND date <= ?`)
err := db.Get(&result, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) err := db.Get(&result, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
return result, err return result, err
} }

View File

@ -7,10 +7,10 @@ import (
"github.com/usefathom/fathom/pkg/models" "github.com/usefathom/fathom/pkg/models"
) )
func (db *sqlstore) GetReferrerStats(date time.Time, hostname string, pathname string) (*models.ReferrerStats, error) { func (db *sqlstore) GetReferrerStats(siteID int64, date time.Time, hostname string, pathname string) (*models.ReferrerStats, error) {
stats := &models.ReferrerStats{} stats := &models.ReferrerStats{}
query := db.Rebind(`SELECT * FROM daily_referrer_stats WHERE date = ? AND hostname = ? AND pathname = ? LIMIT 1`) query := db.Rebind(`SELECT * FROM daily_referrer_stats WHERE site_id = ? AND date = ? AND hostname = ? AND pathname = ? LIMIT 1`)
err := db.Get(stats, query, date.Format("2006-01-02"), hostname, pathname) err := db.Get(stats, query, siteID, date.Format("2006-01-02"), hostname, pathname)
if err != nil && err == sql.ErrNoRows { if err != nil && err == sql.ErrNoRows {
return nil, ErrNoResults return nil, ErrNoResults
} }
@ -18,18 +18,18 @@ func (db *sqlstore) GetReferrerStats(date time.Time, hostname string, pathname s
} }
func (db *sqlstore) InsertReferrerStats(s *models.ReferrerStats) error { func (db *sqlstore) InsertReferrerStats(s *models.ReferrerStats) error {
query := db.Rebind(`INSERT INTO daily_referrer_stats(visitors, pageviews, bounce_rate, avg_duration, known_durations, groupname, hostname, pathname, date) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)`) query := db.Rebind(`INSERT INTO daily_referrer_stats(visitors, pageviews, bounce_rate, avg_duration, known_durations, groupname, site_id, hostname, pathname, date) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
_, err := db.Exec(query, s.Visitors, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.Group, s.Hostname, s.Pathname, s.Date.Format("2006-01-02")) _, err := db.Exec(query, s.Visitors, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.Group, s.SiteID, s.Hostname, s.Pathname, s.Date.Format("2006-01-02"))
return err return err
} }
func (db *sqlstore) UpdateReferrerStats(s *models.ReferrerStats) error { func (db *sqlstore) UpdateReferrerStats(s *models.ReferrerStats) error {
query := db.Rebind(`UPDATE daily_referrer_stats SET visitors = ?, pageviews = ?, bounce_rate = ROUND(?, 4), avg_duration = ROUND(?, 4), known_durations = ?, groupname = ? WHERE hostname = ? AND pathname = ? AND date = ?`) query := db.Rebind(`UPDATE daily_referrer_stats SET visitors = ?, pageviews = ?, bounce_rate = ROUND(?, 4), avg_duration = ROUND(?, 4), known_durations = ?, groupname = ? WHERE site_id = ? AND hostname = ? AND pathname = ? AND date = ?`)
_, err := db.Exec(query, s.Visitors, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.Group, s.Hostname, s.Pathname, s.Date.Format("2006-01-02")) _, err := db.Exec(query, s.Visitors, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.Group, s.SiteID, s.Hostname, s.Pathname, s.Date.Format("2006-01-02"))
return err return err
} }
func (db *sqlstore) GetAggregatedReferrerStats(startDate time.Time, endDate time.Time, limit int) ([]*models.ReferrerStats, error) { func (db *sqlstore) GetAggregatedReferrerStats(siteID int64, startDate time.Time, endDate time.Time, limit int64) ([]*models.ReferrerStats, error) {
var result []*models.ReferrerStats var result []*models.ReferrerStats
sql := `SELECT sql := `SELECT
@ -41,7 +41,7 @@ func (db *sqlstore) GetAggregatedReferrerStats(startDate time.Time, endDate time
COALESCE(ROUND(SUM(pageviews*NULLIF(bounce_rate, 0)) / SUM(pageviews), 4), 0.00) AS bounce_rate, COALESCE(ROUND(SUM(pageviews*NULLIF(bounce_rate, 0)) / SUM(pageviews), 4), 0.00) AS bounce_rate,
COALESCE(ROUND(SUM(avg_duration*pageviews) / SUM(pageviews), 4), 0.00) AS avg_duration COALESCE(ROUND(SUM(avg_duration*pageviews) / SUM(pageviews), 4), 0.00) AS avg_duration
FROM daily_referrer_stats FROM daily_referrer_stats
WHERE date >= ? AND date <= ? ` WHERE site_id = ? AND date >= ? AND date <= ? `
if db.Config.Driver == "sqlite3" { if db.Config.Driver == "sqlite3" {
sql = sql + `GROUP BY COALESCE(NULLIF(groupname, ''), hostname || pathname ) ` sql = sql + `GROUP BY COALESCE(NULLIF(groupname, ''), hostname || pathname ) `
@ -52,13 +52,13 @@ func (db *sqlstore) GetAggregatedReferrerStats(startDate time.Time, endDate time
query := db.Rebind(sql) query := db.Rebind(sql)
err := db.Select(&result, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"), limit) err := db.Select(&result, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"), limit)
return result, err return result, err
} }
func (db *sqlstore) GetAggregatedReferrerStatsPageviews(startDate time.Time, endDate time.Time) (int, error) { func (db *sqlstore) GetAggregatedReferrerStatsPageviews(siteID int64, startDate time.Time, endDate time.Time) (int64, error) {
var result int var result int64
query := db.Rebind(`SELECT COALESCE(SUM(pageviews), 0) FROM daily_referrer_stats WHERE date >= ? AND date <= ?`) query := db.Rebind(`SELECT COALESCE(SUM(pageviews), 0) FROM daily_referrer_stats WHERE site_id = ? AND date >= ? AND date <= ?`)
err := db.Get(&result, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) err := db.Get(&result, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
return result, err return result, err
} }

View File

@ -7,10 +7,10 @@ import (
"github.com/usefathom/fathom/pkg/models" "github.com/usefathom/fathom/pkg/models"
) )
func (db *sqlstore) GetSiteStats(date time.Time) (*models.SiteStats, error) { func (db *sqlstore) GetSiteStats(siteID int64, date time.Time) (*models.SiteStats, error) {
stats := &models.SiteStats{} stats := &models.SiteStats{}
query := db.Rebind(`SELECT * FROM daily_site_stats WHERE date = ? LIMIT 1`) query := db.Rebind(`SELECT * FROM daily_site_stats WHERE site_id = ? AND date = ? LIMIT 1`)
err := db.Get(stats, query, date.Format("2006-01-02")) err := db.Get(stats, query, siteID, date.Format("2006-01-02"))
if err != nil && err == sql.ErrNoRows { if err != nil && err == sql.ErrNoRows {
return nil, ErrNoResults return nil, ErrNoResults
} }
@ -18,26 +18,26 @@ func (db *sqlstore) GetSiteStats(date time.Time) (*models.SiteStats, error) {
} }
func (db *sqlstore) InsertSiteStats(s *models.SiteStats) error { func (db *sqlstore) InsertSiteStats(s *models.SiteStats) error {
query := db.Rebind(`INSERT INTO daily_site_stats(visitors, sessions, pageviews, bounce_rate, avg_duration, known_durations, date) VALUES(?, ?, ?, ?, ?, ?, ?)`) query := db.Rebind(`INSERT INTO daily_site_stats(site_id, visitors, sessions, pageviews, bounce_rate, avg_duration, known_durations, date) VALUES(?, ?, ?, ?, ?, ?, ?)`)
_, err := db.Exec(query, s.Visitors, s.Sessions, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.Date.Format("2006-01-02")) _, err := db.Exec(query, s.SiteID, s.Visitors, s.Sessions, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.Date.Format("2006-01-02"))
return err return err
} }
func (db *sqlstore) UpdateSiteStats(s *models.SiteStats) error { func (db *sqlstore) UpdateSiteStats(s *models.SiteStats) error {
query := db.Rebind(`UPDATE daily_site_stats SET visitors = ?, sessions = ?, pageviews = ?, bounce_rate = ROUND(?, 4), avg_duration = ROUND(?, 4), known_durations = ? WHERE date = ?`) query := db.Rebind(`UPDATE daily_site_stats SET visitors = ?, sessions = ?, pageviews = ?, bounce_rate = ROUND(?, 4), avg_duration = ROUND(?, 4), known_durations = ? WHERE site_id = ? AND date = ?`)
_, err := db.Exec(query, s.Visitors, s.Sessions, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.Date.Format("2006-01-02")) _, err := db.Exec(query, s.Visitors, s.Sessions, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.SiteID, s.Date.Format("2006-01-02"))
return err return err
} }
func (db *sqlstore) GetSiteStatsPerDay(startDate time.Time, endDate time.Time) ([]*models.SiteStats, error) { func (db *sqlstore) GetSiteStatsPerDay(siteID int64, startDate time.Time, endDate time.Time) ([]*models.SiteStats, error) {
results := []*models.SiteStats{} results := []*models.SiteStats{}
sql := `SELECT * FROM daily_site_stats WHERE date >= ? AND date <= ?` sql := `SELECT * FROM daily_site_stats WHERE site_id = ? AND date >= ? AND date <= ?`
query := db.Rebind(sql) query := db.Rebind(sql)
err := db.Select(&results, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) err := db.Select(&results, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
return results, err return results, err
} }
func (db *sqlstore) GetAggregatedSiteStats(startDate time.Time, endDate time.Time) (*models.SiteStats, error) { func (db *sqlstore) GetAggregatedSiteStats(siteID int64, startDate time.Time, endDate time.Time) (*models.SiteStats, error) {
stats := &models.SiteStats{} stats := &models.SiteStats{}
query := db.Rebind(`SELECT query := db.Rebind(`SELECT
COALESCE(SUM(pageviews), 0) AS pageviews, COALESCE(SUM(pageviews), 0) AS pageviews,
@ -45,8 +45,8 @@ func (db *sqlstore) GetAggregatedSiteStats(startDate time.Time, endDate time.Tim
COALESCE(SUM(sessions), 0) AS sessions, COALESCE(SUM(sessions), 0) AS sessions,
COALESCE(ROUND(SUM(pageviews*avg_duration) / NULLIF(SUM(pageviews), 0), 4), 0.00) AS avg_duration, COALESCE(ROUND(SUM(pageviews*avg_duration) / NULLIF(SUM(pageviews), 0), 4), 0.00) AS avg_duration,
COALESCE(ROUND(SUM(sessions*bounce_rate) / NULLIF(SUM(sessions), 0), 4), 0.00) AS bounce_rate COALESCE(ROUND(SUM(sessions*bounce_rate) / NULLIF(SUM(sessions), 0), 4), 0.00) AS bounce_rate
FROM daily_site_stats WHERE date >= ? AND date <= ? LIMIT 1`) FROM daily_site_stats WHERE site_id = ? AND date >= ? AND date <= ? LIMIT 1`)
err := db.Get(stats, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) err := db.Get(stats, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
if err != nil && err == sql.ErrNoRows { if err != nil && err == sql.ErrNoRows {
return nil, ErrNoResults return nil, ErrNoResults
} }
@ -54,50 +54,58 @@ func (db *sqlstore) GetAggregatedSiteStats(startDate time.Time, endDate time.Tim
return stats, err return stats, err
} }
func (db *sqlstore) GetTotalSiteViews(startDate time.Time, endDate time.Time) (int, error) { func (db *sqlstore) GetTotalSiteViews(siteID int64, startDate time.Time, endDate time.Time) (int64, error) {
sql := `SELECT COALESCE(SUM(pageviews), 0) FROM daily_site_stats WHERE date >= ? AND date <= ?` sql := `SELECT COALESCE(SUM(pageviews), 0) FROM daily_site_stats WHERE site_id = ? AND date >= ? AND date <= ?`
query := db.Rebind(sql) query := db.Rebind(sql)
var total int var total int64
err := db.Get(&total, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) err := db.Get(&total, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
return total, err return total, err
} }
func (db *sqlstore) GetTotalSiteVisitors(startDate time.Time, endDate time.Time) (int, error) { func (db *sqlstore) GetTotalSiteVisitors(siteID int64, startDate time.Time, endDate time.Time) (int64, error) {
sql := `SELECT COALESCE(SUM(visitors), 0) FROM daily_site_stats WHERE date >= ? AND date <= ?` sql := `SELECT COALESCE(SUM(visitors), 0) FROM daily_site_stats WHERE site_id = ? AND date >= ? AND date <= ?`
query := db.Rebind(sql) query := db.Rebind(sql)
var total int var total int64
err := db.Get(&total, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) err := db.Get(&total, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
return total, err return total, err
} }
func (db *sqlstore) GetTotalSiteSessions(startDate time.Time, endDate time.Time) (int, error) { func (db *sqlstore) GetTotalSiteSessions(siteID int64, startDate time.Time, endDate time.Time) (int64, error) {
sql := `SELECT COALESCE(SUM(sessions), 0) FROM daily_site_stats WHERE date >= ? AND date <= ?` sql := `SELECT COALESCE(SUM(sessions), 0) FROM daily_site_stats WHERE site_id = ? AND date >= ? AND date <= ?`
query := db.Rebind(sql) query := db.Rebind(sql)
var total int var total int64
err := db.Get(&total, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) err := db.Get(&total, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
return total, err return total, err
} }
func (db *sqlstore) GetAverageSiteDuration(startDate time.Time, endDate time.Time) (float64, error) { func (db *sqlstore) GetAverageSiteDuration(siteID int64, startDate time.Time, endDate time.Time) (float64, error) {
sql := `SELECT COALESCE(ROUND(SUM(pageviews*avg_duration)/SUM(pageviews), 4), 0.00) FROM daily_site_stats WHERE date >= ? AND date <= ?` sql := `SELECT COALESCE(ROUND(SUM(pageviews*avg_duration)/SUM(pageviews), 4), 0.00) FROM daily_site_stats WHERE site_id = ? AND date >= ? AND date <= ?`
query := db.Rebind(sql) query := db.Rebind(sql)
var total float64 var total float64
err := db.Get(&total, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) err := db.Get(&total, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
return total, err return total, err
} }
func (db *sqlstore) GetAverageSiteBounceRate(startDate time.Time, endDate time.Time) (float64, error) { func (db *sqlstore) GetAverageSiteBounceRate(siteID int64, startDate time.Time, endDate time.Time) (float64, error) {
sql := `SELECT COALESCE(ROUND(SUM(sessions*bounce_rate)/SUM(sessions), 4), 0.00) FROM daily_site_stats WHERE date >= ? AND date <= ?` sql := `SELECT COALESCE(ROUND(SUM(sessions*bounce_rate)/SUM(sessions), 4), 0.00) FROM daily_site_stats WHERE site_id = ? AND date >= ? AND date <= ?`
query := db.Rebind(sql) query := db.Rebind(sql)
var total float64 var total float64
err := db.Get(&total, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02")) err := db.Get(&total, query, siteID, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
return total, err return total, err
} }
func (db *sqlstore) GetRealtimeVisitorCount() (int, error) { func (db *sqlstore) GetRealtimeVisitorCount(siteID int64) (int64, error) {
sql := `SELECT COUNT(*) FROM pageviews WHERE ( duration = 0 OR is_bounce = TRUE ) AND timestamp > ?` var siteTrackingID string
var total int64
if err := db.Get(&siteTrackingID, db.Rebind(`SELECT tracking_id FROM sites WHERE id = ?`), siteID); err != nil && err != sql.ErrNoRows {
return 0, err
}
sql := `SELECT COUNT(*) FROM pageviews p WHERE site_tracking_id = ? AND ( duration = 0 OR is_bounce = TRUE) AND timestamp > ?`
query := db.Rebind(sql) query := db.Rebind(sql)
var total int if err := db.Get(&total, query, siteTrackingID, time.Now().Add(-5*time.Minute)); err != nil {
err := db.Get(&total, query, time.Now().Add(-5*time.Minute)) return 0, err
return total, err }
return total, nil
} }