diff --git a/README.md b/README.md index db6b191..7d85272 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ You can request statistics via the `/stats` endpoint and specifying filters via The following filters are available: -`view`: specify what data (and it's view counts) gets displayed, you have the option between `pages` (tracked URLS), `referrers` (tracked refererrers - only hostnames e.g. google.com), `useragents` (tracked useragents with version - browsers or crawl bots with version), `useragentnames` (tracked useragents without version), `hours` / `days` / `weeks` / `months` (tracks grouped by hours / days / weeks / months), `allhours` / `alldays` (tracks grouped by hours / days including hours or days with zero visits, spanning from first to last track in selection), `count` (count all tracked views where filters apply) +`view`: specify what data (and it's view counts) gets displayed, you have the option between `pages` (tracked URLS), `referrers` (tracked refererrers - only hostnames e.g. google.com), `useragents` (tracked useragents with version - browsers or crawl bots with version), `useragentnames` (tracked useragents without version), `os` (tracked operating systems), `hours` / `days` / `weeks` / `months` (tracks grouped by hours / days / weeks / months), `allhours` / `alldays` (tracks grouped by hours / days including hours or days with zero visits, spanning from first to last track in selection), `count` (count all tracked views where filters apply) `from`: start time of the selection in the format `YYYY-MM-DD HH:MM`, e.g. `2019-01` or `2019-01-01 01:00` @@ -94,6 +94,8 @@ The following filters are available: `ua`: filter user agents containing the string provided, so `Firefox` filters out all user agents that don't contain `Firefox` +`os`: filter operating systems containing the string provided, so `Windows` filters out all operating systems that don't contain `Windows` + `bots`: filter out bots (`0`) or show only bots (`1`) `ordercol`: column to use for ordering, `first` for the data groups, `second` for the view counts diff --git a/database.go b/database.go index d602aa2..2bf35df 100644 --- a/database.go +++ b/database.go @@ -32,7 +32,7 @@ func initDatabase() (e error) { return } e = migrateDatabase(db.sqlDB) - db.trackingStmt, e = db.sqlDB.Prepare("insert into views(url, ref, useragent, bot) values(:url, :ref, :ua, :bot)") + db.trackingStmt, e = db.sqlDB.Prepare("insert into views(url, ref, useragent, bot, os) values(:url, :ref, :ua, :bot, :os)") if e != nil { return } @@ -60,6 +60,7 @@ func trackView(urlString string, ref string, ua string) { ref = parsedRef.Hostname() } bot := 0 + osString := "" if ua != "" { // Parse Useragent userAgent := user_agent.New(ua) @@ -68,8 +69,15 @@ func trackView(urlString string, ref string, ua string) { } uaName, uaVersion := userAgent.Browser() ua = uaName + " " + uaVersion + osInfo := userAgent.OSInfo() + if osInfo.FullName != "" { + osString = osInfo.FullName + if osInfo.Version != "" { + osString += " " + osInfo.Version + } + } } - _, e := db.trackingStmt.Exec(sql.Named("url", urlString), sql.Named("ref", ref), sql.Named("ua", ua), sql.Named("bot", bot)) + _, e := db.trackingStmt.Exec(sql.Named("url", urlString), sql.Named("ref", ref), sql.Named("ua", ua), sql.Named("bot", bot), sql.Named("os", osString)) if e != nil { fmt.Println("Inserting into DB failed:", e) } @@ -84,6 +92,7 @@ const ( REFERRERS USERAGENTS USERAGENTNAMES + OS HOURS DAYS WEEKS @@ -106,6 +115,7 @@ type ViewsRequest struct { order string limit string bots string + os string } type RequestResultRow struct { @@ -182,6 +192,8 @@ func (request *ViewsRequest) buildStatement() (statement string, parameters []sq statement = "SELECT useragent as first, count(*) as second from views" + filters + "group by first" + orderStatement + limitStatement + ";" case USERAGENTNAMES: statement = "SELECT substr(useragent, 1, pos-1) as first, COUNT(*) as second from (SELECT *, instr(useragent,' ') AS pos FROM views)" + filters + "group by first" + orderStatement + limitStatement + ";" + case OS: + statement = "SELECT os as first, count(*) as second from views" + filters + "group by first" + orderStatement + limitStatement + ";" case ALLHOURS: statement = "WITH RECURSIVE hours(hour) AS ( VALUES (datetime(strftime('%Y-%m-%dT%H:00', (SELECT min(time) from views" + filters + "), 'localtime'))) UNION ALL SELECT datetime(hour, '+1 hour') FROM hours WHERE hour <= strftime('%Y-%m-%d %H', (SELECT max(time) from views" + filters + "), 'localtime') ) SELECT strftime('%Y-%m-%d %H', hours.hour) as first, COUNT(time) as second FROM hours LEFT OUTER JOIN (SELECT time from views" + filters + ") ON strftime('%Y-%m-%d %H', hours.hour) = strftime('%Y-%m-%d %H', time, 'localtime') GROUP BY first" + orderStatement + limitStatement + ";" case ALLDAYS: @@ -216,6 +228,7 @@ func (request *ViewsRequest) buildFilter() (filters string, parameters []sql.Nam request.buildRefFilter(¶meters), request.buildUseragentFilter(¶meters), request.buildBotFilter(¶meters), + request.buildOSFilter(¶meters), } { if len(filter) > 0 { allFilters = append(allFilters, filter) @@ -288,3 +301,11 @@ func (request *ViewsRequest) buildBotFilter(namedArg *[]sql.NamedArg) (botFilter } return } + +func (request *ViewsRequest) buildOSFilter(namedArg *[]sql.NamedArg) (osFilter string) { + if len(request.os) > 0 { + *namedArg = append(*namedArg, sql.Named("os", "%"+request.os+"%")) + osFilter = "os like :os" + } + return +} diff --git a/migrations/4_Add_OS.sql b/migrations/4_Add_OS.sql new file mode 100644 index 0000000..391c189 --- /dev/null +++ b/migrations/4_Add_OS.sql @@ -0,0 +1,3 @@ +-- +migrate Up +ALTER TABLE views + ADD COLUMN os TEXT DEFAULT '' NOT NULL; diff --git a/stats.go b/stats.go index e249e82..d8290c6 100644 --- a/stats.go +++ b/stats.go @@ -55,6 +55,8 @@ func doRequest(queries url.Values) (result []*RequestResultRow, e error) { view = USERAGENTS case "useragentnames": view = USERAGENTNAMES + case "os": + view = OS case "hours": view = HOURS case "days": @@ -83,6 +85,7 @@ func doRequest(queries url.Values) (result []*RequestResultRow, e error) { order: strings.ToUpper(queries.Get("order")), limit: queries.Get("limit"), bots: queries.Get("bots"), + os: queries.Get("os"), }) return }