diff --git a/pkg/models/demographic/demographic.go b/pkg/models/demographic/demographic.go
index fea99b4..97841cb 100644
--- a/pkg/models/demographic/demographic.go
+++ b/pkg/models/demographic/demographic.go
@@ -12,6 +12,7 @@ import (
"time"
"code.nonshy.com/nonshy/website/pkg/config"
+ "code.nonshy.com/nonshy/website/pkg/utility"
)
// Demographic is the top level container struct with all the insights needed for front-end display.
@@ -43,7 +44,7 @@ type People struct {
type MemberDemographic struct {
Label string // e.g. age range "18-25" or gender
Count int64
- Percent int
+ Percent string
}
/**
@@ -58,32 +59,32 @@ func (d Demographic) PrettyPrint() string {
return string(b)
}
-func (p Photo) PercentExplicit() int {
+func (p Photo) PercentExplicit() string {
if p.Total == 0 {
- return 0
+ return "0"
}
- return int((float64(p.Explicit) / float64(p.Total)) * 100)
+ return utility.FormatFloatToPrecision((float64(p.Explicit)/float64(p.Total))*100, 1)
}
-func (p Photo) PercentNonExplicit() int {
+func (p Photo) PercentNonExplicit() string {
if p.Total == 0 {
- return 0
+ return "0"
}
- return int((float64(p.NonExplicit) / float64(p.Total)) * 100)
+ return utility.FormatFloatToPrecision((float64(p.NonExplicit)/float64(p.Total))*100, 1)
}
-func (p People) PercentExplicit() int {
+func (p People) PercentExplicit() string {
if p.Total == 0 {
- return 0
+ return "0"
}
- return int((float64(p.ExplicitOptIn) / float64(p.Total)) * 100)
+ return utility.FormatFloatToPrecision((float64(p.ExplicitOptIn)/float64(p.Total))*100, 1)
}
-func (p People) PercentExplicitPhoto() int {
+func (p People) PercentExplicitPhoto() string {
if p.Total == 0 {
- return 0
+ return "0"
}
- return int((float64(p.ExplicitPhoto) / float64(p.Total)) * 100)
+ return utility.FormatFloatToPrecision((float64(p.ExplicitPhoto)/float64(p.Total))*100, 1)
}
func (p People) IterAgeRanges() []MemberDemographic {
@@ -104,16 +105,16 @@ func (p People) IterAgeRanges() []MemberDemographic {
for _, age := range values {
var (
count = p.ByAgeRange[age]
- pct int
+ pct float64
)
if p.Total > 0 {
- pct = int((float64(count) / float64(p.Total)) * 100)
+ pct = ((float64(count) / float64(p.Total)) * 100)
}
result = append(result, MemberDemographic{
Label: age,
Count: p.ByAgeRange[age],
- Percent: pct,
+ Percent: utility.FormatFloatToPrecision(pct, 1),
})
}
@@ -141,16 +142,16 @@ func (p People) IterGenders() []MemberDemographic {
for _, gender := range values {
var (
count = p.ByGender[gender]
- pct int
+ pct float64
)
if p.Total > 0 {
- pct = int((float64(count) / float64(p.Total)) * 100)
+ pct = ((float64(count) / float64(p.Total)) * 100)
}
result = append(result, MemberDemographic{
Label: gender,
Count: p.ByGender[gender],
- Percent: pct,
+ Percent: utility.FormatFloatToPrecision(pct, 1),
})
}
@@ -178,16 +179,16 @@ func (p People) IterOrientations() []MemberDemographic {
for _, gender := range values {
var (
count = p.ByOrientation[gender]
- pct int
+ pct float64
)
if p.Total > 0 {
- pct = int((float64(count) / float64(p.Total)) * 100)
+ pct = ((float64(count) / float64(p.Total)) * 100)
}
result = append(result, MemberDemographic{
Label: gender,
Count: p.ByOrientation[gender],
- Percent: pct,
+ Percent: utility.FormatFloatToPrecision(pct, 1),
})
}
diff --git a/pkg/models/demographic/queries.go b/pkg/models/demographic/queries.go
index 8767417..f2d85ef 100644
--- a/pkg/models/demographic/queries.go
+++ b/pkg/models/demographic/queries.go
@@ -70,8 +70,8 @@ func Generate() (Demographic, error) {
func PeopleStatistics() People {
var result = People{
ByAgeRange: map[string]int64{},
- ByGender: map[string]int64{},
- ByOrientation: map[string]int64{},
+ ByGender: map[string]int64{"": 0},
+ ByOrientation: map[string]int64{"": 0},
}
type record struct {
@@ -216,6 +216,11 @@ func PeopleStatistics() People {
}
// Ingest the records.
+ var (
+ totalWithAge int64 // will be the total count of users since age is required
+ totalWithGender int64
+ totalWithOrientation int64
+ )
for _, row := range records {
switch row.MetricType {
case "ExplicitCount":
@@ -230,19 +235,27 @@ func PeopleStatistics() People {
result.ByAgeRange[row.MetricValue] = 0
}
result.ByAgeRange[row.MetricValue] += row.MetricCount
+ totalWithAge += row.MetricCount
case "GenderCount":
if _, ok := result.ByGender[row.MetricValue]; !ok {
result.ByGender[row.MetricValue] = 0
}
result.ByGender[row.MetricValue] += row.MetricCount
+ totalWithGender += row.MetricCount
case "OrientationCount":
if _, ok := result.ByOrientation[row.MetricValue]; !ok {
result.ByOrientation[row.MetricValue] = 0
}
result.ByOrientation[row.MetricValue] += row.MetricCount
+ totalWithOrientation += row.MetricCount
}
}
+ // Gender and Orientation: pad out the "no answer" selection with the count of users
+ // who had no profile_fields stored in the DB at all.
+ result.ByOrientation[""] += (totalWithAge - totalWithOrientation)
+ result.ByGender[""] += (totalWithAge - totalWithGender)
+
return result
}
@@ -263,8 +276,10 @@ func PhotoStatistics() Photo {
count(photos.id) AS c
FROM
photos
- WHERE
- photos.visibility = 'public'
+ JOIN users ON (photos.user_id = users.id)
+ WHERE photos.visibility = 'public'
+ AND users.certified IS TRUE
+ AND users.status = 'active'
GROUP BY photos.explicit
ORDER BY c DESC
`).Scan(&records)
diff --git a/pkg/utility/number_format.go b/pkg/utility/number_format.go
index d6b8ef2..4e761eb 100644
--- a/pkg/utility/number_format.go
+++ b/pkg/utility/number_format.go
@@ -6,6 +6,17 @@ import (
"strings"
)
+// FormatFloatToPrecision will trim a floating point number to at most a number of decimals of precision.
+//
+// If the precision is ".0" the decimal place will be stripped entirely.
+func FormatFloatToPrecision(v float64, prec int) string {
+ s := strconv.FormatFloat(v, 'f', prec, 64)
+ if strings.HasSuffix(s, ".0") {
+ return strings.Split(s, ".")[0]
+ }
+ return s
+}
+
// FormatNumberShort compresses a number into as short a string as possible (e.g. "1.2K" when it gets into the thousands).
func FormatNumberShort(value int64) string {
// Under 1,000?
@@ -20,21 +31,13 @@ func FormatNumberShort(value int64) string {
billions = float64(millions) / 1000
)
- formatFloat := func(v float64) string {
- s := strconv.FormatFloat(v, 'f', 1, 64)
- if strings.HasSuffix(s, ".0") {
- return strings.Split(s, ".")[0]
- }
- return s
- }
-
if thousands < 1000 {
- return fmt.Sprintf("%sK", formatFloat(thousands))
+ return fmt.Sprintf("%sK", FormatFloatToPrecision(thousands, 1))
}
if millions < 1000 {
- return fmt.Sprintf("%sM", formatFloat(millions))
+ return fmt.Sprintf("%sM", FormatFloatToPrecision(millions, 1))
}
- return fmt.Sprintf("%sB", formatFloat(billions))
+ return fmt.Sprintf("%sB", FormatFloatToPrecision(billions, 1))
}
diff --git a/web/templates/admin/certification.html b/web/templates/admin/certification.html
index 3909af1..812dbe9 100644
--- a/web/templates/admin/certification.html
+++ b/web/templates/admin/certification.html
@@ -177,9 +177,11 @@
-
-
-
+
+
+
+
+
@@ -211,7 +213,8 @@
{{end}}
{{if not (eq .Status "approved")}}
-