Skip to content
Snippets Groups Projects
Commit 7d7d3bca authored by Philipp Hochkamp's avatar Philipp Hochkamp
Browse files

Merge branch 'graph-scoreboard' into 'master'

Matrix for leaderboard

Closes #36 and #27

See merge request !18
parents b319cad2 aafa793a
No related branches found
No related tags found
1 merge request!18Matrix for leaderboard
Pipeline #1827 passed with warnings
{{ template "header" . }} {{ template "header" . }}
<main class="flexcell gridcontainer-leader"> <main class="leaderboard-main">
<div id="table" class="col-0 row-0" style="width: 20%;"> <div id="table" class="flexcell">
<table> <table>
<tbody> <tbody>
<tr>
<th>Name</th>
<th>Points</th>
</tr>
{{ range .AllUsers }}
<tr>
<td>{{ .DisplayName }}</td>
<td>{{ .Points }}</td>
</tr>
{{ end }}
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="col-1 row-0"> <div style="min-width: 0; flex-basis: 80%; margin-right: 24px;" class="flexcell">
<canvas id="graph"></canvas> <canvas id="graph"></canvas>
</div> </div>
<script src="static/Chart.bundle.min.js" type="text/javascript"></script> <script src="static/Chart.bundle.min.js" type="text/javascript"></script>
<script> <script src="static/leaderboard.js" type="text/javascript"></script>
var score = {name: [{{ range .AllUsers }}{{ .DisplayName }},{{end}}], points: [ { label: "Points", data: [{{ range .AllUsers }}{{ .Points }},{{end}}]}] };
var chart;
(function(){
var c = document.getElementById("graph").getContext('2d');
c.height = 800;
chart = new Chart(c, {
type: 'horizontalBar',
data: {
labels: score.name,
datasets: score.points
},
options: {
scales: {
xAxes: [{
ticks: {
min: 0,
stepSize: 1
}
}]
},
legend: {
display: false,
},
mantainAspectRatio: false,
responsive: true,
},
});
ws = new WebSocket("ws://"+window.location.host+"/ws");
ws.onopen = function() {
// Web Socket is connected, send data uting send()
console.log("ws connected");
};
ws.onclose = function() {
alert("WS Disconnected, reload the page")
};
ws.onmessage = (evt)=>{
var rec = evt.data;
console.log(evt.data);
score = JSON.parse(rec);
chart.data = {
labels: score.name,
datasets: [{data: score.points, label: "Solved Challenges" }]
};
chart.update();
table = document.getElementsByTagName("tbody")[0];
table.innerHTML = "<tr><th>Name</th><th>Points</th></tr>";
for(i=0; i < score.name.length; i++){
row = table.insertRow();
namecell = row.insertCell();
namecell.innerHTML = score.name[i];
pointcell = row.insertCell();
pointcell.innerHTML = score.points[i];
}
};
})();
</script>
</main> </main>
{{ template "footer" . }} {{ template "footer" . }}
var chart;
(function() {
var c = document.getElementById("graph").getContext("2d");
chart = new Chart(c, {
type: "line",
options: {
scales: {
xAxes: [
{
type: "time",
time: {
unit: "minute",
max: Date.now()
}
}
],
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
},
elements: {
line: {
tension: 0,
fill: false
}
},
legend: {
display: false
},
mantainAspectRatio: false,
responsive: true,
showScale: false
}
});
ws = new WebSocket("ws://" + window.location.host + "/ws");
ws.onopen = function() {
// Web Socket is connected, send data uting send()
console.log("ws connected");
};
ws.onclose = function() {
alert("WS Disconnected, reload the page");
};
ws.onmessage = evt => {
var rec = evt.data;
console.log(evt.data);
score = JSON.parse(rec);
chart.data = {
datasets: score.chart
};
chart.update();
table = document.getElementsByTagName("tbody")[0];
table.innerHTML = "<tr><th>Name</th><th>Points</th></tr>";
for (i = 0; i < score.table.name.length; i++) {
row = table.insertRow();
namecell = row.insertCell();
namecell.innerHTML = score.table.name[i];
pointcell = row.insertCell();
pointcell.innerHTML = score.table.points[i];
}
};
window.addEventListener("resize", function() {
chart.update();
});
})();
...@@ -4,6 +4,20 @@ ...@@ -4,6 +4,20 @@
border-style: solid; border-style: solid;
}*/ }*/
.leaderboard-table {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 20%;
margin-right: 24px;
}
.leaderboard-main {
margin-top: 96px;
justify-content: space-between;
align-items: strech;
display: flex;
}
.button { .button {
border: none; border: none;
color: white; color: white;
...@@ -210,7 +224,7 @@ img { ...@@ -210,7 +224,7 @@ img {
.gridcontainer-leader { .gridcontainer-leader {
display: grid; display: grid;
width: auto; width: 100%;
height: 100%; height: 100%;
} }
......
...@@ -2,11 +2,19 @@ package wtfd ...@@ -2,11 +2,19 @@ package wtfd
import ( import (
"encoding/json" "encoding/json"
"fmt"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"hash/crc32"
"log" "log"
"net/http" "net/http"
"time"
) )
type tableData struct {
Names []string `json:"name"`
Points []int `json:"points"`
}
var ( var (
serverChan = make(chan chan string, 4) serverChan = make(chan chan string, 4)
messageChan = make(chan string, 1) messageChan = make(chan string, 1)
...@@ -52,6 +60,7 @@ func leaderboardWS(w http.ResponseWriter, r *http.Request) { ...@@ -52,6 +60,7 @@ func leaderboardWS(w http.ResponseWriter, r *http.Request) {
} }
client := make(chan string, 1) client := make(chan string, 1)
serverChan <- client // i have no idea what this go magic is serverChan <- client // i have no idea what this go magic is
updateScoreboard()
for { for {
select { select {
...@@ -64,31 +73,72 @@ func leaderboardWS(w http.ResponseWriter, r *http.Request) { ...@@ -64,31 +73,72 @@ func leaderboardWS(w http.ResponseWriter, r *http.Request) {
} }
func updateScoreboard() error { func generateTableData() (tableData, error) {
log.Printf("Scoreboard Update\n")
type userNamePoints struct {
Name []string `json:"name"`
Points []int `json:"points"`
}
var name []string
var points []int
allu, err := ormAllUsersSortedByPoints() allu, err := ormAllUsersSortedByPoints()
if err != nil { if err != nil {
log.Printf("Scoreboard Update Error: %v\n", err) return tableData{}, err
return err
} }
var name []string
var points []int
for _, u := range allu { for _, u := range allu {
name = append(name, u.DisplayName) name = append(name, u.DisplayName)
points = append(points, u.Points) points = append(points, u.Points)
} }
return tableData{Names: name, Points: points}, nil
}
func updateScoreboard() error {
type chartDataPoint struct {
T string `json:"t"`
// Label string `json:"label"`
Y int `json:"y"`
}
type chartData struct {
Label string `json:"label"`
Data []chartDataPoint `json:"data"`
Color string `json:"backgroundColor"`
Pcolor string `json:"borderColor"`
}
type leaderboardData struct {
TableData tableData `json:"table"`
ChartData []chartData `json:"chart"`
}
log.Printf("Scoreboard Update\n")
users, err := ormAllUsersSortedByPoints()
datas := make([]chartData, len(users))
for _, u := range users {
solves := ormGetSolvesWithTime(u.Name)
data := make([]chartDataPoint, len(solves)+1)
sum := 0
data[0] = chartDataPoint{T: u.Created.Format(time.RFC3339), Y: sum} //, Label: s.ChallengeName}
for i, s := range solves {
chall, err := challs.Find(s.ChallengeName)
if err != nil {
log.Printf("Scoreboard Update Error: %v, %v\n", err, s.ChallengeName)
return err
}
sum += chall.Points
data[i+1] = chartDataPoint{T: s.Created.Format(time.RFC3339), Y: sum} //, Label: s.ChallengeName}
}
a := fmt.Sprintf("#%X", crc32.ChecksumIEEE([]byte(u.DisplayName)))[0:7]
datas = append(datas, chartData{Pcolor: a, Color: a, Label: u.DisplayName, Data: data})
}
json, err := json.Marshal(&userNamePoints{Name: name, Points: points}) td, err := generateTableData()
if err != nil {
log.Printf("Scoreboard Update Error: %v\n", err)
return err
}
ld := leaderboardData{TableData: td, ChartData: datas}
jsona, err := json.Marshal(&ld)
if err != nil { if err != nil {
log.Printf("Scoreboard Update Error: %v\n", err) log.Printf("Scoreboard Update Error: %v\n", err)
return err return err
} }
messageChan <- string(json) messageChan <- string(jsona)
log.Printf("Scoreboard Update String: %s\n", string(json))
return nil return nil
} }
...@@ -3,10 +3,11 @@ package wtfd ...@@ -3,10 +3,11 @@ package wtfd
import ( import (
"errors" "errors"
"fmt" "fmt"
"golang.org/x/crypto/bcrypt"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" // needed for xorm _ "github.com/mattn/go-sqlite3" // needed for xorm
"golang.org/x/crypto/bcrypt"
"os" "os"
"time"
"xorm.io/core" "xorm.io/core"
) )
...@@ -25,13 +26,15 @@ var ( ...@@ -25,13 +26,15 @@ var (
type _ORMUser struct { type _ORMUser struct {
Name string `xorm:"unique"` Name string `xorm:"unique"`
DisplayName string `xorm:"unique"` DisplayName string `xorm:"unique"`
Created time.Time `xorm:"created" json:"time"`
Hash []byte Hash []byte
Points int Points int
} }
type _ORMChallengesByUser struct { type _ORMChallengesByUser struct {
UserName string // Foregin keys don't exist UserName string `json:"username"` // Foregin keys don't exist
ChallengeName string Created time.Time `xorm:"created" json:"time"`
ChallengeName string `json:"name"`
} }
func (u _ORMUser) TableName() string { func (u _ORMUser) TableName() string {
...@@ -77,6 +80,7 @@ func NewUser(name, password, displayname string) (User, error) { ...@@ -77,6 +80,7 @@ func NewUser(name, password, displayname string) (User, error) {
return User{Name: name, Hash: hash, DisplayName: displayname}, nil return User{Name: name, Hash: hash, DisplayName: displayname}, nil
} }
// Contains looks if a username is in the datenbank // Contains looks if a username is in the datenbank
func Contains(username, displayname string) bool { func Contains(username, displayname string) bool {
count, _ := ormUserExists(User{Name: username, DisplayName: displayname}) count, _ := ormUserExists(User{Name: username, DisplayName: displayname})
...@@ -146,6 +150,18 @@ func ormNewUser(user User) error { ...@@ -146,6 +150,18 @@ func ormNewUser(user User) error {
return err return err
} }
// ormGetSolveCount returns the number of solves for the Challenge chall
func ormGetSolvesWithTime(u string) []_ORMChallengesByUser {
var a []_ORMChallengesByUser
if err := engine.Where("UserName = ?", u).Find(&a); err != nil {
fmt.Printf("ORM Error: %v\n", err)
return []_ORMChallengesByUser{}
}
return a
}
// ormGetSolveCount returns the number of solves for the Challenge chall // ormGetSolveCount returns the number of solves for the Challenge chall
func ormGetSolveCount(chall Challenge) int64 { func ormGetSolveCount(chall Challenge) int64 {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment