Compare commits
2 Commits
c06bd71a1a
...
37aabb3dfc
Author | SHA1 | Date |
---|---|---|
Martyn | 37aabb3dfc | |
Martyn | 7f1393c72d |
|
@ -6,7 +6,7 @@ replicaCount: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: imartyn/twitchsingstools
|
repository: imartyn/twitchsingstools
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: Always
|
||||||
|
|
||||||
imagePullSecrets: []
|
imagePullSecrets: []
|
||||||
nameOverride: ""
|
nameOverride: ""
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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.
|
|
@ -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.
|
Loading…
Reference in New Issue