Skip to content

Commit 4ba2c7a

Browse files
authored
Merge pull request #1195 from Aalto-LeTech/misc-fixes
Misc fixes
2 parents b2ebe2a + 054d8fe commit 4ba2c7a

File tree

11 files changed

+90
-70
lines changed

11 files changed

+90
-70
lines changed

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88

99
- Support for the Programming 2 course and SBT modules
1010

11+
### Fixed
12+
13+
- Do not submit backups of updated modules
14+
1115
## [4.2.0] - 2025-01-08
1216

1317
### Fixed

src/main/kotlin/fi/aalto/cs/apluscourses/generator/APlusModuleBuilder.kt

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,21 @@ internal class APlusModuleBuilder : ModuleBuilder() {
4949
super.createAndCommitIfNeeded(project, model, true)
5050
}
5151

52-
project.service<ProjectInitializationTracker>()
53-
.addInitializationTask {
54-
val startTime = System.currentTimeMillis()
52+
if (config.programmingLanguage == "scala") {
5553

56-
// The version string only starts with "java version" when the JDK is not downloaded completely
57-
while ((ProjectRootManager.getInstance(
58-
project
59-
).projectSdk?.versionString?.startsWith("java version") != false) && System.currentTimeMillis() - startTime < 300 * 1000
60-
) {
61-
Thread.sleep(1000)
62-
}
63-
}
54+
project.service<ProjectInitializationTracker>()
55+
.addInitializationTask {
56+
val startTime = System.currentTimeMillis()
6457

58+
// The version string only starts with "java version" when the JDK is not downloaded completely
59+
while ((ProjectRootManager.getInstance(
60+
project
61+
).projectSdk?.versionString?.startsWith("java version") != false) && System.currentTimeMillis() - startTime < 300 * 1000
62+
) {
63+
Thread.sleep(1000)
64+
}
65+
}
66+
}
6567

6668
return listOf(module)
6769
}

src/main/kotlin/fi/aalto/cs/apluscourses/generator/CourseSelectStep.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ import javax.swing.JComponent
2727
import javax.swing.JList
2828

