Archived
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7f8f21d9b | ||
|
|
3c6f56c826 | ||
|
|
85a471a66e | ||
|
|
3a1e4431ea | ||
|
|
fd94f8a075 | ||
|
|
0864636ca9 | ||
|
|
ab3ffe9bc0 | ||
|
|
90dfbb2a73 | ||
|
|
528690d836 | ||
|
|
00bad205c2 | ||
|
|
0a46484d2f |
No files matched your search
@@ -0,0 +1,35 @@
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
server = 'https://git.ermnvldmr.com/api/v1/repos/' + os.environ['GITHUB_REPOSITORY']
|
||||
headers = {'Authorization': 'token ' + os.environ['RELEASE_TOKEN']}
|
||||
tag = os.environ['GITHUB_REF_NAME']
|
||||
|
||||
|
||||
def request(path, data=None, content_type=None):
|
||||
current = headers.copy()
|
||||
if content_type:
|
||||
current['Content-Type'] = content_type
|
||||
req = urllib.request.Request(server + path, data=data, headers=current)
|
||||
with urllib.request.urlopen(req, timeout=120) as response:
|
||||
return json.load(response)
|
||||
|
||||
|
||||
try:
|
||||
release = request('/releases/tags/' + urllib.parse.quote(tag, safe=''))
|
||||
except urllib.error.HTTPError as error:
|
||||
if error.code != 404:
|
||||
raise
|
||||
release = request('/releases', json.dumps({'tag_name': tag, 'name': tag, 'draft': False}).encode(), 'application/json')
|
||||
for artifact in sorted(Path('artifacts').glob('*')):
|
||||
if not artifact.is_file():
|
||||
continue
|
||||
boundary = uuid.uuid4().hex
|
||||
filename = artifact.name.replace('"', '_').replace('\r', '_').replace('\n', '_')
|
||||
body = (f'--{boundary}\r\nContent-Disposition: form-data; name="attachment"; filename="{filename}"\r\nContent-Type: application/octet-stream\r\n\r\n'.encode() + artifact.read_bytes() + f'\r\n--{boundary}--\r\n'.encode())
|
||||
request('/releases/' + str(release['id']) + '/assets?name=' + urllib.parse.quote(filename), body, 'multipart/form-data; boundary=' + boundary)
|
||||
@@ -0,0 +1,70 @@
|
||||
name: actions
|
||||
'on':
|
||||
push:
|
||||
tags:
|
||||
- v*.*.*
|
||||
jobs:
|
||||
pkg:
|
||||
runs-on: apex-debian13
|
||||
strategy:
|
||||
matrix:
|
||||
node-version:
|
||||
- 18.x
|
||||
pnpm-version:
|
||||
- 8
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
run: "set -euo pipefail\ncase \"$CHECKOUT_REF\" in -*) exit 1;; esac\nGITHUB_SERVER_URL=${GITHUB_SERVER_URL%/}\n\
|
||||
case \"$GITHUB_SERVER_URL\" in https://git.ermnvldmr.com) ;; *) exit 1;; esac\n\
|
||||
AUTH=$(printf 'deytenit:%s' \"$CHECKOUT_TOKEN\" | base64 -w0)\nHEADER=\"http.${GITHUB_SERVER_URL}/.extraheader=AUTHORIZATION:\
|
||||
\ basic $AUTH\"\ngit init \"$GITHUB_WORKSPACE\"\ncd \"$GITHUB_WORKSPACE\"\n\
|
||||
git remote add origin \"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git\"\ngit -c\
|
||||
\ \"$HEADER\" fetch --tags origin '+refs/heads/*:refs/remotes/origin/*'\n\
|
||||
git -c \"$HEADER\" fetch origin \"$CHECKOUT_REF\"\ngit checkout --detach FETCH_HEAD\n\
|
||||
test \"$(git rev-parse HEAD)\" = \"$GITHUB_SHA\"\nif [ \"$CHECKOUT_SUBMODULES\"\
|
||||
\ = recursive ]; then\n key_file=$(mktemp \"${RUNNER_TEMP:-/tmp}/hextra-key.XXXXXX\"\
|
||||
)\n trap 'rm -f \"$key_file\"' EXIT\n umask 077\n printf '%s\\n'\
|
||||
\ \"$HEXTRA_READ_KEY\" > \"$key_file\"\n chmod 600 \"$key_file\"\n export\
|
||||
\ GIT_SSH_COMMAND=\"ssh -T -i $key_file -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes\
|
||||
\ -o HostKeyAlgorithms=ssh-ed25519 -o UserKnownHostsFile=$GITHUB_WORKSPACE/.gitea/known_hosts\"\
|
||||
\n git submodule update --init --recursive\nfi\nif [ \"$PERSIST_CREDENTIALS\"\
|
||||
\ = true ]; then\n git config \"http.${GITHUB_SERVER_URL}/.extraheader\"\
|
||||
\ \"AUTHORIZATION: basic $AUTH\"\nfi\n"
|
||||
env:
|
||||
CHECKOUT_REF: ${{ github.sha }}
|
||||
CHECKOUT_SUBMODULES: 'false'
|
||||
CHECKOUT_TOKEN: ${{ github.token }}
|
||||
PERSIST_CREDENTIALS: 'false'
|
||||
HEXTRA_READ_KEY: ''
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
run: "case \"$NODE_VERSION\" in\n 18*) major=18;; 20*) major=20;; 22*) major=22;;\
|
||||
\ *) exit 1;;\nesac\ntest -x \"/opt/ci/node/$major/bin/node\"\necho \"/opt/ci/node/$major/bin\"\
|
||||
\ >> \"$GITHUB_PATH\""
|
||||
env:
|
||||
NODE_VERSION: ${{ matrix.node-version }}
|
||||
- name: Install PKG
|
||||
run: 'test -x /opt/ci/pkg/bin/pkg
|
||||
|
||||
echo /opt/ci/pkg/bin >> "$GITHUB_PATH"'
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
- name: ESLint & Prettier
|
||||
run: npm run lint
|
||||
- name: Build
|
||||
run: npm run build
|
||||
- name: Test
|
||||
run: npm run --if-present test
|
||||
- name: Package
|
||||
run: pkg --out-path ./artifacts ./dist/index.js
|
||||
- name: Naming
|
||||
run: for f in ./artifacts/index*; do mv -i -- "$f" "${f//index/styleforces}";
|
||||
done
|
||||
- name: Release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: python3 .gitea/scripts/release.py
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ github.token }}
|
||||
env:
|
||||
PKG_CACHE_PATH: /opt/ci/pkg-cache
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -1,50 +0,0 @@
|
||||
name: actions
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
jobs:
|
||||
pkg:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
pnpm-version: [8]
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install PKG
|
||||
run: npm install -g pkg
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: ESLint & Prettier
|
||||
run: npm run lint
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm run --if-present test
|
||||
|
||||
- name: Package
|
||||
run: pkg --out-path ./artifacts ./dist/index.js
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
files: |
|
||||
artifacts/*
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023-present Vladimir Eremin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,101 @@
|
||||
# StyleForces
|
||||
|
||||
The Codeforces toolkit to make life easier for teachers and worse for students.
|
||||
|
||||
> One year I made one thing, next year another. They're small but still useful, so I've bundled them.
|
||||
|
||||
## Commands
|
||||
|
||||
One binary contains multiple commands, here is the listing.
|
||||
|
||||
### log2StanD
|
||||
|
||||
Convert contest log to JSON with freeze-time.
|
||||
|
||||
```cs
|
||||
Usage: styleforces log2StanD [options...]
|
||||
Options:
|
||||
--freeze, -f (INTEGER) : Freeze time counting from start in minutes.
|
||||
--log, -l (STRING) : Path to the contest log file.
|
||||
--domain, -d (STRING) : Path to the domain's user's file.
|
||||
--only-domain, -i : Ignore everyone not in domain.
|
||||
--out-file, -O <STRING> : Path to the resulted serialization. (default: './result.json')
|
||||
Example: styleforces log2StanD -f 120 -l ./log.txt -d ./domain.txt -o ./result.json
|
||||
```
|
||||
|
||||
> This one was used on a local team Olympiad to make it more exciting for participants.
|
||||
>
|
||||
> ACM ICPC like experience powered by [StanD](https://github.com/OStrekalovsky/S4RiS-StanD).
|
||||
|
||||
### fetch
|
||||
|
||||
Fetch contest submissions ("OK" or point-threshold).
|
||||
|
||||
```cs
|
||||
Usage: styleforces fetch [options...]
|
||||
Options:
|
||||
--contest, -i (STRING) : Contest ID.
|
||||
--group, -g <STRING> : Group ID (if in group).
|
||||
--count, -c <INTEGER> : Positive amount of submissions to fetch. (default: 100)
|
||||
--lookup, -l <INTEGER> : Positive amount of submissions to lookup. (default: 200)
|
||||
--after, -a <INTEGER> : Positive amount of submissions to skip. (default: 1)
|
||||
--points, -p <INTEGER> : Positive amount of points to filter. (default: 100)
|
||||
--exclude, -e [STRING] : Index of problems to exclude from fetching.
|
||||
--out-dir, -O <STRING> : Path to the directory to write submissions to. (default: './result')
|
||||
Example: styleforces fetch -i someId -g anotherId -c 50 -l 100 -a 10 -o './result'
|
||||
```
|
||||
|
||||
> Basically, you do this to make some cool stuff later.
|
||||
|
||||
### copydetect
|
||||
|
||||
Copydetect wrapper compatible with fetch command.
|
||||
|
||||
```cs
|
||||
Usage: styleforces copydetect [copydetect options...]
|
||||
Example: styleforces copydetect -t ./data -o cpp -O ./result.html
|
||||
```
|
||||
|
||||
> ⚠️ [copydetect](https://github.com/blingenf/copydetect) needs to be accessible in your environment!
|
||||
|
||||
> This makes fabulous HTML copy paste reports over submissions. Easily catch cheaters in your contests.
|
||||
|
||||
## Enviroment
|
||||
|
||||
More about usage!
|
||||
|
||||
### Variables
|
||||
|
||||
- **CF_KEY** - Codeforces API Key.
|
||||
- **CF_SECRET** - Codeforces API Secret.
|
||||
- **CF_COOKIE** - Cookies extracted from your session.
|
||||
|
||||
> ❔About cookies: some info isn't accessible through codeforces API, though we need to scrap HTML to access it.
|
||||
|
||||
> ❕Moreover contests require **manager** role to access participant's submissions.
|
||||
|
||||
### Options
|
||||
|
||||
- **--help, -h** - should work everywhere depending on context. **Default:**
|
||||
|
||||
```cs
|
||||
Usage: styleforces (command) [arguments...]
|
||||
Commands:
|
||||
- log2StanD : Convert contest log to JSON with freeze-time.
|
||||
- fetch : Fetch submission from contest.
|
||||
- copydetect : Compare submissions and generate copy report of them. (requires copydetect)
|
||||
Environment:
|
||||
CF_KEY : Codeforces API Key.
|
||||
CF_SECRET : Codeforces API Secret.
|
||||
CF_COOKIE : Cookies extracted from your session.
|
||||
```
|
||||
|
||||
## Bundles
|
||||
|
||||
> Maybe later...
|
||||
|
||||
## Postface
|
||||
|
||||
**[Licence](https://github.com/unknowableshade/StyleForces/blob/master/LICENCE)**
|
||||
|
||||
**_Thank you for your attention!_**
|
||||
Generated
+230
-17
@@ -1,15 +1,17 @@
|
||||
{
|
||||
"name": "auto-codestyle",
|
||||
"name": "styleforces",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "auto-codestyle",
|
||||
"name": "styleforces",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cheerio": "^1.0.0-rc.12"
|
||||
"@types/yargs": "^17.0.24",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cheerio": "^0.22.31",
|
||||
@@ -22,6 +24,10 @@
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"prettier": "^3.0.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18.x.x",
|
||||
"npm": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
@@ -190,6 +196,19 @@
|
||||
"integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
"version": "17.0.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
|
||||
"integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yargs-parser": {
|
||||
"version": "21.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
|
||||
"integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA=="
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz",
|
||||
@@ -419,7 +438,6 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -428,7 +446,6 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
@@ -641,11 +658,23 @@
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
@@ -656,8 +685,7 @@
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
@@ -819,6 +847,11 @@
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
@@ -918,6 +951,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
@@ -1401,6 +1442,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
@@ -1785,6 +1834,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
@@ -2359,6 +2416,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||
@@ -2506,6 +2571,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string.prototype.trim": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
|
||||
@@ -2555,7 +2633,6 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
@@ -2785,18 +2862,67 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
@@ -2937,6 +3063,19 @@
|
||||
"integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "17.0.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
|
||||
"integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/yargs-parser": {
|
||||
"version": "21.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
|
||||
"integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA=="
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz",
|
||||
@@ -3064,14 +3203,12 @@
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
@@ -3224,11 +3361,20 @@
|
||||
"domutils": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
@@ -3236,8 +3382,7 @@
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
@@ -3349,6 +3494,11 @@
|
||||
"domhandler": "^5.0.3"
|
||||
}
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
@@ -3427,6 +3577,11 @@
|
||||
"is-symbol": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
@@ -3810,6 +3965,11 @@
|
||||
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
|
||||
"dev": true
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
@@ -4073,6 +4233,11 @@
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
@@ -4468,6 +4633,11 @@
|
||||
"functions-have-names": "^1.2.3"
|
||||
}
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||
@@ -4561,6 +4731,16 @@
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"string.prototype.trim": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
|
||||
@@ -4598,7 +4778,6 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
@@ -4761,18 +4940,52 @@
|
||||
"is-typed-array": "^1.1.10"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"requires": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
|
||||
},
|
||||
"yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "styleforces",
|
||||
"version": "1.0.0",
|
||||
"description": "Various tooling for codeforces interaction.",
|
||||
"version": "2.0.3",
|
||||
"description": "The Codeforces toolkit to make life easier for teachers and worse for students.",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"start": "node ./dist/index.js",
|
||||
|
||||
@@ -9,13 +9,8 @@ import { ExtendedSubmission, isErrnoException, isExecFileError } from "../lib/ty
|
||||
const execFile = promisify(execFileCallback);
|
||||
|
||||
export const HELP = `
|
||||
Usage: ... copyDetect [options...]
|
||||
Options:
|
||||
-d <STRING> : Path to the directory with submissions.
|
||||
-l <STRING> : Language tokenize during copydetection.
|
||||
-o <STRING> : Path to the resulted report. (default: './result.html')
|
||||
|
||||
Example: ... copyDetect -d ./data -l cpp -o ./result.html
|
||||
Usage: ${process.execPath} copydetect [copydetect options...]
|
||||
Example: ${process.execPath} copydetect -t ./data -o cpp -O ./result.html
|
||||
`;
|
||||
|
||||
function error(msg: string) {
|
||||
@@ -25,35 +20,26 @@ function error(msg: string) {
|
||||
|
||||
export async function execute() {
|
||||
const {
|
||||
values: { dataDir, lang, outFile }
|
||||
values: { "out-file": outFileArg }
|
||||
} = parseArgs({
|
||||
options: {
|
||||
dataDir: {
|
||||
"out-file": {
|
||||
type: "string",
|
||||
short: "d",
|
||||
default: "./result"
|
||||
},
|
||||
lang: {
|
||||
type: "string",
|
||||
short: "l",
|
||||
default: "cpp"
|
||||
},
|
||||
outFile: {
|
||||
type: "string",
|
||||
short: "o",
|
||||
default: "./result.html"
|
||||
short: "O"
|
||||
}
|
||||
},
|
||||
args: process.argv.slice(3)
|
||||
args: process.argv.slice(3),
|
||||
strict: false
|
||||
});
|
||||
|
||||
if (!dataDir || !lang || !outFile) {
|
||||
error("Arguments don't seem to be correct.");
|
||||
return;
|
||||
let outFile = typeof outFileArg === "boolean" || !outFileArg ? "./result.html" : outFileArg;
|
||||
|
||||
if (!outFile.endsWith(".html")) {
|
||||
outFile = path.join(outFile, "./result.html");
|
||||
}
|
||||
|
||||
try {
|
||||
await execFile("copydetect", ["-t", dataDir, "-e", "src", "-o", lang, "-O", outFile, "-a"]);
|
||||
await execFile("copydetect", process.argv.slice(3).concat(["-O", outFile, "-a"]));
|
||||
} catch (err) {
|
||||
if (isErrnoException(err)) {
|
||||
error("copydetect cannot be spawned. Check your environment.");
|
||||
@@ -86,7 +72,9 @@ export async function execute() {
|
||||
) as ExtendedSubmission;
|
||||
|
||||
$(p).prepend(
|
||||
`<b>${firstData.author.members[0].handle} - ${secondData.author.members[0].handle}</b><br>`
|
||||
`<b>${firstData.author.members[0].name ?? firstData.author.members[0].handle} - ${
|
||||
secondData.author.members[0].name ?? secondData.author.members[0].handle
|
||||
}</b><br>`
|
||||
);
|
||||
|
||||
$(first).replaceWith(`<a href="${firstData.url}">${firstData.id}</a>`);
|
||||
@@ -11,16 +11,18 @@ type SubmissionIndex = {
|
||||
};
|
||||
|
||||
export const HELP = `
|
||||
Usage: ... fetchOk [options...]
|
||||
Usage: ${process.execPath} fetch [options...]
|
||||
Options:
|
||||
-i (STRING) : Contest ID.
|
||||
-g <STRING> : Group ID (if in group).
|
||||
-c <INTEGER> : Positive amount of "OK" submissions to fetch. (default: 100)
|
||||
-l <INTEGER> : Positive amouut of submissions to lookup. (default: 200)
|
||||
-a <INTEGER> : Positive amount of submissions to skip. (default: 1)
|
||||
-o <STRING> : Path to the directory to write submissions to. (default: './result')
|
||||
--contest, -i (STRING) : Contest ID.
|
||||
--group, -g <STRING> : Group ID (if in group).
|
||||
--count, -c <INTEGER> : Positive amount of submissions to fetch. (default: 100)
|
||||
--lookup, -l <INTEGER> : Positive amouut of submissions to lookup. (default: 200)
|
||||
--after, -a <INTEGER> : Positive amount of submissions to skip. (default: 1)
|
||||
--points, -p <INTEGER> : Positive amount of points to filter. (default: 100)
|
||||
--exclude, -e [STRING] : Index of problems to exclude from fetching.
|
||||
--out-dir, -O <STRING> : Path to the directory to write submissions to. (default: './result')
|
||||
|
||||
Example: ... fetchOk -i someId -g anotherId -c 50 -l 100 -a 10 -o './result'
|
||||
Example: ${process.execPath} fetch -i someId -g anotherId -c 50 -l 100 -a 10 -o './result'
|
||||
`;
|
||||
|
||||
function error(msg: string) {
|
||||
@@ -30,33 +32,50 @@ function error(msg: string) {
|
||||
|
||||
export async function execute() {
|
||||
const {
|
||||
values: { contestId, groupId, countArg, lookupArg, afterArg, outDir }
|
||||
values: {
|
||||
contest: contestId,
|
||||
group: groupId,
|
||||
count: countArg,
|
||||
lookup: lookupArg,
|
||||
after: afterArg,
|
||||
points: pointsArg,
|
||||
exclude: excludeArg,
|
||||
"out-dir": outDirArg
|
||||
}
|
||||
} = parseArgs({
|
||||
options: {
|
||||
contestId: {
|
||||
contest: {
|
||||
type: "string",
|
||||
short: "i"
|
||||
},
|
||||
groupId: {
|
||||
group: {
|
||||
type: "string",
|
||||
short: "g"
|
||||
},
|
||||
countArg: {
|
||||
count: {
|
||||
type: "string",
|
||||
short: "c"
|
||||
},
|
||||
lookupArg: {
|
||||
lookup: {
|
||||
type: "string",
|
||||
short: "l"
|
||||
},
|
||||
afterArg: {
|
||||
after: {
|
||||
type: "string",
|
||||
short: "a"
|
||||
},
|
||||
outDir: {
|
||||
points: {
|
||||
type: "string",
|
||||
short: "o",
|
||||
default: "./output"
|
||||
short: "p"
|
||||
},
|
||||
exclude: {
|
||||
type: "string",
|
||||
short: "e",
|
||||
multiple: true
|
||||
},
|
||||
"out-dir": {
|
||||
type: "string",
|
||||
short: "O"
|
||||
}
|
||||
},
|
||||
args: process.argv.slice(3)
|
||||
@@ -65,8 +84,11 @@ export async function execute() {
|
||||
const count = countArg && /^(0|[1-9]\d*)$/.test(countArg) ? parseInt(countArg) : 100;
|
||||
const lookup = lookupArg && /^(0|[1-9]\d*)$/.test(lookupArg) ? parseInt(lookupArg) : 200;
|
||||
const after = afterArg && /^(0|[1-9]\d*)$/.test(afterArg) ? parseInt(afterArg) : 1;
|
||||
const points = pointsArg && /^(0|[1-9]\d*)$/.test(pointsArg) ? parseInt(pointsArg) : 100;
|
||||
const exclude = new Set<string>(excludeArg);
|
||||
const outDir = outDirArg ?? "./result";
|
||||
|
||||
if (!contestId || !outDir) {
|
||||
if (!contestId) {
|
||||
error("Arguments don't seem to be correct.");
|
||||
return;
|
||||
}
|
||||
@@ -88,7 +110,10 @@ export async function execute() {
|
||||
}
|
||||
|
||||
const status = (await codeforces.methods.contestStatus(contestId, after, lookup))
|
||||
.filter((submission) => submission.verdict === "OK")
|
||||
.filter(
|
||||
(submission) =>
|
||||
submission.verdict === "OK" || (submission.points && submission.points >= points)
|
||||
)
|
||||
.slice(0, count);
|
||||
|
||||
for (const submission of status) {
|
||||
@@ -101,7 +126,8 @@ export async function execute() {
|
||||
(problem) =>
|
||||
problem.contestId === submission.contestId &&
|
||||
problem.name === submission.problem.name
|
||||
)
|
||||
) ||
|
||||
exclude.has(submission.problem.index)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -17,14 +17,15 @@ interface Output {
|
||||
}
|
||||
|
||||
export const HELP = `
|
||||
Usage: ... logSerializer [options...]
|
||||
Usage: ${process.execPath} log2StanD [options...]
|
||||
Options:
|
||||
-f (INTEGER) : Freeze time counting from start in minutes.
|
||||
-l (STRING) : Path to the contest log file.
|
||||
-d (STRING) : Path to the domain's users file.
|
||||
-o <STRING> : Path to the resulted serialization. (default: './result.json')
|
||||
--freeze, -f (INTEGER) : Freeze time counting from start in minutes.
|
||||
--log, -l (STRING) : Path to the contest log file.
|
||||
--domain, -d (STRING) : Path to the domain's users file.
|
||||
--only-domain, -i : Ignore everyone not in domain.
|
||||
--out-file, -O <STRING> : Path to the resulted serialization. (default: './result.json')
|
||||
|
||||
Example: ... logSerializer -f 120 -l ./log.txt -d ./domain.txt -o ./result.json
|
||||
Example: ${process.execPath} log2StanD -f 120 -l ./log.txt -d ./domain.txt -o ./result.json
|
||||
`;
|
||||
|
||||
function error(msg: string) {
|
||||
@@ -34,31 +35,42 @@ function error(msg: string) {
|
||||
|
||||
export async function execute() {
|
||||
const {
|
||||
values: { freezeTime, logFile, domainFile, outFile }
|
||||
values: {
|
||||
freeze: freezeTime,
|
||||
log: logFile,
|
||||
domain: domainFile,
|
||||
"only-domain": onlyDomain,
|
||||
"out-file": outFileArg
|
||||
}
|
||||
} = parseArgs({
|
||||
options: {
|
||||
freezeTime: {
|
||||
freeze: {
|
||||
type: "string",
|
||||
short: "f"
|
||||
},
|
||||
logFile: {
|
||||
log: {
|
||||
type: "string",
|
||||
short: "l"
|
||||
},
|
||||
domainFile: {
|
||||
domain: {
|
||||
type: "string",
|
||||
short: "d"
|
||||
},
|
||||
outFile: {
|
||||
"only-domain": {
|
||||
type: "boolean",
|
||||
short: "i"
|
||||
},
|
||||
"out-file": {
|
||||
type: "string",
|
||||
short: "o",
|
||||
default: "./result.json"
|
||||
short: "O"
|
||||
}
|
||||
},
|
||||
args: process.argv.slice(3)
|
||||
});
|
||||
|
||||
if (!freezeTime || !logFile || !domainFile || !outFile) {
|
||||
const outFile = outFileArg ?? "./result.json";
|
||||
|
||||
if (!freezeTime || !logFile || !domainFile) {
|
||||
error("Arguments don't seem to be correct.");
|
||||
return;
|
||||
}
|
||||
@@ -94,6 +106,8 @@ export async function execute() {
|
||||
runs: []
|
||||
};
|
||||
|
||||
const ignored = new Set<number>();
|
||||
|
||||
for (const entry of log) {
|
||||
const cmd = entry.slice(0, entry.indexOf(" ")).trim();
|
||||
const csv = entry.slice(entry.indexOf(" ") + 1).trim();
|
||||
@@ -110,19 +124,30 @@ export async function execute() {
|
||||
}
|
||||
case "@t": {
|
||||
const values = csv.split(",");
|
||||
output.contestants.push(names[values[3].split("=")[1]]);
|
||||
const party = /^g\d+?=/.test(values[3]) ? values[3].split("=")[1] : values[3];
|
||||
|
||||
if (onlyDomain && !Object.hasOwn(names, party)) {
|
||||
ignored.add(ignored.size + output.contestants.length);
|
||||
continue;
|
||||
}
|
||||
|
||||
output.contestants.push(names[party] ?? party);
|
||||
break;
|
||||
}
|
||||
case "@s": {
|
||||
const values = csv.split(",");
|
||||
const run: Run = {
|
||||
contestant: output.contestants[parseInt(values[0]) - 1],
|
||||
problemLetter: values[1],
|
||||
timeMinutesFromStart: Math.floor(parseInt(values[3]) / 60),
|
||||
success: values[4] === "OK"
|
||||
};
|
||||
const contestantId = parseInt(values[0]) - 1;
|
||||
|
||||
if (!ignored.has(contestantId)) {
|
||||
const run: Run = {
|
||||
contestant: output.contestants[contestantId - ignored.size],
|
||||
problemLetter: values[1],
|
||||
timeMinutesFromStart: Math.floor(parseInt(values[3]) / 60),
|
||||
success: values[4] === "OK"
|
||||
};
|
||||
output.runs.push(run);
|
||||
}
|
||||
|
||||
output.runs.push(run);
|
||||
break;
|
||||
}
|
||||
}
|
||||
+27
-14
@@ -1,37 +1,50 @@
|
||||
import * as copyDetect from "./command/copyDetect.js";
|
||||
import * as fetchOk from "./command/fetchOk.js";
|
||||
import * as logSerializer from "./command/logSerializer.js";
|
||||
import { parseArgs } from "util";
|
||||
|
||||
import * as copyDetect from "./command/copydetect.js";
|
||||
import * as fetchSubmissions from "./command/fetch.js";
|
||||
import * as log2stanD from "./command/log2StanD.js";
|
||||
|
||||
const HELP = `
|
||||
Usage: ... (command) [arguments...]
|
||||
Commands:
|
||||
- logSerializer : Convert contest log to JSON with freeze-time.
|
||||
- fetchOk : Fetch every "OK" submission from contest.
|
||||
- copyDetect : Compare submissions and generate copy report of them. (requires copydetect)
|
||||
- log2StanD : Convert contest log to JSON with freeze-time.
|
||||
- fetch : Fetch submission from contest.
|
||||
- copydetect : Compare submissions and generate copy report of them. (requires copydetect)
|
||||
Environment:
|
||||
CF_KEY : Codeforces API Key.
|
||||
CF_SECRET : Codeforces API Secret.
|
||||
CF_COOKIE : Cookies extracted from your session.
|
||||
`;
|
||||
|
||||
const help = !!process.argv.find((arg) => arg === "-h");
|
||||
const {
|
||||
values: { help }
|
||||
} = parseArgs({
|
||||
options: {
|
||||
help: {
|
||||
type: "boolean",
|
||||
short: "h"
|
||||
}
|
||||
},
|
||||
args: process.argv.slice(3),
|
||||
strict: false
|
||||
});
|
||||
|
||||
switch (process.argv[2]) {
|
||||
case "logSerializer":
|
||||
case "log2StanD":
|
||||
if (help) {
|
||||
console.log(logSerializer.HELP);
|
||||
console.log(log2stanD.HELP);
|
||||
break;
|
||||
}
|
||||
logSerializer.execute();
|
||||
log2stanD.execute();
|
||||
break;
|
||||
case "fetchOk":
|
||||
case "fetch":
|
||||
if (help) {
|
||||
console.log(fetchOk.HELP);
|
||||
console.log(fetchSubmissions.HELP);
|
||||
break;
|
||||
}
|
||||
fetchOk.execute();
|
||||
fetchSubmissions.execute();
|
||||
break;
|
||||
case "copyDetect":
|
||||
case "copydetect":
|
||||
if (help) {
|
||||
console.log(copyDetect.HELP);
|
||||
break;
|
||||
|
||||
Reference in new issue
Block a user