From adef3f175cdecb14be195639ebe538c1d6486fe0 Mon Sep 17 00:00:00 2001 From: YianXie Date: Fri, 28 Nov 2025 16:37:01 +0800 Subject: [PATCH] fixed ascending/descending sorting order toggle function and formatted code --- README.md | 1 + backend/data.php | 851 +++++++++++++++++----------------- backend/problems_importer.php | 29 +- css/main.css | 36 +- css/utility.css | 14 +- frontend/about.html | 232 ++++++--- frontend/contribute.html | 2 +- frontend/faq.html | 273 +++++++---- frontend/recovery.html | 277 +++++++---- frontend/vote.html | 269 +++++++---- index.html | 523 +++++++++++++++------ js/cookieIO.js | 37 +- js/index.js | 410 +++++++++------- js/localIO.js | 6 +- js/utility.js | 10 +- js/vote.js | 272 ++++++----- scraper.py | 28 +- 17 files changed, 2016 insertions(+), 1254 deletions(-) diff --git a/README.md b/README.md index ff01d47..ec0c9ff 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # USACO Rating + [USACO Rating](https://codetiger.me/project/usaco/) is a tool to estimate the difficulties of USACO problems in terms of CodeForces rating. ![screenshot](./screenshot.png) diff --git a/backend/data.php b/backend/data.php index ccc12c2..f868fb7 100644 --- a/backend/data.php +++ b/backend/data.php @@ -1,419 +1,440 @@ query("SELECT * FROM problems"); - while($row = $res -> fetch_assoc()) { - $obj["id"] = $row["id"]; - $obj["contest"] = $row["contest"]; - $obj["name"] = $row["name"]; - $obj["url"] = $row["url"]; - $obj["type"] = $row["type"]; - $obj["rating"] = $row["rating"]; - $obj["quality"] = $row["quality"]; - $obj["rating2"] = $row["rating2"]; - $obj["quality2"] = $row["quality2"]; - $obj["cnt1"] = $row["cnt1"]; - $obj["cnt2"] = $row["cnt2"]; - array_push($data,$obj); - } - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - }else if($type == 2) { - // Retrieve user data - $data = []; - $uid = $_POST["id"]; - $stmt = $conn -> prepare("SELECT * FROM users WHERE id=?"); - $stmt -> bind_param("s",$uid); - $stmt -> execute(); - $res = $stmt -> get_result(); - if($res -> num_rows == 0) { - array_push($data,-1); - }else{ - $info = $res -> fetch_assoc(); - array_push($data,$info); - } - $stmt -> close(); - $stmt = $conn -> prepare("SELECT * FROM rating WHERE id=?"); - $stmt -> bind_param("s",$uid); - $stmt -> execute(); - $res = $stmt -> get_result(); - $rating = []; - if($res -> num_rows > 0) { - while($row = $res -> fetch_assoc()) { - array_push($rating,$row); - } - } - array_push($data,$rating); - $stmt -> close(); - $stmt = $conn -> prepare("SELECT * FROM quality WHERE id=?"); - $stmt -> bind_param("s",$uid); - $stmt -> execute(); - $res = $stmt -> get_result(); - $quality = []; - if($res -> num_rows > 0) { - while($row = $res -> fetch_assoc()) { - array_push($quality,$row); - } - } - array_push($data,$quality); - $stmt -> close(); - - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - }else if($type == 3) { - // Register an account - if($_POST["pwd"] == PASSWORD) { - $uid = generateRandomString(); - $name = $_POST["name"]; - $cf = $_POST["cf"]; - - registerAccount($conn,$uid,$name,$cf); - - $data = []; - $data["id"] = $uid; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - }else{ - - } - }else if($type == 4) { - // Update votes according to data - $data = []; - $uid = $_POST["id"]; - $votes = json_decode($_POST["votes"]); - if(!checkUserExist($conn,$uid)) { - $data["status"] = -1; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - for($i = 0;$i < count($votes[0]);$i++) { - updateVotesRating($conn,$uid,$i + 1,$votes[0][$i]); - } - for($i = 0;$i < count($votes[1]);$i++) { - updateVotesQuality($conn,$uid,$i + 1,$votes[1][$i]); - } - recalculateEverything($conn); - $data["status"] = 1; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - }else if($type == 5) { - // Temporary and should be removed once done - // recalculateEverything($conn); - }else if($type == 6) { - // Increase visit counter :D - $date = date("m-d-Y"); - $conn -> query("INSERT INTO stats (type,value) VALUES ('" . $date . "',1) +// Sensitive information about the database +include("mysql_info.php"); +$conn = new mysqli("localhost", USERNAME, PASSWORD, "usaco"); + +$type = $_POST['type']; +if ($type == 1) { + // Retrieve all data + $data = []; + $res = $conn->query("SELECT * FROM problems"); + while ($row = $res->fetch_assoc()) { + $obj["id"] = $row["id"]; + $obj["contest"] = $row["contest"]; + $obj["name"] = $row["name"]; + $obj["url"] = $row["url"]; + $obj["type"] = $row["type"]; + $obj["rating"] = $row["rating"]; + $obj["quality"] = $row["quality"]; + $obj["rating2"] = $row["rating2"]; + $obj["quality2"] = $row["quality2"]; + $obj["cnt1"] = $row["cnt1"]; + $obj["cnt2"] = $row["cnt2"]; + array_push($data, $obj); + } + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); +} else if ($type == 2) { + // Retrieve user data + $data = []; + $uid = $_POST["id"]; + $stmt = $conn->prepare("SELECT * FROM users WHERE id=?"); + $stmt->bind_param("s", $uid); + $stmt->execute(); + $res = $stmt->get_result(); + if ($res->num_rows == 0) { + array_push($data, -1); + } else { + $info = $res->fetch_assoc(); + array_push($data, $info); + } + $stmt->close(); + $stmt = $conn->prepare("SELECT * FROM rating WHERE id=?"); + $stmt->bind_param("s", $uid); + $stmt->execute(); + $res = $stmt->get_result(); + $rating = []; + if ($res->num_rows > 0) { + while ($row = $res->fetch_assoc()) { + array_push($rating, $row); + } + } + array_push($data, $rating); + $stmt->close(); + $stmt = $conn->prepare("SELECT * FROM quality WHERE id=?"); + $stmt->bind_param("s", $uid); + $stmt->execute(); + $res = $stmt->get_result(); + $quality = []; + if ($res->num_rows > 0) { + while ($row = $res->fetch_assoc()) { + array_push($quality, $row); + } + } + array_push($data, $quality); + $stmt->close(); + + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); +} else if ($type == 3) { + // Register an account + if ($_POST["pwd"] == PASSWORD) { + $uid = generateRandomString(); + $name = $_POST["name"]; + $cf = $_POST["cf"]; + + registerAccount($conn, $uid, $name, $cf); + + $data = []; + $data["id"] = $uid; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + } else { + + } +} else if ($type == 4) { + // Update votes according to data + $data = []; + $uid = $_POST["id"]; + $votes = json_decode($_POST["votes"]); + if (!checkUserExist($conn, $uid)) { + $data["status"] = -1; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + for ($i = 0; $i < count($votes[0]); $i++) { + updateVotesRating($conn, $uid, $i + 1, $votes[0][$i]); + } + for ($i = 0; $i < count($votes[1]); $i++) { + updateVotesQuality($conn, $uid, $i + 1, $votes[1][$i]); + } + recalculateEverything($conn); + $data["status"] = 1; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); +} else if ($type == 5) { + // Temporary and should be removed once done + // recalculateEverything($conn); +} else if ($type == 6) { + // Increase visit counter :D + $date = date("m-d-Y"); + $conn->query("INSERT INTO stats (type,value) VALUES ('" . $date . "',1) ON DUPLICATE KEY UPDATE value = value + 1;"); - $conn -> query("UPDATE stats SET value = value + 1 WHERE type = 'visit'"); - }else if($type == 7) { - // Automatic registration - - if(!isset($_POST["username"]) || !isset($_POST["name"])) { - $data["success"] = -5; - $data["message"] = "A required field is empty"; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - - $username = strtolower($_POST["username"]); - $CFUsers = callAPI("http://codeforces.com/api/user.info?handles=" . $username) -> result; - - if(count($CFUsers) == 0) { - $data["success"] = -1; - $data["message"] = "The codeforces account does not exist!"; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - - $CFUsers = $CFUsers[0]; - $firstName = $CFUsers -> firstName; - $rating = $CFUsers -> rating; - - // 1900 Threshold - if($rating < 1900) { - $data["success"] = -2; - $data["message"] = "The codeforces account's rating does not meet the 1900 threshold. If you believe you are still qualified, please fill out the manual review form."; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - - if(trim($firstName) != "USACO-Rating-verify") { - $data["success"] = -3; - $data["message"] = "The codeforces account's first name is not 'USACO-Rating-verify'"; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - - if(checkCodeForcesExist($conn,$username)) { - $data["success"] = -4; - $data["message"] = "You have already registered an account! Contact CodeTiger on discord if you lost your personalized link."; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - - // Everything passes :D - $uid = generateRandomString(); - $name = $_POST["name"]; - - registerAccount($conn,$uid,$name,$username); - - $data["status"] = 1; - $data["id"] = $uid; - $data["message"] = "Success!"; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - }else if($type == 8) { - // Invitation Link Recovery - - if(!isset($_POST["username"])) { - $data["success"] = -1; - $data["message"] = "A required field is empty"; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - - $username = strtolower($_POST["username"]); - - if(!checkCodeForcesExist($conn,$username)) { - $data["success"] = -2; - $data["message"] = "The account does not exist!"; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - - $CFUsers = callAPI("http://codeforces.com/api/user.info?handles=" . $username) -> result; - - if(count($CFUsers) == 0) { - $data["success"] = -4; - $data["message"] = "The codeforces account does not exist!"; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - - $CFUsers = $CFUsers[0]; - $firstName = $CFUsers -> firstName; - - if(trim($firstName) != "USACO-Rating-verify") { - $data["success"] = -3; - $data["message"] = "The codeforces account's first name is not 'USACO-Rating-verify'"; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - } - - $uid = getUserId($conn,$_POST["username"]); - $data["success"] = 0; - $data["id"] = $uid; - header('Content-type:application/json;charset=utf-8'); - echo json_encode($data); - exit(""); - - } - - function registerAccount($conn,$uid,$name,$cf) { - $stmt = $conn -> prepare("INSERT INTO users (id,name,cf) VALUES (?,?,?)"); - $stmt -> bind_param("sss",$uid,$name,$cf); - $stmt -> execute(); - $stmt -> close(); - } - - function recalculateEverything($conn) { - recalculateRatings($conn); - recalculateQualities($conn); - } - - function updateVotesRating($conn,$uid,$pid,$rating) { - if($rating == -2) return; - if($rating != -1 && ($rating < 800 || $rating > 3500)) return; - $stmt = $conn -> prepare("SELECT * FROM rating WHERE id=? AND pid=?"); - $stmt -> bind_param("si",$uid,$pid); - $stmt -> execute(); - $res = $stmt -> get_result(); - $stmt -> close(); - if($res -> num_rows == 0) { - if($rating == -1) return; - $stmt = $conn -> prepare("INSERT INTO rating (id,pid,val) VALUES (?,?,?)"); - $stmt -> bind_param("sid",$uid,$pid,$rating); - $stmt -> execute(); - $stmt -> close(); - return; - } - $stmt = $conn -> prepare("UPDATE rating SET val=? WHERE id=? AND pid=?"); - $stmt -> bind_param("dsi",$rating,$uid,$pid); - $stmt -> execute(); - $stmt -> close(); - return; - } - - function updateVotesQuality($conn,$uid,$pid,$quality) { - if($quality == -2) return; - if($quality != -1 && ($quality < 1 || $quality > 5)) return; - $stmt = $conn -> prepare("SELECT * FROM quality WHERE id=? AND pid=?"); - $stmt -> bind_param("si",$uid,$pid); - $stmt -> execute(); - $res = $stmt -> get_result(); - $stmt -> close(); - if($res -> num_rows == 0) { - if($quality == -1) return; - $stmt = $conn -> prepare("INSERT INTO quality (id,pid,val) VALUES (?,?,?)"); - $stmt -> bind_param("sid",$uid,$pid,$quality); - $stmt -> execute(); - $stmt -> close(); - return; - } - $stmt = $conn -> prepare("UPDATE quality SET val=? WHERE id=? AND pid=?"); - $stmt -> bind_param("dsi",$quality,$uid,$pid); - $stmt -> execute(); - $stmt -> close(); - return; - } - - function findAverage($arr) { - return $average = array_sum($arr) / count($arr); - } - - function findMedian($arr) { - sort($arr); - return $arr[floor(count($arr) / 2)]; - } - - function recalculateRatings($conn) { - $stmt = $conn -> prepare("SELECT * FROM problems"); - $stmt -> execute(); - $res = $stmt -> get_result(); - $N = $res -> num_rows; - $stmt -> close(); - $rating = []; - for($i = 1;$i <= $N;$i++) { - $rating[$i] = []; - } - $stmt = $conn -> prepare("SELECT * FROM rating"); - $stmt -> execute(); - $res = $stmt -> get_result(); - $stmt -> close(); - if($res -> num_rows > 0) { - while($row = $res -> fetch_assoc()) { - if($row["val"] != -1) array_push($rating[$row["pid"]],$row["val"]); - } - } - for($i = 1;$i <= $N;$i++) { - if(count($rating[$i]) == 0) { - $stmt = $conn -> prepare("UPDATE problems SET rating=NULL,rating2=NULL,cnt1=0 WHERE id=" . $i); - $stmt -> execute(); - $stmt -> close(); - continue; - } - $stmt = $conn -> prepare("UPDATE problems SET rating=?,rating2=?,cnt1=? WHERE id=?"); - $average = findAverage($rating[$i]); - $median = findMedian($rating[$i]); - $arrLen = count($rating[$i]); - $stmt -> bind_param("ddii",$average,$median,$arrLen,$i); - $stmt -> execute(); - $stmt -> close(); - } - return; - } - - function recalculateQualities($conn) { - $stmt = $conn -> prepare("SELECT * FROM problems"); - $stmt -> execute(); - $res = $stmt -> get_result(); - $N = $res -> num_rows; - $stmt -> close(); - $quality = []; - for($i = 1;$i <= $N;$i++) { - $quality[$i] = []; - } - $stmt = $conn -> prepare("SELECT * FROM quality"); - $stmt -> execute(); - $res = $stmt -> get_result(); - $stmt -> close(); - if($res -> num_rows > 0) { - while($row = $res -> fetch_assoc()) { - if($row["val"] != -1) array_push($quality[$row["pid"]],$row["val"]); - } - } - for($i = 1;$i <= $N;$i++) { - if(count($quality[$i]) == 0) { - $stmt = $conn -> prepare("UPDATE problems SET quality=NULL,quality2=NULL,cnt2=0 WHERE id=" . $i); - $stmt -> execute(); - $stmt -> close(); - continue; - } - $stmt = $conn -> prepare("UPDATE problems SET quality=?,quality2=?,cnt2=? WHERE id=?"); - $average = findAverage($quality[$i]); - $median = findMedian($quality[$i]); - $arrLen = count($quality[$i]); - $stmt -> bind_param("ddii",$average,$median,$arrLen,$i); - $stmt -> execute(); - $stmt -> close(); - } - return; - } - - function checkUserExist($conn,$uid) { - $stmt = $conn -> prepare("SELECT * FROM users WHERE id=?"); - $stmt -> bind_param("s",$uid); - $stmt -> execute(); - $res = $stmt -> get_result(); - if($res -> num_rows == 0) { - return false; - } - return true; - } - - function generateRandomString($length = 10) { - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $charactersLength = strlen($characters); - $randomString = ''; - for($i = 0;$i < $length;$i++) { - $randomString .= $characters[rand(0, $charactersLength - 1)]; - } - return $randomString; - } - - function callAPI($url){ - $result = json_decode(file_get_contents($url)); - return $result; - } - - function checkCodeForcesExist($conn,$cf) { - $stmt = $conn -> prepare("SELECT * FROM users WHERE cf=?"); - $stmt -> bind_param("s",$cf); - $stmt -> execute(); - $res = $stmt -> get_result(); - if($res -> num_rows == 0) { - return false; - } - return true; - } - - function getUserId($conn,$cf_handle) { - $stmt = $conn -> prepare("SELECT * FROM users WHERE cf=?"); - $stmt -> bind_param("s",$cf_handle); - $stmt -> execute(); - $res = $stmt -> get_result(); - if($res -> num_rows == 0) { - return "-1"; - } - $info = $res -> fetch_assoc(); - return $info["id"]; - } + $conn->query("UPDATE stats SET value = value + 1 WHERE type = 'visit'"); +} else if ($type == 7) { + // Automatic registration + + if (!isset($_POST["username"]) || !isset($_POST["name"])) { + $data["success"] = -5; + $data["message"] = "A required field is empty"; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + + $username = strtolower($_POST["username"]); + $CFUsers = callAPI("http://codeforces.com/api/user.info?handles=" . $username)->result; + + if (count($CFUsers) == 0) { + $data["success"] = -1; + $data["message"] = "The codeforces account does not exist!"; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + + $CFUsers = $CFUsers[0]; + $firstName = $CFUsers->firstName; + $rating = $CFUsers->rating; + + // 1900 Threshold + if ($rating < 1900) { + $data["success"] = -2; + $data["message"] = "The codeforces account's rating does not meet the 1900 threshold. If you believe you are still qualified, please fill out the manual review form."; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + + if (trim($firstName) != "USACO-Rating-verify") { + $data["success"] = -3; + $data["message"] = "The codeforces account's first name is not 'USACO-Rating-verify'"; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + + if (checkCodeForcesExist($conn, $username)) { + $data["success"] = -4; + $data["message"] = "You have already registered an account! Contact CodeTiger on discord if you lost your personalized link."; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + + // Everything passes :D + $uid = generateRandomString(); + $name = $_POST["name"]; + + registerAccount($conn, $uid, $name, $username); + + $data["status"] = 1; + $data["id"] = $uid; + $data["message"] = "Success!"; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); +} else if ($type == 8) { + // Invitation Link Recovery + + if (!isset($_POST["username"])) { + $data["success"] = -1; + $data["message"] = "A required field is empty"; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + + $username = strtolower($_POST["username"]); + + if (!checkCodeForcesExist($conn, $username)) { + $data["success"] = -2; + $data["message"] = "The account does not exist!"; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + + $CFUsers = callAPI("http://codeforces.com/api/user.info?handles=" . $username)->result; + + if (count($CFUsers) == 0) { + $data["success"] = -4; + $data["message"] = "The codeforces account does not exist!"; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + + $CFUsers = $CFUsers[0]; + $firstName = $CFUsers->firstName; + + if (trim($firstName) != "USACO-Rating-verify") { + $data["success"] = -3; + $data["message"] = "The codeforces account's first name is not 'USACO-Rating-verify'"; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + } + + $uid = getUserId($conn, $_POST["username"]); + $data["success"] = 0; + $data["id"] = $uid; + header('Content-type:application/json;charset=utf-8'); + echo json_encode($data); + exit(""); + +} + +function registerAccount($conn, $uid, $name, $cf) +{ + $stmt = $conn->prepare("INSERT INTO users (id,name,cf) VALUES (?,?,?)"); + $stmt->bind_param("sss", $uid, $name, $cf); + $stmt->execute(); + $stmt->close(); +} + +function recalculateEverything($conn) +{ + recalculateRatings($conn); + recalculateQualities($conn); +} + +function updateVotesRating($conn, $uid, $pid, $rating) +{ + if ($rating == -2) + return; + if ($rating != -1 && ($rating < 800 || $rating > 3500)) + return; + $stmt = $conn->prepare("SELECT * FROM rating WHERE id=? AND pid=?"); + $stmt->bind_param("si", $uid, $pid); + $stmt->execute(); + $res = $stmt->get_result(); + $stmt->close(); + if ($res->num_rows == 0) { + if ($rating == -1) + return; + $stmt = $conn->prepare("INSERT INTO rating (id,pid,val) VALUES (?,?,?)"); + $stmt->bind_param("sid", $uid, $pid, $rating); + $stmt->execute(); + $stmt->close(); + return; + } + $stmt = $conn->prepare("UPDATE rating SET val=? WHERE id=? AND pid=?"); + $stmt->bind_param("dsi", $rating, $uid, $pid); + $stmt->execute(); + $stmt->close(); + return; +} + +function updateVotesQuality($conn, $uid, $pid, $quality) +{ + if ($quality == -2) + return; + if ($quality != -1 && ($quality < 1 || $quality > 5)) + return; + $stmt = $conn->prepare("SELECT * FROM quality WHERE id=? AND pid=?"); + $stmt->bind_param("si", $uid, $pid); + $stmt->execute(); + $res = $stmt->get_result(); + $stmt->close(); + if ($res->num_rows == 0) { + if ($quality == -1) + return; + $stmt = $conn->prepare("INSERT INTO quality (id,pid,val) VALUES (?,?,?)"); + $stmt->bind_param("sid", $uid, $pid, $quality); + $stmt->execute(); + $stmt->close(); + return; + } + $stmt = $conn->prepare("UPDATE quality SET val=? WHERE id=? AND pid=?"); + $stmt->bind_param("dsi", $quality, $uid, $pid); + $stmt->execute(); + $stmt->close(); + return; +} + +function findAverage($arr) +{ + return $average = array_sum($arr) / count($arr); +} + +function findMedian($arr) +{ + sort($arr); + return $arr[floor(count($arr) / 2)]; +} + +function recalculateRatings($conn) +{ + $stmt = $conn->prepare("SELECT * FROM problems"); + $stmt->execute(); + $res = $stmt->get_result(); + $N = $res->num_rows; + $stmt->close(); + $rating = []; + for ($i = 1; $i <= $N; $i++) { + $rating[$i] = []; + } + $stmt = $conn->prepare("SELECT * FROM rating"); + $stmt->execute(); + $res = $stmt->get_result(); + $stmt->close(); + if ($res->num_rows > 0) { + while ($row = $res->fetch_assoc()) { + if ($row["val"] != -1) + array_push($rating[$row["pid"]], $row["val"]); + } + } + for ($i = 1; $i <= $N; $i++) { + if (count($rating[$i]) == 0) { + $stmt = $conn->prepare("UPDATE problems SET rating=NULL,rating2=NULL,cnt1=0 WHERE id=" . $i); + $stmt->execute(); + $stmt->close(); + continue; + } + $stmt = $conn->prepare("UPDATE problems SET rating=?,rating2=?,cnt1=? WHERE id=?"); + $average = findAverage($rating[$i]); + $median = findMedian($rating[$i]); + $arrLen = count($rating[$i]); + $stmt->bind_param("ddii", $average, $median, $arrLen, $i); + $stmt->execute(); + $stmt->close(); + } + return; +} + +function recalculateQualities($conn) +{ + $stmt = $conn->prepare("SELECT * FROM problems"); + $stmt->execute(); + $res = $stmt->get_result(); + $N = $res->num_rows; + $stmt->close(); + $quality = []; + for ($i = 1; $i <= $N; $i++) { + $quality[$i] = []; + } + $stmt = $conn->prepare("SELECT * FROM quality"); + $stmt->execute(); + $res = $stmt->get_result(); + $stmt->close(); + if ($res->num_rows > 0) { + while ($row = $res->fetch_assoc()) { + if ($row["val"] != -1) + array_push($quality[$row["pid"]], $row["val"]); + } + } + for ($i = 1; $i <= $N; $i++) { + if (count($quality[$i]) == 0) { + $stmt = $conn->prepare("UPDATE problems SET quality=NULL,quality2=NULL,cnt2=0 WHERE id=" . $i); + $stmt->execute(); + $stmt->close(); + continue; + } + $stmt = $conn->prepare("UPDATE problems SET quality=?,quality2=?,cnt2=? WHERE id=?"); + $average = findAverage($quality[$i]); + $median = findMedian($quality[$i]); + $arrLen = count($quality[$i]); + $stmt->bind_param("ddii", $average, $median, $arrLen, $i); + $stmt->execute(); + $stmt->close(); + } + return; +} + +function checkUserExist($conn, $uid) +{ + $stmt = $conn->prepare("SELECT * FROM users WHERE id=?"); + $stmt->bind_param("s", $uid); + $stmt->execute(); + $res = $stmt->get_result(); + if ($res->num_rows == 0) { + return false; + } + return true; +} + +function generateRandomString($length = 10) +{ + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charactersLength = strlen($characters); + $randomString = ''; + for ($i = 0; $i < $length; $i++) { + $randomString .= $characters[rand(0, $charactersLength - 1)]; + } + return $randomString; +} + +function callAPI($url) +{ + $result = json_decode(file_get_contents($url)); + return $result; +} + +function checkCodeForcesExist($conn, $cf) +{ + $stmt = $conn->prepare("SELECT * FROM users WHERE cf=?"); + $stmt->bind_param("s", $cf); + $stmt->execute(); + $res = $stmt->get_result(); + if ($res->num_rows == 0) { + return false; + } + return true; +} + +function getUserId($conn, $cf_handle) +{ + $stmt = $conn->prepare("SELECT * FROM users WHERE cf=?"); + $stmt->bind_param("s", $cf_handle); + $stmt->execute(); + $res = $stmt->get_result(); + if ($res->num_rows == 0) { + return "-1"; + } + $info = $res->fetch_assoc(); + return $info["id"]; +} ?> \ No newline at end of file diff --git a/backend/problems_importer.php b/backend/problems_importer.php index 787d324..d0d50a1 100644 --- a/backend/problems_importer.php +++ b/backend/problems_importer.php @@ -1,18 +1,19 @@ prepare("INSERT INTO problems (contest,name,url,type) VALUES (?,?,?,?)"); - $stmt -> bind_param("sssi",$info[0],$info[1],$info[2],$info[3]); - $stmt -> execute(); - $stmt -> close(); - } +while (!feof($file)) { + $line = fgets($file); + $info = explode("|", $line); + $stmt = $conn->prepare("INSERT INTO problems (contest,name,url,type) VALUES (?,?,?,?)"); + $stmt->bind_param("sssi", $info[0], $info[1], $info[2], $info[3]); + $stmt->execute(); + $stmt->close(); +} - fclose($file); +fclose($file); ?> \ No newline at end of file diff --git a/css/main.css b/css/main.css index 41bd245..948f6d8 100644 --- a/css/main.css +++ b/css/main.css @@ -1,37 +1,37 @@ .main-container { - margin-top: 3rem; - margin-left: 6%; - margin-right: 6%; - margin-bottom: 80px; - width: 88%; + margin-top: 3rem; + margin-left: 6%; + margin-right: 6%; + margin-bottom: 80px; + width: 88%; } span.difficulty-circle { - display: inline-block; - border-radius: 50%; - border-style: solid; - border-width: 1px; - margin-right: 5px; - height: 12px; - width: 12px; + display: inline-block; + border-radius: 50%; + border-style: solid; + border-width: 1px; + margin-right: 5px; + height: 12px; + width: 12px; } .vote-input { - border: none; - outline: none; - font-size: 18px; - width: 100%; + border: none; + outline: none; + font-size: 18px; + width: 100%; } .grayContainer { margin-bottom: 60px; width: 80%; margin-left: 10%; - background-color: #FAFAFA; + background-color: #fafafa; box-shadow: 0 4px 6px rgb(8 8 8 / 10%), 0 0 0 1px rgb(8 8 8 / 10%); padding: 40px 50px 40px 50px; word-break: break-word; word-wrap: break-word; font-size: 18px; color: #404040; -} \ No newline at end of file +} diff --git a/css/utility.css b/css/utility.css index da29708..cd8755e 100644 --- a/css/utility.css +++ b/css/utility.css @@ -1,11 +1,11 @@ hr { - margin-top: 10px; - margin-bottom: 10px + margin-top: 10px; + margin-bottom: 10px; } .unselectable { - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; -} \ No newline at end of file + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} diff --git a/frontend/about.html b/frontend/about.html index 5bcc845..7bce530 100644 --- a/frontend/about.html +++ b/frontend/about.html @@ -1,78 +1,170 @@ - - USACO Rating - About - - - - - - - + + USACO Rating - About + + + - - + + + - - - + + - - - + + + - - + + + +
+

