Archived
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7f8f21d9b | ||
|
|
3c6f56c826 | ||
|
|
85a471a66e | ||
|
|
3a1e4431ea | ||
|
|
fd94f8a075 | ||
|
|
0864636ca9 | ||
|
|
ab3ffe9bc0 | ||
|
|
90dfbb2a73 | ||
|
|
528690d836 |
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,53 +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: Naming
|
||||
run: for f in ./artifacts/index*; do mv -i -- "$f" "${f//index/styleforces}"; done
|
||||
|
||||
- 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!_**
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "styleforces",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"description": "The Codeforces toolkit to make life easier for teachers and worse for students.",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
|
||||
+10
-1
@@ -19,6 +19,7 @@ Options:
|
||||
--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: ${process.execPath} fetch -i someId -g anotherId -c 50 -l 100 -a 10 -o './result'
|
||||
@@ -38,6 +39,7 @@ export async function execute() {
|
||||
lookup: lookupArg,
|
||||
after: afterArg,
|
||||
points: pointsArg,
|
||||
exclude: excludeArg,
|
||||
"out-dir": outDirArg
|
||||
}
|
||||
} = parseArgs({
|
||||
@@ -66,6 +68,11 @@ export async function execute() {
|
||||
type: "string",
|
||||
short: "p"
|
||||
},
|
||||
exclude: {
|
||||
type: "string",
|
||||
short: "e",
|
||||
multiple: true
|
||||
},
|
||||
"out-dir": {
|
||||
type: "string",
|
||||
short: "O"
|
||||
@@ -78,6 +85,7 @@ export async function execute() {
|
||||
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) {
|
||||
@@ -118,7 +126,8 @@ export async function execute() {
|
||||
(problem) =>
|
||||
problem.contestId === submission.contestId &&
|
||||
problem.name === submission.problem.name
|
||||
)
|
||||
) ||
|
||||
exclude.has(submission.problem.index)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user