course-format-ts 1.0.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/.idea/edu-format-ts.iml +10 -0
- package/.idea/inspectionProfiles/Project_Default.xml +28 -0
- package/.idea/misc.xml +4 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/AGENTS.md +1 -0
- package/dist/courseFormat/AnswerPlaceholder.d.ts +37 -0
- package/dist/courseFormat/AnswerPlaceholder.js +101 -0
- package/dist/courseFormat/AnswerPlaceholderComparator.d.ts +4 -0
- package/dist/courseFormat/AnswerPlaceholderComparator.js +8 -0
- package/dist/courseFormat/AnswerPlaceholderDependency.d.ts +19 -0
- package/dist/courseFormat/AnswerPlaceholderDependency.js +91 -0
- package/dist/courseFormat/CheckFeedback.d.ts +20 -0
- package/dist/courseFormat/CheckFeedback.js +58 -0
- package/dist/courseFormat/CheckResult.d.ts +33 -0
- package/dist/courseFormat/CheckResult.js +58 -0
- package/dist/courseFormat/CheckResultSeverity.d.ts +7 -0
- package/dist/courseFormat/CheckResultSeverity.js +17 -0
- package/dist/courseFormat/CheckStatus.d.ts +5 -0
- package/dist/courseFormat/CheckStatus.js +9 -0
- package/dist/courseFormat/Course.d.ts +68 -0
- package/dist/courseFormat/Course.js +165 -0
- package/dist/courseFormat/CourseMode.d.ts +4 -0
- package/dist/courseFormat/CourseMode.js +8 -0
- package/dist/courseFormat/CourseVisibility.d.ts +4 -0
- package/dist/courseFormat/CourseVisibility.js +8 -0
- package/dist/courseFormat/CourseraCourse.d.ts +5 -0
- package/dist/courseFormat/CourseraCourse.js +15 -0
- package/dist/courseFormat/DescriptionFormat.d.ts +5 -0
- package/dist/courseFormat/DescriptionFormat.js +9 -0
- package/dist/courseFormat/EduCourse.d.ts +17 -0
- package/dist/courseFormat/EduCourse.js +42 -0
- package/dist/courseFormat/EduFile.d.ts +22 -0
- package/dist/courseFormat/EduFile.js +110 -0
- package/dist/courseFormat/EduFileErrorHighlightLevel.d.ts +5 -0
- package/dist/courseFormat/EduFileErrorHighlightLevel.js +9 -0
- package/dist/courseFormat/EduFormatNames.d.ts +75 -0
- package/dist/courseFormat/EduFormatNames.js +80 -0
- package/dist/courseFormat/EduTestInfo.d.ts +28 -0
- package/dist/courseFormat/EduTestInfo.js +68 -0
- package/dist/courseFormat/EduVersions.d.ts +2 -0
- package/dist/courseFormat/EduVersions.js +5 -0
- package/dist/courseFormat/FileContents.d.ts +36 -0
- package/dist/courseFormat/FileContents.js +67 -0
- package/dist/courseFormat/FileContentsFactory.d.ts +17 -0
- package/dist/courseFormat/FileContentsFactory.js +2 -0
- package/dist/courseFormat/FrameworkLesson.d.ts +9 -0
- package/dist/courseFormat/FrameworkLesson.js +28 -0
- package/dist/courseFormat/ItemContainer.d.ts +13 -0
- package/dist/courseFormat/ItemContainer.js +45 -0
- package/dist/courseFormat/JBAccountUserInfo.d.ts +9 -0
- package/dist/courseFormat/JBAccountUserInfo.js +20 -0
- package/dist/courseFormat/Language.d.ts +4 -0
- package/dist/courseFormat/Language.js +33 -0
- package/dist/courseFormat/Lesson.d.ts +20 -0
- package/dist/courseFormat/Lesson.js +56 -0
- package/dist/courseFormat/LessonContainer.d.ts +16 -0
- package/dist/courseFormat/LessonContainer.js +54 -0
- package/dist/courseFormat/PluginInfo.d.ts +7 -0
- package/dist/courseFormat/PluginInfo.js +15 -0
- package/dist/courseFormat/Section.d.ts +8 -0
- package/dist/courseFormat/Section.js +27 -0
- package/dist/courseFormat/StudyItem.d.ts +20 -0
- package/dist/courseFormat/StudyItem.js +47 -0
- package/dist/courseFormat/Tags.d.ts +16 -0
- package/dist/courseFormat/Tags.js +42 -0
- package/dist/courseFormat/TaskFile.d.ts +26 -0
- package/dist/courseFormat/TaskFile.js +72 -0
- package/dist/courseFormat/UserInfo.d.ts +3 -0
- package/dist/courseFormat/UserInfo.js +2 -0
- package/dist/courseFormat/Vendor.d.ts +7 -0
- package/dist/courseFormat/Vendor.js +14 -0
- package/dist/courseFormat/attempts/Attempt.d.ts +12 -0
- package/dist/courseFormat/attempts/Attempt.js +25 -0
- package/dist/courseFormat/attempts/AttemptBase.d.ts +9 -0
- package/dist/courseFormat/attempts/AttemptBase.js +28 -0
- package/dist/courseFormat/attempts/DataTaskAttempt.d.ts +6 -0
- package/dist/courseFormat/attempts/DataTaskAttempt.js +24 -0
- package/dist/courseFormat/attempts/Dataset.d.ts +12 -0
- package/dist/courseFormat/attempts/Dataset.js +21 -0
- package/dist/courseFormat/fileUtils.d.ts +5 -0
- package/dist/courseFormat/fileUtils.js +32 -0
- package/dist/courseFormat/hyperskill/HyperskillCourse.d.ts +13 -0
- package/dist/courseFormat/hyperskill/HyperskillCourse.js +25 -0
- package/dist/courseFormat/hyperskill/HyperskillProject.d.ts +10 -0
- package/dist/courseFormat/hyperskill/HyperskillProject.js +16 -0
- package/dist/courseFormat/hyperskill/HyperskillStage.d.ts +8 -0
- package/dist/courseFormat/hyperskill/HyperskillStage.js +20 -0
- package/dist/courseFormat/hyperskill/HyperskillTaskType.d.ts +4 -0
- package/dist/courseFormat/hyperskill/HyperskillTaskType.js +26 -0
- package/dist/courseFormat/hyperskill/HyperskillTopic.d.ts +5 -0
- package/dist/courseFormat/hyperskill/HyperskillTopic.js +11 -0
- package/dist/courseFormat/loggerUtils.d.ts +1 -0
- package/dist/courseFormat/loggerUtils.js +6 -0
- package/dist/courseFormat/stepik/StepikCourse.d.ts +5 -0
- package/dist/courseFormat/stepik/StepikCourse.js +15 -0
- package/dist/courseFormat/stepik/StepikLesson.d.ts +6 -0
- package/dist/courseFormat/stepik/StepikLesson.js +16 -0
- package/dist/courseFormat/tasks/AnswerTask.d.ts +8 -0
- package/dist/courseFormat/tasks/AnswerTask.js +11 -0
- package/dist/courseFormat/tasks/CodeTask.d.ts +12 -0
- package/dist/courseFormat/tasks/CodeTask.js +21 -0
- package/dist/courseFormat/tasks/DataTask.d.ts +18 -0
- package/dist/courseFormat/tasks/DataTask.js +32 -0
- package/dist/courseFormat/tasks/EduTask.d.ts +12 -0
- package/dist/courseFormat/tasks/EduTask.js +22 -0
- package/dist/courseFormat/tasks/IdeTask.d.ts +9 -0
- package/dist/courseFormat/tasks/IdeTask.js +14 -0
- package/dist/courseFormat/tasks/NumberTask.d.ts +9 -0
- package/dist/courseFormat/tasks/NumberTask.js +14 -0
- package/dist/courseFormat/tasks/OutputTask.d.ts +10 -0
- package/dist/courseFormat/tasks/OutputTask.js +18 -0
- package/dist/courseFormat/tasks/OutputTaskBase.d.ts +14 -0
- package/dist/courseFormat/tasks/OutputTaskBase.js +19 -0
- package/dist/courseFormat/tasks/RemoteEduTask.d.ts +9 -0
- package/dist/courseFormat/tasks/RemoteEduTask.js +15 -0
- package/dist/courseFormat/tasks/StringTask.d.ts +9 -0
- package/dist/courseFormat/tasks/StringTask.js +14 -0
- package/dist/courseFormat/tasks/TableTask.d.ts +17 -0
- package/dist/courseFormat/tasks/TableTask.js +43 -0
- package/dist/courseFormat/tasks/Task.d.ts +45 -0
- package/dist/courseFormat/tasks/Task.js +155 -0
- package/dist/courseFormat/tasks/TheoryTask.d.ts +10 -0
- package/dist/courseFormat/tasks/TheoryTask.js +15 -0
- package/dist/courseFormat/tasks/UnsupportedTask.d.ts +9 -0
- package/dist/courseFormat/tasks/UnsupportedTask.js +14 -0
- package/dist/courseFormat/tasks/choice/ChoiceOption.d.ts +10 -0
- package/dist/courseFormat/tasks/choice/ChoiceOption.js +33 -0
- package/dist/courseFormat/tasks/choice/ChoiceOptionStatus.d.ts +5 -0
- package/dist/courseFormat/tasks/choice/ChoiceOptionStatus.js +9 -0
- package/dist/courseFormat/tasks/choice/ChoiceTask.d.ts +23 -0
- package/dist/courseFormat/tasks/choice/ChoiceTask.js +47 -0
- package/dist/courseFormat/tasks/matching/MatchingTask.d.ts +10 -0
- package/dist/courseFormat/tasks/matching/MatchingTask.js +15 -0
- package/dist/courseFormat/tasks/matching/SortingBasedTask.d.ts +16 -0
- package/dist/courseFormat/tasks/matching/SortingBasedTask.js +50 -0
- package/dist/courseFormat/tasks/matching/SortingTask.d.ts +9 -0
- package/dist/courseFormat/tasks/matching/SortingTask.js +14 -0
- package/dist/courseFormat/uiMessages.d.ts +3 -0
- package/dist/courseFormat/uiMessages.js +14 -0
- package/dist/disk-loader.d.ts +4 -0
- package/dist/disk-loader.js +389 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +64 -0
- package/dist/loader.d.ts +7 -0
- package/dist/loader.js +435 -0
- package/dist/models.d.ts +49 -0
- package/dist/models.js +2 -0
- package/dist/zip-loader.d.ts +4 -0
- package/dist/zip-loader.js +431 -0
- package/example-course-project/course-info.yaml +15 -0
- package/example-course-project/lesson1/lesson-info.yaml +3 -0
- package/example-course-project/lesson1/lesson-remote-info.yaml +1 -0
- package/example-course-project/lesson1/task1/Task.txt +1 -0
- package/example-course-project/lesson1/task1/task-info.yaml +12 -0
- package/example-course-project/lesson1/task1/task-remote-info.yaml +1 -0
- package/example-course-project/lesson1/task1/task.md +47 -0
- package/example-course-project/lesson1/task1/tests/Tests.txt +0 -0
- package/example-course-project/lesson1/task2/Task.txt +1 -0
- package/example-course-project/lesson1/task2/task-info.yaml +12 -0
- package/example-course-project/lesson1/task2/task-remote-info.yaml +1 -0
- package/example-course-project/lesson1/task2/task.md +47 -0
- package/example-course-project/lesson1/task2/tests/Tests.txt +0 -0
- package/package.json +19 -0
- package/src/@types/mime-types.d.ts +3 -0
- package/src/courseFormat/AnswerPlaceholder.ts +121 -0
- package/src/courseFormat/AnswerPlaceholderComparator.ts +7 -0
- package/src/courseFormat/AnswerPlaceholderDependency.ts +122 -0
- package/src/courseFormat/CheckFeedback.ts +71 -0
- package/src/courseFormat/CheckResult.ts +92 -0
- package/src/courseFormat/CheckResultSeverity.ts +13 -0
- package/src/courseFormat/CheckStatus.ts +5 -0
- package/src/courseFormat/Course.ts +201 -0
- package/src/courseFormat/CourseMode.ts +4 -0
- package/src/courseFormat/CourseVisibility.ts +4 -0
- package/src/courseFormat/CourseraCourse.ts +10 -0
- package/src/courseFormat/DescriptionFormat.ts +5 -0
- package/src/courseFormat/EduCourse.ts +41 -0
- package/src/courseFormat/EduFile.ts +133 -0
- package/src/courseFormat/EduFileErrorHighlightLevel.ts +5 -0
- package/src/courseFormat/EduFormatNames.ts +95 -0
- package/src/courseFormat/EduTestInfo.ts +87 -0
- package/src/courseFormat/EduVersions.ts +2 -0
- package/src/courseFormat/FileContents.ts +97 -0
- package/src/courseFormat/FileContentsFactory.ts +19 -0
- package/src/courseFormat/FrameworkLesson.ts +29 -0
- package/src/courseFormat/ItemContainer.ts +47 -0
- package/src/courseFormat/JBAccountUserInfo.ts +21 -0
- package/src/courseFormat/Language.ts +31 -0
- package/src/courseFormat/Lesson.ts +69 -0
- package/src/courseFormat/LessonContainer.ts +65 -0
- package/src/courseFormat/PluginInfo.ts +15 -0
- package/src/courseFormat/Section.ts +29 -0
- package/src/courseFormat/StudyItem.ts +55 -0
- package/src/courseFormat/Tags.ts +45 -0
- package/src/courseFormat/TaskFile.ts +88 -0
- package/src/courseFormat/UserInfo.ts +3 -0
- package/src/courseFormat/Vendor.ts +15 -0
- package/src/courseFormat/attempts/Attempt.ts +28 -0
- package/src/courseFormat/attempts/AttemptBase.ts +24 -0
- package/src/courseFormat/attempts/DataTaskAttempt.ts +19 -0
- package/src/courseFormat/attempts/Dataset.ts +13 -0
- package/src/courseFormat/fileUtils.ts +31 -0
- package/src/courseFormat/hyperskill/HyperskillCourse.ts +24 -0
- package/src/courseFormat/hyperskill/HyperskillProject.ts +10 -0
- package/src/courseFormat/hyperskill/HyperskillStage.ts +15 -0
- package/src/courseFormat/hyperskill/HyperskillTaskType.ts +23 -0
- package/src/courseFormat/hyperskill/HyperskillTopic.ts +5 -0
- package/src/courseFormat/loggerUtils.ts +3 -0
- package/src/courseFormat/stepik/StepikCourse.ts +10 -0
- package/src/courseFormat/stepik/StepikLesson.ts +11 -0
- package/src/courseFormat/tasks/AnswerTask.ts +13 -0
- package/src/courseFormat/tasks/CodeTask.ts +42 -0
- package/src/courseFormat/tasks/DataTask.ts +37 -0
- package/src/courseFormat/tasks/EduTask.ts +26 -0
- package/src/courseFormat/tasks/IdeTask.ts +17 -0
- package/src/courseFormat/tasks/NumberTask.ts +17 -0
- package/src/courseFormat/tasks/OutputTask.ts +21 -0
- package/src/courseFormat/tasks/OutputTaskBase.ts +23 -0
- package/src/courseFormat/tasks/RemoteEduTask.ts +18 -0
- package/src/courseFormat/tasks/StringTask.ts +17 -0
- package/src/courseFormat/tasks/TableTask.ts +51 -0
- package/src/courseFormat/tasks/Task.ts +181 -0
- package/src/courseFormat/tasks/TheoryTask.ts +19 -0
- package/src/courseFormat/tasks/UnsupportedTask.ts +17 -0
- package/src/courseFormat/tasks/choice/ChoiceOption.ts +37 -0
- package/src/courseFormat/tasks/choice/ChoiceOptionStatus.ts +5 -0
- package/src/courseFormat/tasks/choice/ChoiceTask.ts +57 -0
- package/src/courseFormat/tasks/matching/MatchingTask.ts +19 -0
- package/src/courseFormat/tasks/matching/SortingBasedTask.ts +59 -0
- package/src/courseFormat/tasks/matching/SortingTask.ts +17 -0
- package/src/courseFormat/uiMessages.ts +12 -0
- package/src/disk-loader.ts +463 -0
- package/src/index.ts +33 -0
- package/src/models.ts +54 -0
- package/src/zip-loader.ts +583 -0
- package/test/load-course.test.js +279 -0
- package/test/load-zip-course.test.js +73 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Course } from "./Course"
|
|
2
|
+
import { MARKETPLACE } from "./EduFormatNames"
|
|
3
|
+
import { JSON_FORMAT_VERSION } from "./EduVersions"
|
|
4
|
+
|
|
5
|
+
export class EduCourse extends Course {
|
|
6
|
+
get itemType(): string {
|
|
7
|
+
return this.isMarketplace ? MARKETPLACE : super.itemType
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get isStepikRemote(): boolean {
|
|
11
|
+
return this.id !== 0 && !this.isMarketplace
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get isMarketplaceRemote(): boolean {
|
|
15
|
+
return this.id !== 0 && this.isMarketplace
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
formatVersion = JSON_FORMAT_VERSION
|
|
19
|
+
isUpToDate = true
|
|
20
|
+
learnersCount = 0
|
|
21
|
+
reviewScore = 0.0
|
|
22
|
+
generatedEduId?: string
|
|
23
|
+
isPreview = false
|
|
24
|
+
sectionIds: number[] = []
|
|
25
|
+
instructors: number[] = []
|
|
26
|
+
isStepikPublic = false
|
|
27
|
+
reviewSummary = 0
|
|
28
|
+
|
|
29
|
+
convertToLocal(): void {
|
|
30
|
+
if (this.isMarketplace) {
|
|
31
|
+
this.marketplaceCourseVersion = 1
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.isStepikPublic = false
|
|
35
|
+
this.sectionIds = []
|
|
36
|
+
this.instructors = []
|
|
37
|
+
}
|
|
38
|
+
this.id = 0
|
|
39
|
+
this.updateDate = new Date(0)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BinaryContents,
|
|
3
|
+
FileContents,
|
|
4
|
+
InMemoryTextualContents,
|
|
5
|
+
InMemoryUndeterminedContents,
|
|
6
|
+
TextualContents,
|
|
7
|
+
UndeterminedContents,
|
|
8
|
+
} from "./FileContents"
|
|
9
|
+
import { EduFileErrorHighlightLevel } from "./EduFileErrorHighlightLevel"
|
|
10
|
+
import { exceedsBase64ContentLimit, getBinaryFileLimit, isBinary, mimeFileType } from "./fileUtils"
|
|
11
|
+
import { logger } from "./loggerUtils"
|
|
12
|
+
import type { TaskFile } from "./TaskFile"
|
|
13
|
+
|
|
14
|
+
export class EduFile {
|
|
15
|
+
name = ""
|
|
16
|
+
|
|
17
|
+
private _contents: FileContents = UndeterminedContentsEmpty
|
|
18
|
+
|
|
19
|
+
get text(): string {
|
|
20
|
+
return this.contents.textualRepresentation
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
set text(value: string) {
|
|
24
|
+
this.contents = new InMemoryUndeterminedContents(value)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get contents(): FileContents {
|
|
28
|
+
return this._contents
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
set contents(value: FileContents) {
|
|
32
|
+
this._contents = value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setContentsIfEquals(expectedValue: FileContents, newValue: FileContents): boolean {
|
|
36
|
+
if (this._contents === expectedValue) {
|
|
37
|
+
this._contents = newValue
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get isBinary(): boolean | undefined {
|
|
44
|
+
if (isTextualContents(this.contents)) return false
|
|
45
|
+
if (isBinaryContents(this.contents)) return true
|
|
46
|
+
return undefined
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
isTrackChanges = true
|
|
50
|
+
errorHighlightLevel: EduFileErrorHighlightLevel = EduFileErrorHighlightLevel.TEMPORARY_SUPPRESSION
|
|
51
|
+
isVisible = false
|
|
52
|
+
isEditable = true
|
|
53
|
+
isPropagatable = true
|
|
54
|
+
isLearnerCreated = false
|
|
55
|
+
|
|
56
|
+
constructor(name?: string, contents?: FileContents | string) {
|
|
57
|
+
if (name !== undefined) {
|
|
58
|
+
this.name = name
|
|
59
|
+
}
|
|
60
|
+
if (contents !== undefined) {
|
|
61
|
+
if (typeof contents === "string") {
|
|
62
|
+
this.contents = new InMemoryTextualContents(contents)
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
this.contents = contents
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getTextToSerialize(): string | null {
|
|
71
|
+
if (!(this instanceof requireTaskFile().TaskFile) || !this.task.course.needWriteYamlText) {
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (isBinaryContents(this.contents)) return null
|
|
76
|
+
if (isUndeterminedContents(this.contents)) {
|
|
77
|
+
const contentType = mimeFileType(this.name)
|
|
78
|
+
if (contentType && isBinary(String(contentType))) return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const text = this.contents.textualRepresentation
|
|
82
|
+
if (exceedsBase64ContentLimit(text)) {
|
|
83
|
+
logger("EduFile").warn(
|
|
84
|
+
`Base64 encoding of \`${this.name}\` file exceeds limit (${getBinaryFileLimit()}), its content isn't serialized`
|
|
85
|
+
)
|
|
86
|
+
return null
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return text
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get pathInCourse(): string {
|
|
93
|
+
const pathPrefix = this instanceof requireTaskFile().TaskFile ? this.task.pathInCourse : ""
|
|
94
|
+
const pathInCourse = `${pathPrefix}/${this.name}`
|
|
95
|
+
return pathInCourse.startsWith("/") ? pathInCourse.substring(1) : pathInCourse
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get pathInArchive(): string {
|
|
99
|
+
return this.pathInCourse
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function requireTaskFile(): { TaskFile: typeof TaskFile } {
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
105
|
+
return require("./TaskFile")
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isTextualContents(contents: FileContents): contents is TextualContents {
|
|
109
|
+
return (contents as TextualContents).text !== undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isBinaryContents(contents: FileContents): contents is BinaryContents {
|
|
113
|
+
return (contents as BinaryContents).bytes !== undefined
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function isUndeterminedContents(contents: FileContents): contents is UndeterminedContents {
|
|
117
|
+
return (contents as UndeterminedContents).textualRepresentation !== undefined && !isTextualContents(contents)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const UndeterminedContentsEmpty: UndeterminedContents = {
|
|
121
|
+
textualRepresentation: "",
|
|
122
|
+
get text() {
|
|
123
|
+
return this.textualRepresentation
|
|
124
|
+
},
|
|
125
|
+
get bytes() {
|
|
126
|
+
try {
|
|
127
|
+
return Buffer.from(this.textualRepresentation, "base64")
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return new Uint8Array()
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { message } from "./uiMessages"
|
|
2
|
+
|
|
3
|
+
export const EDU_PREFIX = "edu"
|
|
4
|
+
export const REST_PREFIX = "api"
|
|
5
|
+
export const CODE_ARGUMENT = "code"
|
|
6
|
+
|
|
7
|
+
export const MARKETPLACE = "Marketplace"
|
|
8
|
+
export const COURSE_META_FILE = "course.json"
|
|
9
|
+
export const COURSE_ICON_FILE = "courseIcon.svg"
|
|
10
|
+
export const COURSE_CONTENTS_FOLDER = "contents"
|
|
11
|
+
|
|
12
|
+
export const COURSE = "course"
|
|
13
|
+
export const SECTION = "section"
|
|
14
|
+
export const LESSON = "lesson"
|
|
15
|
+
export const FRAMEWORK = "framework"
|
|
16
|
+
export const TASK = "task"
|
|
17
|
+
export const ITEM = "item"
|
|
18
|
+
|
|
19
|
+
export const SOLUTION = "solution"
|
|
20
|
+
export const STATES_ON_CLOSE = "states_on_close"
|
|
21
|
+
export const SUBMISSIONS = "submissions"
|
|
22
|
+
|
|
23
|
+
export const EMAIL = "email"
|
|
24
|
+
export const NAME = "name"
|
|
25
|
+
export const URL = "url"
|
|
26
|
+
|
|
27
|
+
export const CORRECT = "correct"
|
|
28
|
+
export const WRONG = "wrong"
|
|
29
|
+
export const UNCHECKED = "unchecked"
|
|
30
|
+
|
|
31
|
+
export const TIME_LEFT = "time_left"
|
|
32
|
+
export const ID = "id"
|
|
33
|
+
export const TIME = "time"
|
|
34
|
+
export const STEP = "step"
|
|
35
|
+
export const DATASET = "dataset"
|
|
36
|
+
export const STATUS = "status"
|
|
37
|
+
export const USER = "user"
|
|
38
|
+
|
|
39
|
+
export const IS_MULTIPLE_CHOICE = "is_multiple_choice"
|
|
40
|
+
export const OPTIONS = "options"
|
|
41
|
+
export const PAIRS = "pairs"
|
|
42
|
+
export const ROWS = "rows"
|
|
43
|
+
export const COLUMNS = "columns"
|
|
44
|
+
export const IS_CHECKBOX = "is_checkbox"
|
|
45
|
+
|
|
46
|
+
export const DEFAULT_ENVIRONMENT = ""
|
|
47
|
+
|
|
48
|
+
export const PYCHARM = "PyCharm"
|
|
49
|
+
|
|
50
|
+
export const TITLE = "title"
|
|
51
|
+
export const THEORY_ID = "theory"
|
|
52
|
+
export const STEP_ID = "step"
|
|
53
|
+
export const IS_COMPLETED = "is_completed"
|
|
54
|
+
export const DESCRIPTION = "description"
|
|
55
|
+
export const IDE_FILES = "ide_files"
|
|
56
|
+
export const USE_IDE = "use_ide"
|
|
57
|
+
export const LANGUAGE = "language"
|
|
58
|
+
export const ENVIRONMENT = "environment"
|
|
59
|
+
export const IS_TEMPLATE_BASED = "is_template_based"
|
|
60
|
+
export const HYPERSKILL_PROBLEMS = "Problems"
|
|
61
|
+
export const HYPERSKILL_TOPICS = "Topics"
|
|
62
|
+
export const TOPICS = "topics"
|
|
63
|
+
export const HYPERSKILL_PROJECTS_URL = "https://hyperskill.org/projects"
|
|
64
|
+
export const HYPERSKILL = "Hyperskill"
|
|
65
|
+
|
|
66
|
+
export const COURSERA = "Coursera"
|
|
67
|
+
|
|
68
|
+
export const STEPIK = "Stepik"
|
|
69
|
+
export const ATTEMPT = "attempt"
|
|
70
|
+
export const CHECK_PROFILE = "check_profile"
|
|
71
|
+
|
|
72
|
+
export const JAVA = "JAVA"
|
|
73
|
+
export const KOTLIN = "kotlin"
|
|
74
|
+
export const PYTHON = "Python"
|
|
75
|
+
export const SCALA = "Scala"
|
|
76
|
+
export const JAVASCRIPT = "JavaScript"
|
|
77
|
+
export const RUST = "Rust"
|
|
78
|
+
export const SHELL = "Shell Script"
|
|
79
|
+
export const CPP = "C++"
|
|
80
|
+
export const OBJECTIVE_C = "ObjectiveC"
|
|
81
|
+
export const GO = "go"
|
|
82
|
+
export const PHP = "PHP"
|
|
83
|
+
export const CSHARP = "C#"
|
|
84
|
+
|
|
85
|
+
export const PYTHON_2_VERSION = "2.x"
|
|
86
|
+
export const PYTHON_3_VERSION = "3.x"
|
|
87
|
+
|
|
88
|
+
export const TROUBLESHOOTING_GUIDE_URL =
|
|
89
|
+
"https://plugins.jetbrains.com/plugin/10081-jetbrains-academy/docs/troubleshooting-guide.html"
|
|
90
|
+
export const NO_TESTS_URL = `${TROUBLESHOOTING_GUIDE_URL}#no_tests_have_run`
|
|
91
|
+
export const FAILED_TO_CHECK_URL = `${TROUBLESHOOTING_GUIDE_URL}#failed_to_launch_checking`
|
|
92
|
+
export const FAILED_TO_DELETE_SUBMISSIONS =
|
|
93
|
+
`${TROUBLESHOOTING_GUIDE_URL}#failed_to_delete_submissions`
|
|
94
|
+
|
|
95
|
+
export const LOGIN_NEEDED_MESSAGE = message("check.error.login.needed")
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { CheckResultDiff } from "./CheckResult"
|
|
2
|
+
|
|
3
|
+
export class EduTestInfo {
|
|
4
|
+
readonly name: string
|
|
5
|
+
readonly status: number
|
|
6
|
+
readonly message: string
|
|
7
|
+
readonly details?: string
|
|
8
|
+
readonly checkResultDiff?: CheckResultDiff
|
|
9
|
+
|
|
10
|
+
private readonly isSuccessInternal: boolean
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
name = "",
|
|
14
|
+
status = -1,
|
|
15
|
+
message = "",
|
|
16
|
+
details?: string | null,
|
|
17
|
+
isFinishedSuccessfully?: boolean | null,
|
|
18
|
+
checkResultDiff?: CheckResultDiff | null
|
|
19
|
+
) {
|
|
20
|
+
this.name = name
|
|
21
|
+
this.status = status
|
|
22
|
+
this.message = message
|
|
23
|
+
this.details = details ?? undefined
|
|
24
|
+
this.checkResultDiff = checkResultDiff ?? undefined
|
|
25
|
+
this.isSuccessInternal =
|
|
26
|
+
isFinishedSuccessfully === true ||
|
|
27
|
+
(EduTestInfo.get(status) !== undefined && EduTestInfo.isSuccess(EduTestInfo.get(status)!))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get isSuccess(): boolean {
|
|
31
|
+
return this.isSuccessInternal
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
toString(): string {
|
|
35
|
+
return `[${EduTestInfo.getPresentableStatus(this.status)}] ${this.name}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static firstFailed(tests: EduTestInfo[]): EduTestInfo | undefined {
|
|
39
|
+
return tests.find((test) => !test.isSuccess)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export namespace EduTestInfo {
|
|
44
|
+
export enum PresentableStatus {
|
|
45
|
+
SKIPPED = 0,
|
|
46
|
+
COMPLETED = 1,
|
|
47
|
+
NOT_RUN = 2,
|
|
48
|
+
RUNNING = 3,
|
|
49
|
+
TERMINATED = 4,
|
|
50
|
+
IGNORED = 5,
|
|
51
|
+
FAILED = 6,
|
|
52
|
+
ERROR = 8,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const STATUS_TITLES: Record<PresentableStatus, string> = {
|
|
56
|
+
[PresentableStatus.SKIPPED]: "Skipped",
|
|
57
|
+
[PresentableStatus.COMPLETED]: "Completed",
|
|
58
|
+
[PresentableStatus.NOT_RUN]: "Not run",
|
|
59
|
+
[PresentableStatus.RUNNING]: "Running",
|
|
60
|
+
[PresentableStatus.TERMINATED]: "Terminated",
|
|
61
|
+
[PresentableStatus.IGNORED]: "Ignored",
|
|
62
|
+
[PresentableStatus.FAILED]: "Failed",
|
|
63
|
+
[PresentableStatus.ERROR]: "Error",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function get(status: number): PresentableStatus | undefined {
|
|
67
|
+
return (Object.values(PresentableStatus) as Array<string | number>)
|
|
68
|
+
.filter((value) => typeof value === "number")
|
|
69
|
+
.map((value) => value as number)
|
|
70
|
+
.includes(status)
|
|
71
|
+
? (status as PresentableStatus)
|
|
72
|
+
: undefined
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function getPresentableStatus(status: number): string {
|
|
76
|
+
const value = get(status)
|
|
77
|
+
return value === undefined ? "Unknown" : STATUS_TITLES[value]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function isSuccess(status: PresentableStatus): boolean {
|
|
81
|
+
return (
|
|
82
|
+
status === PresentableStatus.COMPLETED ||
|
|
83
|
+
status === PresentableStatus.SKIPPED ||
|
|
84
|
+
status === PresentableStatus.IGNORED
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export interface FileContents {
|
|
2
|
+
readonly textualRepresentation: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface DeterminedContents extends FileContents {}
|
|
6
|
+
|
|
7
|
+
export interface TextualContents extends DeterminedContents {
|
|
8
|
+
readonly text: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BinaryContents extends DeterminedContents {
|
|
12
|
+
readonly bytes: Uint8Array
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface UndeterminedContents extends FileContents {
|
|
16
|
+
readonly textualRepresentation: string
|
|
17
|
+
readonly text: string
|
|
18
|
+
readonly bytes: Uint8Array
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const TextualContentsEmpty: TextualContents = {
|
|
22
|
+
text: "",
|
|
23
|
+
get textualRepresentation() {
|
|
24
|
+
return this.text
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const BinaryContentsEmpty: BinaryContents = {
|
|
29
|
+
bytes: new Uint8Array(),
|
|
30
|
+
get textualRepresentation() {
|
|
31
|
+
return Buffer.from(this.bytes).toString("base64")
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const UndeterminedContentsEmpty: UndeterminedContents = {
|
|
36
|
+
textualRepresentation: "",
|
|
37
|
+
get text() {
|
|
38
|
+
return this.textualRepresentation
|
|
39
|
+
},
|
|
40
|
+
get bytes() {
|
|
41
|
+
try {
|
|
42
|
+
return Buffer.from(this.textualRepresentation, "base64")
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return new Uint8Array()
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class InMemoryBinaryContents implements BinaryContents {
|
|
51
|
+
readonly bytes: Uint8Array
|
|
52
|
+
|
|
53
|
+
constructor(bytes: Uint8Array) {
|
|
54
|
+
this.bytes = bytes
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get textualRepresentation(): string {
|
|
58
|
+
return Buffer.from(this.bytes).toString("base64")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static parseBase64Encoding(base64: string): InMemoryBinaryContents {
|
|
62
|
+
return new InMemoryBinaryContents(Buffer.from(base64, "base64"))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class InMemoryTextualContents implements TextualContents {
|
|
67
|
+
readonly text: string
|
|
68
|
+
|
|
69
|
+
constructor(text: string) {
|
|
70
|
+
this.text = text
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get textualRepresentation(): string {
|
|
74
|
+
return this.text
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class InMemoryUndeterminedContents implements UndeterminedContents {
|
|
79
|
+
readonly textualRepresentation: string
|
|
80
|
+
|
|
81
|
+
constructor(textualRepresentation: string) {
|
|
82
|
+
this.textualRepresentation = textualRepresentation
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get text(): string {
|
|
86
|
+
return this.textualRepresentation
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get bytes(): Uint8Array {
|
|
90
|
+
try {
|
|
91
|
+
return Buffer.from(this.textualRepresentation, "base64")
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return new Uint8Array()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { EduFile } from "./EduFile"
|
|
2
|
+
import type { BinaryContents, TextualContents } from "./FileContents"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Factory used to create file contents during deserialization of EduFile, TaskFile.
|
|
6
|
+
* Used when json or yaml do not have the contents themselves (text field is empty).
|
|
7
|
+
*/
|
|
8
|
+
export interface FileContentsFactory {
|
|
9
|
+
/**
|
|
10
|
+
* [file] is the EduFile object that will contain these contents.
|
|
11
|
+
* This file may not be fully initialized at the moment of the call.
|
|
12
|
+
*/
|
|
13
|
+
createBinaryContents(file: EduFile): BinaryContents
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* see createBinaryContents
|
|
17
|
+
*/
|
|
18
|
+
createTextualContents(file: EduFile): TextualContents
|
|
19
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Lesson } from "./Lesson"
|
|
2
|
+
import { FRAMEWORK } from "./EduFormatNames"
|
|
3
|
+
import { Task } from "./tasks/Task"
|
|
4
|
+
|
|
5
|
+
export class FrameworkLesson extends Lesson {
|
|
6
|
+
currentTaskIndex = 0
|
|
7
|
+
isTemplateBased = true
|
|
8
|
+
|
|
9
|
+
constructor(lesson?: Lesson) {
|
|
10
|
+
super()
|
|
11
|
+
if (lesson) {
|
|
12
|
+
this.id = lesson.id
|
|
13
|
+
this.updateDate = lesson.updateDate
|
|
14
|
+
this.name = lesson.name
|
|
15
|
+
this.items = lesson.items
|
|
16
|
+
this.parent = lesson.parent
|
|
17
|
+
this.index = lesson.index
|
|
18
|
+
this.customPresentableName = lesson.customPresentableName
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
currentTask(): Task | undefined {
|
|
23
|
+
return this.taskList[this.currentTaskIndex]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get itemType(): string {
|
|
27
|
+
return FRAMEWORK
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { StudyItem } from "./StudyItem"
|
|
2
|
+
|
|
3
|
+
export abstract class ItemContainer extends StudyItem {
|
|
4
|
+
private _items: StudyItem[] = []
|
|
5
|
+
|
|
6
|
+
get items(): StudyItem[] {
|
|
7
|
+
return this._items
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
set items(value: StudyItem[]) {
|
|
11
|
+
this._items = [...value]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
override init(parentItem: ItemContainer, isRestarted: boolean): void {
|
|
15
|
+
this.parent = parentItem
|
|
16
|
+
this.items.forEach((item, index) => {
|
|
17
|
+
item.index = index + 1
|
|
18
|
+
item.init(this, isRestarted)
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getItem(name: string): StudyItem | undefined {
|
|
23
|
+
return this.items.find((item) => item.name === name)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
addItem(item: StudyItem): void {
|
|
27
|
+
this._items.push(item)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
addItemAt(index: number, item: StudyItem): void {
|
|
31
|
+
this._items.splice(index, 0, item)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
replaceItem(existingItem: StudyItem, newItem: StudyItem): void {
|
|
35
|
+
const index = this._items.indexOf(existingItem)
|
|
36
|
+
if (index < 0) return
|
|
37
|
+
this._items[index] = newItem
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
removeItem(item: StudyItem): void {
|
|
41
|
+
this._items = this._items.filter((value) => value !== item)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
sortItems(): void {
|
|
45
|
+
this._items.sort((a, b) => a.index - b.index)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { UserInfo } from "./UserInfo"
|
|
2
|
+
|
|
3
|
+
export class JBAccountUserInfo implements UserInfo {
|
|
4
|
+
email = ""
|
|
5
|
+
name = ""
|
|
6
|
+
jbaLogin = ""
|
|
7
|
+
|
|
8
|
+
constructor(userName?: string) {
|
|
9
|
+
if (userName) {
|
|
10
|
+
this.name = userName
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getFullName(): string {
|
|
15
|
+
return this.name
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
toString(): string {
|
|
19
|
+
return this.getFullName()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const languages: Record<string, string> = {
|
|
2
|
+
Python: "Python",
|
|
3
|
+
"C++": "C/C++",
|
|
4
|
+
ObjectiveC: "C/C++",
|
|
5
|
+
go: "Go",
|
|
6
|
+
JAVA: "Java",
|
|
7
|
+
kotlin: "Kotlin",
|
|
8
|
+
Scala: "Scala",
|
|
9
|
+
JavaScript: "JavaScript",
|
|
10
|
+
Rust: "Rust",
|
|
11
|
+
PHP: "PHP",
|
|
12
|
+
"Shell Script": "Shell Script",
|
|
13
|
+
SQL: "SQL",
|
|
14
|
+
"C#": "C#",
|
|
15
|
+
unity: "unity",
|
|
16
|
+
TEXT: "Plain text",
|
|
17
|
+
FakeGradleBasedLanguage: "FakeGradleBasedLanguage",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Language = {
|
|
21
|
+
findLanguageByID(id: string): string | undefined {
|
|
22
|
+
return languages[id]
|
|
23
|
+
},
|
|
24
|
+
findLanguageByName(name: string): string | undefined {
|
|
25
|
+
if (name === "C/C++") {
|
|
26
|
+
return "C++"
|
|
27
|
+
}
|
|
28
|
+
const entry = Object.entries(languages).find(([, value]) => value === name)
|
|
29
|
+
return entry ? entry[0] : undefined
|
|
30
|
+
},
|
|
31
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { LessonContainer } from "./LessonContainer"
|
|
2
|
+
import type { Course } from "./Course"
|
|
3
|
+
import type { Section } from "./Section"
|
|
4
|
+
import { Task } from "./tasks/Task"
|
|
5
|
+
import { LESSON } from "./EduFormatNames"
|
|
6
|
+
import { ItemContainer } from "./ItemContainer"
|
|
7
|
+
|
|
8
|
+
function getLessonContainerModule(): { Section: typeof Section } {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
10
|
+
return require("./Section")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class Lesson extends LessonContainer {
|
|
14
|
+
override init(parentItem: ItemContainer, isRestarted: boolean): void {
|
|
15
|
+
if (!(parentItem instanceof LessonContainer)) {
|
|
16
|
+
throw new Error(`Parent for lesson ${this.name} should be either course or section`)
|
|
17
|
+
}
|
|
18
|
+
super.init(parentItem, isRestarted)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get taskList(): Task[] {
|
|
22
|
+
return this.items.filter((item): item is Task => item instanceof Task)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get course(): Course {
|
|
26
|
+
return this.parent.course
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get section(): Section | undefined {
|
|
30
|
+
return this.parent instanceof getLessonContainerModule().Section ? this.parent : undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get content(): Task[] {
|
|
34
|
+
return this.items.filter((item): item is Task => item instanceof Task)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get itemType(): string {
|
|
38
|
+
return LESSON
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
addTask(task: Task): void {
|
|
42
|
+
this.addItem(task)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
addTaskAt(index: number, task: Task): void {
|
|
46
|
+
this.addItemAt(index, task)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
removeTask(task: Task): void {
|
|
50
|
+
this.removeItem(task)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getTask(name: string): Task | undefined
|
|
54
|
+
getTask(id: number): Task | undefined
|
|
55
|
+
getTask(param: string | number): Task | undefined {
|
|
56
|
+
if (typeof param === "string") {
|
|
57
|
+
return this.taskList.find((task) => task.name === param)
|
|
58
|
+
}
|
|
59
|
+
return this.taskList.find((task) => task.id === param)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get container(): LessonContainer {
|
|
63
|
+
return this.section ?? this.course
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
visitTasks(visit: (task: Task) => void): void {
|
|
67
|
+
this.taskList.forEach(visit)
|
|
68
|
+
}
|
|
69
|
+
}
|