About the Author

+
+ Avatar +

CodeTiger

+
+

+ Hello. I am an active Competitive-Programmer who has been doing + USACO since 7th grade. You can find more about me on my personal + website. The reason I created + this project was because there currently isn't a concrete system + for evaluating USACO problem difficulties. I remember when I + first started USACO, I had no idea what to train on. Especially + as I reach higher divisions, problems on the same contest can be + vastly different in terms of difficulties. Some were in my + range, some were too hard, and some were too easy. Furthermore, + since I did not know the difficulties beforehand, I often waste + a high-quality problem that isn't in my reach by reading the + editorial. Therefore I hope this project serves as a remedy for + that issue. By quantifying the difficulties and qualities, + people can much easily train on problems that fit for them. +

+
+

Contact

+
+ Email: + codetiger927@gmail.com
+ Discord: + CodeTiger#1869
+ Github: + CodeTiger927
+ CodeForces: + codetiger927 +
+ + diff --git a/frontend/contribute.html b/frontend/contribute.html index 335ea7e..7a73835 100644 --- a/frontend/contribute.html +++ b/frontend/contribute.html @@ -5,7 +5,7 @@ - + diff --git a/frontend/faq.html b/frontend/faq.html index 202a4f9..02236e8 100644 --- a/frontend/faq.html +++ b/frontend/faq.html @@ -1,90 +1,201 @@ - - USACO Rating - FAQ + + USACO Rating - FAQ - - - - - - - + + + - - + + + - - - + + - - - + + + - - + + + +
+