2929
class CourseSelectStep(private val config: APlusModuleConfig) : ModuleWizardStep() {
30-
private val courses = AtomicProperty<List<CoursesFetcher.CourseConfig>>(emptyList())
30+
private val courses = AtomicProperty<List<CoursesFetcher.CourseInfo>>(emptyList())
3131

3232
private val courseConfigUrl = AtomicProperty("")
33-
private val course = AtomicProperty<CoursesFetcher.CourseConfig?>(null)
33+
private val course = AtomicProperty<CoursesFetcher.CourseInfo?>(null)
3434
private val language = AtomicProperty(PluginSettings.SUPPORTED_LANGUAGES.first())
3535

3636
init {
@@ -62,10 +62,10 @@ class CourseSelectStep(private val config: APlusModuleConfig) : ModuleWizardStep
6262
text(message("generator.APlusModuleBuilder.selectCourse"))
6363
}
6464
row {
65-
list(courses, object : ColoredListCellRenderer<CoursesFetcher.CourseConfig>() {
65+
list(courses, object : ColoredListCellRenderer<CoursesFetcher.CourseInfo>() {
6666
override fun customizeCellRenderer(
67-
list: JList<out CoursesFetcher.CourseConfig>,
68-
item: CoursesFetcher.CourseConfig?,
67+
list: JList<out CoursesFetcher.CourseInfo>,
68+
item: CoursesFetcher.CourseInfo?,
6969
index: Int,
7070
selected: Boolean,
7171
hasFocus: Boolean

src/main/kotlin/fi/aalto/cs/apluscourses/model/component/Module.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import fi.aalto.cs.apluscourses.utils.FileUtil
2424
import fi.aalto.cs.apluscourses.utils.Version
2525
import kotlinx.coroutines.Dispatchers
2626
import kotlinx.coroutines.withContext
27+
import org.jetbrains.annotations.NonNls
2728
import java.nio.file.Path
2829
import java.nio.file.StandardCopyOption
2930
import kotlin.io.path.exists
@@ -51,8 +52,7 @@ open class Module(
5152
get() = CourseFileManager.getInstance(project).getMetadata(name)
5253

5354
override fun findDependencies(): Set<String> {
54-
val module = platformObject
55-
if (module == null) return emptySet()
55+
val module = platformObject ?: return emptySet()
5656
return module
5757
.rootManager
5858
.orderEntries()
@@ -130,7 +130,7 @@ open class Module(
130130
!UpdateModuleDialog(project, this@Module, filesWithChanges).showAndGet()
131131
}
132132
if (canceled) return
133-
val backupDir = fullPath.resolve("backup")
133+
val backupDir = fullPath.resolve(backupDir)
134134

135135
for (file in filesWithChanges) {
136136
val relativePath = fullPath.relativize(file)
@@ -143,12 +143,12 @@ open class Module(
143143
}
144144
}
145145

146-
FileUtil.deleteFilesInDirectory(fullPath.toFile(), fullPath.resolve("backup"))
146+
FileUtil.deleteFilesInDirectory(fullPath.toFile(), fullPath.resolve(backupDir))
147147
downloadAndInstall(updating = true)
148148

149149
val newFiles = FileUtil.getAllFilesInDirectory(fullPath.toFile())
150-
val deletedFiles = allFiles - newFiles
151-
val addedFiles = newFiles - allFiles
150+
val deletedFiles = allFiles - newFiles.toSet()
151+
val addedFiles = newFiles - allFiles.toSet()
152152
Notifier.notifyAndHide(
153153
ModuleUpdatedNotification(this, addedFiles, deletedFiles),
154154
project
@@ -199,6 +199,9 @@ open class Module(
199199
}
200200

201201
companion object {
202+
@NonNls
203+
val backupDir: String = "backup"
204+
202205

203206
/**
204207
* This class is a [RootPolicy] that builds a set of the names of those

src/main/kotlin/fi/aalto/cs/apluscourses/services/ModuleImportExport.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,10 @@ class ModuleImportExport(
6161

6262
val zipFile = FileUtil.createTempDirectory("apluscourses", "modules")
6363
zip.entries().asSequence().forEach { entry ->
64-
// Fix: Replace backslashes with slashes in entry names
65-
val fixedEntryName = entry.name.replace('\\', '/')
66-
val entryName = if (fixedEntryName.endsWith(".iml")) {
64+
val entryName = if (entry.name.endsWith(".iml")) {
6765
"$desiredModuleName.iml"
6866
} else {
69-
fixedEntryName
67+
entry.name
7068
}
7169
val entryFile = File(zipFile, entryName)
7270

@@ -193,7 +191,9 @@ class ModuleImportExport(
193191

194192
moduleDir.walkTopDown()
195193
.forEach { file ->
196-
// Fix: Use forward slashes in zip entry names
194+
// File names in ZIP should always use forward slashes.
195+
// See section 4.4.17 of the ".ZIP File Format Specification" v6.3.6 FINAL.
196+
// Available online: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
197197
val relativePath = file.relativeTo(moduleDir).invariantSeparatorsPath
198198
val entryName = if (file.isDirectory) {
199199
"$relativePath/"

src/main/kotlin/fi/aalto/cs/apluscourses/services/course/CoursesFetcher.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package fi.aalto.cs.apluscourses.services.course
33
import com.intellij.openapi.components.Service
44
import fi.aalto.cs.apluscourses.api.CourseConfig
55
import fi.aalto.cs.apluscourses.utils.CoursesLogger
6-
import io.ktor.client.HttpClient
7-
import io.ktor.client.engine.cio.CIO
8-
import io.ktor.client.request.get
9-
import io.ktor.client.statement.bodyAsText
6+
import io.ktor.client.*
7+
import io.ktor.client.engine.cio.*
8+
import io.ktor.client.request.*
9+
import io.ktor.client.statement.*
1010
import kotlinx.coroutines.CoroutineScope
1111
import kotlinx.coroutines.launch
1212
import kotlinx.coroutines.runBlocking
@@ -16,16 +16,16 @@ import org.yaml.snakeyaml.Yaml
1616

1717
@Service(Service.Level.APP)
1818
class CoursesFetcher(private val cs: CoroutineScope) {
19-
data class CourseConfig(
19+
data class CourseInfo(
2020
val name: String,
2121
val semester: String,
2222
val url: String,
2323
val language: String?
2424
) {
2525
companion object {
2626
@NonNls
27-
fun fromYaml(map: Map<String, String>): CourseConfig {
28-
return CourseConfig(map["name"]!!, map["semester"]!!, map["url"]!!, map["language"])
27+
fun fromYaml(map: Map<String, String>): CourseInfo {
28+
return CourseInfo(map["name"]!!, map["semester"]!!, map["url"]!!, map["language"])
2929
}
3030
}
3131
}
@@ -35,7 +35,7 @@ class CoursesFetcher(private val cs: CoroutineScope) {
3535
isLenient = true
3636
}
3737

38-
fun fetchCourses(setCourses: (List<CoursesFetcher.CourseConfig>) -> Unit) {
38+
fun fetchCourses(setCourses: (List<CourseInfo>) -> Unit) {
3939
cs.launch {
4040
val client = HttpClient(CIO) {
4141
engine {
@@ -48,7 +48,7 @@ class CoursesFetcher(private val cs: CoroutineScope) {
4848
val courses = Yaml()
4949
.load<List<Map<String, String>>>(res.bodyAsText())
5050
.map { course: Map<String, String> ->
51-
CourseConfig.fromYaml(course)
51+
CourseInfo.fromYaml(course)
5252
}
5353

5454
setCourses(courses)

src/main/kotlin/fi/aalto/cs/apluscourses/services/exercise/ExercisesTreeFilter.kt

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ import com.intellij.util.messages.Topic
88
import com.intellij.util.messages.Topic.ProjectLevel
99
import com.intellij.util.xmlb.annotations.XCollection
1010
import fi.aalto.cs.apluscourses.BUNDLE
11-
import fi.aalto.cs.apluscourses.ui.exercise.ExercisesView.ExerciseGroupItem
12-
import fi.aalto.cs.apluscourses.ui.exercise.ExercisesView.ExerciseItem
13-
import fi.aalto.cs.apluscourses.ui.exercise.ExercisesView.ExercisesTreeItem
11+
import fi.aalto.cs.apluscourses.ui.exercise.ExercisesView.*
1412
import org.jetbrains.annotations.PropertyKey
15-
import java.util.Collections
13+
import java.util.*
1614
import kotlin.reflect.KClass
1715

1816
@Service(Service.Level.PROJECT)
@@ -28,7 +26,7 @@ class ExercisesTreeFilter(private val project: Project) :
2826
valueAttributeName = "",
2927
style = XCollection.Style.v2
3028
)
31-
var enabledFilters by list<String>()
29+
var enabledFilters: MutableList<String> by list()
3230
}
3331

3432
private val filters: MutableMap<Filter<out ExercisesTreeItem>, Boolean> =
@@ -54,7 +52,7 @@ class ExercisesTreeFilter(private val project: Project) :
5452
}
5553
}
5654

57-
fun saveState() {
55+
private fun saveState() {
5856
state.enabledFilters = filters.entries
5957
.filter { it.value }
6058
.map { it.key.displayName }.toMutableList()
@@ -75,7 +73,10 @@ class ExercisesTreeFilter(private val project: Project) :
7573
// Filter out disabled filters and match the target type
7674
.filter { it.value && it.key.targetType == targetType }
7775
// Map to filter functions with correct type
78-
.map { it.key.getFilterFunction() as (T) -> Boolean }
76+
.map {
77+
@Suppress("UNCHECKED_CAST", "HardCodedStringLiteral")
78+
it.key.getFilterFunction() as (T) -> Boolean
79+
}
7980
// Combine filters to a single lambda
8081
.fold({ false }) { acc, filter -> { item -> filter(item) || acc(item) } }
8182

@@ -89,7 +90,7 @@ class ExercisesTreeFilter(private val project: Project) :
8990

9091
class ExerciseItemFilter(
9192
displayName: String,
92-
private val filter: (ExerciseItem) -> Boolean
93+
val filter: (ExerciseItem) -> Boolean
9394
) : Filter<ExerciseItem>(displayName, ExerciseItem::class) {
9495
override fun getFilterFunction(): (ExerciseItem) -> Boolean = filter
9596
}
@@ -102,23 +103,24 @@ class ExercisesTreeFilter(private val project: Project) :
102103
}
103104

104105
companion object {
105-
val NON_SUBMITTABLE = ExerciseItemFilter(
106+
private val NON_SUBMITTABLE: ExerciseItemFilter = ExerciseItemFilter(
106107
"services.ExercisesTreeFilter.nonSubmittable"
107-
) { item -> !item.exercise.isSubmittable }
108+
) { !it.exercise.isSubmittable }
108109

109-
val COMPLETED = ExerciseItemFilter(
110+
private val COMPLETED: ExerciseItemFilter = ExerciseItemFilter(
110111
"services.ExercisesTreeFilter.Completed"
111-
) { item -> item.exercise.isCompleted() }
112+
) { it.exercise.isCompleted() }
112113

113-
val OPTIONAL = ExerciseItemFilter(
114+
private val OPTIONAL: ExerciseItemFilter = ExerciseItemFilter(
114115
"services.ExercisesTreeFilter.Optional"
115-
) { item -> item.exercise.isOptional }
116+
) { it.exercise.isOptional }
116117

117-
val CLOSED = ExerciseGroupFilter(
118+
private val CLOSED: ExerciseGroupFilter = ExerciseGroupFilter(
118119
"services.ExercisesTreeFilter.Closed"
119-
) { item -> !item.group.isOpen }
120+
) { !it.group.isOpen }
120121

121-
val allFilters = listOf(NON_SUBMITTABLE, COMPLETED, OPTIONAL, CLOSED)
122+
val allFilters: List<Filter<out ExercisesTreeItem>> =
123+
listOf(NON_SUBMITTABLE, COMPLETED, OPTIONAL, CLOSED)
122124
}
123125
}
124126

src/main/kotlin/fi/aalto/cs/apluscourses/services/exercise/SubmitExercise.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package fi.aalto.cs.apluscourses.services.exercise
22

33
import com.intellij.openapi.application.EDT
4+
import com.intellij.openapi.application.writeAction
45
import com.intellij.openapi.components.Service
56
import com.intellij.openapi.fileEditor.FileDocumentManager
67
import com.intellij.openapi.module.ModuleUtilCore
78
import com.intellij.openapi.project.Project
89
import com.intellij.serialization.PropertyMapping
910
import fi.aalto.cs.apluscourses.api.APlusApi
11+
import fi.aalto.cs.apluscourses.model.component.Module
1012
import fi.aalto.cs.apluscourses.model.exercise.Exercise
1113
import fi.aalto.cs.apluscourses.model.exercise.Submission
1214
import fi.aalto.cs.apluscourses.model.exercise.SubmissionInfo
@@ -85,7 +87,9 @@ class SubmitExercise(
8587
}
8688

8789
withContext(Dispatchers.EDT) {
88-
FileDocumentManager.getInstance().saveAllDocuments()
90+
writeAction {
91+
FileDocumentManager.getInstance().saveAllDocuments()
92+
}
8993
}
9094

9195
var modulePath = Path(ModuleUtilCore.getModuleDirPath(platformModule))
@@ -99,10 +103,12 @@ class SubmitExercise(
99103
val files: MutableMap<String, Path> = HashMap()
100104
for ((key, name) in submissionInfo.getFiles(language)) {
101105
files[key] = FileUtil.findFileInDirectory(modulePath.invariantSeparatorsPathString, name)
102-
?: throw FileDoesNotExistException(
103-
modulePath.invariantSeparatorsPathString,
104-
name
105-
)
106+
{ file ->
107+
!file.invariantSeparatorsPath.startsWith("${module.fullPath.resolve(Module.backupDir).invariantSeparatorsPathString}/")
108+
} ?: throw FileDoesNotExistException(
109+
modulePath.invariantSeparatorsPathString,
110+
name
111+
)
106112
}
107113
CoursesLogger.info("Submission files: $files")
108114

src/main/kotlin/fi/aalto/cs/apluscourses/utils/FileUtil.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,13 @@ object FileUtil {
4141
* @param directory The directory to search for the file.
4242
* @param fileName The name of the file to find.
4343
*/
44-
fun findFileInDirectory(directory: String, fileName: String): Path? {
44+
fun findFileInDirectory(
45+
directory: String,
46+
fileName: String,
47+
filter: (File) -> Boolean = { _: File -> true }
48+
): Path? {
4549
return File(directory).walk().find {
46-
it.name == fileName
50+
it.name == fileName && filter(it)
4751
}?.toPath()
4852
}
4953

0 commit comments

Comments
 (0)