diff --git a/index.html b/index.html index b6c63d4..a8f052e 100644 --- a/index.html +++ b/index.html @@ -8,12 +8,19 @@

:

TEXT:

-

+

RANK: + + + + + +

diff --git a/jstrr.js b/jstrr.js index c8eefdd..2b39260 100644 --- a/jstrr.js +++ b/jstrr.js @@ -352,6 +352,7 @@ switchToArea(0); } function showRank(list) { + mystate["ranklist"] = list; // user, max(score), max(step), count(score), sum(time)/60, max(at) let tbl = document.getElementById("ranking-table"), me; if (false) { @@ -364,10 +365,10 @@ for (let i in list) { let tr = document.createElement("tr"), rank = document.createElement("th"); - rank.textContent = parseInt(i)+1; + if (i>0) rank.textContent = parseInt(i); tr.appendChild(rank); for (let j=0; j{div.setAttribute("class", "ranking")}, 1000); - div.innerHTML = "

" + var p1 = document.createElement("p"); + p1.className = "c t"; + p1.innerHTML = ""; + var p2 = document.createElement("p"); + p2.className = "c t"; + var b2 = document.createElement("button"); + b2.innerText = "Download CSV"; + b2.addEventListener("click", downloadCSV, false); + p2.appendChild(b2); var d2 = document.createElement("div"); d2.classList.add("tbl"); var tbl = document.createElement("table"); tbl.id = "ranking-table"; tbl.classList.add("ranking"); - tbl.innerHTML = "
\ -\ -" + div.appendChild(p1); div.appendChild(d2); + div.appendChild(p2); d2.appendChild(tbl); document.body.appendChild(div); if (history.pushState) { @@ -497,7 +532,10 @@ }, false); } let text = textlist[nArea].file; - sendJSONtoServer({ranking: text}); + mystate["ranklist"] = null; // Clear ranking list + let request = {ranking: text}; + if (mode) request["mode"] = mode; + sendJSONtoServer(request); div.addEventListener("click", ()=>{history.back();}); div.addEventListener("keydown", ()=>{history.back();}, false); // div.querySelector("button").focus(); @@ -529,10 +567,6 @@ } function _send(e) { - loginhead = document.getElementById("loginhead"); - prompt = document.getElementById("prompt"); - input = document.getElementById("inputvalue"); - console.log("prom="+prompt.innerText+", val="+input.value); if (prompt && prompt.innerText > "" && input && input.value > "") { let user = mystate.user||localStorage.getItem("trrUser"), skey = localStorage.getItem("trrSkey"); @@ -546,18 +580,23 @@ } }; tryLogin(); - document.getElementById("inputvalue").addEventListener( + input.addEventListener( "keypress", (e) => {if (e.key=='Enter') _send(e);}); - document.getElementById("login").addEventListener("click", _send); - document.getElementById("reset").addEventListener("click", _reset); + let login = document.getElementById("login"); + if (login) { + login.addEventListener("click", _send); + document.getElementById("reset").addEventListener("click", _reset); + } } function wsInit() { + loginhead.textContent = "Connecting Server....." ws = new WebSocket(wsURL); infoBox.removeEventListener("click", wsInit); var typing = document.getElementById("typing"); ws.onopen = function () { console.log("WS-OK"); infoBox.classList.remove("warn"); + loginhead.textContent = "jsTRR"; initLogin(); }; ws.onmessage = getServerMessage; @@ -571,10 +610,18 @@ if (entry) entry.focus(); } function init() { + let btn; + prompt = document.getElementById("prompt"); + input = document.getElementById("inputvalue"); + console.log("prom="+prompt.innerText+", val="+input.value); tmsel = document.querySelector("select[name=team]"); nmsel = document.querySelector("select[name=name]"); txsel = document.getElementById("text"); - document.getElementById("ranking").addEventListener("click", ranking); + loginhead = document.getElementById("loginhead"); + for (let b of ["ranking", "today", "2h", "2w", "byteam", "byteam2h"]) { + if (btn = document.getElementById(b)) + btn.addEventListener("click", ranking); + } let entry = document.createElement("input"); entry.id = "entry"; entry.type = "text"; document.body.appendChild(entry); diff --git a/jtserv/jtserv.rb b/jtserv/jtserv.rb index feed927..eebbf47 100755 --- a/jtserv/jtserv.rb +++ b/jtserv/jtserv.rb @@ -19,7 +19,7 @@ class UserDB def initialize() - sq3 = "users.sq3" + sq3 = ENV["JTSV_DB"]||"users.sq3" @db = SQLite3::Database.new(sq3) ### @db.results_as_hash = true @expireskey = '+36 hours' @@ -313,12 +313,47 @@ end return rv end - def ranking(text) - @db.execute(<<~EOF, text) - SELECT user, max(score) hs, max(step), count(score), + def ranking(text, mode=nil) + rankbase = <<~EOF + SELECT "User", "Team", "Score", "Step", "Try", "Minutes", "Time" + UNION + SELECT user, team, max(score) hs, max(step), count(score), cast(round(sum(time)/60) as INT), max(at) - FROM score WHERE text=? GROUP BY user ORDER BY hs DESC; + FROM score NATURAL LEFT JOIN users WHERE text=? %s + GROUP BY user ORDER BY hs DESC; EOF + teambase = <<~EOF + WITH hs AS ( + SELECT user, gecos, team, max(score) hs + FROM score NATURAL LEFT JOIN users + WHERE text=? %s + GROUP BY USER + ) SELECT team, + round(cast(sum(hs) as real)/count(team), 1) avg, + count(team) n + FROM hs GROUP BY team ORDER BY avg DESC; + EOF + limit = if mode && /(\d+)([hdw])/ =~ mode + n = $1.to_i + t = 3600*[1, 24, 24*7]["hdw".index($2.downcase)] + (Time.now-t*n).strftime("%F %T") + end + p limit + case mode + when "today", /^(\d+)[hdw]/i + limit = Time.now.strftime("%F") if mode == "today" + STDERR.printf("limit=%s\n", limit) + @db.execute(sprintf(rankbase, "AND at > ?"), text, limit) + when /^byteam/ + [["Team", "Average", "n"]] + + if limit + @db.execute(sprintf(teambase, "AND at > ?"), text, limit) + else + @db.execute(sprintf(teambase, ""), text) + end + else + @db.execute(sprintf(rankbase, ""), text) + end end end class Text @@ -448,8 +483,9 @@ userinfo["typelist"][json["types"]] = Time.now.to_f elsif json["ranking"] text = json["ranking"] - p rank=db.ranking(text) - ws_conn.send(JSON.generate({"ranking": db.ranking(text)})) + mode = json["mode"] + p rank = db.ranking(text, mode) + ws_conn.send(JSON.generate({"ranking": rank, "mode": mode})) elsif json["gettext"] db.updateSkey(user, skey) userinfo.delete("typelist") # Reset running typing information
TimeSTEP
MissTarget
RankUserScoreStepTryMinutesTime