diff --git a/assets/src/js/components/Table.js b/assets/src/js/components/Table.js index abce798..c908811 100644 --- a/assets/src/js/components/Table.js +++ b/assets/src/js/components/Table.js @@ -57,21 +57,25 @@ class Table extends Component { render(props, state) { const tableRows = state.records !== null && state.records.length > 0 ? state.records.map((p, i) => { - let ahref = document.createElement('a'); - ahref.href = (p.Hostname + p.Pathname) || p.URL; + + let href = (p.Hostname + p.Pathname) || p.URL; let classes = "table-row"; if(state.total > 0) { classes += " w" + Math.min(98, Math.round(p.Pageviews / state.total * 100 * 2.5)); } - let label = ahref.pathname + ahref.search; + let label = p.Pathname if( props.showHostname ) { - label = ahref.hostname.replace('www.', '') + (ahref.pathname.length > 1 ? ahref.pathname : ''); + if( p.Group) { + label = p.Group + } else { + label = p.Hostname.replace('www.', '').replace('https://', '').replace('http://', '') + (p.Pathname.length > 1 ? p.Pathname : '') + } } return(
-
{label}
+
{label}
{p.Pageviews}
{p.Visitors||"-"}
@@ -81,8 +85,7 @@ class Table extends Component {
{props.headers.map((header, i) => { - let classes = i === 0 ? 'main-col cell' : 'cell'; - return (
{header}
) + return (
{header}
) })}
diff --git a/pkg/aggregator/aggregator.go b/pkg/aggregator/aggregator.go index 936bc67..5b4ab81 100644 --- a/pkg/aggregator/aggregator.go +++ b/pkg/aggregator/aggregator.go @@ -3,6 +3,7 @@ package aggregator import ( "github.com/usefathom/fathom/pkg/datastore" "github.com/usefathom/fathom/pkg/models" + "net/url" log "github.com/sirupsen/logrus" ) @@ -124,7 +125,8 @@ func (agg *aggregator) Process(pageviews []*models.Pageview) *results { // referrer stats if p.Referrer != "" { - referrerStats, err := agg.getReferrerStats(results, p.Timestamp, p.Referrer) + hostname, pathname, _ := parseUrlParts(p.Referrer) + referrerStats, err := agg.getReferrerStats(results, p.Timestamp, hostname, pathname) if err != nil { log.Error(err) continue @@ -153,3 +155,12 @@ func (agg *aggregator) Process(pageviews []*models.Pageview) *results { return results } + +func parseUrlParts(s string) (string, string, error) { + u, err := url.Parse(s) + if err != nil { + return "", "", err + } + + return u.Scheme + "://" + u.Host, u.Path, nil +} diff --git a/pkg/aggregator/store.go b/pkg/aggregator/store.go index b7b0631..0090660 100644 --- a/pkg/aggregator/store.go +++ b/pkg/aggregator/store.go @@ -64,14 +64,14 @@ func (agg *aggregator) getPageStats(r *results, t time.Time, hostname string, pa return stats, nil } -func (agg *aggregator) getReferrerStats(r *results, t time.Time, url string) (*models.ReferrerStats, error) { +func (agg *aggregator) getReferrerStats(r *results, t time.Time, hostname string, pathname string) (*models.ReferrerStats, error) { date := t.Format("2006-01-02") - if stats, ok := r.Referrers[date+url]; ok { + if stats, ok := r.Referrers[date+hostname+pathname]; ok { return stats, nil } // get from db - stats, err := agg.database.GetReferrerStats(t, url) + stats, err := agg.database.GetReferrerStats(t, hostname, pathname) if err != nil && err != datastore.ErrNoResults { return nil, err } @@ -79,8 +79,9 @@ func (agg *aggregator) getReferrerStats(r *results, t time.Time, url string) (*m // create in db if stats == nil { stats = &models.ReferrerStats{ - URL: url, - Date: t, + Hostname: hostname, + Pathname: pathname, + Date: t, } err = agg.database.InsertReferrerStats(stats) if err != nil { @@ -88,6 +89,6 @@ func (agg *aggregator) getReferrerStats(r *results, t time.Time, url string) (*m } } - r.Referrers[date+url] = stats + r.Referrers[date+hostname+pathname] = stats return stats, nil } diff --git a/pkg/datastore/datastore.go b/pkg/datastore/datastore.go index 28d91b1..a13a090 100644 --- a/pkg/datastore/datastore.go +++ b/pkg/datastore/datastore.go @@ -43,7 +43,7 @@ type Datastore interface { GetAggregatedPageStatsPageviews(time.Time, time.Time) (int, error) // referrer stats - GetReferrerStats(time.Time, string) (*models.ReferrerStats, error) + GetReferrerStats(time.Time, string, string) (*models.ReferrerStats, error) InsertReferrerStats(*models.ReferrerStats) error UpdateReferrerStats(*models.ReferrerStats) error GetAggregatedReferrerStats(time.Time, time.Time, int) ([]*models.ReferrerStats, error) diff --git a/pkg/datastore/sqlstore/migrations/mysql/3_referrer_group_column.sql b/pkg/datastore/sqlstore/migrations/mysql/3_referrer_group_column.sql new file mode 100644 index 0000000..9b19891 --- /dev/null +++ b/pkg/datastore/sqlstore/migrations/mysql/3_referrer_group_column.sql @@ -0,0 +1,19 @@ +-- +migrate Up + +ALTER TABLE daily_referrer_stats ADD COLUMN groupname VARCHAR(255) NULL; +ALTER TABLE daily_referrer_stats ADD COLUMN hostname VARCHAR(255); +ALTER TABLE daily_referrer_stats ADD COLUMN pathname VARCHAR(255); + +UPDATE daily_referrer_stats SET hostname = SUBSTRING_INDEX( url, "/", 3) WHERE url != "" ANd hostname = ""; +UPDATE daily_referrer_stats SET pathname = CONCAT("/", SUBSTRING_INDEX( url, "/", -1)) WHERE url != "" AND pathname = ""; + +ALTER TABLE daily_referrer_stats DROP COLUMN url; + +-- +migrate Down + +ALTER TABLE daily_referrer_stats DROP COLUMN groupname; +ALTER TABLE daily_referrer_stats DROP COLUMN hostname; +ALTER TABLE daily_referrer_stats DROP COLUMN pathname; + +ALTER TABLE daily_referrer_stats ADD COLUMN url VARCHAR(255) NOT NULL; + diff --git a/pkg/datastore/sqlstore/migrations/postgres/3_referrer_group_column.sql b/pkg/datastore/sqlstore/migrations/postgres/3_referrer_group_column.sql new file mode 100644 index 0000000..b06b5bf --- /dev/null +++ b/pkg/datastore/sqlstore/migrations/postgres/3_referrer_group_column.sql @@ -0,0 +1,18 @@ +-- +migrate Up + +ALTER TABLE daily_referrer_stats ADD COLUMN groupname VARCHAR(255) NULL; +ALTER TABLE daily_referrer_stats ADD COLUMN hostname VARCHAR(255); +ALTER TABLE daily_referrer_stats ADD COLUMN pathname VARCHAR(255); + +UPDATE daily_referrer_stats SET hostname = CONCAT( SPLIT_PART(url, '://', 1), '://', SPLIT_PART(SPLIT_PART(url, '://', 2), '/', 1) ) WHERE url != '' AND hostname = ''; +UPDATE daily_referrer_stats SET pathname = SPLIT_PART( url, hostname, 2 ) WHERE url != '' AND pathname = ''; + +ALTER TABLE daily_referrer_stats DROP COLUMN url; + +-- +migrate Down + +ALTER TABLE daily_referrer_stats DROP COLUMN groupname; +ALTER TABLE daily_referrer_stats DROP COLUMN hostname; +ALTER TABLE daily_referrer_stats DROP COLUMN pathname; + +ALTER TABLE daily_referrer_stats ADD COLUMN url VARCHAR(255) NOT NULL; diff --git a/pkg/datastore/sqlstore/migrations/sqlite3/3_referrer_group_column.sql b/pkg/datastore/sqlstore/migrations/sqlite3/3_referrer_group_column.sql new file mode 100644 index 0000000..16fff36 --- /dev/null +++ b/pkg/datastore/sqlstore/migrations/sqlite3/3_referrer_group_column.sql @@ -0,0 +1,18 @@ +-- +migrate Up + +ALTER TABLE daily_referrer_stats ADD COLUMN groupname VARCHAR(255) NULL; +ALTER TABLE daily_referrer_stats ADD COLUMN hostname VARCHAR(255); +ALTER TABLE daily_referrer_stats ADD COLUMN pathname VARCHAR(255); + +UPDATE daily_referrer_stats SET hostname = SUBSTR(url, 0, INSTR(url, '://')+3+INSTR(SUBSTR(url, INSTR(url, '://')+3), '/'-1)) WHERE url != '' AND hostname = ''; +UPDATE daily_referrer_stats SET pathname = SUBSTR(url, LENGTH(hostname)) WHERE url != '' AND pathname = ''; + +ALTER TABLE daily_referrer_stats DROP COLUMN url; + +-- +migrate Down + +ALTER TABLE daily_referrer_stats DROP COLUMN groupname; +ALTER TABLE daily_referrer_stats DROP COLUMN hostname; +ALTER TABLE daily_referrer_stats DROP COLUMN pathname; + +ALTER TABLE daily_referrer_stats ADD COLUMN url VARCHAR(255) NOT NULL; diff --git a/pkg/datastore/sqlstore/referrer_stats.go b/pkg/datastore/sqlstore/referrer_stats.go index 244cca6..96566dd 100644 --- a/pkg/datastore/sqlstore/referrer_stats.go +++ b/pkg/datastore/sqlstore/referrer_stats.go @@ -7,10 +7,10 @@ import ( "github.com/usefathom/fathom/pkg/models" ) -func (db *sqlstore) GetReferrerStats(date time.Time, url string) (*models.ReferrerStats, error) { +func (db *sqlstore) GetReferrerStats(date time.Time, hostname string, pathname string) (*models.ReferrerStats, error) { stats := &models.ReferrerStats{} - query := db.Rebind(`SELECT * FROM daily_referrer_stats WHERE url = ? AND date = ? LIMIT 1`) - err := db.Get(stats, query, url, date.Format("2006-01-02")) + query := db.Rebind(`SELECT * FROM daily_referrer_stats WHERE date = ? AND hostname = ? AND pathname = ? LIMIT 1`) + err := db.Get(stats, query, date.Format("2006-01-02"), hostname, pathname) if err != nil && err == sql.ErrNoRows { return nil, ErrNoResults } @@ -18,20 +18,31 @@ func (db *sqlstore) GetReferrerStats(date time.Time, url string) (*models.Referr } func (db *sqlstore) InsertReferrerStats(s *models.ReferrerStats) error { - query := db.Rebind(`INSERT INTO daily_referrer_stats(visitors, pageviews, bounce_rate, avg_duration, known_durations, url, date) VALUES(?, ?, ?, ?, ?, ?, ?)`) - _, err := db.Exec(query, s.Visitors, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.URL, s.Date.Format("2006-01-02")) + query := db.Rebind(`INSERT INTO daily_referrer_stats(visitors, pageviews, bounce_rate, avg_duration, known_durations, groupname, 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")) return err } 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 = ? WHERE url = ? AND date = ?`) - _, err := db.Exec(query, s.Visitors, s.Pageviews, s.BounceRate, s.AvgDuration, s.KnownDurations, s.URL, s.Date.Format("2006-01-02")) + 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 = ?`) + _, 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")) return err } func (db *sqlstore) GetAggregatedReferrerStats(startDate time.Time, endDate time.Time, limit int) ([]*models.ReferrerStats, error) { var result []*models.ReferrerStats - query := db.Rebind(`SELECT url, SUM(visitors) AS visitors, SUM(pageviews) AS pageviews, COALESCE(ROUND(SUM(pageviews*bounce_rate)/SUM(pageviews), 4), 0.00) AS bounce_rate, COALESCE(ROUND(SUM(avg_duration*pageviews)/SUM(pageviews), 4), 0.00) AS avg_duration FROM daily_referrer_stats WHERE date >= ? AND date <= ? GROUP BY url ORDER BY pageviews DESC LIMIT ?`) + query := db.Rebind(` + SELECT + MIN(hostname) AS hostname, + MIN(pathname) AS pathname, + MIN(COALESCE(groupname, "")) AS groupname, + SUM(visitors) AS visitors, + SUM(pageviews) AS pageviews, + COALESCE(ROUND(SUM(pageviews*bounce_rate)/SUM(pageviews), 4), 0.00) AS bounce_rate, + COALESCE(ROUND(SUM(avg_duration*pageviews)/SUM(pageviews), 4), 0.00) AS avg_duration + FROM daily_referrer_stats + WHERE date >= ? AND date <= ? + GROUP BY COALESCE(groupname, CONCAT(hostname, pathname)) ORDER BY pageviews DESC LIMIT ?`) err := db.Select(&result, query, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"), limit) return result, err } diff --git a/pkg/models/referrer_stats.go b/pkg/models/referrer_stats.go index c107dc8..ef8093e 100644 --- a/pkg/models/referrer_stats.go +++ b/pkg/models/referrer_stats.go @@ -5,7 +5,9 @@ import ( ) type ReferrerStats struct { - URL string `db:"url"` + Hostname string `db:"hostname"` + Pathname string `db:"pathname"` + Group string `db:"groupname"` Visitors int64 `db:"visitors"` Pageviews int64 `db:"pageviews"` BounceRate float64 `db:"bounce_rate"`