Skip to content

Commit

Permalink
Add error messages if user is not enrolled or course update fails
Browse files Browse the repository at this point in the history
  • Loading branch information
jaakkonakaza committed Aug 20, 2024
1 parent d27d147 commit 1392796
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 87 deletions.
2 changes: 2 additions & 0 deletions src/main/kotlin/fi/aalto/cs/apluscourses/api/APlusApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ object APlusApi {
body.fullName ?: body.username,
body.studentId,
body.id,
body.enrolledCourses.map { it.id },
body.staffCourses.map { it.id }
)
}
Expand All @@ -413,6 +414,7 @@ object APlusApi {
val fullName: String?,
val studentId: String,
val id: Long,
val enrolledCourses: List<Course> = emptyList(),
val staffCourses: List<Course> = emptyList(),
)

Expand Down
13 changes: 8 additions & 5 deletions src/main/kotlin/fi/aalto/cs/apluscourses/model/people/User.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package fi.aalto.cs.apluscourses.model.people

import fi.aalto.cs.apluscourses.model.Course

/**
* @property userName The name of the user. If the full name is not available, the username is used.
* @property studentId The student ID of the user.
Expand All @@ -12,9 +10,14 @@ class User(
val userName: String,
val studentId: String,
val aplusId: Long,
private val staffCourses: List<Long>,
val enrolledCourses: List<Long>,
val staffCourses: List<Long>,
) {
fun isStaffOf(course: Course): Boolean {
return course.id in staffCourses
fun isStaffOf(courseId: Long): Boolean {
return courseId in staffCourses
}

fun isEnrolledIn(courseId: Long): Boolean {
return courseId in enrolledCourses
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class CourseManager(
var aPlusUrl: String? = null
var grading: CourseConfig.Grading? = null
var settingsImported = false
var error: Error? = null
var missingDependencies = mapOf<String, List<Component<*>>>()
fun clearAll() {
course = null
Expand All @@ -54,18 +55,23 @@ class CourseManager(
}
}

enum class Error {
NOT_ENROLLED,
NETWORK_ERROR,
}

val state = State()

private val notifiedModules: MutableSet<String> = ConcurrentHashMap.newKeySet()

private var job: Job? = null
fun restart() {
job?.cancel(CancellationException("test"))
job?.cancel()
run()
}

fun stop() {
job?.cancel(CancellationException("test"))
job?.cancel()
}

private fun run(
Expand Down Expand Up @@ -110,71 +116,80 @@ class CourseManager(
return
}
state.authenticated = true
state.error = null

try {
runBlocking {
state.grading = courseConfig.grading
// async {
val extraCourseData = APlusApi.Course(courseConfig.id.toLong()).get(project)
val modules = courseConfig.modules.map {
Module(
it.name,
it.url,
it.changelog,
it.version,
it.language,
project
)
}
val exerciseModules = courseConfig.exerciseModules.map { (exerciseId, languagesToModule) ->
println("exerciseId: $exerciseId languagesToModule: $languagesToModule")
exerciseId to
languagesToModule
.map { (language, moduleName) ->
var module = modules.find { it.name == moduleName }
if (module == null) {
println("Module $moduleName not found")
module = Module(
moduleName,
"",
"",
Version.EMPTY,
null,
project
)
}
language to module
}
.toMap()
}.toMap()
state.course = Course(
id = courseConfig.id.toLong(),
name = courseConfig.name,
htmlUrl = extraCourseData.htmlUrl,
imageUrl = extraCourseData.image,
endingTime = extraCourseData.endingTime,
languages = courseConfig.languages,
modules = modules,
exerciseModules = exerciseModules,
resourceUrls = CourseConfig.resourceUrls(courseConfig.resources),
optionalCategories = courseConfig.optionalCategories,
autoInstallComponentNames = courseConfig.autoInstall,
replInitialCommands = courseConfig.scalaRepl?.initialCommands,
replAdditionalArguments = courseConfig.scalaRepl?.arguments,
minimumPluginVersion = courseConfig.version,
hiddenElements = courseConfig.hiddenElements,
callbacks = Callbacks.fromJsonObject(courseConfig.callbacks),
project
)
importSettings(state.course!!)
state.course?.components?.values?.forEach { it.load() }
// }
// async {
state.grading = courseConfig.grading

val extraCourseData = try {
state.user = withContext(Dispatchers.IO) {
APlusApi.me().get(project)
}
// }
APlusApi.Course(courseConfig.id.toLong()).get(project)
} catch (_: Exception) {
val courseId = courseConfig.id.toLong()
val user = state.user
if (user != null && (!user.isStaffOf(courseId) || !user.isEnrolledIn(courseId))) {
state.error = Error.NOT_ENROLLED
} else {
state.error = Error.NETWORK_ERROR
}
fireCourseUpdated()
throw CancellationException()
}
val modules = courseConfig.modules.map {
Module(
it.name,
it.url,
it.changelog,
it.version,
it.language,
project
)
}
val exerciseModules = courseConfig.exerciseModules.map { (exerciseId, languagesToModule) ->
println("exerciseId: $exerciseId languagesToModule: $languagesToModule")
exerciseId to
languagesToModule
.map { (language, moduleName) ->
var module = modules.find { it.name == moduleName }
if (module == null) {
println("Module $moduleName not found")
module = Module(
moduleName,
"",
"",
Version.EMPTY,
null,
project
)
}
language to module
}
.toMap()
}.toMap()
state.course = Course(
id = courseConfig.id.toLong(),
name = courseConfig.name,
htmlUrl = extraCourseData.htmlUrl,
imageUrl = extraCourseData.image,
endingTime = extraCourseData.endingTime,
languages = courseConfig.languages,
modules = modules,
exerciseModules = exerciseModules,
resourceUrls = CourseConfig.resourceUrls(courseConfig.resources),
optionalCategories = courseConfig.optionalCategories,
autoInstallComponentNames = courseConfig.autoInstall,
replInitialCommands = courseConfig.scalaRepl?.initialCommands,
replAdditionalArguments = courseConfig.scalaRepl?.arguments,
minimumPluginVersion = courseConfig.version,
hiddenElements = courseConfig.hiddenElements,
callbacks = Callbacks.fromJsonObject(courseConfig.callbacks),
project
)
importSettings(state.course!!)
state.course?.components?.values?.forEach { it.load() }

val course = state.course ?: return
course.autoInstallComponents.forEach {
val status = it.loadAndGetStatus()
Expand All @@ -192,7 +207,9 @@ class CourseManager(

state.news = newNews
fireNewsUpdated(newNews)
} catch (e: IOException) {
} catch (_: IOException) {
state.error = Error.NETWORK_ERROR
fireCourseUpdated()
return
}

Expand Down Expand Up @@ -334,6 +351,10 @@ class CourseManager(
return getInstance(project).state.course
}

fun error(project: Project): Error? {
return getInstance(project).state.error
}

fun user(project: Project): User? {
return getInstance(project).state.user
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,16 @@ class ExercisesUpdater(
private var points = -1

fun restart() {
exerciseJob?.cancel(CancellationException("test"))
gradingJob?.cancel(CancellationException("test"))
exerciseJob?.cancel()
gradingJob?.cancel()
println("restart")
runExerciseUpdater()
runGradingUpdater()
}

fun stop() {
exerciseJob?.cancel(CancellationException("test"))
gradingJob?.cancel(CancellationException("test"))
exerciseJob?.cancel()
gradingJob?.cancel()
}

private fun runExerciseUpdater(
Expand All @@ -83,7 +83,7 @@ class ExercisesUpdater(
cs.ensureActive()
delay(updateInterval)
}
} catch (e: CancellationException) {
} catch (_: CancellationException) {
println("Task was cancelled 1")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,28 +54,74 @@ class OverviewView(private val project: Project) : SimpleToolWindowPanel(true, t
}
}

private fun createPanel(loading: Boolean = false): DialogPanel {
if (loading) return loadingPanel()
val authenticated = CourseManager.authenticated(project) ?: return loadingPanel()
if (!authenticated) {
private fun authPanel(): DialogPanel {
return panel {
val courseName = CourseManager.getInstance(project).state.courseName ?: ""
val tokenForm = TokenForm(project) {
update(loading = true)
CourseManager.getInstance(project).restart()
}
return panel {
panel {
row {
text("Welcome to $courseName").applyToComponent {
font = JBFont.h1()
}.comment("You need to log in to access the course content:")
panel {
row {
text("Welcome to $courseName").applyToComponent {
font = JBFont.h1()
}.comment("You need to log in to access the course content:")
}
with(tokenForm) {
token()
validation()
}
}.customize(UnscaledGaps(16, 32, 16, 32))
}
}

private fun networkErrorPanel(): DialogPanel {
return panel {
panel {
row {
text("The plugin encountered a network error").applyToComponent {
font = JBFont.h1()
}.comment("Please check your internet connection and that A+ is accessible.")
}
row {
button("Refresh") {
update(loading = true)
CourseManager.getInstance(project).restart()
}
with(tokenForm) {
token()
validation()
}
}.customize(UnscaledGaps(16, 32, 16, 32))
}
}

private fun notEnrolledPanel(): DialogPanel {
return panel {
val courseName = CourseManager.getInstance(project).state.courseName
panel {
row {
text("Looks like you are not enrolled on this course").applyToComponent {
font = JBFont.h1()
}.comment("Please check the ${courseName ?: "course"} page on A+.")
}
row {
button("Refresh") {
update(loading = true)
CourseManager.getInstance(project).restart()
}
}.customize(UnscaledGaps(16, 32, 16, 32))
}
}

}.customize(UnscaledGaps(16, 32, 16, 32))
}
}

private fun createPanel(loading: Boolean = false): DialogPanel {
if (loading) return loadingPanel()
val authenticated = CourseManager.authenticated(project) ?: return loadingPanel()
if (!authenticated) return authPanel()
val error = CourseManager.error(project)
if (error == CourseManager.Error.NETWORK_ERROR) {
return networkErrorPanel()
} else if (error == CourseManager.Error.NOT_ENROLLED) {
return notEnrolledPanel()
}
val course = CourseManager.course(project) ?: return loadingPanel()
val user = CourseManager.user(project) ?: return loadingPanel()
Expand Down

0 comments on commit 1392796

Please sign in to comment.