Skip to content

Instantly share code, notes, and snippets.

@maxint137
Last active April 6, 2025 13:09
Show Gist options
  • Save maxint137/bbd5b75e5ca66ea29f5bf277690c2eaf to your computer and use it in GitHub Desktop.
Save maxint137/bbd5b75e5ca66ea29f5bf277690c2eaf to your computer and use it in GitHub Desktop.
get the results from https://www3.zkai.co.jp/
Extract the pure data from https://www3.zkai.co.jp/ to render it graphically
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Scores Graph</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation@latest"></script>
<style>
#myChart {
max-width: 1800px;
/* margin: 20px auto; */
}
@media print {
.pagebreak {
page-break-before: always;
}
}
</style>
</head>
<body>
<h1>Absolute Scores</h1>
<canvas id="myChart"></canvas>
<div class="pagebreak"></div>
<h1>Relative Scores</h1>
<canvas id="myChart2"></canvas>
</body>
</html>
async function fetchData() {
const response = await fetch("transformed_results.json");
return await response.json();
}
Chart.defaults.datasets.line.spanGaps = true;
const name_for_subject = (s) => {
switch (s) {
case "2":
case "3":
case "4":
return ${s}`;
case "J":
return "Sociology";
case "M":
return "Math";
case "P":
return "Physics";
case "L":
return "Japanese";
default:
return s;
}
};
function prepareChartData(data, useScore) {
const labels = [];
const subjects = Object.keys(data);
const datasets = subjects.map((subject) => ({
label: name_for_subject(subject),
data: [],
borderColor: getRandomColor(),
backgroundColor: getRandomColor(0.2),
hidden: true,
}));
// Create an array to hold all results for sorting
const resultsArray = [];
subjects.forEach((subject) => {
data[subject].forEach((result) => {
const { monthTitle, score, ranking } = result;
const [numerator, denominator] = ranking.split("/").map(Number);
const rank = numerator / denominator;
// Extract year and month from monthTitle
const [year, grade, month, round] = monthTitle.match(/\d+/g);
// handle the 202X-01 glitch
const yearFixed = parseInt(year, 10) + ("1" == month ? 1 : 0);
// Push to results array for later sorting
resultsArray.push({
monthTitle: `${yearFixed}-${month.padStart(2, "0")}${2 == round ? "b" : "a"
}`,
year: yearFixed,
month: parseInt(month, 10),
score: parseInt(score), // Convert score to a number
rank: rank,
subject,
});
});
});
// Sort results array by year and month
resultsArray.sort((a, b) => a.monthTitle.localeCompare(b.monthTitle));
// Fill datasets and labels based on sorted results
resultsArray.forEach(({ monthTitle, score, rank, subject }) => {
if (!labels.includes(monthTitle)) {
labels.push(monthTitle);
}
const datasetIndex = subjects.indexOf(subject);
const index = labels.indexOf(monthTitle);
datasets[datasetIndex].data[index] = Math.round(useScore ? score : (100 - rank * 100));
datasets[datasetIndex].hidden =
datasets[datasetIndex].label.startsWith("Σ");
});
return { labels, datasets };
}
function getRandomColor(alpha = 1) {
const randomColor = Math.floor(Math.random() * 16777215).toString(16);
return `rgba(${parseInt(randomColor.slice(0, 2), 16)}, ${parseInt(
randomColor.slice(2, 4),
16
)}, ${parseInt(randomColor.slice(4, 6), 16)}, ${alpha})`;
}
async function renderChart() {
const data = await fetchData();
const plugins = {
annotation: {
annotations: {
line: {
type: "line",
yMin: 85, // Set the Y value for the line
yMax: 95, // Same value to make it horizontal
borderColor: "orange",
borderWidth: 2,
label: {
content: "Target Level",
enabled: true,
position: "right",
},
},
},
},
};
const options = {
responsive: true,
plugins,
scales: {
y: {
type: "logarithmic",
beginAtZero: false,
},
},
};
new Chart(document.getElementById("myChart").getContext("2d"), {
type: "line", // Change to 'bar' for bar chart
data: prepareChartData(data, true),
options,
});
new Chart(document.getElementById("myChart2").getContext("2d"), {
type: "line", // Change to 'bar' for bar chart
data: prepareChartData(data, false),
options,
});
}
renderChart();
import get from "axios";
import { writeFileSync } from "fs";
import dotenv from "dotenv";
dotenv.config();
let AUTH_TOKEN = "";
const COOKIE =
"_gid=GA1.3.1769416064.1728788736; _gat_UA-2006405-8=1; _ga=GA1.1.1027775214.1728788736; _ga_81GV8D7XVC=GS1.1.1728818610.3.1.1728818635.0.0.0; _ga_QYD53YSE01=GS1.3.1728818611.3.1.1728818635.0.0.0";
// API endpoints
const LIST_URL =
"https://prd-22eltab-lb-aws.zkai.co.jp/pa/monthlist?reportType=1&offset=0&limit=100";
const TEST_RESULT_BASE_URL =
"https://prd-22eltab-lb-aws.zkai.co.jp/pa/monthlyTestResult";
// Function to fetch the list of reports
async function fetchReportList() {
try {
const response = await get(LIST_URL, {
headers: {
authorization: AUTH_TOKEN,
cookie: COOKIE,
accept: "application/json, text/plain, */*",
"user-agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.57",
},
});
return response.data.reportList;
} catch (error) {
console.error("Error fetching report list:", error);
return [];
}
}
// Function to fetch test results based on report data
async function fetchTestResult(fiscalYear, month, times, grade, courseId) {
const url = `${TEST_RESULT_BASE_URL}?fiscalYear=${fiscalYear}&month=${month}&times=${times}&grade=${grade}&courseId=${courseId}`;
console.log(url);
try {
const response = await get(url, {
headers: {
authorization: AUTH_TOKEN,
cookie: COOKIE,
accept: "application/json, text/plain, */*",
"user-agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.57",
},
});
return response.data;
} catch (error) {
console.error(
`Error fetching test result for fiscalYear: ${fiscalYear}, month: ${month}, times: ${times}, grade: ${grade}:`,
error.message
);
return null;
}
}
// Function to determine the course ID based on the grade
function determineCourseId(grade) {
return `A${grade}`;
}
// Main function to fetch all test results and save to a JSON file
async function fetchAllTestResults(token) {
AUTH_TOKEN = `Bearer ${token}`;
const reportList = await fetchReportList();
const allTestResults = [];
for (const report of reportList) {
const { fiscalYear, grade, testCountPerMonth, attendedMonth } = report;
const courseId = determineCourseId(grade);
const testResult = await fetchTestResult(
fiscalYear,
attendedMonth,
testCountPerMonth,
grade,
courseId
);
if (testResult) {
allTestResults.push(testResult);
}
}
// Save the collected test results to a JSON file
writeFileSync(
"all_test_results.json",
JSON.stringify(allTestResults, null, 2)
);
console.log("All test results saved to all_test_results.json");
return allTestResults;
}
function massageData(testResults) {
// Initialize the transformed data structure
const transformedData = {};
// Process each test result
testResults.forEach((result) => {
const { monthlyTestResults } = result;
const { gradeDataResults } = monthlyTestResults;
gradeDataResults.forEach((subjectResult) => {
const { subjectCode, score, ranking, average, deviationValue } =
subjectResult;
// Create the subject entry if it doesn't exist
if (!transformedData[subjectCode]) {
transformedData[subjectCode] = [];
}
// Add the test result to the corresponding subject
transformedData[subjectCode].push({
score,
ranking,
average,
deviationValue,
monthTitle: monthlyTestResults.title, // Including title for reference
});
});
});
// Save the transformed data to a new JSON file
writeFileSync(
"transformed_results.json",
JSON.stringify(transformedData, null, 2)
);
console.log("Transformed data saved to transformed_results.json");
}
fetch("https://prd-22eltab-lb-aws.zkai.co.jp/pa/authoricate", {
headers: {
accept: "application/json, text/plain, */*",
"accept-language": "en-US,en;q=0.9,ru;q=0.8",
"content-type": "application/json;charset=UTF-8",
Referer: "https://www3.zkai.co.jp/",
"Referrer-Policy": "strict-origin-when-cross-origin",
},
body: `{"id":"${process.env.id}","password":"${process.env.password}"}`,
method: "POST",
})
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
// Parsing the response as JSON
return response.json();
})
.then((data) => fetchAllTestResults(data.token))
.then((res) => massageData(res));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment