track-cli 3.1.0 → 4.0.0-rc1
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/esm/_dnt.shims.d.ts +1 -1
- package/esm/_dnt.shims.js +1 -0
- package/esm/src/action/add.js +8 -8
- package/esm/src/action/clone.js +46 -76
- package/esm/src/action/lang.js +11 -16
- package/esm/src/action/pull.js +8 -8
- package/esm/src/action/remove.js +8 -6
- package/esm/src/action/reset.js +9 -10
- package/esm/src/action/run.js +13 -16
- package/esm/src/action/status.js +6 -3
- package/esm/src/meta.d.ts +1 -1
- package/esm/src/meta.js +1 -1
- package/esm/src/shared/config.d.ts +16 -9
- package/esm/src/shared/config.js +0 -3
- package/esm/src/shared/errors.d.ts +1 -1
- package/esm/src/shared/errors.js +4 -3
- package/esm/src/shared/mod.d.ts +10 -10
- package/esm/src/shared/mod.js +73 -93
- package/esm/src/shared/types.d.ts +141 -11
- package/esm/src/shared/types.js +100 -1
- package/esm/src/track/client.d.ts +35 -38
- package/esm/src/track/client.js +31 -122
- package/esm/src/track/test.d.ts +32 -0
- package/esm/src/track/test.js +184 -0
- package/esm/src/track/training.d.ts +29 -0
- package/esm/src/track/training.js +142 -0
- package/package.json +1 -1
- package/esm/src/track/types.d.ts +0 -211
- package/esm/src/track/types.js +0 -40
package/esm/src/shared/mod.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
-
import {
|
|
3
|
-
import { BadTarballURL, ChallengeAlreadyFinished,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { save as saveConfig, } from "./config.js";
|
|
3
|
+
import { BadTarballURL, ChallengeAlreadyFinished, OtherError, } from "./errors.js";
|
|
4
|
+
import { toNativeStyle, untgz } from "./file.js";
|
|
5
|
+
import { ChallengeResultStatus, FileListType, TrackApp, } from "./types.js";
|
|
6
|
+
import { TestClient } from "../track/test.js";
|
|
7
|
+
import { TrainingClient } from "../track/training.js";
|
|
7
8
|
import * as datetime from "../../deps/deno.land/std@0.195.0/datetime/mod.js";
|
|
8
9
|
import * as colors from "../../deps/deno.land/std@0.195.0/fmt/colors.js";
|
|
9
10
|
import { format as duration } from "../../deps/deno.land/std@0.195.0/fmt/duration.js";
|
|
10
11
|
import * as fs from "../../deps/deno.land/std@0.195.0/fs/mod.js";
|
|
12
|
+
import { exists } from "../../deps/deno.land/std@0.195.0/fs/mod.js";
|
|
11
13
|
export const _internals = {
|
|
12
14
|
prompt: dntShim.prompt,
|
|
13
15
|
scoreRatio: scoreRatio,
|
|
@@ -19,6 +21,10 @@ export const HttpStatus = {
|
|
|
19
21
|
const status = typeof r === "number" ? r : r.status;
|
|
20
22
|
return 300 > status && status >= 200;
|
|
21
23
|
},
|
|
24
|
+
isRedirect(r) {
|
|
25
|
+
const status = typeof r === "number" ? r : r.status;
|
|
26
|
+
return 400 > status && status >= 300;
|
|
27
|
+
},
|
|
22
28
|
isClientError(r) {
|
|
23
29
|
const status = typeof r === "number" ? r : r.status;
|
|
24
30
|
return 500 > status && status >= 400;
|
|
@@ -68,30 +74,8 @@ export function printTimeLeft(timeLeftSeconds) {
|
|
|
68
74
|
console.log("The exam deadline has passed");
|
|
69
75
|
}
|
|
70
76
|
}
|
|
71
|
-
export function
|
|
72
|
-
|
|
73
|
-
case ApplicantExamStatus.Submitted:
|
|
74
|
-
case ApplicantExamStatus.Reviewed:
|
|
75
|
-
throw new ExamSubmitted();
|
|
76
|
-
case ApplicantExamStatus.Expired:
|
|
77
|
-
throw new ExamExpired();
|
|
78
|
-
case ApplicantExamStatus.Canceled:
|
|
79
|
-
throw new ExamCanceled();
|
|
80
|
-
case ApplicantExamStatus.Unread:
|
|
81
|
-
throw new ExamUnread(webUrlRedirect);
|
|
82
|
-
case ApplicantExamStatus.InProgress:
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
export function getChallengeFromExamSession(examSession, challengeId) {
|
|
87
|
-
return examSession.challengesSets
|
|
88
|
-
.flatMap((s) => s.challenges)
|
|
89
|
-
.find((c) => c.id === challengeId &&
|
|
90
|
-
(c.style === ChallengeStyle.Development ||
|
|
91
|
-
c.style === ChallengeStyle.Algorithm));
|
|
92
|
-
}
|
|
93
|
-
export async function tryStartingChallenge(challenge, api, applicantExam, resultOpt) {
|
|
94
|
-
let result = resultOpt;
|
|
77
|
+
export async function tryStartingChallenge(api, challenge) {
|
|
78
|
+
let result = challenge.result;
|
|
95
79
|
while (true) {
|
|
96
80
|
if (result) {
|
|
97
81
|
switch (result.status) {
|
|
@@ -109,14 +93,14 @@ export async function tryStartingChallenge(challenge, api, applicantExam, result
|
|
|
109
93
|
throw new OtherError("Aborted. Challenge was not started.");
|
|
110
94
|
}
|
|
111
95
|
if (challenge.programmingLanguages.length > 0) {
|
|
112
|
-
const programmingLanguages = (await api.
|
|
96
|
+
const programmingLanguages = (await api.languages())
|
|
113
97
|
.filter((pl) => challenge.programmingLanguages.includes(pl.value));
|
|
114
98
|
const desiredLanguage = chooseProgrammingLanguage(programmingLanguages);
|
|
115
99
|
if (desiredLanguage.value !== result.programmingLanguage) {
|
|
116
|
-
await api.
|
|
100
|
+
await api.updateLanguage(desiredLanguage.value);
|
|
117
101
|
}
|
|
118
102
|
}
|
|
119
|
-
result = await api.
|
|
103
|
+
result = await api.start();
|
|
120
104
|
break;
|
|
121
105
|
}
|
|
122
106
|
case ChallengeResultStatus.Started:
|
|
@@ -133,13 +117,13 @@ export async function tryStartingChallenge(challenge, api, applicantExam, result
|
|
|
133
117
|
}
|
|
134
118
|
else {
|
|
135
119
|
// Challenge hasn't been prepared yet
|
|
136
|
-
result = await api.
|
|
120
|
+
result = await api.prepare();
|
|
137
121
|
}
|
|
138
122
|
}
|
|
139
123
|
}
|
|
140
124
|
export function chooseProgrammingLanguage(languages) {
|
|
141
125
|
while (true) {
|
|
142
|
-
const desiredLanguageIndex = Prompt.select("Please select a programming language:", languages.map((l) => l.
|
|
126
|
+
const desiredLanguageIndex = Prompt.select("Please select a programming language:", languages.map((l) => l.name));
|
|
143
127
|
if (desiredLanguageIndex !== undefined) {
|
|
144
128
|
return languages[desiredLanguageIndex];
|
|
145
129
|
}
|
|
@@ -244,57 +228,31 @@ export const SnakeCase = {
|
|
|
244
228
|
}
|
|
245
229
|
},
|
|
246
230
|
};
|
|
247
|
-
export async function getCommonChallengeContext() {
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
ensureExamInProgress(applicantExam, webUrl(config));
|
|
252
|
-
const codingContext = await api.getChallengeCodingContext(config.applicantExamId, config.challengeResultId);
|
|
253
|
-
const examSession = await api.getExamSession(config.applicantExamId);
|
|
254
|
-
let result = getResultFromExamSession(examSession, config.challengeId);
|
|
255
|
-
if (!result) {
|
|
256
|
-
throw new OtherError(`Cchallenge result for challenge id ${config.challengeId} is missing - has the challenge been started yet?`);
|
|
257
|
-
}
|
|
258
|
-
const challenge = getChallengeFromExamSession(examSession, config.challengeId);
|
|
259
|
-
if (!challenge) {
|
|
260
|
-
throw new OtherError(`Challenge for challenge id ${config.challengeId} is missing - has the challenge been started yet?`);
|
|
231
|
+
export async function getCommonChallengeContext(config, api) {
|
|
232
|
+
const challenge = await api.startChallengeSession();
|
|
233
|
+
if (!challenge.result) {
|
|
234
|
+
throw new OtherError(`Challenge for challenge id ${challenge.challengeId} is missing - has the challenge been started yet?`);
|
|
261
235
|
}
|
|
236
|
+
const result = challenge.result;
|
|
262
237
|
switch (result.status) {
|
|
263
238
|
case ChallengeResultStatus.Finished:
|
|
264
239
|
case ChallengeResultStatus.NotModified:
|
|
265
240
|
throw new ChallengeAlreadyFinished();
|
|
266
|
-
case ChallengeResultStatus.CanRestart:
|
|
241
|
+
case ChallengeResultStatus.CanRestart: {
|
|
267
242
|
console.log("This challenge can be restarted");
|
|
268
243
|
console.log();
|
|
269
|
-
|
|
270
|
-
if (!
|
|
244
|
+
const result2 = await tryStartingChallenge(api, challenge);
|
|
245
|
+
if (!result2) {
|
|
271
246
|
throw new OtherError("Expected to have a challenge result after preparing and starting");
|
|
272
247
|
}
|
|
273
248
|
break;
|
|
249
|
+
}
|
|
274
250
|
}
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (api.authCookieExpired()) {
|
|
281
|
-
config.cookies = api.getCookies();
|
|
282
|
-
await saveConfig(config);
|
|
283
|
-
}
|
|
284
|
-
return {
|
|
285
|
-
config,
|
|
286
|
-
api,
|
|
287
|
-
codingContext,
|
|
288
|
-
applicantExam,
|
|
289
|
-
challengeResult: result,
|
|
290
|
-
challenge,
|
|
291
|
-
challengePosition,
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
function getResultFromExamSession(examSession, challengeId) {
|
|
295
|
-
return examSession
|
|
296
|
-
.results
|
|
297
|
-
.find((cr) => cr.challengeId === challengeId);
|
|
251
|
+
const codingContext = await api.context();
|
|
252
|
+
// TODO: 保存条件
|
|
253
|
+
config.cookies = api.getCookies();
|
|
254
|
+
await saveConfig(config);
|
|
255
|
+
return [codingContext, challenge];
|
|
298
256
|
}
|
|
299
257
|
function scoreRatio(editorScore, openTestcases) {
|
|
300
258
|
// @ts-expect-error: isFinite(undefined) === false, isFinite(0) === true. See MDN
|
|
@@ -305,16 +263,17 @@ function scoreRatio(editorScore, openTestcases) {
|
|
|
305
263
|
return "\tN/A - Use 'track run' to save and run your submission";
|
|
306
264
|
}
|
|
307
265
|
}
|
|
308
|
-
export async function printChallengeInfo(
|
|
309
|
-
const { config, api, challengeResult: result } = trackContext;
|
|
310
|
-
const
|
|
311
|
-
|
|
266
|
+
export async function printChallengeInfo(api, codingContext, challenge) {
|
|
267
|
+
// const { config, api, challengeResult: result } = trackContext;
|
|
268
|
+
const result = challenge.result;
|
|
269
|
+
const timeLeft = await api.timeLeft();
|
|
270
|
+
printWorkingFileSet(codingContext);
|
|
312
271
|
console.log();
|
|
313
272
|
const lastScoredAt = result.lastScoredAt
|
|
314
273
|
? ` (Ran at ${datetime.format(new Date(result.lastScoredAt), "yyyy-MM-dd HH:mm:ss")})`
|
|
315
274
|
: "";
|
|
316
275
|
console.log(`Latest score${lastScoredAt}`);
|
|
317
|
-
console.log(colors.green(scoreRatio(result.editorScore,
|
|
276
|
+
console.log(colors.green(scoreRatio(result.editorScore, challenge.openTestcases)));
|
|
318
277
|
console.log();
|
|
319
278
|
printTimeLeft(timeLeft);
|
|
320
279
|
}
|
|
@@ -338,7 +297,7 @@ export function printWorkingFileSet(codingContext) {
|
|
|
338
297
|
console.log();
|
|
339
298
|
}
|
|
340
299
|
}
|
|
341
|
-
export async function downloadChallengeFilesTo(codingContext, dest, api,
|
|
300
|
+
export async function downloadChallengeFilesTo(codingContext, dest, api, showFileDiff, includeTarball) {
|
|
342
301
|
if (includeTarball) {
|
|
343
302
|
console.log("Fetching challenge tarball...");
|
|
344
303
|
const tarballResp = await dntShim.fetch(codingContext.tarballUrl);
|
|
@@ -350,7 +309,7 @@ export async function downloadChallengeFilesTo(codingContext, dest, api, applica
|
|
|
350
309
|
}
|
|
351
310
|
console.log("Downloading editable files");
|
|
352
311
|
const filesToDownload = listFileNames(codingContext, FileListType.Editable);
|
|
353
|
-
const presignedUrls = Object.entries(await api.
|
|
312
|
+
const presignedUrls = Object.entries(await api.presigned(filesToDownload))
|
|
354
313
|
.map(([file, url]) => ({ file, url }));
|
|
355
314
|
presignedUrls.sort((a, b) => a.file.localeCompare(b.file));
|
|
356
315
|
const promises = presignedUrls.map(async ({ file, url }) => {
|
|
@@ -370,21 +329,42 @@ export async function downloadChallengeFilesTo(codingContext, dest, api, applica
|
|
|
370
329
|
export /**
|
|
371
330
|
* Archives all files in a challenge directory into a subdirectory of
|
|
372
331
|
* the challenge directory. Ignores .track/ and other existing archival directories
|
|
373
|
-
*/ async function archiveExistingChallengeFiles(orgName
|
|
374
|
-
const challengeDir =
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
332
|
+
*/ async function archiveExistingChallengeFiles(orgName) {
|
|
333
|
+
const challengeDir = dntShim.Deno.cwd();
|
|
334
|
+
const base = `${orgName}_challenge_old`;
|
|
335
|
+
let archiveDir;
|
|
336
|
+
let i = 1;
|
|
337
|
+
while (true) {
|
|
338
|
+
archiveDir = `${base}_${i}`;
|
|
339
|
+
const pathExists = await exists(archiveDir);
|
|
340
|
+
if (pathExists) {
|
|
341
|
+
i++;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
console.log(`Your old challenge contents will be archived in ${archiveDir}`);
|
|
347
|
+
await fs.ensureDir(archiveDir);
|
|
348
|
+
const oldArchivePattern = /.*_challenge_old_\d+/;
|
|
383
349
|
for await (const dirEntry of dntShim.Deno.readDir(challengeDir)) {
|
|
384
|
-
if (dirEntry.name ===
|
|
350
|
+
if (dirEntry.name === archiveDir || dirEntry.name === ".track" ||
|
|
385
351
|
(dirEntry.isDirectory && oldArchivePattern.test(dirEntry.name))) {
|
|
386
352
|
continue;
|
|
387
353
|
}
|
|
388
|
-
await fs.move(`${challengeDir}/${dirEntry.name}`, `${
|
|
354
|
+
await fs.move(`${challengeDir}/${dirEntry.name}`, `${archiveDir}/${dirEntry.name}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
export function trackClientFromConfig(config) {
|
|
358
|
+
let api;
|
|
359
|
+
switch (config.app) {
|
|
360
|
+
case TrackApp.Test:
|
|
361
|
+
api = new TestClient(config.baseUrl, config.orgName, config.token, config.challengeId, config.basicAuth, config.cookies);
|
|
362
|
+
break;
|
|
363
|
+
case TrackApp.Training:
|
|
364
|
+
api = new TrainingClient(config.baseUrl, config.orgName, config.token, config.courseMaterialId, config.klassId, config.klassResultId, config.basicAuth, config.cookies);
|
|
365
|
+
break;
|
|
366
|
+
default:
|
|
367
|
+
throw new OtherError(`Unsupported app: ${config.app}`);
|
|
389
368
|
}
|
|
369
|
+
return api;
|
|
390
370
|
}
|
|
@@ -1,12 +1,142 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
export declare const TrackApp: {
|
|
2
|
+
readonly Test: 1;
|
|
3
|
+
readonly Training: 2;
|
|
4
|
+
};
|
|
5
|
+
export type TrackApp = typeof TrackApp[keyof typeof TrackApp];
|
|
6
|
+
export declare class CloneUrl {
|
|
7
|
+
app: TrackApp;
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
orgName: string;
|
|
10
|
+
token: string;
|
|
11
|
+
id: number;
|
|
12
|
+
constructor(url: string);
|
|
13
|
+
toString(): string;
|
|
12
14
|
}
|
|
15
|
+
export interface ProgrammingLanguageInfo {
|
|
16
|
+
value: ProgrammingLanguage;
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ChallengeSession {
|
|
20
|
+
challengeId: number;
|
|
21
|
+
challengeVersionId: number;
|
|
22
|
+
style: ChallengeStyle;
|
|
23
|
+
title: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
timeLimitMinutes?: number;
|
|
26
|
+
displayOrder: number;
|
|
27
|
+
programmingLanguages: ProgrammingLanguage[];
|
|
28
|
+
localCodingAllowed: boolean;
|
|
29
|
+
openTestcases?: number;
|
|
30
|
+
result?: ChallengeResult;
|
|
31
|
+
}
|
|
32
|
+
export interface ChallengeResult {
|
|
33
|
+
challengeId: number;
|
|
34
|
+
challengeVersionId: number;
|
|
35
|
+
dueDate?: string;
|
|
36
|
+
storageId: string;
|
|
37
|
+
status: ChallengeResultStatus;
|
|
38
|
+
openedAt?: string;
|
|
39
|
+
finishedAt?: string;
|
|
40
|
+
totalTestcases?: number;
|
|
41
|
+
successfulTestcases?: number;
|
|
42
|
+
editorScore?: number;
|
|
43
|
+
testOutput?: string;
|
|
44
|
+
lastScoredAt?: string;
|
|
45
|
+
programmingLanguage?: ProgrammingLanguage;
|
|
46
|
+
}
|
|
47
|
+
export interface CodingContext {
|
|
48
|
+
challengeId: number;
|
|
49
|
+
challengeVersionId: number;
|
|
50
|
+
challengeResultId: number;
|
|
51
|
+
title: string;
|
|
52
|
+
urlForPresign: string;
|
|
53
|
+
urlForOrcaToken?: string;
|
|
54
|
+
methodForPresign: string;
|
|
55
|
+
urlForSave?: string;
|
|
56
|
+
tarballUrl: string;
|
|
57
|
+
tarballUrls: string[];
|
|
58
|
+
allowSave: boolean;
|
|
59
|
+
allowEdit?: boolean;
|
|
60
|
+
programmingLanguages: ProgrammingLanguage[];
|
|
61
|
+
selectedLanguage?: ProgrammingLanguage;
|
|
62
|
+
challengeLanguage: SpokenLanguage;
|
|
63
|
+
settings: ChallengeSettings;
|
|
64
|
+
answers: CodingAnswers;
|
|
65
|
+
orcaHost?: string;
|
|
66
|
+
}
|
|
67
|
+
export declare const FileListType: {
|
|
68
|
+
readonly All: 0;
|
|
69
|
+
readonly ReadOnlyChallengeFiles: 1;
|
|
70
|
+
readonly EditableChallengeFiles: 2;
|
|
71
|
+
readonly UserAddedFiles: 3;
|
|
72
|
+
readonly Editable: 4;
|
|
73
|
+
};
|
|
74
|
+
export type FileListType = (typeof FileListType)[keyof typeof FileListType];
|
|
75
|
+
export interface ChallengeSettings {
|
|
76
|
+
files: FileTree;
|
|
77
|
+
hiddenFiles: string[];
|
|
78
|
+
testExcludes: string[];
|
|
79
|
+
images: string[];
|
|
80
|
+
build: string[];
|
|
81
|
+
test: string;
|
|
82
|
+
mainFile?: string;
|
|
83
|
+
allowNewFile: boolean;
|
|
84
|
+
languages: string[];
|
|
85
|
+
sample?: string;
|
|
86
|
+
envConfig?: EnvSettings;
|
|
87
|
+
initialize: string[];
|
|
88
|
+
}
|
|
89
|
+
export type FileTree = {
|
|
90
|
+
[key: string]: FileTree | boolean;
|
|
91
|
+
};
|
|
92
|
+
export interface EnvSettings {
|
|
93
|
+
imageName: string;
|
|
94
|
+
workingDir?: string;
|
|
95
|
+
baseDir?: string;
|
|
96
|
+
username?: string;
|
|
97
|
+
variables?: Record<string, string>;
|
|
98
|
+
cacheDirs: string[];
|
|
99
|
+
}
|
|
100
|
+
export declare const EnvSettings: {
|
|
101
|
+
default: EnvSettings;
|
|
102
|
+
};
|
|
103
|
+
export interface TestCases {
|
|
104
|
+
open: number;
|
|
105
|
+
secret: number;
|
|
106
|
+
}
|
|
107
|
+
export interface CodingAnswers {
|
|
108
|
+
build?: string[];
|
|
109
|
+
appCommand?: string;
|
|
110
|
+
mainFile?: string;
|
|
111
|
+
addedFiles?: string[];
|
|
112
|
+
fileVersions: object;
|
|
113
|
+
envConfig?: EnvSettings;
|
|
114
|
+
}
|
|
115
|
+
export interface SaveFilesRequest {
|
|
116
|
+
versionId: number;
|
|
117
|
+
addedFiles: string[];
|
|
118
|
+
updatedFiles: Record<string, string>;
|
|
119
|
+
}
|
|
120
|
+
export declare const ChallengeStyle: {
|
|
121
|
+
readonly Quiz: 1;
|
|
122
|
+
readonly Development: 2;
|
|
123
|
+
readonly Algorithm: 3;
|
|
124
|
+
readonly Ai: 4;
|
|
125
|
+
};
|
|
126
|
+
export type ChallengeStyle = (typeof ChallengeStyle)[keyof typeof ChallengeStyle];
|
|
127
|
+
export declare const ChallengeResultStatus: {
|
|
128
|
+
readonly Prepared: 0;
|
|
129
|
+
readonly Started: 1;
|
|
130
|
+
readonly InProgress: 2;
|
|
131
|
+
readonly Finished: 3;
|
|
132
|
+
readonly NotModified: 4;
|
|
133
|
+
readonly CanRestart: 5;
|
|
134
|
+
readonly ScoringWaiting: 6;
|
|
135
|
+
};
|
|
136
|
+
export type ChallengeResultStatus = (typeof ChallengeResultStatus)[keyof typeof ChallengeResultStatus];
|
|
137
|
+
export declare const SpokenLanguage: {
|
|
138
|
+
readonly English: "en";
|
|
139
|
+
readonly Japanese: "ja";
|
|
140
|
+
};
|
|
141
|
+
export type SpokenLanguage = (typeof SpokenLanguage)[keyof typeof SpokenLanguage];
|
|
142
|
+
export type ProgrammingLanguage = number;
|
package/esm/src/shared/types.js
CHANGED
|
@@ -1 +1,100 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export const TrackApp = {
|
|
2
|
+
Test: 1,
|
|
3
|
+
Training: 2,
|
|
4
|
+
};
|
|
5
|
+
const TRACK_TEST_URL_PATTERN = new RegExp("([^/]+)/exams/([^/]+)/challenges/(\\d+)");
|
|
6
|
+
const TRACK_TRAINING_URL_PATTERN = new RegExp("([^/]+)/train/([^/]+)/challenges/(\\d+)");
|
|
7
|
+
export class CloneUrl {
|
|
8
|
+
constructor(url) {
|
|
9
|
+
Object.defineProperty(this, "app", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value: void 0
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(this, "baseUrl", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: void 0
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "orgName", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: void 0
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(this, "token", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: void 0
|
|
32
|
+
});
|
|
33
|
+
Object.defineProperty(this, "id", {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: true,
|
|
37
|
+
value: void 0
|
|
38
|
+
});
|
|
39
|
+
const urlObj = new URL(url);
|
|
40
|
+
const scheme = urlObj.protocol.slice(0, -1);
|
|
41
|
+
const host = urlObj.host + (urlObj.port ? ":" + urlObj.port : "");
|
|
42
|
+
this.baseUrl = `${scheme}://${host}`;
|
|
43
|
+
let caps;
|
|
44
|
+
if ((caps = TRACK_TEST_URL_PATTERN.exec(url))) {
|
|
45
|
+
this.app = TrackApp.Test;
|
|
46
|
+
this.orgName = caps[1];
|
|
47
|
+
this.token = caps[2];
|
|
48
|
+
this.id = parseInt(caps[3]);
|
|
49
|
+
}
|
|
50
|
+
else if ((caps = TRACK_TRAINING_URL_PATTERN.exec(url))) {
|
|
51
|
+
this.app = TrackApp.Training;
|
|
52
|
+
this.orgName = caps[1];
|
|
53
|
+
this.token = caps[2];
|
|
54
|
+
this.id = parseInt(caps[3]);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
toString() {
|
|
61
|
+
switch (this.app) {
|
|
62
|
+
case TrackApp.Test:
|
|
63
|
+
return `${this.baseUrl}/${this.orgName}/exams/${this.token}/challenges/${this.id}`;
|
|
64
|
+
case TrackApp.Training:
|
|
65
|
+
return `${this.baseUrl}/${this.orgName}/train/${this.token}/challenges/${this.id}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export const FileListType = {
|
|
70
|
+
All: 0,
|
|
71
|
+
ReadOnlyChallengeFiles: 1,
|
|
72
|
+
EditableChallengeFiles: 2,
|
|
73
|
+
UserAddedFiles: 3,
|
|
74
|
+
Editable: 4,
|
|
75
|
+
};
|
|
76
|
+
export const EnvSettings = {
|
|
77
|
+
default: {
|
|
78
|
+
imageName: "givery/codecheck:latest",
|
|
79
|
+
cacheDirs: [],
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
export const ChallengeStyle = {
|
|
83
|
+
Quiz: 1,
|
|
84
|
+
Development: 2,
|
|
85
|
+
Algorithm: 3,
|
|
86
|
+
Ai: 4,
|
|
87
|
+
};
|
|
88
|
+
export const ChallengeResultStatus = {
|
|
89
|
+
Prepared: 0,
|
|
90
|
+
Started: 1,
|
|
91
|
+
InProgress: 2,
|
|
92
|
+
Finished: 3,
|
|
93
|
+
NotModified: 4,
|
|
94
|
+
CanRestart: 5,
|
|
95
|
+
ScoringWaiting: 6,
|
|
96
|
+
};
|
|
97
|
+
export const SpokenLanguage = {
|
|
98
|
+
English: "en",
|
|
99
|
+
Japanese: "ja",
|
|
100
|
+
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { TrackTestConfigPart, TrackTrainingConfigPart } from "../shared/config.js";
|
|
3
|
+
import { ChallengeResult, ChallengeSession, CodingContext, ProgrammingLanguage, ProgrammingLanguageInfo, SaveFilesRequest } from "../shared/types.js";
|
|
4
|
+
import { CookieJar } from "node-fetch-cookies";
|
|
5
|
+
import { ProxyAgent } from "proxy-agent";
|
|
3
6
|
export interface BasicAuth {
|
|
4
7
|
username: string;
|
|
5
8
|
password?: string;
|
|
6
9
|
}
|
|
7
|
-
export interface AuthData {
|
|
8
|
-
examToken: string;
|
|
9
|
-
orgName: string;
|
|
10
|
-
}
|
|
11
10
|
export interface ApiResponse<T> {
|
|
12
11
|
code: number;
|
|
13
12
|
message?: string[];
|
|
@@ -18,43 +17,41 @@ export declare const FormType: {
|
|
|
18
17
|
JSON: string;
|
|
19
18
|
};
|
|
20
19
|
export type FormType = typeof FormType[keyof typeof FormType];
|
|
21
|
-
export declare class
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
constructor(
|
|
20
|
+
export declare abstract class TrackClientBase {
|
|
21
|
+
protected baseUrl: string;
|
|
22
|
+
protected headers: Record<string, string>;
|
|
23
|
+
protected cookies: CookieJar;
|
|
24
|
+
protected agent: ProxyAgent;
|
|
25
|
+
private isUnauthorized;
|
|
26
|
+
abstract authenticate(): Promise<void>;
|
|
27
|
+
constructor(baseUrl: string, basic?: BasicAuth, cookies?: Record<string, string>, isUnauthorized?: (res: dntShim.Response) => boolean);
|
|
28
|
+
protected _get<T>(path: string, asText?: boolean): Promise<T>;
|
|
29
|
+
protected _post<T, F>(path: string, formType: FormType, body: F): Promise<T>;
|
|
30
|
+
protected _put<T, F>(path: string, formType: FormType, body: F): Promise<T>;
|
|
31
|
+
private _result;
|
|
29
32
|
getCookies(): {
|
|
30
33
|
[name: string]: string;
|
|
31
34
|
};
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
}
|
|
36
|
+
export interface TrackClient {
|
|
37
|
+
config(): TrackTestConfigPart | TrackTrainingConfigPart;
|
|
34
38
|
authenticate(): Promise<void>;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
timeLeft(applicantExamId: number, resultId: number): Promise<number>;
|
|
47
|
-
getChallengeCodingContext(applicantExamId: number, resultId: number): Promise<CodingContext>;
|
|
48
|
-
getPresignedUrls(applicantExamId: number, resultId: number, files: string[]): Promise<{
|
|
39
|
+
getCookies(): {
|
|
40
|
+
[name: string]: string;
|
|
41
|
+
};
|
|
42
|
+
startChallengeSession(): Promise<ChallengeSession>;
|
|
43
|
+
languages(): Promise<ProgrammingLanguageInfo[]>;
|
|
44
|
+
start(): Promise<ChallengeResult>;
|
|
45
|
+
prepare(): Promise<ChallengeResult>;
|
|
46
|
+
updateLanguage(language: ProgrammingLanguage): Promise<void>;
|
|
47
|
+
timeLeft(): Promise<number>;
|
|
48
|
+
context(): Promise<CodingContext>;
|
|
49
|
+
presigned(files: string[]): Promise<{
|
|
49
50
|
[name: string]: string;
|
|
50
51
|
}>;
|
|
51
|
-
orcaToken(url: string): Promise<string>;
|
|
52
|
-
/**
|
|
53
|
-
* Temporary code to retrieve the Orca host.
|
|
54
|
-
* Will be able to get from CodingContext in the future.
|
|
55
|
-
*/
|
|
56
52
|
orcaHost(): Promise<string>;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
orcaToken(context: CodingContext): Promise<string>;
|
|
54
|
+
saveFiles(files: SaveFilesRequest): Promise<void>;
|
|
55
|
+
updateEditorScore(score: number): Promise<void>;
|
|
56
|
+
reset(): Promise<void>;
|
|
60
57
|
}
|