Frequently Asked Questions

+
+

How should I interpret the numbers?

+
+

+ There are two metrics offered by USACO Rating: difficulty and + quality. Difficulty is a number between 800 - 3500, which + roughly (or at least aims to) corresponds with + CodeForce's rating system. + Quality is on a scale of 1 to 5, with 5 being super high + quality, and 1 being very uninteresting. It is relatively more + subjective, but can act as a nice tool to select good problems, + as well as avoid bad ones. +

+
+

How are the numbers calculated?

+
+

+ We use a democratic voting system where users are allowed to + give their estimates on the difficulty, as well as opinions on + the problem quality. Note that only verified users who we deem + qualified can vote, so there should be minimal trolling, + ensuring a (hopefully) accurate estimate. +

+
+

I want to help contribute! How?

+
+

+ You can help contribute in many ways. The most common way is to + apply for a verified account and voice your opinions on these + problems. You can also directly help improve the project by + creating a pull request on our + GitHub + page. More information can be found on the + Contribute page. +

+
+

I have a suggestion/question/found a bug.

+
+

+ Any feedback is greatly appreciated! You can do so by creating a + new issue on our GitHub issues page. We will review them as fast + as possible. +

+
+

+ Why does this website look similar to Kenkoooo's Atcoder + Problems? +

