aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Hanley <felix@userspace.com.au>2020-03-16 11:24:04 +0000
committerFelix Hanley <felix@userspace.com.au>2020-03-16 11:24:04 +0000
commit2248b4d7e1d083a103e94985ee4b373d689ae0e8 (patch)
tree20a3f12a7d793ddadd5225e3e0cb44fa738682a5
parentc16f2a68eeb6550743354245f95605a141c1d020 (diff)
downloadsws-2248b4d7e1d083a103e94985ee4b373d689ae0e8.tar.gz
sws-2248b4d7e1d083a103e94985ee4b373d689ae0e8.tar.bz2
Update graph displays and supporting helpers
-rw-r--r--browser_set.go7
-rw-r--r--cmd/server/helpers.go5
-rw-r--r--cmd/server/hits.go3
-rw-r--r--cmd/server/routes.go14
-rw-r--r--country.go7
-rw-r--r--page_set.go22
-rw-r--r--referrer.go7
-rw-r--r--static/default.css31
-rw-r--r--tmpl/site.tmpl59
9 files changed, 110 insertions, 45 deletions
diff --git a/browser_set.go b/browser_set.go
index b53ddb7..4d3107c 100644
--- a/browser_set.go
+++ b/browser_set.go
@@ -76,6 +76,13 @@ func (bs BrowserSet) YMax() int {
}
return max
}
+func (bs BrowserSet) YSum() int {
+ sum := 0
+ for _, b := range bs {
+ sum += b.hitSet.Count()
+ }
+ return sum
+}
func (bs BrowserSet) XSeries() []*Browser {
return bs
}
diff --git a/cmd/server/helpers.go b/cmd/server/helpers.go
index 01552b2..6c498e5 100644
--- a/cmd/server/helpers.go
+++ b/cmd/server/helpers.go
@@ -3,6 +3,7 @@ package main
import (
"fmt"
"html/template"
+ "math"
"net/http"
"strconv"
"time"
@@ -63,6 +64,10 @@ var funcMap = template.FuncMap{
"percentInv": func(a, b int) float64 {
return 100.0 - ((float64(a) / float64(b)) * 100)
},
+ "round": func(n int, a float64) float64 {
+ n = n * 10
+ return math.Round(a*float64(n)) / float64(n)
+ },
}
func httpError(w http.ResponseWriter, code int, msg string) {
diff --git a/cmd/server/hits.go b/cmd/server/hits.go
index 27bb11d..e5aaf73 100644
--- a/cmd/server/hits.go
+++ b/cmd/server/hits.go
@@ -99,12 +99,15 @@ func verifyHit(db sws.SiteGetter, h *sws.Hit) (*sws.Site, error) {
return nil, err
}
if site.Name == h.Host {
+ debug(h.Host, "equals site name:", site.Name)
return site, nil
}
if strings.Contains(site.Aliases, h.Host) {
+ debug(h.Host, "equals site alias:", site.Name)
return site, nil
}
if site.AcceptSubdomains && strings.HasSuffix(h.Host, site.Name) {
+ debug(h.Host, "is subdomain:", site.Name)
return site, nil
}
return nil, fmt.Errorf("invalid host")
diff --git a/cmd/server/routes.go b/cmd/server/routes.go
index 2b9aaec..5b93713 100644
--- a/cmd/server/routes.go
+++ b/cmd/server/routes.go
@@ -3,6 +3,8 @@ package main
import (
"bytes"
"context"
+ "crypto/sha1"
+ "fmt"
"net/http"
"path/filepath"
"strconv"
@@ -79,11 +81,23 @@ func createRouter(db sws.Store, mmdbPath string) (chi.Router, error) {
r.Post(loginURL, handleLogin(db, rndr))
+ // Static files
r.Get("/*", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p := strings.TrimPrefix(r.URL.Path, "/")
debug("loading", p)
if b, err := loadTemplate(p); err == nil {
name := filepath.Base(p)
+ etag := fmt.Sprintf(`"%x"`, sha1.Sum(b))
+
+ if match := r.Header.Get("If-None-Match"); match != "" {
+ if strings.Contains(match, etag) {
+ w.WriteHeader(http.StatusNotModified)
+ return
+ }
+ }
+
+ w.Header().Set("Etag", etag)
+ w.Header().Set("Cache-Control", "no-cache")
http.ServeContent(w, r, name, time.Now(), bytes.NewReader(b))
}
}))
diff --git a/country.go b/country.go
index a875f66..d5bbe48 100644
--- a/country.go
+++ b/country.go
@@ -67,6 +67,13 @@ func (cs CountrySet) YMax() int {
}
return max
}
+func (cs CountrySet) YSum() int {
+ sum := 0
+ for _, c := range cs {
+ sum += c.hitSet.Count()
+ }
+ return sum
+}
func (cs CountrySet) XSeries() []*Country {
return cs
}
diff --git a/page_set.go b/page_set.go
index c9a77ff..9e1b57e 100644
--- a/page_set.go
+++ b/page_set.go
@@ -42,8 +42,8 @@ func NewPageSet(hs *HitSet) (PageSet, error) {
func (ps PageSet) Hits() []*Hit {
out := make([]*Hit, 0)
- for _, p := range ps {
- out = append(out, p.hitSet.Hits()...)
+ for i := 0; i < len(ps); i++ {
+ out = append(out, ps[i].hitSet.Hits()...)
}
return out
}
@@ -61,9 +61,9 @@ func (ps *PageSet) SortByHits() {
}
func (ps PageSet) GetPage(s string) *Page {
- for _, p := range ps {
- if p.Path == s {
- return p
+ for i := 0; i < len(ps); i++ {
+ if ps[i].Path == s {
+ return ps[i]
}
}
return nil
@@ -71,13 +71,17 @@ func (ps PageSet) GetPage(s string) *Page {
func (ps PageSet) YMax() int {
max := 0
- for _, p := range ps {
- if p.Count() > max {
- max = p.Count()
+ for i := 0; i < len(ps); i++ {
+ if ps[i].Count() > max {
+ max = ps[i].Count()
}
}
return max
}
func (ps PageSet) XSeries() []*Page {
- return ps
+ max := 10
+ if len(ps) < 10 {
+ max = len(ps)
+ }
+ return ps[0:max]
}
diff --git a/referrer.go b/referrer.go
index 800b98d..337a6ff 100644
--- a/referrer.go
+++ b/referrer.go
@@ -104,6 +104,13 @@ func (rs ReferrerSet) YMax() int {
}
return max
}
+func (rs ReferrerSet) YSum() int {
+ sum := 0
+ for _, r := range rs {
+ sum += r.hitSet.Count()
+ }
+ return sum
+}
func (rs ReferrerSet) XSeries() []*Referrer {
return rs
}
diff --git a/static/default.css b/static/default.css
index e9b18ee..44dde56 100644
--- a/static/default.css
+++ b/static/default.css
@@ -56,7 +56,10 @@ ul {
main {
background-color: #f7f7f7;
+ display: flex;
+ flex-wrap: wrap;
flex: 1;
+ justify-content: space-between;
padding-left: 16px;
padding-right: 16px;
position: relative;
@@ -78,6 +81,7 @@ main {
.site__header {
margin-bottom: 1em;
+ flex: 1 0 100%;
}
.site__title {
margin-bottom: 0;
@@ -85,6 +89,7 @@ main {
.site__summary {
display: inline-block;
+ flex: 1 0 100%;
}
.summary {
float: left;
@@ -122,10 +127,14 @@ main {
.panel {
background-color: #fff;
border: 1px solid #e6e9ed;
+ flex: 0 1 33%;
margin-bottom: .5em;
margin-top: .5em;
padding: .5em;
}
+.panel--wide {
+ flex: 1 0 100%;
+}
.panel__header {
border-bottom: 2px solid #e6e9ed;
font-size: 1.75em;
@@ -158,10 +167,17 @@ main {
top: 0;
}
+.details__percent {
+ font-weight: bold;
+}
+
/* Figures */
-.graph {
+.figure {
+ margin: 1em .5em;
+}
+.figure--graph {
}
-.map svg {
+.figure--map svg {
max-width: 100%;
max-height: 400px;
}
@@ -176,13 +192,13 @@ main {
.chart.time {
flex-direction: row;
height: 200px;
- padding-top: 2em;
- padding-bottom: 2em;
+ padding-top: 1em;
+ padding-bottom: 1em;
}
.chart.vertical {
align-items: stretch;
flex-direction: row;
- height: 100px;
+ min-height: 200px;
padding-top: 2em;
padding-bottom: 2em;
}
@@ -201,6 +217,7 @@ main {
.chart.horizontal .slot {
margin-top: 1px;
margin-bottom: 1px;
+ max-height: 1.5em;
}
.chart .slot.midnight {
border-left: 1px solid #ddd;
@@ -257,10 +274,10 @@ main {
bottom: 100%;
left: 50%;
margin-bottom: .5rem;
- margin-left: -2.5rem;
+ margin-left: -3.5rem;
position: absolute;
text-align: center;
- width: 5rem;
+ width: 7rem;
}
.chart.time .slot:after,
.chart.vertical .slot:after {
diff --git a/tmpl/site.tmpl b/tmpl/site.tmpl
index ea72abd..923cbc3 100644
--- a/tmpl/site.tmpl
+++ b/tmpl/site.tmpl
@@ -41,29 +41,13 @@
</li>
{{ end }}
-{{ define "browserForList" }}
- <li>
- <h4 class="name">{{ .Name }}</h4>
- <span class="last-seen">{{ .LastSeenAt|datetimeLong }}</span>
- <span class="count">{{ .Count }}</span>
- </li>
-{{ end }}
-
-{{ define "referrerForList" }}
- <li>
- <h4 class="name">{{ .Name }}</h4>
- <span class="last-seen">{{ .LastSeenAt|datetimeLong }}</span>
- <span class="count">{{ .Count }}</span>
- </li>
-{{ end }}
-
{{ define "siteView" }}
<section class="panel panel--wide">
<header class="panel__header">
<h3 class="panel__title">Hits</h3>
</header>
{{ if .Hits }}
- <figure class="graph">
+ <figure class="figure figure--graph">
{{ template "timeBarChart" .Hits }}
</figure>
{{ else }}
@@ -73,10 +57,10 @@
<section class="panel">
<header class="panel__header">
- <h3 class="panel__title">Popular pages</h3>
+ <h3 class="panel__title">Top 10 pages</h3>
</header>
{{ if .PageSet }}
- <figure class="graph">
+ <figure class="figure figure--graph">
{{ template "barChartHorizontal" .PageSet }}
</figure>
@@ -85,7 +69,7 @@
{{ $pages := .PageSet }}
{{ range .PageSet }}
{{ template "pageForList" . }}
- <figure>
+ <figure class="figure figure--graph">
{{ $pathHits := $pages.GetPage .Path }}
{{ template "timeBarChart" $pathHits }}
</figure>
@@ -102,9 +86,18 @@
<h3 class="panel__title">Countries</h3>
</header>
{{ if .CountrySet }}
- <figure class="map">
+ <figure class="figure figure--map">
{{ template "worldMap" .CountrySet }}
</figure>
+ {{ $sum := .CountrySet.YSum }}
+ <table class="details details--countries">
+ {{ range .CountrySet }}
+ <tr>
+ <td class="details__name">{{ .Name }}</td>
+ <td class="details__count"><span class="details__percent">{{ percent .Count $sum | round 1 }}%</span> ({{ .Count }})</td>
+ </tr>
+ {{ end }}
+ </table>
{{ else }}
<p>No page views yet</p>
{{ end }}
@@ -115,14 +108,18 @@
<h3 class="panel__title">Referrers</h3>
</header>
{{ if .ReferrerSet }}
- <figure class="graph">
+ <figure class="figure figure--graph">
{{ template "barChart" .ReferrerSet }}
</figure>
- <ul class="referrers">
+ {{ $sum := .ReferrerSet.YSum }}
+ <table class="details details--referrers">
{{ range .ReferrerSet }}
- {{ template "referrerForList" . }}
+ <tr>
+ <td class="details__name">{{ .Name }}</td>
+ <td class="details__count"><span class="details__percent">{{ percent .Count $sum | round 1 }}%</span> ({{ .Count }})</td>
+ </tr>
{{ end }}
- </ul>
+ </table>
{{ else }}
<p>No referrers yet</p>
{{ end }}
@@ -133,14 +130,18 @@
<h3 class="panel__title">User agents</h3>
</header>
{{ if .Browsers }}
- <figure class="graph">
+ <figure class="figure figure--graph">
{{ template "barChart" .Browsers }}
</figure>
- <ul class="browsers">
+ {{ $sum := .Browsers.YSum }}
+ <table class="details details--browsers">
{{ range .Browsers }}
- {{ template "browserForList" . }}
+ <tr>
+ <td class="details__name">{{ .Name }}</td>
+ <td class="details__count"><span class="details__percent">{{ percent .Count $sum | round 1 }}%</span> ({{ .Count }})</td>
+ </tr>
{{ end }}
- </ul>
+ </table>
{{ else }}
<p>No browsers visits yet</p>
{{ end }}