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.
Files changed (239) hide show
  1. package/.idea/edu-format-ts.iml +10 -0
  2. package/.idea/inspectionProfiles/Project_Default.xml +28 -0
  3. package/.idea/misc.xml +4 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/vcs.xml +6 -0
  6. package/AGENTS.md +1 -0
  7. package/dist/courseFormat/AnswerPlaceholder.d.ts +37 -0
  8. package/dist/courseFormat/AnswerPlaceholder.js +101 -0
  9. package/dist/courseFormat/AnswerPlaceholderComparator.d.ts +4 -0
  10. package/dist/courseFormat/AnswerPlaceholderComparator.js +8 -0
  11. package/dist/courseFormat/AnswerPlaceholderDependency.d.ts +19 -0
  12. package/dist/courseFormat/AnswerPlaceholderDependency.js +91 -0
  13. package/dist/courseFormat/CheckFeedback.d.ts +20 -0
  14. package/dist/courseFormat/CheckFeedback.js +58 -0
  15. package/dist/courseFormat/CheckResult.d.ts +33 -0
  16. package/dist/courseFormat/CheckResult.js +58 -0
  17. package/dist/courseFormat/CheckResultSeverity.d.ts +7 -0
  18. package/dist/courseFormat/CheckResultSeverity.js +17 -0
  19. package/dist/courseFormat/CheckStatus.d.ts +5 -0
  20. package/dist/courseFormat/CheckStatus.js +9 -0
  21. package/dist/courseFormat/Course.d.ts +68 -0
  22. package/dist/courseFormat/Course.js +165 -0
  23. package/dist/courseFormat/CourseMode.d.ts +4 -0
  24. package/dist/courseFormat/CourseMode.js +8 -0
  25. package/dist/courseFormat/CourseVisibility.d.ts +4 -0
  26. package/dist/courseFormat/CourseVisibility.js +8 -0
  27. package/dist/courseFormat/CourseraCourse.d.ts +5 -0
  28. package/dist/courseFormat/CourseraCourse.js +15 -0
  29. package/dist/courseFormat/DescriptionFormat.d.ts +5 -0
  30. package/dist/courseFormat/DescriptionFormat.js +9 -0
  31. package/dist/courseFormat/EduCourse.d.ts +17 -0
  32. package/dist/courseFormat/EduCourse.js +42 -0
  33. package/dist/courseFormat/EduFile.d.ts +22 -0
  34. package/dist/courseFormat/EduFile.js +110 -0
  35. package/dist/courseFormat/EduFileErrorHighlightLevel.d.ts +5 -0
  36. package/dist/courseFormat/EduFileErrorHighlightLevel.js +9 -0
  37. package/dist/courseFormat/EduFormatNames.d.ts +75 -0
  38. package/dist/courseFormat/EduFormatNames.js +80 -0
  39. package/dist/courseFormat/EduTestInfo.d.ts +28 -0
  40. package/dist/courseFormat/EduTestInfo.js +68 -0
  41. package/dist/courseFormat/EduVersions.d.ts +2 -0
  42. package/dist/courseFormat/EduVersions.js +5 -0
  43. package/dist/courseFormat/FileContents.d.ts +36 -0
  44. package/dist/courseFormat/FileContents.js +67 -0
  45. package/dist/courseFormat/FileContentsFactory.d.ts +17 -0
  46. package/dist/courseFormat/FileContentsFactory.js +2 -0
  47. package/dist/courseFormat/FrameworkLesson.d.ts +9 -0
  48. package/dist/courseFormat/FrameworkLesson.js +28 -0
  49. package/dist/courseFormat/ItemContainer.d.ts +13 -0
  50. package/dist/courseFormat/ItemContainer.js +45 -0
  51. package/dist/courseFormat/JBAccountUserInfo.d.ts +9 -0
  52. package/dist/courseFormat/JBAccountUserInfo.js +20 -0
  53. package/dist/courseFormat/Language.d.ts +4 -0
  54. package/dist/courseFormat/Language.js +33 -0
  55. package/dist/courseFormat/Lesson.d.ts +20 -0
  56. package/dist/courseFormat/Lesson.js +56 -0
  57. package/dist/courseFormat/LessonContainer.d.ts +16 -0
  58. package/dist/courseFormat/LessonContainer.js +54 -0
  59. package/dist/courseFormat/PluginInfo.d.ts +7 -0
  60. package/dist/courseFormat/PluginInfo.js +15 -0
  61. package/dist/courseFormat/Section.d.ts +8 -0
  62. package/dist/courseFormat/Section.js +27 -0
  63. package/dist/courseFormat/StudyItem.d.ts +20 -0
  64. package/dist/courseFormat/StudyItem.js +47 -0
  65. package/dist/courseFormat/Tags.d.ts +16 -0
  66. package/dist/courseFormat/Tags.js +42 -0
  67. package/dist/courseFormat/TaskFile.d.ts +26 -0
  68. package/dist/courseFormat/TaskFile.js +72 -0
  69. package/dist/courseFormat/UserInfo.d.ts +3 -0
  70. package/dist/courseFormat/UserInfo.js +2 -0
  71. package/dist/courseFormat/Vendor.d.ts +7 -0
  72. package/dist/courseFormat/Vendor.js +14 -0
  73. package/dist/courseFormat/attempts/Attempt.d.ts +12 -0
  74. package/dist/courseFormat/attempts/Attempt.js +25 -0
  75. package/dist/courseFormat/attempts/AttemptBase.d.ts +9 -0
  76. package/dist/courseFormat/attempts/AttemptBase.js +28 -0
  77. package/dist/courseFormat/attempts/DataTaskAttempt.d.ts +6 -0
  78. package/dist/courseFormat/attempts/DataTaskAttempt.js +24 -0
  79. package/dist/courseFormat/attempts/Dataset.d.ts +12 -0
  80. package/dist/courseFormat/attempts/Dataset.js +21 -0
  81. package/dist/courseFormat/fileUtils.d.ts +5 -0
  82. package/dist/courseFormat/fileUtils.js +32 -0
  83. package/dist/courseFormat/hyperskill/HyperskillCourse.d.ts +13 -0
  84. package/dist/courseFormat/hyperskill/HyperskillCourse.js +25 -0
  85. package/dist/courseFormat/hyperskill/HyperskillProject.d.ts +10 -0
  86. package/dist/courseFormat/hyperskill/HyperskillProject.js +16 -0
  87. package/dist/courseFormat/hyperskill/HyperskillStage.d.ts +8 -0
  88. package/dist/courseFormat/hyperskill/HyperskillStage.js +20 -0
  89. package/dist/courseFormat/hyperskill/HyperskillTaskType.d.ts +4 -0
  90. package/dist/courseFormat/hyperskill/HyperskillTaskType.js +26 -0
  91. package/dist/courseFormat/hyperskill/HyperskillTopic.d.ts +5 -0
  92. package/dist/courseFormat/hyperskill/HyperskillTopic.js +11 -0
  93. package/dist/courseFormat/loggerUtils.d.ts +1 -0
  94. package/dist/courseFormat/loggerUtils.js +6 -0
  95. package/dist/courseFormat/stepik/StepikCourse.d.ts +5 -0
  96. package/dist/courseFormat/stepik/StepikCourse.js +15 -0
  97. package/dist/courseFormat/stepik/StepikLesson.d.ts +6 -0
  98. package/dist/courseFormat/stepik/StepikLesson.js +16 -0
  99. package/dist/courseFormat/tasks/AnswerTask.d.ts +8 -0
  100. package/dist/courseFormat/tasks/AnswerTask.js +11 -0
  101. package/dist/courseFormat/tasks/CodeTask.d.ts +12 -0
  102. package/dist/courseFormat/tasks/CodeTask.js +21 -0
  103. package/dist/courseFormat/tasks/DataTask.d.ts +18 -0
  104. package/dist/courseFormat/tasks/DataTask.js +32 -0
  105. package/dist/courseFormat/tasks/EduTask.d.ts +12 -0
  106. package/dist/courseFormat/tasks/EduTask.js +22 -0
  107. package/dist/courseFormat/tasks/IdeTask.d.ts +9 -0
  108. package/dist/courseFormat/tasks/IdeTask.js +14 -0
  109. package/dist/courseFormat/tasks/NumberTask.d.ts +9 -0
  110. package/dist/courseFormat/tasks/NumberTask.js +14 -0
  111. package/dist/courseFormat/tasks/OutputTask.d.ts +10 -0
  112. package/dist/courseFormat/tasks/OutputTask.js +18 -0
  113. package/dist/courseFormat/tasks/OutputTaskBase.d.ts +14 -0
  114. package/dist/courseFormat/tasks/OutputTaskBase.js +19 -0
  115. package/dist/courseFormat/tasks/RemoteEduTask.d.ts +9 -0
  116. package/dist/courseFormat/tasks/RemoteEduTask.js +15 -0
  117. package/dist/courseFormat/tasks/StringTask.d.ts +9 -0
  118. package/dist/courseFormat/tasks/StringTask.js +14 -0
  119. package/dist/courseFormat/tasks/TableTask.d.ts +17 -0
  120. package/dist/courseFormat/tasks/TableTask.js +43 -0
  121. package/dist/courseFormat/tasks/Task.d.ts +45 -0
  122. package/dist/courseFormat/tasks/Task.js +155 -0
  123. package/dist/courseFormat/tasks/TheoryTask.d.ts +10 -0
  124. package/dist/courseFormat/tasks/TheoryTask.js +15 -0
  125. package/dist/courseFormat/tasks/UnsupportedTask.d.ts +9 -0
  126. package/dist/courseFormat/tasks/UnsupportedTask.js +14 -0
  127. package/dist/courseFormat/tasks/choice/ChoiceOption.d.ts +10 -0
  128. package/dist/courseFormat/tasks/choice/ChoiceOption.js +33 -0
  129. package/dist/courseFormat/tasks/choice/ChoiceOptionStatus.d.ts +5 -0
  130. package/dist/courseFormat/tasks/choice/ChoiceOptionStatus.js +9 -0
  131. package/dist/courseFormat/tasks/choice/ChoiceTask.d.ts +23 -0
  132. package/dist/courseFormat/tasks/choice/ChoiceTask.js +47 -0
  133. package/dist/courseFormat/tasks/matching/MatchingTask.d.ts +10 -0
  134. package/dist/courseFormat/tasks/matching/MatchingTask.js +15 -0
  135. package/dist/courseFormat/tasks/matching/SortingBasedTask.d.ts +16 -0
  136. package/dist/courseFormat/tasks/matching/SortingBasedTask.js +50 -0
  137. package/dist/courseFormat/tasks/matching/SortingTask.d.ts +9 -0
  138. package/dist/courseFormat/tasks/matching/SortingTask.js +14 -0
  139. package/dist/courseFormat/uiMessages.d.ts +3 -0
  140. package/dist/courseFormat/uiMessages.js +14 -0
  141. package/dist/disk-loader.d.ts +4 -0
  142. package/dist/disk-loader.js +389 -0
  143. package/dist/index.d.ts +31 -0
  144. package/dist/index.js +64 -0
  145. package/dist/loader.d.ts +7 -0
  146. package/dist/loader.js +435 -0
  147. package/dist/models.d.ts +49 -0
  148. package/dist/models.js +2 -0
  149. package/dist/zip-loader.d.ts +4 -0
  150. package/dist/zip-loader.js +431 -0
  151. package/example-course-project/course-info.yaml +15 -0
  152. package/example-course-project/lesson1/lesson-info.yaml +3 -0
  153. package/example-course-project/lesson1/lesson-remote-info.yaml +1 -0
  154. package/example-course-project/lesson1/task1/Task.txt +1 -0
  155. package/example-course-project/lesson1/task1/task-info.yaml +12 -0
  156. package/example-course-project/lesson1/task1/task-remote-info.yaml +1 -0
  157. package/example-course-project/lesson1/task1/task.md +47 -0
  158. package/example-course-project/lesson1/task1/tests/Tests.txt +0 -0
  159. package/example-course-project/lesson1/task2/Task.txt +1 -0
  160. package/example-course-project/lesson1/task2/task-info.yaml +12 -0
  161. package/example-course-project/lesson1/task2/task-remote-info.yaml +1 -0
  162. package/example-course-project/lesson1/task2/task.md +47 -0
  163. package/example-course-project/lesson1/task2/tests/Tests.txt +0 -0
  164. package/package.json +19 -0
  165. package/src/@types/mime-types.d.ts +3 -0
  166. package/src/courseFormat/AnswerPlaceholder.ts +121 -0
  167. package/src/courseFormat/AnswerPlaceholderComparator.ts +7 -0
  168. package/src/courseFormat/AnswerPlaceholderDependency.ts +122 -0
  169. package/src/courseFormat/CheckFeedback.ts +71 -0
  170. package/src/courseFormat/CheckResult.ts +92 -0
  171. package/src/courseFormat/CheckResultSeverity.ts +13 -0
  172. package/src/courseFormat/CheckStatus.ts +5 -0
  173. package/src/courseFormat/Course.ts +201 -0
  174. package/src/courseFormat/CourseMode.ts +4 -0
  175. package/src/courseFormat/CourseVisibility.ts +4 -0
  176. package/src/courseFormat/CourseraCourse.ts +10 -0
  177. package/src/courseFormat/DescriptionFormat.ts +5 -0
  178. package/src/courseFormat/EduCourse.ts +41 -0
  179. package/src/courseFormat/EduFile.ts +133 -0
  180. package/src/courseFormat/EduFileErrorHighlightLevel.ts +5 -0
  181. package/src/courseFormat/EduFormatNames.ts +95 -0
  182. package/src/courseFormat/EduTestInfo.ts +87 -0
  183. package/src/courseFormat/EduVersions.ts +2 -0
  184. package/src/courseFormat/FileContents.ts +97 -0
  185. package/src/courseFormat/FileContentsFactory.ts +19 -0
  186. package/src/courseFormat/FrameworkLesson.ts +29 -0
  187. package/src/courseFormat/ItemContainer.ts +47 -0
  188. package/src/courseFormat/JBAccountUserInfo.ts +21 -0
  189. package/src/courseFormat/Language.ts +31 -0
  190. package/src/courseFormat/Lesson.ts +69 -0
  191. package/src/courseFormat/LessonContainer.ts +65 -0
  192. package/src/courseFormat/PluginInfo.ts +15 -0
  193. package/src/courseFormat/Section.ts +29 -0
  194. package/src/courseFormat/StudyItem.ts +55 -0
  195. package/src/courseFormat/Tags.ts +45 -0
  196. package/src/courseFormat/TaskFile.ts +88 -0
  197. package/src/courseFormat/UserInfo.ts +3 -0
  198. package/src/courseFormat/Vendor.ts +15 -0
  199. package/src/courseFormat/attempts/Attempt.ts +28 -0
  200. package/src/courseFormat/attempts/AttemptBase.ts +24 -0
  201. package/src/courseFormat/attempts/DataTaskAttempt.ts +19 -0
  202. package/src/courseFormat/attempts/Dataset.ts +13 -0
  203. package/src/courseFormat/fileUtils.ts +31 -0
  204. package/src/courseFormat/hyperskill/HyperskillCourse.ts +24 -0
  205. package/src/courseFormat/hyperskill/HyperskillProject.ts +10 -0
  206. package/src/courseFormat/hyperskill/HyperskillStage.ts +15 -0
  207. package/src/courseFormat/hyperskill/HyperskillTaskType.ts +23 -0
  208. package/src/courseFormat/hyperskill/HyperskillTopic.ts +5 -0
  209. package/src/courseFormat/loggerUtils.ts +3 -0
  210. package/src/courseFormat/stepik/StepikCourse.ts +10 -0
  211. package/src/courseFormat/stepik/StepikLesson.ts +11 -0
  212. package/src/courseFormat/tasks/AnswerTask.ts +13 -0
  213. package/src/courseFormat/tasks/CodeTask.ts +42 -0
  214. package/src/courseFormat/tasks/DataTask.ts +37 -0
  215. package/src/courseFormat/tasks/EduTask.ts +26 -0
  216. package/src/courseFormat/tasks/IdeTask.ts +17 -0
  217. package/src/courseFormat/tasks/NumberTask.ts +17 -0
  218. package/src/courseFormat/tasks/OutputTask.ts +21 -0
  219. package/src/courseFormat/tasks/OutputTaskBase.ts +23 -0
  220. package/src/courseFormat/tasks/RemoteEduTask.ts +18 -0
  221. package/src/courseFormat/tasks/StringTask.ts +17 -0
  222. package/src/courseFormat/tasks/TableTask.ts +51 -0
  223. package/src/courseFormat/tasks/Task.ts +181 -0
  224. package/src/courseFormat/tasks/TheoryTask.ts +19 -0
  225. package/src/courseFormat/tasks/UnsupportedTask.ts +17 -0
  226. package/src/courseFormat/tasks/choice/ChoiceOption.ts +37 -0
  227. package/src/courseFormat/tasks/choice/ChoiceOptionStatus.ts +5 -0
  228. package/src/courseFormat/tasks/choice/ChoiceTask.ts +57 -0
  229. package/src/courseFormat/tasks/matching/MatchingTask.ts +19 -0
  230. package/src/courseFormat/tasks/matching/SortingBasedTask.ts +59 -0
  231. package/src/courseFormat/tasks/matching/SortingTask.ts +17 -0
  232. package/src/courseFormat/uiMessages.ts +12 -0
  233. package/src/disk-loader.ts +463 -0
  234. package/src/index.ts +33 -0
  235. package/src/models.ts +54 -0
  236. package/src/zip-loader.ts +583 -0
  237. package/test/load-course.test.js +279 -0
  238. package/test/load-zip-course.test.js +73 -0
  239. package/tsconfig.json +15 -0
