@jutge.org/toolkit 4.0.5 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/prompts/ask/ask.md +11 -0
- package/dist/index.js +431 -435
- package/docs/getting-started-guide.md +515 -0
- package/docs/jutge-ai.md +82 -0
- package/docs/windows.md +114 -0
- package/package.json +3 -1
- package/toolkit/about.ts +40 -0
- package/toolkit/ai.ts +56 -0
- package/toolkit/ask.ts +45 -0
- package/toolkit/clean.ts +25 -0
- package/toolkit/clone.ts +12 -0
- package/toolkit/compilers.ts +29 -0
- package/toolkit/config.ts +113 -0
- package/toolkit/create.ts +36 -0
- package/toolkit/doctor.ts +22 -0
- package/toolkit/generate.ts +213 -0
- package/toolkit/index.ts +63 -0
- package/toolkit/make.ts +92 -0
- package/toolkit/quiz.ts +44 -0
- package/toolkit/upgrade.ts +9 -0
- package/toolkit/upload.ts +20 -0
- package/toolkit/verify.ts +15 -0
package/toolkit/make.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import { join, resolve } from 'path'
|
|
3
|
+
import { findRealDirectories } from '../lib/helpers'
|
|
4
|
+
import { newMaker } from '../lib/maker'
|
|
5
|
+
import tui from '../lib/tui'
|
|
6
|
+
import { nothing, projectDir } from '../lib/utils'
|
|
7
|
+
|
|
8
|
+
export const makeCmd = new Command('make')
|
|
9
|
+
.description('Make problem elements')
|
|
10
|
+
|
|
11
|
+
.argument('[tasks...]', 'tasks to make: all|info|exe|cor|pdf|txt|md|html', ['all'])
|
|
12
|
+
.option('-d, --directories <directories...>', 'problem directories', ['.'])
|
|
13
|
+
.option('-i, --ignore-errors', 'ignore errors on a directory and continue processing', false)
|
|
14
|
+
.option('-e, --only-errors', 'only show errors at the final summary', false)
|
|
15
|
+
|
|
16
|
+
.action(async (tasks, { directories, ignoreErrors, onlyErrors }) => {
|
|
17
|
+
if (tasks.length === 0) {
|
|
18
|
+
tasks = ['all']
|
|
19
|
+
}
|
|
20
|
+
if (tasks.includes('all') && tasks.length > 1) {
|
|
21
|
+
throw new Error("When 'all' is specified, no other tasks should be provided")
|
|
22
|
+
}
|
|
23
|
+
if (!tasks.every((t) => ['all', 'info', 'exe', 'cor', 'pdf', 'txt', 'md', 'html'].includes(t))) {
|
|
24
|
+
throw new Error('Tasks must be one of: all, info, exe, cor, pdf, txt, md, html')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log()
|
|
28
|
+
await tui.image(join(projectDir(), 'assets', 'images', 'jutge-toolkit.png'), 8, 4)
|
|
29
|
+
|
|
30
|
+
const errors: Record<string, string> = {} // directory -> error message
|
|
31
|
+
|
|
32
|
+
const realDirectories = await findRealDirectories(directories)
|
|
33
|
+
|
|
34
|
+
for (const directory of realDirectories) {
|
|
35
|
+
try {
|
|
36
|
+
tui.title(`Making problem in directory ${tui.hyperlink(directory, resolve(directory))}`)
|
|
37
|
+
|
|
38
|
+
const maker = await newMaker(directory)
|
|
39
|
+
|
|
40
|
+
// If tasks include 'all', run makeProblem
|
|
41
|
+
if (tasks.includes('all')) {
|
|
42
|
+
await maker.makeProblem()
|
|
43
|
+
} else {
|
|
44
|
+
// Run specific tasks
|
|
45
|
+
if (tasks.includes('info')) {
|
|
46
|
+
// already done in maker initialization
|
|
47
|
+
}
|
|
48
|
+
if (tasks.includes('exe')) {
|
|
49
|
+
await maker.makeExecutables()
|
|
50
|
+
}
|
|
51
|
+
if (tasks.includes('cor')) {
|
|
52
|
+
await maker.makeCorrects()
|
|
53
|
+
}
|
|
54
|
+
if (tasks.includes('pdf')) {
|
|
55
|
+
await maker.makePdfStatements()
|
|
56
|
+
}
|
|
57
|
+
if (tasks.includes('txt') || tasks.includes('html') || tasks.includes('md')) {
|
|
58
|
+
await maker.makeTextualStatements(
|
|
59
|
+
tasks.filter((t) => ['txt', 'html', 'md'].includes(t)) as Array<'txt' | 'html' | 'md'>,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
65
|
+
|
|
66
|
+
if (ignoreErrors) {
|
|
67
|
+
errors[directory] = errorMessage
|
|
68
|
+
tui.error(`Error: ${errorMessage}`)
|
|
69
|
+
} else {
|
|
70
|
+
throw error
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
console.log()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
tui.title('Summary')
|
|
77
|
+
await tui.section('', async () => {
|
|
78
|
+
await nothing()
|
|
79
|
+
if (realDirectories.length === 0) {
|
|
80
|
+
tui.warning('No problem directories found')
|
|
81
|
+
}
|
|
82
|
+
for (const directory of realDirectories) {
|
|
83
|
+
if (errors[directory]) {
|
|
84
|
+
tui.directory(directory)
|
|
85
|
+
tui.error(` ${errors[directory]}`)
|
|
86
|
+
} else if (!onlyErrors) {
|
|
87
|
+
tui.directory(directory)
|
|
88
|
+
tui.success(` No errors found`)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
})
|
package/toolkit/quiz.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import { makeQuiz, validateQuiz } from '../lib/quiz'
|
|
3
|
+
import tui from '../lib/tui'
|
|
4
|
+
import { findRealDirectories } from '../lib/helpers'
|
|
5
|
+
import { random } from 'radash'
|
|
6
|
+
|
|
7
|
+
export const quizCmd = new Command('quiz')
|
|
8
|
+
.description('Commands related to quizzes')
|
|
9
|
+
|
|
10
|
+
.action(() => {
|
|
11
|
+
quizCmd.help()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
quizCmd
|
|
15
|
+
.command('validate')
|
|
16
|
+
.description('Validate a quiz problem')
|
|
17
|
+
|
|
18
|
+
.option('-d, --directories <directories...>', 'problem directories', ['.'])
|
|
19
|
+
|
|
20
|
+
.action(async ({ directories }) => {
|
|
21
|
+
const realDirectories = await findRealDirectories(directories)
|
|
22
|
+
for (const directory of realDirectories) {
|
|
23
|
+
await tui.section(`Validating quiz in directory ${tui.hyperlink(directory)}`, async () => {
|
|
24
|
+
await validateQuiz(directory)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
quizCmd
|
|
30
|
+
.command('make')
|
|
31
|
+
.description('Make a quiz problem')
|
|
32
|
+
|
|
33
|
+
.option('-d, --directory <directory>', 'problem directory', '.')
|
|
34
|
+
.option('-s, --seed <seed>', 'random seed')
|
|
35
|
+
|
|
36
|
+
.action(async ({ directory, seed }) => {
|
|
37
|
+
tui.warning('The quiz make command is work-in-progress and may not work as expected yet.')
|
|
38
|
+
const realDirectories = await findRealDirectories([directory])
|
|
39
|
+
const seedValue = seed ? parseInt(seed, 10) : random(1000000, 9999999)
|
|
40
|
+
for (const directory of realDirectories) {
|
|
41
|
+
await makeQuiz(directory, seedValue)
|
|
42
|
+
return // in this case we only process one directory as quizzes should only have one language
|
|
43
|
+
}
|
|
44
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import { uploadProblem } from '../lib/upload'
|
|
3
|
+
import { newProblem } from '../lib/problem'
|
|
4
|
+
|
|
5
|
+
export const uploadCmd = new Command('upload')
|
|
6
|
+
.summary('Upload problem to Jutge.org')
|
|
7
|
+
.description(
|
|
8
|
+
`Upload problem to Jutge.org
|
|
9
|
+
|
|
10
|
+
If problem.yml exists, the problem will be updated at Jutge.org using that information (which includes its problem id).
|
|
11
|
+
If problem.yml does not exist, a new problem will be created at Jutge.org and problem.yml will be generated.
|
|
12
|
+
`,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
.option('-d, --directory <directory>', 'problem directory', '.')
|
|
16
|
+
|
|
17
|
+
.action(async ({ directory }) => {
|
|
18
|
+
const problem = await newProblem(directory)
|
|
19
|
+
await uploadProblem(problem)
|
|
20
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import { newMaker } from '../lib/maker'
|
|
3
|
+
|
|
4
|
+
export const verifyCmd = new Command('verify')
|
|
5
|
+
.description('Verify programs against golden solution')
|
|
6
|
+
|
|
7
|
+
.argument('<programs...>', 'source programs to verify')
|
|
8
|
+
.option('-d, --directory <path>', 'problem directory', '.')
|
|
9
|
+
|
|
10
|
+
.action(async (programs, { directory }) => {
|
|
11
|
+
const maker = await newMaker(directory)
|
|
12
|
+
for (const program of programs) {
|
|
13
|
+
await maker.verifyCandidate(program)
|
|
14
|
+
}
|
|
15
|
+
})
|