Skip to content

Commit

Permalink
feat(vjudge): support download rank list from HDU
Browse files Browse the repository at this point in the history
  • Loading branch information
memset0 committed Jul 22, 2024
1 parent e4f6ad7 commit 3263d06
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 4 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
"homepage": "https://github.com/memset0/CPAssistant.js#readme",
"dependencies": {
"@types/node": "^20.3.0",
"file-saver": "^2.0.5",
"h": "^1.0.0",
"less": "^4.1.3",
"papaparse": "^5.4.1",
"query-string": "^8.1.0",
"typescript": "^5.1.3",
"vite": "^4.3.9",
Expand Down
87 changes: 87 additions & 0 deletions src/modules/vjudge/features/DownloadRanklist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as Papa from 'papaparse';
import saveAs from 'file-saver';
import Module from '../../../types/module';
import Feature from '../../../types/feature';

export default class ForkContest extends Feature {
run() {}

registerPlugins() {
this.plugin('hdu', function (this: Feature) {
this.on('/contest/rank', (_, params) => {
const contestId = params.cid;
const csvLink = `/contest/rank?cid=${contestId}&export=csv`;

const $downloadButton = document.createElement('button');
$downloadButton.innerText = 'Download Ranklist (VJ)';
$downloadButton.setAttribute('href', '#');
$downloadButton.onclick = async () => {
$downloadButton.setAttribute('disabled', '');

const response = await fetch(csvLink);
if (!response.ok) {
const errorMessage = `Request failed (code: ${response.status})!`;
alert(errorMessage);
throw new Error(errorMessage);
}

const csvPlain = await response.text();
const csvData = Papa.parse(csvPlain).data.slice(1);

const parse = (pattern: string): string => {
let [acTime, penalty] = pattern.split(' ');
if (pattern == '') {
acTime = '--';
penalty = '--';
} else {
if (acTime.startsWith('(')) {
penalty = acTime;
acTime = '--';
} else {
const [hour, minute, _second] = acTime.split(':');
acTime = String(Number(hour) * 60 + Number(minute));
}
if (penalty !== undefined && penalty.startsWith('(')) {
penalty = penalty.slice(2, -1);
} else {
penalty = '--';
}
if (acTime != '--' && penalty != '--') {
penalty = String(Number(penalty) + 1); // The accepted submission counted.
}
}
return acTime + ' # ' + penalty;
};

const data = [];
for (const source of csvData) {
const parsed = [source[1]];
for (let i = 4; i < source.length; i++) {
parsed.push(parse(source[i]));
}
data.push(parsed);
}

let blob = new Blob(
[
data.map((row) => row.join(',')).join('\n'), //
],
{ type: 'text/csv;charset=utf-8;' }
);
saveAs(blob, 'output.csv');

$downloadButton.removeAttribute('disabled');
};

const $actionBar = document.querySelector('.page-card-heading-actions')!;
$actionBar.appendChild($downloadButton);
});
});
}

constructor(module: Module, name: string) {
super(module, name);

this.registerPlugins();
}
}
2 changes: 0 additions & 2 deletions src/modules/vjudge/features/ForkContest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ export default class ForkContest extends Feature {
}

this.plugin('codeforces', function (this: Feature) {
this.log('setup');

function setup(situation: string, roundId: string) {
const $menu = document.getElementsByClassName('second-level-menu')[0];
const $menuList = $menu.children[0];
Expand Down
2 changes: 2 additions & 0 deletions src/modules/vjudge/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import config from '../../config';
import App from '../../app';
import Module from '../../types/module';
import AcceptedCounter from './features/AcceptedCounter';
import DownloadRanklist from './features/DownloadRanklist';
import ForkContest from './features/ForkContest';

export default class ModuleVjudge extends Module {
Expand All @@ -11,6 +12,7 @@ export default class ModuleVjudge extends Module {
super(app, 'vjudge', config.match.vjudge);

this.register(new AcceptedCounter(this, 'accepted-counter'));
this.register(new DownloadRanklist(this, 'download-ranklist'));
this.register(new ForkContest(this, 'fork-contest'));
}
}
4 changes: 2 additions & 2 deletions src/types/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default class Feature {
}
}

on(match: string | Array<string>, func: (args: Dict<string>) => void): boolean {
on(match: string | Array<string>, func: (args: Dict<string>, params: Dict<string>) => void): boolean {
if (match instanceof Array) {
let ok = false;
for (const singleMatch of match) {
Expand Down Expand Up @@ -75,7 +75,7 @@ export default class Feature {
}
}

func(args);
func(args, Object.fromEntries(new URLSearchParams(location.search)));
return true;
}

Expand Down
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ esbuild@^0.17.5:
"@esbuild/win32-ia32" "0.17.19"
"@esbuild/win32-x64" "0.17.19"

file-saver@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==

filter-obj@^5.1.0:
version "5.1.0"
resolved "https://registry.npmmirror.com/filter-obj/-/filter-obj-5.1.0.tgz"
Expand Down Expand Up @@ -402,6 +407,11 @@ open@^8.4.1:
is-docker "^2.1.1"
is-wsl "^2.2.0"

papaparse@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.4.1.tgz#f45c0f871853578bd3a30f92d96fdcfb6ebea127"
integrity sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==

parse-node-version@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz"
Expand Down

0 comments on commit 3263d06

Please sign in to comment.