+
+

+ Haha. Yes, we indeed greatly copied referenced many of + Atcoder Problem's styles. They are under the + MIT License, + so in the spirit of Open Source, we also use MIT License :D. So + yeah, shout-out to Kenkoooo! +

+
+

Can I use the code from this project for other purposes?

+
+

+ Definitely. Under the MIT License, you can reuse the code for + any purpose, sometimes even if they are part of proprietary + softwares. +

+
+

Who made this project?

+
+

+ CodeTiger was the creator of this project. You can learn more + about him on the About page. +

+
+ + diff --git a/frontend/recovery.html b/frontend/recovery.html index 010701e..88d4362 100644 --- a/frontend/recovery.html +++ b/frontend/recovery.html @@ -1,100 +1,195 @@ - - USACO Rating - Recovery - - - - - - - + + USACO Rating - Recovery + + + - - + + + - - - + + - - - + + + - - + + - - - \ No newline at end of file +
+

Invitation Link Recovery

+
+

+ Have you received a link in the past but can no longer find it? + If that is the case for, please use the form below to recover + your invitation link! +

+

+ If you have never obtained an invitation link before, please go + to the Contribute Page and + register instead. It will give you the necessary instructions to + become a volunteer. Thanks for volunteering! +

+
+ Do This First: Change your first name in + CodeForces (temporarily) + HERE + to "USACO-Rating-verify" before recovering the link. +
+
+
+
+ + +
+ +
+
+ + + + diff --git a/frontend/vote.html b/frontend/vote.html index 47086b3..b56e915 100644 --- a/frontend/vote.html +++ b/frontend/vote.html @@ -1,103 +1,186 @@ - - USACO Rating - Vote + + USACO Rating - Vote - - - - - - - + + + - + + + - - - + - - - - - - + + + - - + + - - - - - \ No newline at end of file +
+
+

Welcome back

+
+
+
+ + + + +
+
+ + + + + + + + + + + + + +
ContestNameDifficultyQuality
+ + +
+ + + diff --git a/index.html b/index.html index f8918de..4063cea 100644 --- a/index.html +++ b/index.html @@ -1,163 +1,392 @@ - - USACO Rating + + USACO Rating - - - + + + - - + + - - + + - - - - + + + + - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + + - + + +
+
+ + +
+ ? +
+ + +
+ +
+
+ + + + +
+
-
-
- - -
- ? -
- - -
- -
-
- - - - -
-
+ + + + + + + + -
+ Contest ▾ + Name + Difficulty ▴ + + Quality ▴ +
- - - - - - - - - - -
Contest ▾NameDifficulty ▴Quality ▴
-
+ + - - -
+ + + + + + diff --git a/js/cookieIO.js b/js/cookieIO.js index 9038ab0..9b16711 100644 --- a/js/cookieIO.js +++ b/js/cookieIO.js @@ -1,35 +1,38 @@ -function writeCookie(name,value,days) { - var date,expires; - if(days) { +function writeCookie(name, value, days) { + var date, expires; + if (days) { date = new Date(); - date.setTime(date.getTime()+(days*24*60*60*1000)); + date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); expires = "; expires=" + date.toGMTString(); - }else{ + } else { expires = ""; } document.cookie = name + "=" + value + expires + "; path=/"; } function readCookie(name) { - var c,ca,nameEQ = name + "="; - ca = document.cookie.split(';'); - for(var i = 0;i < ca.length;++i) { + var c, + ca, + nameEQ = name + "="; + ca = document.cookie.split(";"); + for (var i = 0; i < ca.length; ++i) { c = ca[i]; - while(c.charAt(0)==' ') { - c = c.substring(1,c.length); + while (c.charAt(0) == " ") { + c = c.substring(1, c.length); } - if(c.indexOf(nameEQ) == 0) { - return c.substring(nameEQ.length,c.length); + if (c.indexOf(nameEQ) == 0) { + return c.substring(nameEQ.length, c.length); } } - return ''; + return ""; } // To help users transition between the two storage methods function copyOverCookies() { var cookies = document.cookie.split(";"); - for(var i = 0;i < cookies.length;i++) { - if(cookies[i].split("=")[1]) writeRecord(cookies[i].split("=")[0],cookies[i].split("=")[1]); - writeCookie(cookies[i].split("=")[0],"",-1); + for (var i = 0; i < cookies.length; i++) { + if (cookies[i].split("=")[1]) + writeRecord(cookies[i].split("=")[0], cookies[i].split("=")[1]); + writeCookie(cookies[i].split("=")[0], "", -1); } -} \ No newline at end of file +} diff --git a/js/index.js b/js/index.js index 5bc529f..6ac535b 100644 --- a/js/index.js +++ b/js/index.js @@ -1,5 +1,6 @@ var data = null; var sortMode = 0; +var sortDirection = -1; // -1 for descending, 1 for ascending var curD = 0; var useMedian = false; var practiceMode = true; @@ -8,198 +9,287 @@ var lastUpdate = "1-14-2023"; var lastSeen; function init() { - copyOverCookies(); - lastSeen = readRecord("lastSeen"); - $.post("./backend/data.php",{"type": 6}); - - $.post("./backend/data.php",{"type": 1},function(res) { - data = res; - $('#median').bind('change', function(){useMedian ^= 1;display(curD)}); - $('#practice').bind('change', function(){practiceMode ^= 1;display(curD)}); - $('#practice').prop('checked',true); - sortProblems(0); - displayAnnouncement(); - }); + copyOverCookies(); + lastSeen = readRecord("lastSeen"); + $.post("./backend/data.php", { type: 6 }); + + $.post("./backend/data.php", { type: 1 }, function (res) { + data = res; + $("#median").bind("change", function () { + useMedian ^= 1; + display(curD); + }); + $("#practice").bind("change", function () { + practiceMode ^= 1; + display(curD); + }); + $("#practice").prop("checked", true); + sortProblems(0); + displayAnnouncement(); + }); } $(init); function displayAnnouncement() { - if(lastSeen == lastUpdate) return; - writeRecord("lastSeen",lastUpdate,365); - $('#Announcements').modal('show'); + if (lastSeen == lastUpdate) return; + writeRecord("lastSeen", lastUpdate, 365); + $("#Announcements").modal("show"); } -function rating2Str(rating,cnt) { - var res = '' + rating2Circle(rating) + (rating == null ? 'N/A' : Math.round(rating)) + ''; - return res; +function rating2Str(rating, cnt) { + var res = '= 2100) { + res += "color: rgb(255,140,0);"; + } else if (rating >= 1900) { + res += "color: rgb(170,0,170);"; + } else if (rating >= 1600) { + res += "color: blue;"; + } else if (rating >= 1400) { + res += "color: rgb(3,168,158);"; + } else if (rating >= 1200) { + res += "color: green;"; + } else if (rating >= 1000) { + res += "color: rgb(136,204,34);"; + } else { + res += "color: gray;"; + } + res += + 'font-weight: 500;" data-toggle="popover" data-content="Number of votes: ' + + cnt + + '" data-original-title="" title="">' + + rating2Circle(rating) + + (rating == null ? "N/A" : Math.round(rating)) + + ""; + return res; } function rating2Circle(rating) { - var percentage = 0; - var col = ""; - if(rating >= 2400) { - col += 'red'; - percentage = (rating - 2400) / 9; - }else if(rating >= 2100) { - col += 'rgb(255,140,0)'; - percentage = (rating - 2100) / 3; - }else if(rating >= 1900) { - col += 'rgb(170,0,170)'; - percentage = (rating - 1900) / 2; - }else if(rating >= 1600) { - col += 'blue'; - percentage = (rating - 1600) / 3; - }else if(rating >= 1400) { - col += 'rgb(3,168,158)'; - percentage = (rating - 1400) / 2; - }else if(rating >= 1200) { - col += 'green'; - percentage = (rating - 1200) / 2; - }else if(rating >= 1000) { - col += 'rgb(136,204,34)'; - percentage = (rating - 1000) / 2; - }else{ - col += 'gray'; - percentage = (rating) / 10; - } - percentage = Math.round(10 * percentage) / 10; - var res = ''; - return res; + var percentage = 0; + var col = ""; + if (rating >= 2400) { + col += "red"; + percentage = (rating - 2400) / 9; + } else if (rating >= 2100) { + col += "rgb(255,140,0)"; + percentage = (rating - 2100) / 3; + } else if (rating >= 1900) { + col += "rgb(170,0,170)"; + percentage = (rating - 1900) / 2; + } else if (rating >= 1600) { + col += "blue"; + percentage = (rating - 1600) / 3; + } else if (rating >= 1400) { + col += "rgb(3,168,158)"; + percentage = (rating - 1400) / 2; + } else if (rating >= 1200) { + col += "green"; + percentage = (rating - 1200) / 2; + } else if (rating >= 1000) { + col += "rgb(136,204,34)"; + percentage = (rating - 1000) / 2; + } else { + col += "gray"; + percentage = rating / 10; + } + percentage = Math.round(10 * percentage) / 10; + var res = + ''; + return res; } -function quality2Str(quality,cnt) { - var showQuality = Math.round(quality * 10) / 10; - var res = '' + showQuality + ''; - return res; +function quality2Str(quality, cnt) { + var showQuality = Math.round(quality * 10) / 10; + var res = '' + + showQuality + + ""; + return res; } function updateStatus(id) { - var cur = (getStatus(id) + 1) % 4; - writeRecord("Status" + id,cur); - return cur; + var cur = (getStatus(id) + 1) % 4; + writeRecord("Status" + id, cur); + return cur; } function statusToColor(status) { - if(status == 0) return "#FFFFFF"; - if(status == 1) return "#FFEEBA"; - if(status == 2) return "#B8DAFF"; - return "#C3E6CB"; + if (status == 0) return "#FFFFFF"; + if (status == 1) return "#FFEEBA"; + if (status == 2) return "#B8DAFF"; + return "#C3E6CB"; } function getStatus(id) { - var res = parseInt(readRecord("Status" + id)); - if(res) { - return res; - } - writeRecord("Status" + id,0); - return 0; + var res = parseInt(readRecord("Status" + id)); + if (res) { + return res; + } + writeRecord("Status" + id, 0); + return 0; } function display(type) { - $("#b" + curD).removeClass("active"); - $("#b" + type).addClass("active"); - curD = type; - var p = $(".problems"); - p.empty(); - for(var i = 0;i < data.length;++i) { - var curEntry = data[i]; - if(curEntry["type"] != type) continue; - p.append(""); - p.append('' + curEntry["contest"] + ''); - if(practiceMode) { - p.append('' + curEntry["name"] + ''); - $('#' + curEntry["id"] + 'p').click(function(event) { - var id = parseInt($(event.target).attr("id").slice(0,-1)); - var status = updateStatus(id); - $(event.target).css("background-color",statusToColor(status)); - }) - }else{ - p.append('' + curEntry["name"] + ''); - } - if(useMedian) { - p.append(rating2Str(curEntry["rating2"],curEntry["cnt1"])); - p.append(quality2Str(curEntry["quality2"],curEntry["cnt2"])); - }else{ - p.append(rating2Str(curEntry["rating"],curEntry["cnt1"])); - p.append(quality2Str(curEntry["quality"],curEntry["cnt2"])); - } - p.append(""); - } - activatePopOver(); + $("#b" + curD).removeClass("active"); + $("#b" + type).addClass("active"); + curD = type; + var p = $(".problems"); + p.empty(); + for (var i = 0; i < data.length; ++i) { + var curEntry = data[i]; + if (curEntry["type"] != type) continue; + p.append(""); + p.append('' + curEntry["contest"] + ""); + if (practiceMode) { + p.append( + '' + + curEntry["name"] + + "" + ); + $("#" + curEntry["id"] + "p").click(function (event) { + var id = parseInt($(event.target).attr("id").slice(0, -1)); + var status = updateStatus(id); + $(event.target).css("background-color", statusToColor(status)); + }); + } else { + p.append( + '' + + curEntry["name"] + + "" + ); + } + if (useMedian) { + p.append(rating2Str(curEntry["rating2"], curEntry["cnt1"])); + p.append(quality2Str(curEntry["quality2"], curEntry["cnt2"])); + } else { + p.append(rating2Str(curEntry["rating"], curEntry["cnt1"])); + p.append(quality2Str(curEntry["quality"], curEntry["cnt2"])); + } + p.append(""); + } + activatePopOver(); } function monthToValue(s) { - if(s == "Dec") return 3; - if(s == "Open") return 2; - if(s == "Feb") return 1; - return 0; + if (s == "Dec") return 3; + if (s == "Open") return 2; + if (s == "Feb") return 1; + return 0; } -function sortProblemsCmp(a,b) { - if(a["type"] != b["type"]) return a["type"] - b["type"]; - if(sortMode == 0) { - var aa = a["contest"].split(" "); - var ab = b["contest"].split(" "); - if(aa[0] != ab[0]) return ab[0] - aa[0]; - if(monthToValue(aa[1]) != monthToValue(ab[1])) return monthToValue(ab[1]) - monthToValue(aa[1]); - return a["id"] - b["id"]; - }else if(sortMode == 1) { - var ra = a["rating"] == null ? 0 : (useMedian ? a["rating2"] : a["rating"]); - var rb = b["rating"] == null ? 0 : (useMedian ? b["rating2"] : b["rating"]); - return rb - ra; - }else{ - var ra = a["quality"] == null ? 0 : (useMedian ? a["quality2"] : a["quality"]); - var rb = b["quality"] == null ? 0 : (useMedian ? b["quality2"] : b["quality"]); - return rb - ra; - } +function sortProblemsCmp(a, b) { + if (a["type"] != b["type"]) return a["type"] - b["type"]; + var result = 0; + if (sortMode == 0) { + var aa = a["contest"].split(" "); + var ab = b["contest"].split(" "); + if (aa[0] != ab[0]) { + result = ab[0] - aa[0]; + } else if (monthToValue(aa[1]) != monthToValue(ab[1])) { + result = monthToValue(ab[1]) - monthToValue(aa[1]); + } else { + result = a["id"] - b["id"]; + } + } else if (sortMode == 1) { + var ra = + a["rating"] == null ? 0 : useMedian ? a["rating2"] : a["rating"]; + var rb = + b["rating"] == null ? 0 : useMedian ? b["rating2"] : b["rating"]; + result = rb - ra; + } else { + var ra = + a["quality"] == null ? 0 : useMedian ? a["quality2"] : a["quality"]; + var rb = + b["quality"] == null ? 0 : useMedian ? b["quality2"] : b["quality"]; + result = rb - ra; + } + return result * sortDirection; } function sortProblems(type) { - $("#contest").text("Contest " + (type == 0 ? '▾' : '▴')); - $("#difficulty").text("Difficulty " + (type == 1 ? '▾' : '▴')); - $("#quality").text("Quality " + (type == 2 ? '▾' : '▴')); - sortMode = type; - data.sort(sortProblemsCmp); - display(curD); + // If clicking the same column, toggle direction; otherwise, set new column and default to descending + if (sortMode == type) { + sortDirection *= -1; + } else { + sortMode = type; + sortDirection = -1; // Default to descending + } + + // Update UI arrows: ▾ for descending (-1), ▴ for ascending (1) + var contestArrow = + sortMode == 0 && sortDirection == -1 + ? "▾" + : sortMode == 0 && sortDirection == 1 + ? "▴" + : ""; + var difficultyArrow = + sortMode == 1 && sortDirection == -1 + ? "▾" + : sortMode == 1 && sortDirection == 1 + ? "▴" + : ""; + var qualityArrow = + sortMode == 2 && sortDirection == -1 + ? "▾" + : sortMode == 2 && sortDirection == 1 + ? "▴" + : ""; + + $("#contest").text("Contest " + contestArrow); + $("#difficulty").text("Difficulty " + difficultyArrow); + $("#quality").text("Quality " + qualityArrow); + + data.sort(sortProblemsCmp); + display(curD); } function activatePopOver() { - $(function () {$('[data-toggle="popover"]').popover({"trigger":"hover","placement":"auto"})}) -} \ No newline at end of file + $(function () { + $('[data-toggle="popover"]').popover({ + trigger: "hover", + placement: "auto", + }); + }); +} diff --git a/js/localIO.js b/js/localIO.js index 16ae6bb..b634aa6 100644 --- a/js/localIO.js +++ b/js/localIO.js @@ -1,7 +1,7 @@ -function writeRecord(name,value) { - window.localStorage.setItem(name,value); +function writeRecord(name, value) { + window.localStorage.setItem(name, value); } function readRecord(name) { return window.localStorage.getItem(name); -} \ No newline at end of file +} diff --git a/js/utility.js b/js/utility.js index 5a54084..2cdc67a 100644 --- a/js/utility.js +++ b/js/utility.js @@ -1,15 +1,17 @@ var getUrlParameter = function getUrlParameter(sParam) { var sPageURL = window.location.search.substring(1), - sURLVariables = sPageURL.split('&'), + sURLVariables = sPageURL.split("&"), sParameterName, i; for (i = 0; i < sURLVariables.length; i++) { - sParameterName = sURLVariables[i].split('='); + sParameterName = sURLVariables[i].split("="); if (sParameterName[0] === sParam) { - return typeof sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]); + return typeof sParameterName[1] === undefined + ? true + : decodeURIComponent(sParameterName[1]); } } return false; -}; \ No newline at end of file +}; diff --git a/js/vote.js b/js/vote.js index d6064d0..8648997 100644 --- a/js/vote.js +++ b/js/vote.js @@ -1,150 +1,178 @@ var data = null; var curD = 0; -var udata,urating,uquality; +var udata, urating, uquality; function init() { - copyOverCookies(); - - var id = getUrlParameter("id"); - if(!id) { - id = readRecord("uid"); - } - if(!id) { - alert("D: You need to use your invite link at least once first to activate this page."); - window.location.replace("./recovery.html"); - }else{ - writeRecord("uid",id,365); - $.post("../backend/data.php",{"type": 2,"id": id},function(res) { - if(res[0] == -1) { - alert("Your invite link is invalid >.<"); - window.location.replace("../"); - } - udata = res[0]; - urating = res[1]; - uquality = res[2]; - window.setTimeout(function(){displayVotes();addName();},300); - }); - } + copyOverCookies(); - $.post("../backend/data.php",{"type": 1},function(res) { - data = res; - sortProblems(); - }); + var id = getUrlParameter("id"); + if (!id) { + id = readRecord("uid"); + } + if (!id) { + alert( + "D: You need to use your invite link at least once first to activate this page." + ); + window.location.replace("./recovery.html"); + } else { + writeRecord("uid", id, 365); + $.post("../backend/data.php", { type: 2, id: id }, function (res) { + if (res[0] == -1) { + alert("Your invite link is invalid >.<"); + window.location.replace("../"); + } + udata = res[0]; + urating = res[1]; + uquality = res[2]; + window.setTimeout(function () { + displayVotes(); + addName(); + }, 300); + }); + } + + $.post("../backend/data.php", { type: 1 }, function (res) { + data = res; + sortProblems(); + }); } $(init); function displayVotes() { - for(var i = 0;i < uquality.length;++i) { - var q = $("#" + uquality[i].pid + "q"); - q.empty(); - if(uquality[i].val != -1) q.val(uquality[i].val); - } - for(var i = 0;i < urating.length;++i) { - var r = $("#" + urating[i].pid + "r"); - r.empty(); - if(urating[i].val != -1) r.val(urating[i].val); - } + for (var i = 0; i < uquality.length; ++i) { + var q = $("#" + uquality[i].pid + "q"); + q.empty(); + if (uquality[i].val != -1) q.val(uquality[i].val); + } + for (var i = 0; i < urating.length; ++i) { + var r = $("#" + urating[i].pid + "r"); + r.empty(); + if (urating[i].val != -1) r.val(urating[i].val); + } } function saveVotes() { - var rating = []; - var quality = []; - for(var i = 1;i <= data.length;++i) { - rating.push(-1); - quality.push(-1); - } - for(var i = 1;i <= data.length;++i) { - if(data[i - 1]["type"] != curD) { - rating[data[i - 1]["id"] - 1] = -2; - continue; - } - var v = $("#" + data[i - 1]["id"] + "r").val(); - if(v == "") { - rating[data[i - 1]["id"] - 1] = -1; - }else if($.isNumeric(v)){ - v = parseInt(v); - if(v < 800 || v > 3500) { - alert("Rating can only be between 800 to 3500!"); - return; - } - rating[data[i - 1]["id"] - 1] = v; - }else{ - alert("Rating can only be integers >.< "); - return; - } - } - for(var i = 1;i <= data.length;++i) { - if(data[i - 1]["type"] != curD) { - quality[data[i - 1]["id"] - 1] = -2; - continue; - } - var v = $("#" + data[i - 1]["id"] + "q").val(); - if(v == "") { - quality[data[i - 1]["id"] - 1] = -1; - }else if($.isNumeric(v)){ - v = parseInt(v); - if(v < 1 || v > 5) { - alert("Quality can only be between 1 to 5!"); - return; - } - quality[data[i - 1]["id"] - 1] = v; - }else{ - alert("Quality can only be integers >.< "); - return; - } - } - var d = [rating,quality]; - $.post("../backend/data.php",{"type":4,"id":id,"votes":JSON.stringify(d)},function(res) { - if(res["status"] == 1) { - alert("Success!"); - location.reload(); - }else{ - console.log(res); - // alert("Fail for some reason ;-;"); - } - }); + var rating = []; + var quality = []; + for (var i = 1; i <= data.length; ++i) { + rating.push(-1); + quality.push(-1); + } + for (var i = 1; i <= data.length; ++i) { + if (data[i - 1]["type"] != curD) { + rating[data[i - 1]["id"] - 1] = -2; + continue; + } + var v = $("#" + data[i - 1]["id"] + "r").val(); + if (v == "") { + rating[data[i - 1]["id"] - 1] = -1; + } else if ($.isNumeric(v)) { + v = parseInt(v); + if (v < 800 || v > 3500) { + alert("Rating can only be between 800 to 3500!"); + return; + } + rating[data[i - 1]["id"] - 1] = v; + } else { + alert("Rating can only be integers >.< "); + return; + } + } + for (var i = 1; i <= data.length; ++i) { + if (data[i - 1]["type"] != curD) { + quality[data[i - 1]["id"] - 1] = -2; + continue; + } + var v = $("#" + data[i - 1]["id"] + "q").val(); + if (v == "") { + quality[data[i - 1]["id"] - 1] = -1; + } else if ($.isNumeric(v)) { + v = parseInt(v); + if (v < 1 || v > 5) { + alert("Quality can only be between 1 to 5!"); + return; + } + quality[data[i - 1]["id"] - 1] = v; + } else { + alert("Quality can only be integers >.< "); + return; + } + } + var d = [rating, quality]; + $.post( + "../backend/data.php", + { type: 4, id: id, votes: JSON.stringify(d) }, + function (res) { + if (res["status"] == 1) { + alert("Success!"); + location.reload(); + } else { + console.log(res); + // alert("Fail for some reason ;-;"); + } + } + ); } function display(type) { - $("#b" + curD).removeClass("active"); - $("#b" + type).addClass("active"); - curD = type; - var p = $(".problems"); - p.empty(); - for(var i = 0;i < data.length;++i) { - var curEntry = data[i]; - if(curEntry["type"] != type) continue; - p.append(""); - p.append('' + curEntry["contest"] + ''); - p.append('' + curEntry["name"] + ''); - p.append(''); - p.append(''); - p.append(""); - } - displayVotes(); + $("#b" + curD).removeClass("active"); + $("#b" + type).addClass("active"); + curD = type; + var p = $(".problems"); + p.empty(); + for (var i = 0; i < data.length; ++i) { + var curEntry = data[i]; + if (curEntry["type"] != type) continue; + p.append(""); + p.append( + '' + + curEntry["contest"] + + "" + ); + p.append( + '' + + curEntry["name"] + + "" + ); + p.append( + '' + ); + p.append( + '' + ); + p.append(""); + } + displayVotes(); } function monthToValue(s) { - if(s == "Dec") return 3; - if(s == "Open") return 2; - if(s == "Feb") return 1; - return 0; + if (s == "Dec") return 3; + if (s == "Open") return 2; + if (s == "Feb") return 1; + return 0; } -function sortProblemsCmp(a,b) { - var aa = a["contest"].split(" "); - var ab = b["contest"].split(" "); - if(aa[0] != ab[0]) return ab[0] - aa[0]; - if(monthToValue(aa[1]) != monthToValue(ab[1])) return monthToValue(ab[1]) - monthToValue(aa[1]); - return a["id"] - b["id"]; +function sortProblemsCmp(a, b) { + var aa = a["contest"].split(" "); + var ab = b["contest"].split(" "); + if (aa[0] != ab[0]) return ab[0] - aa[0]; + if (monthToValue(aa[1]) != monthToValue(ab[1])) + return monthToValue(ab[1]) - monthToValue(aa[1]); + return a["id"] - b["id"]; } function addName() { - $("#welcome").text("Welcome back " + udata["name"] + "!"); + $("#welcome").text("Welcome back " + udata["name"] + "!"); } function sortProblems() { - data.sort(sortProblemsCmp); - display(curD); + data.sort(sortProblemsCmp); + display(curD); } diff --git a/scraper.py b/scraper.py index a0d7c3b..7343d62 100644 --- a/scraper.py +++ b/scraper.py @@ -1,19 +1,25 @@ import requests contest = "jan23results" -url = "http://usaco.org/index.php?page=" + contest; -r = requests.get(url); -lines = r.text.split("\n"); +url = "http://usaco.org/index.php?page=" + contest +r = requests.get(url) +lines = r.text.split("\n") contestName = "2023 Jan" -lineno = 96; +lineno = 96 # INSERT INTO problems (contest,name,url,type) VALUES (?,?,?,?); -for i in range(3,-1,-1): - # type = i - for j in range(3): - name = (lines[lineno + 7 * i + j * 2 + 1].split("")[-1].split("")[0]); - url = ("http://usaco.org/" + lines[lineno + 7 * i + j * 2 + 2].split("'>")[0][15:]); - print("INSERT INTO problems (contest,name,url,type) VALUES (\"{a}\",\"{b}\",\"{c}\",{d});".format(a=contestName,b=name,c=url,d=i)); - print() +for i in range(3, -1, -1): + # type = i + for j in range(3): + name = lines[lineno + 7 * i + j * 2 + 1].split("")[-1].split("")[0] + url = ( + "http://usaco.org/" + lines[lineno + 7 * i + j * 2 + 2].split("'>")[0][15:] + ) + print( + 'INSERT INTO problems (contest,name,url,type) VALUES ("{a}","{b}","{c}",{d});'.format( + a=contestName, b=name, c=url, d=i + ) + ) + print()