Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ import org.hyperskill.academy.learning.yaml.format.student.StudentTaskYamlMixin
class RemoteEduTaskYamlMixin : StudentTaskYamlMixin() {
@get:JsonProperty(CHECK_PROFILE)
@set:JsonProperty(CHECK_PROFILE)
@get:JsonInclude(JsonInclude.Include.ALWAYS)
@get:JsonInclude(JsonInclude.Include.NON_EMPTY)
var checkProfile: String = ""
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ sealed class Change {
@Throws(IOException::class)
constructor(input: DataInput) : super(input)
override fun apply(state: MutableMap<String, String>) {
state[path] = text
state -= path
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.hyperskill.academy.learning.courseFormat.CheckStatus
import org.hyperskill.academy.learning.courseFormat.Course
import org.hyperskill.academy.learning.courseFormat.FrameworkLesson
import org.hyperskill.academy.learning.courseFormat.InMemoryTextualContents
import org.hyperskill.academy.learning.courseFormat.TaskFile
import org.hyperskill.academy.learning.courseFormat.ext.*
import org.hyperskill.academy.learning.courseFormat.tasks.Task
import org.hyperskill.academy.learning.courseGeneration.GeneratorUtils
Expand Down Expand Up @@ -303,12 +304,29 @@ abstract class SolutionLoaderBase(protected val project: Project) : Disposable {
// storeOriginalTemplateFiles uses task.taskFiles which may have stale disk content.
frameworkLessonManager.ensureTemplateFilesCached(task)

val solutionMap = taskSolutions.solutions.mapValues { it.value.text }
val solutionMap = taskSolutions.visibleSolutions()
frameworkLessonManager.saveExternalChanges(task, solutionMap, taskSolutions.submissionId)
for (taskFile in task.taskFiles.values) {
val solution = taskSolutions.solutions[taskFile.name] ?: continue

taskFile.isVisible = solution.isVisible
var taskFilesChanged = false
for ((path, solution) in taskSolutions.solutions) {
val taskFile = task.getTaskFile(path)
if (taskFile == null) {
if (!solution.isVisible) continue

task.addTaskFile(TaskFile(path, solution.text).apply {
isVisible = solution.isVisible
isLearnerCreated = true
})
taskFilesChanged = true
}
else if (taskFile.isVisible != solution.isVisible) {
taskFile.isVisible = solution.isVisible
taskFilesChanged = true
}
}

if (taskFilesChanged) {
YamlFormatSynchronizer.saveItem(task)
}
}

Expand All @@ -317,15 +335,9 @@ abstract class SolutionLoaderBase(protected val project: Project) : Disposable {
for ((path, solution) in taskSolutions.solutions) {
val taskFile = task.getTaskFile(path)

// Skip test files from submissions to prevent corrupted tests from being applied
// Test files should always come from step source (API), not from user submissions
// See ALT-10961: user submissions may contain stale test files from previous stages
if (taskFile != null && !taskFile.isLearnerCreated && taskFile.isTestFile) {
LOG.warn("Skipping test file '$path' from submission for task '${task.name}' - test files should come from API, not submissions")
continue
}

if (taskFile == null) {
if (!solution.isVisible) continue

GeneratorUtils.createChildFile(project, taskDir, path, InMemoryTextualContents(solution.text))
val createdFile = task.getTaskFile(path)
if (createdFile == null) {
Expand Down Expand Up @@ -356,10 +368,15 @@ abstract class SolutionLoaderBase(protected val project: Project) : Disposable {
val lesson = task.lesson
if (lesson is FrameworkLesson) {
val frameworkLessonManager = FrameworkLessonManager.getInstance(project)
val solutionMap = taskSolutions.solutions.mapValues { it.value.text }
val solutionMap = taskSolutions.visibleSolutions()
frameworkLessonManager.saveExternalChanges(task, solutionMap, taskSolutions.submissionId)
}
}

private fun TaskSolutions.visibleSolutions(): Map<String, String> =
solutions
.filter { (_, solution) -> solution.isVisible }
.mapValues { (_, solution) -> solution.text }
}

protected data class Solution(val text: String, val isVisible: Boolean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,18 @@ private fun FileEditor.setViewer(isViewer: Boolean) {
private val FileEditor.loadingPanel: JBLoadingPanel?
get() = UIUtil.findComponentOfType(component, JBLoadingPanel::class.java)

fun VirtualFile.findFileByRelativePathOrSelf(path: String): VirtualFile? {
return if (path.isEmpty()) this else findFileByRelativePath(path)
}

fun VirtualFile.getSection(project: Project): Section? {
return getSection(project.toCourseInfoHolder())
}

fun VirtualFile.getSection(holder: CourseInfoHolder<out Course?>): Section? {
val course = holder.course ?: return null
if (!isDirectory) return null
return if (holder.courseDir.findFileByRelativePath(course.customContentPath) == parent) course.getSection(name) else null
return if (holder.courseDir.findFileByRelativePathOrSelf(course.customContentPath) == parent) course.getSection(name) else null
}

fun VirtualFile.isSectionDirectory(project: Project): Boolean {
Expand All @@ -104,7 +108,7 @@ fun VirtualFile.getLesson(holder: CourseInfoHolder<out Course?>): Lesson? {
if (section != null) {
return section.getLesson(name)
}
return if (holder.courseDir.findFileByRelativePath(course.customContentPath) == parent) course.getLesson(name) else null
return if (holder.courseDir.findFileByRelativePathOrSelf(course.customContentPath) == parent) course.getLesson(name) else null
}

fun VirtualFile.isLessonDirectory(project: Project): Boolean {
Expand Down Expand Up @@ -135,6 +139,9 @@ fun VirtualFile.getTask(project: Project): Task? {
fun VirtualFile.getTask(holder: CourseInfoHolder<out Course?>): Task? {
if (!isDirectory) return null
val lesson: Lesson = parent?.getLesson(holder) ?: return null
if (lesson is FrameworkLesson && name == TASK) {
return lesson.currentTask()
}
return lesson.getTask(name)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.hyperskill.academy.coursecreator.StudyItemType
import org.hyperskill.academy.coursecreator.StudyItemType.*
import org.hyperskill.academy.learning.courseFormat.*
import org.hyperskill.academy.learning.courseFormat.tasks.Task
import org.hyperskill.academy.learning.findFileByRelativePathOrSelf

val StudyItem.studyItemType: StudyItemType
get() {
Expand All @@ -22,12 +23,12 @@ fun StudyItem.getDir(courseDir: VirtualFile): VirtualFile? {
is Course -> courseDir
is Section -> {
val sectionParent = (parentOrNull as? StudyItem) ?: return null
courseDir.findFileByRelativePath(sectionParent.getPathToChildren())?.findChild(name)
courseDir.findFileByRelativePathOrSelf(sectionParent.getPathToChildren())?.findChild(name)
}

is Lesson -> {
val lessonParent = (parentOrNull as? StudyItem) ?: return null
lessonParent.getDir(courseDir)?.findFileByRelativePath(lessonParent.getPathToChildren())?.findChild(name)
lessonParent.getDir(courseDir)?.findFileByRelativePathOrSelf(lessonParent.getPathToChildren())?.findChild(name)
}

is Task -> (parentOrNull as? Lesson)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,10 @@ val Task.dirName: String
}

val Task.targetDirName: String
get() = when (this) {
is TheoryTask,
is CodeTask -> name

else -> dirName
get() = when {
this is TheoryTask || this is CodeTask -> name
isFrameworkTask -> dirName
else -> name
}

fun Task.findSourceDir(taskDir: VirtualFile): VirtualFile? {
Expand Down Expand Up @@ -238,4 +237,4 @@ fun Task.getTaskText(project: Project): String? {
return taskDescription
}

private val LOG = logger<Task>()
private val LOG = logger<Task>()
Loading
Loading