Fix two bugs. Column misname and case insensitivity
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details

Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
This commit is contained in:
Martyn 2020-07-16 22:00:49 +02:00
parent 7f1393c72d
commit 37aabb3dfc
4 changed files with 102 additions and 16 deletions

View File

@ -1,10 +1,12 @@
package webserver package webserver
import ( import (
"container/heap"
"errors" "errors"
"math/rand" "math/rand"
"regexp" "regexp"
"sort" "sort"
"unicode"
irc "git.martyn.berlin/martyn/twitchsingstools/internal/irc" irc "git.martyn.berlin/martyn/twitchsingstools/internal/irc"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
@ -249,6 +251,12 @@ func AdminHandler(response http.ResponseWriter, request *http.Request) {
} else { } 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()) 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) topNSongs := calculateTopNSongs(channelData.VideoCache, 10)
topNSingers := calculateTopNSingers(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"]} 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 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 { func calculateTopNSingers(songCache []irc.SingsVideoStruct, howMany int) []SingerSings {
songMap := map[string]int{} songMap := map[string]int{}
songCount := 0
for _, record := range songCache { for _, record := range songCache {
if record.Duet { if record.Duet {
sings := songMap[record.OtherSinger] sings := songMap[strings.ToUpper(record.OtherSinger)]
sings += 1 sings++
songMap[record.OtherSinger] = sings songCount++
songMap[strings.ToUpper(record.OtherSinger)] = sings
} }
} }
slice := make([]SingerSings, 0) slice := make([]SingerSings, 0)
for key, value := range songMap { h := getHeap(songMap)
ss := SingerSings{key, value} 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) slice = append(slice, ss)
} }
sort.SliceStable(slice, func(i, j int) bool { fmt.Printf("Considered %d songs, Shan has %d\n", songCount, songMap["SHANXOX_"])
return slice[i].Sings > slice[j].Sings return slice
})
var ret []SingerSings
for i := 1; i <= howMany; i++ {
ret = append(ret, slice[i])
}
return ret
} }
func calculateLastSungSongDate(songCache []irc.SingsVideoStruct, SongTitle string) time.Time { 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 strings.ToUpper(record.OtherSinger) == strings.ToUpper(Singer) {
if record.Date.After(t) { if record.Date.After(t) {
t = record.Date t = record.Date
if strings.ToUpper(Singer) == "SHANXOX_" {
fmt.Printf("Last sang with %s (%s) %s", Singer, record.OtherSinger, t)
}
} }
} }
} }

View File

@ -147,7 +147,7 @@
</div> </div>
<div style="width: 100%; overflow-y: scroll; display: none;" id="datapanel" class="controlpanel"> <div style="width: 100%; overflow-y: scroll; display: none;" id="datapanel" class="controlpanel">
<table> <table>
<thead><tr><th>Published</th><th>Who</th><th>What</th><th>Last sang this song</th><th>Last dueted with performer</th></th></thead> <thead><tr><th>Published</th><th>What</th><th>Who</th><th>Last sang this song</th><th>Last dueted with performer</th></th></thead>
{{ range .SongData }} {{ range .SongData }}
<tr><td title="{{ .Date }}">{{ .NiceDate }}</td><td>{{ .SongTitle }}</td><td>{{ .OtherSinger }}</td><td title="{{ .LastSungSong }}">{{ .NiceLastSungSong }}</td><td title="{{ .LastSungSinger }}">{{ .NiceLastSungSinger }}</td></tr> <tr><td title="{{ .Date }}">{{ .NiceDate }}</td><td>{{ .SongTitle }}</td><td>{{ .OtherSinger }}</td><td title="{{ .LastSungSong }}">{{ .NiceLastSungSong }}</td><td title="{{ .LastSungSinger }}">{{ .NiceLastSungSinger }}</td></tr>
{{ end }} {{ end }}
@ -164,6 +164,7 @@
<h3>Excel is not very good at handling CSV format it seems...</h3> <h3>Excel is not very good at handling CSV format it seems...</h3>
<p>It is important to "Import Data" not "Open" the csv in many cases (8 year old discussion of this behaviour here) - from that post the instructions are : </p> <p>It is important to "Import Data" not "Open" the csv in many cases (8 year old discussion of this behaviour here) - from that post the instructions are : </p>
<blockquote>In Excel, DATA tab, in the Get External Data subsection, click "From Text" and import your CSV in the Wizard.</blockquote> <blockquote>In Excel, DATA tab, in the Get External Data subsection, click "From Text" and import your CSV in the Wizard.</blockquote>
<p>LibreOffice calc kinda just works...just sayin' ;-)</p>
</div> </div>
<div style="width: 100%; overflow-y: scroll; display: none;" id="botpanel" class="controlpanel"> <div style="width: 100%; overflow-y: scroll; display: none;" id="botpanel" class="controlpanel">
<h2>The bot isn't really ready yet... it just has the old Karaokards facility at the moment. I woudn't bother inviting it yet.</h2> <h2>The bot isn't really ready yet... it just has the old Karaokards facility at the moment. I woudn't bother inviting it yet.</h2>

View File

@ -1,4 +1,4 @@
Published,Who,What,Last sang this song,Last dueted with performer Published,What,Who,Last sang this song,Last dueted with performer
{{ range .SongData -}} {{ range .SongData -}}
{{- .NiceDate }},"{{ .SongTitle }}",{{ .OtherSinger }},{{ .NiceLastSungSong }},{{ .NiceLastSungSinger }} {{- .NiceDate }},"{{ .SongTitle }}",{{ .OtherSinger }},{{ .NiceLastSungSong }},{{ .NiceLastSungSinger }}
{{ end }} {{ end }}

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -1,4 +1,4 @@
Published Who What Last sang this song Last dueted with performer Published What Who Last sang this song Last dueted with performer
{{ range .SongData -}} {{ range .SongData -}}
{{- .NiceDate }} "{{ .SongTitle }}" {{ .OtherSinger }} {{ .NiceLastSungSong }} {{ .NiceLastSungSinger }} {{- .NiceDate }} "{{ .SongTitle }}" {{ .OtherSinger }} {{ .NiceLastSungSong }} {{ .NiceLastSungSinger }}
{{ end }} {{ end }}

Can't render this file because it has a wrong number of fields in line 2.