Archived
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d394ffc18
|
||
|
|
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,54 @@
|
||||
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
|
||||
uses: actions/checkout@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
run: |
|
||||
case "$NODE_VERSION" in
|
||||
18*) major=18;; 20*) major=20;; 22*) major=22;; *) exit 1;;
|
||||
esac
|
||||
test -x "/opt/ci/node/$major/bin/node"
|
||||
echo "/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