From 60d47ead7f3badb986a9a888a1e07a1b4ea3bbce Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Fri, 7 Jul 2017 12:10:27 +0200 Subject: [PATCH] Improved statistics --- jobs/statistics/statistics.go | 151 +++++++++++++++++++++++ pages/statistics/anime.go | 68 +--------- pages/statistics/statistics.go | 63 +--------- pages/statistics/statistics.pixy | 4 +- patches/import-anilist/import-anilist.go | 4 +- utils/AnalyticsItem.go | 7 -- utils/PieChart.go | 91 -------------- 7 files changed, 161 insertions(+), 227 deletions(-) create mode 100644 jobs/statistics/statistics.go delete mode 100644 utils/AnalyticsItem.go diff --git a/jobs/statistics/statistics.go b/jobs/statistics/statistics.go new file mode 100644 index 00000000..85ddf9a8 --- /dev/null +++ b/jobs/statistics/statistics.go @@ -0,0 +1,151 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/animenotifier/arn" + "github.com/fatih/color" +) + +type stats map[string]float64 + +func main() { + color.Yellow("Generating statistics") + + userStats := getUserStats() + animeStats := getAnimeStats() + + arn.PanicOnError(arn.DB.Set("Cache", "user statistics", &arn.StatisticsCategory{ + Name: "Users", + PieCharts: userStats, + })) + arn.PanicOnError(arn.DB.Set("Cache", "anime statistics", &arn.StatisticsCategory{ + Name: "Anime", + PieCharts: animeStats, + })) + + color.Green("Finished.") +} + +func getUserStats() []*arn.PieChart { + println("Generating user statistics") + + analytics, err := arn.AllAnalytics() + arn.PanicOnError(err) + + screenSize := stats{} + pixelRatio := stats{} + browser := stats{} + country := stats{} + gender := stats{} + os := stats{} + + for _, info := range analytics { + pixelRatio[fmt.Sprintf("%.1f", info.Screen.PixelRatio)]++ + + size := arn.ToString(info.Screen.Width) + " x " + arn.ToString(info.Screen.Height) + screenSize[size]++ + } + + for user := range arn.MustStreamUsers() { + if user.Gender != "" { + gender[user.Gender]++ + } + + if user.Browser.Name != "" { + browser[user.Browser.Name]++ + } + + if user.Location.CountryName != "" { + country[user.Location.CountryName]++ + } + + if user.OS.Name != "" { + if strings.HasPrefix(user.OS.Name, "CrOS") { + user.OS.Name = "Chrome OS" + } + + os[user.OS.Name]++ + } + } + + println("Finished user statistics") + + return []*arn.PieChart{ + arn.NewPieChart("OS", os), + arn.NewPieChart("Screen size", screenSize), + arn.NewPieChart("Browser", browser), + arn.NewPieChart("Country", country), + arn.NewPieChart("Gender", gender), + arn.NewPieChart("Pixel ratio", pixelRatio), + } +} + +func getAnimeStats() []*arn.PieChart { + println("Generating anime statistics") + + allAnime, err := arn.AllAnime() + arn.PanicOnError(err) + + shoboi := stats{} + anilist := stats{} + status := stats{} + types := stats{} + shoboiEdits := stats{} + anilistEdits := stats{} + rating := stats{} + + for _, anime := range allAnime { + for _, external := range anime.Mappings { + if external.Service == "shoboi/anime" { + if external.CreatedBy == "" { + shoboiEdits["Bot"]++ + } else { + user, err := arn.GetUser(external.CreatedBy) + arn.PanicOnError(err) + shoboiEdits[user.Nick]++ + } + } + + if external.Service == "anilist/anime" { + if external.CreatedBy == "" { + anilistEdits["Bot"]++ + } else { + user, err := arn.GetUser(external.CreatedBy) + arn.PanicOnError(err) + anilistEdits[user.Nick]++ + } + } + } + + if anime.GetMapping("shoboi/anime") != "" { + shoboi["Connected with Shoboi"]++ + } else { + shoboi["Not connected with Shoboi"]++ + } + + if anime.GetMapping("anilist/anime") != "" { + anilist["Connected with Anilist"]++ + } else { + anilist["Not connected with Anilist"]++ + } + + rating[arn.ToString(int(anime.Rating.Overall+0.5))]++ + + status[anime.Status]++ + types[anime.Type]++ + } + + println("Finished anime statistics") + + return []*arn.PieChart{ + arn.NewPieChart("Type", types), + arn.NewPieChart("Status", status), + arn.NewPieChart("Rating", rating), + arn.NewPieChart("Anilist", anilist), + arn.NewPieChart("Shoboi", shoboi), + arn.NewPieChart("Anilist Editors", anilistEdits), + arn.NewPieChart("Shoboi Editors", shoboiEdits), + } +} diff --git a/pages/statistics/anime.go b/pages/statistics/anime.go index 5f2ce51a..87e20c28 100644 --- a/pages/statistics/anime.go +++ b/pages/statistics/anime.go @@ -1,76 +1,14 @@ package statistics import ( - "net/http" - "github.com/aerogo/aero" "github.com/animenotifier/arn" "github.com/animenotifier/notify.moe/components" - "github.com/animenotifier/notify.moe/utils" ) // Anime ... func Anime(ctx *aero.Context) string { - allAnime, err := arn.AllAnime() - - if err != nil { - return ctx.Error(http.StatusInternalServerError, "Couldn't fetch anime", err) - } - - shoboi := stats{} - anilist := stats{} - status := stats{} - types := stats{} - shoboiEdits := stats{} - anilistEdits := stats{} - rating := stats{} - - for _, anime := range allAnime { - for _, external := range anime.Mappings { - if external.Service == "shoboi/anime" { - if external.CreatedBy == "" { - shoboiEdits["Bot"]++ - } else { - user, _ := arn.GetUser(external.CreatedBy) - shoboiEdits[user.Nick]++ - } - } - - if external.Service == "anilist/anime" { - if external.CreatedBy == "" { - anilistEdits["Bot"]++ - } else { - user, _ := arn.GetUser(external.CreatedBy) - anilistEdits[user.Nick]++ - } - } - } - - if anime.GetMapping("shoboi/anime") != "" { - shoboi["Connected with Shoboi"]++ - } else { - shoboi["Not connected with Shoboi"]++ - } - - if anime.GetMapping("anilist/anime") != "" { - anilist["Connected with Anilist"]++ - } else { - anilist["Not connected with Anilist"]++ - } - - rating[arn.ToString(int(anime.Rating.Overall+0.5))]++ - - status[anime.Status]++ - types[anime.Type]++ - } - - return ctx.HTML(components.Statistics( - utils.NewPieChart("Type", types), - utils.NewPieChart("Status", status), - utils.NewPieChart("Rating", rating), - utils.NewPieChart("Anilist", anilist), - utils.NewPieChart("Shoboi", shoboi), - utils.NewPieChart("Anilist Editors", anilistEdits), - utils.NewPieChart("Shoboi Editors", shoboiEdits), - )) + statistics := arn.StatisticsCategory{} + arn.DB.GetObject("Cache", "anime statistics", &statistics) + return ctx.HTML(components.Statistics(statistics.PieCharts...)) } diff --git a/pages/statistics/statistics.go b/pages/statistics/statistics.go index 4343706c..04d1855d 100644 --- a/pages/statistics/statistics.go +++ b/pages/statistics/statistics.go @@ -1,71 +1,14 @@ package statistics import ( - "fmt" - "net/http" - "strings" - "github.com/aerogo/aero" "github.com/animenotifier/arn" "github.com/animenotifier/notify.moe/components" - "github.com/animenotifier/notify.moe/utils" ) -type stats = map[string]float64 - // Get ... func Get(ctx *aero.Context) string { - analytics, err := arn.AllAnalytics() - - if err != nil { - return ctx.Error(http.StatusInternalServerError, "Couldn't fetch analytics", err) - } - - screenSize := stats{} - // platform := stats{} - pixelRatio := stats{} - browser := stats{} - country := stats{} - gender := stats{} - os := stats{} - - for _, info := range analytics { - // platform[info.System.Platform]++ - pixelRatio[fmt.Sprintf("%.1f", info.Screen.PixelRatio)]++ - - size := arn.ToString(info.Screen.Width) + " x " + arn.ToString(info.Screen.Height) - screenSize[size]++ - } - - for user := range arn.MustStreamUsers() { - if user.Gender != "" { - gender[user.Gender]++ - } - - if user.Browser.Name != "" { - browser[user.Browser.Name]++ - } - - if user.Location.CountryName != "" { - country[user.Location.CountryName]++ - } - - if user.OS.Name != "" { - if strings.HasPrefix(user.OS.Name, "CrOS") { - user.OS.Name = "Chrome OS" - } - - os[user.OS.Name]++ - } - } - - return ctx.HTML(components.Statistics( - utils.NewPieChart("OS", os), - // utils.NewPieChart("Platform", platform), - utils.NewPieChart("Screen size", screenSize), - utils.NewPieChart("Browser", browser), - utils.NewPieChart("Country", country), - utils.NewPieChart("Gender", gender), - utils.NewPieChart("Pixel ratio", pixelRatio), - )) + statistics := arn.StatisticsCategory{} + arn.DB.GetObject("Cache", "user statistics", &statistics) + return ctx.HTML(components.Statistics(statistics.PieCharts...)) } diff --git a/pages/statistics/statistics.pixy b/pages/statistics/statistics.pixy index 0899237f..8cb99c08 100644 --- a/pages/statistics/statistics.pixy +++ b/pages/statistics/statistics.pixy @@ -1,4 +1,4 @@ -component Statistics(pieCharts ...*utils.PieChart) +component Statistics(pieCharts ...*arn.PieChart) h1 Statistics StatisticsHeader @@ -19,7 +19,7 @@ component StatisticsHeader Icon("tv") span Anime -component PieChart(slices []*utils.PieChartSlice) +component PieChart(slices []*arn.PieChartSlice) svg.pie-chart(viewBox="-1.1 -1.1 2.2 2.2") each slice in slices g diff --git a/patches/import-anilist/import-anilist.go b/patches/import-anilist/import-anilist.go index da1a1276..5bfee012 100644 --- a/patches/import-anilist/import-anilist.go +++ b/patches/import-anilist/import-anilist.go @@ -20,8 +20,8 @@ func main() { for aniListAnime := range stream { anime := arn.FindAniListAnime(aniListAnime, allAnime) - if anime != nil { - fmt.Println(aniListAnime.TitleRomaji, "=>", anime.Title.Canonical) + if anime == nil { + fmt.Println(anime.ID, aniListAnime.TitleRomaji) } count++ diff --git a/utils/AnalyticsItem.go b/utils/AnalyticsItem.go deleted file mode 100644 index 82b3288f..00000000 --- a/utils/AnalyticsItem.go +++ /dev/null @@ -1,7 +0,0 @@ -package utils - -// AnalyticsItem ... -type AnalyticsItem struct { - Key string - Value float64 -} diff --git a/utils/PieChart.go b/utils/PieChart.go index 203cdad9..b349fe46 100644 --- a/utils/PieChart.go +++ b/utils/PieChart.go @@ -5,20 +5,6 @@ import ( "math" ) -// PieChart ... -type PieChart struct { - Title string - Slices []*PieChartSlice -} - -// PieChartSlice ... -type PieChartSlice struct { - From float64 - To float64 - Title string - Color string -} - // coords returns the coordinates for the given percentage. func coords(percent float64) (float64, float64) { x := math.Cos(2 * math.Pi * percent) @@ -39,80 +25,3 @@ func SVGSlicePath(from float64, to float64) string { return fmt.Sprintf("M %.3f %.3f A 1 1 0 %s 1 %.3f %.3f L 0 0", x1, y1, largeArc, x2, y2) } - -// NewPieChart ... -func NewPieChart(title string, data map[string]float64) *PieChart { - return &PieChart{ - Title: title, - Slices: ToPieChartSlices(data), - } -} - -// ToPieChartSlices ... -func ToPieChartSlices(data map[string]float64) []*PieChartSlice { - if len(data) == 0 { - return nil - } - - dataSorted := []*AnalyticsItem{} - sum := 0.0 - - for key, value := range data { - sum += value - - item := &AnalyticsItem{ - Key: key, - Value: value, - } - - if len(dataSorted) == 0 { - dataSorted = append(dataSorted, item) - continue - } - - found := false - - for i := 0; i < len(dataSorted); i++ { - if value >= dataSorted[i].Value { - // Append empty element - dataSorted = append(dataSorted, nil) - - // Move all elements after index "i" 1 position up - copy(dataSorted[i+1:], dataSorted[i:]) - - // Set value for index "i" - dataSorted[i] = item - - // Set flag - found = true - - // Leave loop - break - } - } - - if !found { - dataSorted = append(dataSorted, item) - } - } - - slices := []*PieChartSlice{} - current := 0.0 - hueOffset := 230.0 - hueScaling := -30.0 - - for i, item := range dataSorted { - percentage := float64(item.Value) / sum - - slices = append(slices, &PieChartSlice{ - From: current, - To: current + percentage, - Title: fmt.Sprintf("%s (%d%%)", item.Key, int(percentage*100+0.5)), - Color: fmt.Sprintf("hsl(%.2f, 75%%, 50%%)", float64(i)*hueScaling+hueOffset), - }) - - current += percentage - } - - return slices -}