diff --git a/internal/webserver/webserver.go b/internal/webserver/webserver.go index f79db20..ecb4e06 100755 --- a/internal/webserver/webserver.go +++ b/internal/webserver/webserver.go @@ -1,10 +1,12 @@ package webserver import ( + "container/heap" "errors" "math/rand" "regexp" "sort" + "unicode" irc "git.martyn.berlin/martyn/twitchsingstools/internal/irc" "github.com/dustin/go-humanize" @@ -249,6 +251,12 @@ func AdminHandler(response http.ResponseWriter, request *http.Request) { } else { fmt.Printf("Cache of %d performances is younger than an hour - %.1f hours old to be precise... not fetching.\n", len(ircBot.ChannelData[vars["channel"]].VideoCache), time.Now().Sub(ircBot.ChannelData[vars["channel"]].VideoCacheUpdated).Hours()) } + updateCalculatedFields(channelData.VideoCache) + for _, song := range channelData.VideoCache { + if song.Duet && song.OtherSinger == "" { + fmt.Printf("WARNING: found duet with no other singer! %s", song.SongTitle) // should never happen but debug in case it does! + } + } topNSongs := calculateTopNSongs(channelData.VideoCache, 10) topNSingers := calculateTopNSingers(channelData.VideoCache, 10) var td = TemplateData{channelData.Name, channelData.Command, channelData.ExtraStrings, channelData.JoinTime, channelData.JoinTime.Format(irc.UTCFormat), false, channelData.HasLeft, AugmentSingsVideoStructSlice(channelData.VideoCache), topNSongs, topNSingers, vars["key"]} @@ -408,28 +416,102 @@ func calculateTopNSongs(songCache []irc.SingsVideoStruct, howMany int) []SongSin return ret } +func IsLower(s string) bool { + for _, r := range s { + if !unicode.IsLower(r) && unicode.IsLetter(r) { + return false + } + } + return true +} + +func unmangleSingerName(MangledCaseName string, songCache []irc.SingsVideoStruct) string { + options := make(map[string]string, 0) + for _, record := range songCache { + if strings.ToUpper(MangledCaseName) == strings.ToUpper(record.OtherSinger) { + options[record.OtherSinger] = "WHATEVER" + } + } + // One answer means we don't care upper, lower, mixed. + if len(options) == 1 { + for key := range options { + return key + } + } + // More than one is probably closed-beta where the name was lowercased. + for key := range options { + if !IsLower(key) { + return key + } + } + // Eep, we shouldn't get here, let's just return something. + for key := range options { + return key + } + return "" +} + +func prependSong(x []SingerSings, y SingerSings) []SingerSings { + x = append(x, SingerSings{}) + copy(x[1:], x) + x[0] = y + return x +} + +type kv struct { + Key string + Value int +} + +func getHeap(m map[string]int) *KVHeap { + h := &KVHeap{} + heap.Init(h) + for k, v := range m { + heap.Push(h, kv{k, v}) + } + return h +} + +type KVHeap []kv + +func (h KVHeap) Len() int { return len(h) } +func (h KVHeap) Less(i, j int) bool { return h[i].Value > h[j].Value } +func (h KVHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *KVHeap) Push(x interface{}) { + *h = append(*h, x.(kv)) +} + +func (h *KVHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + func calculateTopNSingers(songCache []irc.SingsVideoStruct, howMany int) []SingerSings { songMap := map[string]int{} + songCount := 0 for _, record := range songCache { if record.Duet { - sings := songMap[record.OtherSinger] - sings += 1 - songMap[record.OtherSinger] = sings + sings := songMap[strings.ToUpper(record.OtherSinger)] + sings++ + songCount++ + songMap[strings.ToUpper(record.OtherSinger)] = sings } } slice := make([]SingerSings, 0) - for key, value := range songMap { - ss := SingerSings{key, value} + h := getHeap(songMap) + for i := 0; i < howMany; i++ { + deets := heap.Pop(h) + position := i + 1 + fmt.Printf("%d) %#v\n", position, deets) + ss := SingerSings{unmangleSingerName(deets.(kv).Key, songCache), deets.(kv).Value} slice = append(slice, ss) } - sort.SliceStable(slice, func(i, j int) bool { - return slice[i].Sings > slice[j].Sings - }) - var ret []SingerSings - for i := 1; i <= howMany; i++ { - ret = append(ret, slice[i]) - } - return ret + fmt.Printf("Considered %d songs, Shan has %d\n", songCount, songMap["SHANXOX_"]) + return slice } func calculateLastSungSongDate(songCache []irc.SingsVideoStruct, SongTitle string) time.Time { @@ -450,6 +532,9 @@ func calculateLastSungSingerDate(songCache []irc.SingsVideoStruct, Singer string if strings.ToUpper(record.OtherSinger) == strings.ToUpper(Singer) { if record.Date.After(t) { t = record.Date + if strings.ToUpper(Singer) == "SHANXOX_" { + fmt.Printf("Last sang with %s (%s) %s", Singer, record.OtherSinger, t) + } } } } diff --git a/web/admin.html b/web/admin.html index 898cdc8..00e2eaa 100755 --- a/web/admin.html +++ b/web/admin.html @@ -147,7 +147,7 @@