@@ -0,0 +1,17 @@
1
+ import { CheckStatus } from "../CheckStatus"
2
+ import { Task } from "./Task"
3
+
4
+ export class IdeTask extends Task {
5
+ constructor()
6
+ constructor(name: string)
7
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
8
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
9
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
10
+ }
11
+
12
+ get itemType(): string {
13
+ return IdeTask.IDE_TASK_TYPE
14
+ }
15
+
16
+ static IDE_TASK_TYPE = "ide"
17
+ }
@@ -0,0 +1,17 @@
1
+ import { CheckStatus } from "../CheckStatus"
2
+ import { AnswerTask } from "./AnswerTask"
3
+
4
+ export class NumberTask extends AnswerTask {
5
+ constructor()
6
+ constructor(name: string)
7
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
8
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
9
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
10
+ }
11
+
12
+ get itemType(): string {
13
+ return NumberTask.NUMBER_TASK_TYPE
14
+ }
15
+
16
+ static NUMBER_TASK_TYPE = "number"
17
+ }
@@ -0,0 +1,21 @@
1
+ import { CheckStatus } from "../CheckStatus"
2
+ import { OutputTaskBase } from "./OutputTaskBase"
3
+
4
+ export class OutputTask extends OutputTaskBase {
5
+ constructor()
6
+ constructor(name: string)
7
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
8
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
9
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
10
+ }
11
+
12
+ get itemType(): string {
13
+ return OutputTask.OUTPUT_TASK_TYPE
14
+ }
15
+
16
+ get isToSubmitToRemote(): boolean {
17
+ return this.checkStatus !== CheckStatus.Unchecked
18
+ }
19
+
20
+ static OUTPUT_TASK_TYPE = "output"
21
+ }
@@ -0,0 +1,23 @@
1
+ import { CheckStatus } from "../CheckStatus"
2
+ import { Task } from "./Task"
3
+
4
+ export abstract class OutputTaskBase extends Task {
5
+ inputFileName = OutputTaskBase.INPUT_PATTERN_NAME
6
+ outputFileName = OutputTaskBase.OUTPUT_PATTERN_NAME
7
+ latestOutputFileName = OutputTaskBase.LATEST_OUTPUT_PATTERN_NAME
8
+
9
+ constructor()
10
+ constructor(name: string)
11
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
12
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
13
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
14
+ }
15
+
16
+ get supportSubmissions(): boolean {
17
+ return true
18
+ }
19
+
20
+ static OUTPUT_PATTERN_NAME = "output.txt"
21
+ static INPUT_PATTERN_NAME = "input.txt"
22
+ static LATEST_OUTPUT_PATTERN_NAME = "latest_output.txt"
23
+ }
@@ -0,0 +1,18 @@
1
+ import { CheckStatus } from "../CheckStatus"
2
+ import { EduTask } from "./EduTask"
3
+
4
+ export class RemoteEduTask extends EduTask {
5
+ checkProfile = ""
6
+
7
+ constructor()
8
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
9
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
10
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
11
+ }
12
+
13
+ get itemType(): string {
14
+ return RemoteEduTask.REMOTE_EDU_TASK_TYPE
15
+ }
16
+
17
+ static REMOTE_EDU_TASK_TYPE = "remote_edu"
18
+ }
@@ -0,0 +1,17 @@
1
+ import { CheckStatus } from "../CheckStatus"
2
+ import { AnswerTask } from "./AnswerTask"
3
+
4
+ export class StringTask extends AnswerTask {
5
+ constructor()
6
+ constructor(name: string)
7
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
8
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
9
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
10
+ }
11
+
12
+ get itemType(): string {
13
+ return StringTask.STRING_TASK_TYPE
14
+ }
15
+
16
+ static STRING_TASK_TYPE = "string"
17
+ }
@@ -0,0 +1,51 @@
1
+ import { CheckStatus } from "../CheckStatus"
2
+ import { Task } from "./Task"
3
+ import { logger } from "../loggerUtils"
4
+
5
+ export class TableTask extends Task {
6
+ isMultipleChoice = false
7
+ rows: string[] = []
8
+ columns: string[] = []
9
+ selected: boolean[][] = []
10
+
11
+ constructor()
12
+ constructor(name: string)
13
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
14
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
15
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
16
+ }
17
+
18
+ get itemType(): string {
19
+ return TableTask.TABLE_TASK_TYPE
20
+ }
21
+
22
+ choose(rowIndex: number, columnIndex: number): void {
23
+ if (rowIndex < 0 || rowIndex >= this.rows.length || columnIndex < 0 || columnIndex >= this.columns.length) {
24
+ logger("TableTask").error("There was an attempt to choose a cell with invalid indices")
25
+ return
26
+ }
27
+ if (!this.isMultipleChoice) {
28
+ this.clearSelectedInRow(rowIndex)
29
+ }
30
+ this.selected[rowIndex][columnIndex] = !this.selected[rowIndex][columnIndex]
31
+ }
32
+
33
+ clearSelectedVariants(): void {
34
+ for (let rowIndex = 0; rowIndex < this.selected.length; rowIndex += 1) {
35
+ this.clearSelectedInRow(rowIndex)
36
+ }
37
+ }
38
+
39
+ private clearSelectedInRow(rowIndex: number): void {
40
+ this.selected[rowIndex] = this.selected[rowIndex].map(() => false)
41
+ }
42
+
43
+ createTable(rows: string[], columns: string[], isCheckbox = false): void {
44
+ this.rows = rows
45
+ this.columns = columns
46
+ this.isMultipleChoice = isCheckbox
47
+ this.selected = rows.map(() => columns.map(() => false))
48
+ }
49
+
50
+ static TABLE_TASK_TYPE = "table"
51
+ }
@@ -0,0 +1,181 @@
1
+ import { CheckFeedback } from "../CheckFeedback"
2
+ import { CheckStatus } from "../CheckStatus"
3
+ import { DescriptionFormat } from "../DescriptionFormat"
4
+ import { ItemContainer } from "../ItemContainer"
5
+ import type { Lesson } from "../Lesson"
6
+ import { Course } from "../Course"
7
+ import { TaskFile } from "../TaskFile"
8
+ import { StudyItem } from "../StudyItem"
9
+ import { logger } from "../loggerUtils"
10
+
11
+ function getLessonModuleLazy(): { Lesson: typeof Lesson } {
12
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
13
+ return require("../Lesson")
14
+ }
15
+
16
+ export abstract class Task extends StudyItem {
17
+ private _taskFiles: Map<string, TaskFile> = new Map()
18
+
19
+ get taskFiles(): Map<string, TaskFile> {
20
+ return this._taskFiles
21
+ }
22
+
23
+ set taskFiles(value: Map<string, TaskFile>) {
24
+ this._taskFiles = value
25
+ }
26
+
27
+ feedback?: CheckFeedback
28
+ descriptionText = ""
29
+ descriptionFormat: DescriptionFormat = DescriptionFormat.MD
30
+ feedbackLink?: string
31
+ solutionHidden?: boolean
32
+ record = -1
33
+ isUpToDate = true
34
+
35
+ protected checkStatus: CheckStatus = CheckStatus.Unchecked
36
+
37
+ get status(): CheckStatus {
38
+ return this.checkStatus
39
+ }
40
+
41
+ set status(status: CheckStatus) {
42
+ for (const taskFile of this._taskFiles.values()) {
43
+ for (const placeholder of taskFile.answerPlaceholders) {
44
+ placeholder.status = status
45
+ }
46
+ }
47
+ if (this.checkStatus !== status) {
48
+ this.feedback = undefined
49
+ }
50
+ this.checkStatus = status
51
+ }
52
+
53
+ get lesson(): Lesson {
54
+ const LessonClass = getLessonModuleLazy().Lesson
55
+ if (!(this.parent instanceof LessonClass)) {
56
+ throw new Error(`Lesson is null for task ${this.name}`)
57
+ }
58
+ return this.parent as Lesson
59
+ }
60
+
61
+ get isPluginTaskType(): boolean {
62
+ return true
63
+ }
64
+
65
+ get supportSubmissions(): boolean {
66
+ return false
67
+ }
68
+
69
+ get isToSubmitToRemote(): boolean {
70
+ return false
71
+ }
72
+
73
+ get isChangedOnFailed(): boolean {
74
+ return false
75
+ }
76
+
77
+ get course(): Course {
78
+ return this.lesson.course
79
+ }
80
+
81
+ get files(): TaskFile[] {
82
+ return this.getTaskFileValues()
83
+ }
84
+
85
+ constructor()
86
+ constructor(name: string)
87
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
88
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
89
+ super(name)
90
+ if (id !== undefined) this.id = id
91
+ if (position !== undefined) this.index = position
92
+ if (updateDate !== undefined) this.updateDate = updateDate
93
+ if (status !== undefined) this.checkStatus = status
94
+ }
95
+
96
+ override init(parentItem: ItemContainer, isRestarted: boolean): void {
97
+ this.parent = parentItem
98
+ for (const taskFile of this._taskFiles.values()) {
99
+ taskFile.initTaskFile(this, isRestarted)
100
+ }
101
+ }
102
+
103
+ getTaskFile(name: string): TaskFile | undefined {
104
+ return this._taskFiles.get(name)
105
+ }
106
+
107
+ addTaskFile(name: string, isVisible = true): TaskFile {
108
+ const taskFile = new TaskFile()
109
+ taskFile.task = this
110
+ taskFile.name = name
111
+ taskFile.isVisible = isVisible
112
+ this._taskFiles.set(name, taskFile)
113
+ return taskFile
114
+ }
115
+
116
+ addTaskFileInstance(taskFile: TaskFile): void {
117
+ taskFile.task = this
118
+ this._taskFiles.set(taskFile.name, taskFile)
119
+ }
120
+
121
+ addTaskFileAt(taskFile: TaskFile, position: number): void {
122
+ taskFile.task = this
123
+ if (position < 0 || position > this._taskFiles.size) {
124
+ throw new Error("IndexOutOfBounds")
125
+ }
126
+ const entries = Array.from(this._taskFiles.entries())
127
+ entries.splice(position, 0, [taskFile.name, taskFile])
128
+ this._taskFiles = new Map(entries)
129
+ }
130
+
131
+ setTaskFileValues(taskFiles: TaskFile[]): void {
132
+ this._taskFiles.clear()
133
+ for (const taskFile of taskFiles) {
134
+ this._taskFiles.set(taskFile.name, taskFile)
135
+ }
136
+ }
137
+
138
+ getTaskFileValues(): TaskFile[] {
139
+ return Array.from(this._taskFiles.values())
140
+ }
141
+
142
+ removeTaskFile(taskFile: string): TaskFile | undefined {
143
+ const value = this._taskFiles.get(taskFile)
144
+ this._taskFiles.delete(taskFile)
145
+ return value
146
+ }
147
+
148
+ taskFileIndex(taskFile: string): number | undefined {
149
+ const index = Array.from(this._taskFiles.keys()).indexOf(taskFile)
150
+ return index === -1 ? undefined : index
151
+ }
152
+
153
+ equals(other: Task): boolean {
154
+ if (this === other) return true
155
+ if (this.index !== other.index) return false
156
+ if (this.name !== other.name) return false
157
+ if (this._taskFiles !== other._taskFiles) return false
158
+ if (this.descriptionText !== other.descriptionText) return false
159
+ return this.descriptionFormat === other.descriptionFormat
160
+ }
161
+
162
+ hashCode(): number {
163
+ let result = hashString(this.name)
164
+ result = 31 * result + this.index
165
+ result = 31 * result + this._taskFiles.size
166
+ result = 31 * result + hashString(this.descriptionText)
167
+ result = 31 * result + hashString(this.descriptionFormat)
168
+ return result
169
+ }
170
+
171
+ static LOG = logger("Task")
172
+ }
173
+
174
+ function hashString(value: string): number {
175
+ let hash = 0
176
+ for (let i = 0; i < value.length; i += 1) {
177
+ hash = (hash << 5) - hash + value.charCodeAt(i)
178
+ hash |= 0
179
+ }
180
+ return hash
181
+ }
@@ -0,0 +1,19 @@
1
+ import { CheckStatus } from "../CheckStatus"
2
+ import { Task } from "./Task"
3
+
4
+ export class TheoryTask extends Task {
5
+ postSubmissionOnOpen = true
6
+
7
+ constructor()
8
+ constructor(name: string)
9
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
10
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
11
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
12
+ }
13
+
14
+ get itemType(): string {
15
+ return TheoryTask.THEORY_TASK_TYPE
16
+ }
17
+
18
+ static THEORY_TASK_TYPE = "theory"
19
+ }
@@ -0,0 +1,17 @@
1
+ import { CheckStatus } from "../CheckStatus"
2
+ import { Task } from "./Task"
3
+
4
+ export class UnsupportedTask extends Task {
5
+ constructor()
6
+ constructor(name: string)
7
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
8
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
9
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
10
+ }
11
+
12
+ get itemType(): string {
13
+ return UnsupportedTask.UNSUPPORTED_TASK_TYPE
14
+ }
15
+
16
+ static UNSUPPORTED_TASK_TYPE = "unsupported"
17
+ }
@@ -0,0 +1,37 @@
1
+ import { ChoiceOptionStatus } from "./ChoiceOptionStatus"
2
+
3
+ export class ChoiceOption {
4
+ text = ""
5
+ status: ChoiceOptionStatus = ChoiceOptionStatus.UNKNOWN
6
+
7
+ constructor()
8
+ constructor(text: string)
9
+ constructor(text: string, status: ChoiceOptionStatus)
10
+ constructor(text?: string, status?: ChoiceOptionStatus) {
11
+ if (text !== undefined) {
12
+ this.text = text
13
+ }
14
+ if (status !== undefined) {
15
+ this.status = status
16
+ }
17
+ }
18
+
19
+ equals(other: ChoiceOption): boolean {
20
+ return this.text === other.text && this.status === other.status
21
+ }
22
+
23
+ hashCode(): number {
24
+ let result = hashString(this.text)
25
+ result = 31 * result + hashString(this.status)
26
+ return result
27
+ }
28
+ }
29
+
30
+ function hashString(value: string): number {
31
+ let hash = 0
32
+ for (let i = 0; i < value.length; i += 1) {
33
+ hash = (hash << 5) - hash + value.charCodeAt(i)
34
+ hash |= 0
35
+ }
36
+ return hash
37
+ }
@@ -0,0 +1,5 @@
1
+ export enum ChoiceOptionStatus {
2
+ CORRECT = "CORRECT",
3
+ INCORRECT = "INCORRECT",
4
+ UNKNOWN = "UNKNOWN",
5
+ }
@@ -0,0 +1,57 @@
1
+ import { CheckStatus } from "../../CheckStatus"
2
+ import { message } from "../../uiMessages"
3
+ import { Task } from "../Task"
4
+ import { ChoiceOption } from "./ChoiceOption"
5
+
6
+ export class ChoiceTask extends Task {
7
+ choiceOptions: ChoiceOption[] = []
8
+ isMultipleChoice = false
9
+ selectedVariants: number[] = []
10
+ messageCorrect: string = message("check.correct.solution")
11
+ messageIncorrect: string = message("check.incorrect.solution")
12
+ quizHeader?: string
13
+ canCheckLocally = true
14
+
15
+ constructor()
16
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
17
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
18
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
19
+ }
20
+
21
+ get itemType(): string {
22
+ return ChoiceTask.CHOICE_TASK_TYPE
23
+ }
24
+
25
+ get isPluginTaskType(): boolean {
26
+ return false
27
+ }
28
+
29
+ get isChangedOnFailed(): boolean {
30
+ return !this.canCheckLocally
31
+ }
32
+
33
+ get isToSubmitToRemote(): boolean {
34
+ return this.status !== CheckStatus.Unchecked
35
+ }
36
+
37
+ get presentableQuizHeader(): string {
38
+ if (this.quizHeader) return this.quizHeader
39
+ return this.isMultipleChoice
40
+ ? message("course.creator.create.choice.task.multiple.label")
41
+ : message("course.creator.create.choice.task.single.label")
42
+ }
43
+
44
+ addSelectedVariant(variant: number): void {
45
+ this.selectedVariants.push(variant)
46
+ }
47
+
48
+ removeSelectedVariant(variant: number): void {
49
+ this.selectedVariants = this.selectedVariants.filter((value) => value !== variant)
50
+ }
51
+
52
+ clearSelectedVariants(): void {
53
+ this.selectedVariants = []
54
+ }
55
+
56
+ static CHOICE_TASK_TYPE = "choice"
57
+ }
@@ -0,0 +1,19 @@
1
+ import { CheckStatus } from "../../CheckStatus"
2
+ import { SortingBasedTask } from "./SortingBasedTask"
3
+
4
+ export class MatchingTask extends SortingBasedTask {
5
+ captions: string[] = []
6
+
7
+ constructor()
8
+ constructor(name: string)
9
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
10
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
11
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
12
+ }
13
+
14
+ get itemType(): string {
15
+ return MatchingTask.MATCHING_TASK_TYPE
16
+ }
17
+
18
+ static MATCHING_TASK_TYPE = "matching"
19
+ }
@@ -0,0 +1,59 @@
1
+ import { CheckStatus } from "../../CheckStatus"
2
+ import { logger } from "../../loggerUtils"
3
+ import { Task } from "../Task"
4
+
5
+ export abstract class SortingBasedTask extends Task {
6
+ private _options: string[] = []
7
+ ordering: number[] = []
8
+
9
+ get options(): string[] {
10
+ return this._options
11
+ }
12
+
13
+ set options(value: string[]) {
14
+ this.ordering = value.map((_, index) => index)
15
+ this._options = value
16
+ }
17
+
18
+ constructor()
19
+ constructor(name: string)
20
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
21
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
22
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
23
+ }
24
+
25
+ getOrderedOptions(): string[] {
26
+ return this.options.map((_, index) => this.options[this.ordering[index]])
27
+ }
28
+
29
+ moveOptionUp(index: number): void {
30
+ const targetIndex = index - 1
31
+ if (targetIndex < 0) {
32
+ const message = "There was an attempt to move the option up out of bounds"
33
+ logger("SortingBasedTask").error(message)
34
+ throw new Error(message)
35
+ }
36
+ this.swapOptions(index, targetIndex)
37
+ }
38
+
39
+ moveOptionDown(index: number): void {
40
+ const targetIndex = index + 1
41
+ if (targetIndex === this.options.length) {
42
+ const message = "There was an attempt to move the option down out of bounds"
43
+ logger("SortingBasedTask").error(message)
44
+ throw new Error(message)
45
+ }
46
+ this.swapOptions(index, targetIndex)
47
+ }
48
+
49
+ swapOptions(firstColumnIndex: number, secondColumnIndex: number): void {
50
+ const firstOptionIndex = this.ordering[firstColumnIndex]
51
+ const secondOptionIndex = this.ordering[secondColumnIndex]
52
+ this.ordering[firstColumnIndex] = secondOptionIndex
53
+ this.ordering[secondColumnIndex] = firstOptionIndex
54
+ }
55
+
56
+ restoreInitialOrdering(): void {
57
+ this.ordering = this.options.map((_, index) => index)
58
+ }
59
+ }
@@ -0,0 +1,17 @@
1
+ import { CheckStatus } from "../../CheckStatus"
2
+ import { SortingBasedTask } from "./SortingBasedTask"
3
+
4
+ export class SortingTask extends SortingBasedTask {
5
+ constructor()
6
+ constructor(name: string)
7
+ constructor(name: string, id: number, position: number, updateDate: Date, status: CheckStatus)
8
+ constructor(name?: string, id?: number, position?: number, updateDate?: Date, status?: CheckStatus) {
9
+ super(name as string, id as number, position as number, updateDate as Date, status as CheckStatus)
10
+ }
11
+
12
+ get itemType(): string {
13
+ return SortingTask.SORTING_TASK_TYPE
14
+ }
15
+
16
+ static SORTING_TASK_TYPE = "sorting"
17
+ }
@@ -0,0 +1,12 @@
1
+ const FORMAT_BUNDLE = "messages.EduFormatBundle"
2
+
3
+ export function message(key: string, ...params: Array<string | number>): string {
4
+ if (params.length === 0) {
5
+ return key
6
+ }
7
+ return params.reduce((result: string, param, index) => {
8
+ return result.replace(new RegExp(`\\{${index}\\}`, "g"), String(param))
9
+ }, key)
10
+ }
11
+
12
+ export { FORMAT_BUNDLE }