Criteria for assignment grading
This commit is contained in:
parent
a7aafccd19
commit
c88d0d2e58
|
@ -18,3 +18,4 @@ captures
|
||||||
!*.xcworkspace/contents.xcworkspacedata
|
!*.xcworkspace/contents.xcworkspacedata
|
||||||
**/xcshareddata/WorkspaceSettings.xcsettings
|
**/xcshareddata/WorkspaceSettings.xcsettings
|
||||||
**/grader.db
|
**/grader.db
|
||||||
|
**/*.backup
|
|
@ -85,34 +85,34 @@ object PeerEvaluations : UUIDTable("peerEvals") {
|
||||||
}
|
}
|
||||||
|
|
||||||
object GroupFeedbacks : CompositeIdTable("grpFdbks") {
|
object GroupFeedbacks : CompositeIdTable("grpFdbks") {
|
||||||
val groupAssignmentId = reference("group_assignment_id", GroupAssignments.id)
|
val assignmentId = reference("group_assignment_id", GroupAssignments.id)
|
||||||
val criterionId = reference("criterion_id", GroupAssignments.id).nullable()
|
val criterionId = reference("criterion_id", GroupAssignmentCriteria.id).nullable()
|
||||||
val groupId = reference("group_id", Groups.id)
|
val groupId = reference("group_id", Groups.id)
|
||||||
val feedback = text("feedback")
|
val feedback = text("feedback")
|
||||||
val grade = varchar("grade", 32)
|
val grade = varchar("grade", 32)
|
||||||
|
|
||||||
override val primaryKey = PrimaryKey(groupAssignmentId, groupId)
|
override val primaryKey = PrimaryKey(groupId, criterionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
object IndividualFeedbacks : CompositeIdTable("indivFdbks") {
|
object IndividualFeedbacks : CompositeIdTable("indivFdbks") {
|
||||||
val groupAssignmentId = reference("group_assignment_id", GroupAssignments.id)
|
val assignmentId = reference("group_assignment_id", GroupAssignments.id)
|
||||||
val criterionId = reference("criterion_id", GroupAssignments.id).nullable()
|
val criterionId = reference("criterion_id", GroupAssignmentCriteria.id).nullable()
|
||||||
val groupId = reference("group_id", Groups.id)
|
val groupId = reference("group_id", Groups.id)
|
||||||
val studentId = reference("student_id", Students.id)
|
val studentId = reference("student_id", Students.id)
|
||||||
val feedback = text("feedback")
|
val feedback = text("feedback")
|
||||||
val grade = varchar("grade", 32)
|
val grade = varchar("grade", 32)
|
||||||
|
|
||||||
override val primaryKey = PrimaryKey(groupAssignmentId, studentId)
|
override val primaryKey = PrimaryKey(studentId, criterionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
object SoloFeedbacks : CompositeIdTable("soloFdbks") {
|
object SoloFeedbacks : CompositeIdTable("soloFdbks") {
|
||||||
val soloAssignmentId = reference("solo_assignment_id", SoloAssignments.id)
|
val assignmentId = reference("solo_assignment_id", SoloAssignments.id)
|
||||||
val criterionId = reference("criterion_id", SoloAssignments.id).nullable()
|
val criterionId = reference("criterion_id", SoloAssignmentCriteria.id).nullable()
|
||||||
val studentId = reference("student_id", Students.id)
|
val studentId = reference("student_id", Students.id)
|
||||||
val feedback = text("feedback")
|
val feedback = text("feedback")
|
||||||
val grade = varchar("grade", 32)
|
val grade = varchar("grade", 32)
|
||||||
|
|
||||||
override val primaryKey = PrimaryKey(soloAssignmentId, studentId)
|
override val primaryKey = PrimaryKey(studentId, criterionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
object PeerEvaluationContents : CompositeIdTable("peerEvalCnts") {
|
object PeerEvaluationContents : CompositeIdTable("peerEvalCnts") {
|
||||||
|
|
|
@ -70,6 +70,24 @@ class GroupAssignmentCriterion(id: EntityID<UUID>) : Entity<UUID>(id) {
|
||||||
var assignment by GroupAssignment referencedOn GroupAssignmentCriteria.assignmentId
|
var assignment by GroupAssignment referencedOn GroupAssignmentCriteria.assignmentId
|
||||||
var name by GroupAssignmentCriteria.name
|
var name by GroupAssignmentCriteria.name
|
||||||
var description by GroupAssignmentCriteria.desc
|
var description by GroupAssignmentCriteria.desc
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as GroupAssignmentCriterion
|
||||||
|
|
||||||
|
if (name != other.name) return false
|
||||||
|
if (description != other.description) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = name.hashCode()
|
||||||
|
result = 31 * result + description.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SoloAssignment(id: EntityID<UUID>) : Entity<UUID>(id) {
|
class SoloAssignment(id: EntityID<UUID>) : Entity<UUID>(id) {
|
||||||
|
@ -104,8 +122,7 @@ class SoloAssignmentCriterion(id: EntityID<UUID>) : Entity<UUID>(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = assignment.hashCode()
|
var result = name.hashCode()
|
||||||
result = 31 * result + name.hashCode()
|
|
||||||
result = 31 * result + description.hashCode()
|
result = 31 * result + description.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,7 +255,8 @@ fun groupFeedback(state: GroupAssignmentState, fdbk: GroupAssignmentState.LocalG
|
||||||
groupFeedbackPane(
|
groupFeedbackPane(
|
||||||
criteria, critIdx, { critIdx = it }, feedback.global,
|
criteria, critIdx, { critIdx = it }, feedback.global,
|
||||||
if(critIdx == 0) feedback.global else feedback.byCriterion[critIdx - 1].entry,
|
if(critIdx == 0) feedback.global else feedback.byCriterion[critIdx - 1].entry,
|
||||||
suggestions, updateGrade, updateFeedback, Modifier.weight(0.75f).padding(10.dp)
|
suggestions, updateGrade, updateFeedback, Modifier.weight(0.75f).padding(10.dp),
|
||||||
|
key = idx to critIdx
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,10 +271,11 @@ fun groupFeedbackPane(
|
||||||
autofill: List<String>,
|
autofill: List<String>,
|
||||||
onSetGrade: (String) -> Unit,
|
onSetGrade: (String) -> Unit,
|
||||||
onSetFeedback: (String) -> Unit,
|
onSetFeedback: (String) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier,
|
||||||
|
key: Any? = null
|
||||||
) {
|
) {
|
||||||
var grade by remember(globFeedback) { mutableStateOf(globFeedback?.grade ?: "") }
|
var grade by remember(globFeedback, key) { mutableStateOf(globFeedback?.grade ?: "") }
|
||||||
var feedback by remember(currentCriterion, criteria) { mutableStateOf(TextFieldValue(criterionFeedback?.feedback ?: "")) }
|
var feedback by remember(currentCriterion, criteria, criterionFeedback, key) { mutableStateOf(TextFieldValue(criterionFeedback?.feedback ?: "")) }
|
||||||
Column(modifier) {
|
Column(modifier) {
|
||||||
Row {
|
Row {
|
||||||
Text("Overall grade: ", Modifier.align(Alignment.CenterVertically))
|
Text("Overall grade: ", Modifier.align(Alignment.CenterVertically))
|
||||||
|
|
|
@ -310,15 +310,23 @@ class EditionState(val edition: Edition) {
|
||||||
}
|
}
|
||||||
fun delete(sa: SoloAssignment) {
|
fun delete(sa: SoloAssignment) {
|
||||||
transaction {
|
transaction {
|
||||||
SoloFeedbacks.deleteWhere { soloAssignmentId eq sa.id }
|
SoloAssignmentCriteria.selectAll().where { SoloAssignmentCriteria.assignmentId eq sa.id }.forEach { it ->
|
||||||
|
val id = it[SoloAssignmentCriteria.assignmentId]
|
||||||
|
SoloFeedbacks.deleteWhere { criterionId eq id }
|
||||||
|
}
|
||||||
|
SoloAssignmentCriteria.deleteWhere { assignmentId eq sa.id }
|
||||||
sa.delete()
|
sa.delete()
|
||||||
}
|
}
|
||||||
solo.refresh()
|
solo.refresh()
|
||||||
}
|
}
|
||||||
fun delete(ga: GroupAssignment) {
|
fun delete(ga: GroupAssignment) {
|
||||||
transaction {
|
transaction {
|
||||||
GroupFeedbacks.deleteWhere { groupAssignmentId eq ga.id }
|
GroupAssignmentCriteria.selectAll().where { GroupAssignmentCriteria.assignmentId eq ga.id }.forEach { it ->
|
||||||
IndividualFeedbacks.deleteWhere { groupAssignmentId eq ga.id }
|
val id = it[GroupAssignmentCriteria.assignmentId]
|
||||||
|
GroupFeedbacks.deleteWhere { criterionId eq id }
|
||||||
|
IndividualFeedbacks.deleteWhere { criterionId eq id }
|
||||||
|
}
|
||||||
|
GroupAssignmentCriteria.deleteWhere { assignmentId eq ga.id }
|
||||||
ga.delete()
|
ga.delete()
|
||||||
}
|
}
|
||||||
groupAs.refresh()
|
groupAs.refresh()
|
||||||
|
@ -363,13 +371,15 @@ class StudentState(val student: Student, edition: Edition) {
|
||||||
(Groups.editionId eq edition.id) and (Groups.id inList student.groups.map { it.id })
|
(Groups.editionId eq edition.id) and (Groups.id inList student.groups.map { it.id })
|
||||||
}.associate { it.id to it.name }
|
}.associate { it.id to it.name }
|
||||||
|
|
||||||
val asGroup = (GroupAssignments innerJoin GroupFeedbacks innerJoin Groups).selectAll().where {
|
val asGroup = (GroupAssignments innerJoin GroupAssignmentCriteria innerJoin GroupFeedbacks innerJoin Groups).selectAll().where {
|
||||||
GroupFeedbacks.groupId inList groupsForEdition.keys.toList()
|
(GroupFeedbacks.groupId inList groupsForEdition.keys.toList()) and
|
||||||
}.map { it[GroupFeedbacks.groupAssignmentId] to it }
|
(GroupAssignmentCriteria.name eq "")
|
||||||
|
}.map { it[GroupAssignments.id] to it }
|
||||||
|
|
||||||
val asIndividual = (GroupAssignments innerJoin IndividualFeedbacks innerJoin Groups).selectAll().where {
|
val asIndividual = (GroupAssignments innerJoin GroupAssignmentCriteria innerJoin IndividualFeedbacks innerJoin Groups).selectAll().where {
|
||||||
IndividualFeedbacks.studentId eq student.id
|
(IndividualFeedbacks.studentId eq student.id) and
|
||||||
}.map { it[IndividualFeedbacks.groupAssignmentId] to it }
|
(GroupAssignmentCriteria.name eq "")
|
||||||
|
}.map { it[GroupAssignments.id] to it }
|
||||||
|
|
||||||
val res = mutableMapOf<EntityID<UUID>, LocalGroupGrade>()
|
val res = mutableMapOf<EntityID<UUID>, LocalGroupGrade>()
|
||||||
asGroup.forEach {
|
asGroup.forEach {
|
||||||
|
@ -391,8 +401,9 @@ class StudentState(val student: Student, edition: Edition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
val soloGrades = RawDbState {
|
val soloGrades = RawDbState {
|
||||||
(SoloAssignments innerJoin SoloFeedbacks).selectAll().where {
|
(SoloAssignments innerJoin SoloAssignmentCriteria innerJoin SoloFeedbacks).selectAll().where {
|
||||||
SoloFeedbacks.studentId eq student.id
|
(SoloFeedbacks.studentId eq student.id) and
|
||||||
|
(SoloAssignmentCriteria.name eq "")
|
||||||
}.map { LocalSoloGrade(it[SoloAssignments.name], it[SoloFeedbacks.grade]) }.toList()
|
}.map { LocalSoloGrade(it[SoloAssignments.name], it[SoloFeedbacks.grade]) }.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,7 +467,7 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
|
||||||
data class LocalGFeedback(
|
data class LocalGFeedback(
|
||||||
val group: Group,
|
val group: Group,
|
||||||
val feedback: LocalFeedback,
|
val feedback: LocalFeedback,
|
||||||
val individuals: List<Pair<Student, Pair<String?, LocalFeedback>>>
|
val individuals: List<Pair<Student, Pair<String?, LocalFeedback>>> // Student -> (Role, Feedback)
|
||||||
)
|
)
|
||||||
|
|
||||||
val editionCourse = transaction { assignment.edition.course to assignment.edition }
|
val editionCourse = transaction { assignment.edition.course to assignment.edition }
|
||||||
|
@ -469,11 +480,11 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
|
||||||
val feedback = RawDbState { loadFeedback() }
|
val feedback = RawDbState { loadFeedback() }
|
||||||
|
|
||||||
val autofill = RawDbState {
|
val autofill = RawDbState {
|
||||||
val forGroups = GroupFeedbacks.selectAll().where { GroupFeedbacks.groupAssignmentId eq assignment.id }.flatMap {
|
val forGroups = (GroupFeedbacks innerJoin GroupAssignmentCriteria).selectAll().where { GroupAssignmentCriteria.assignmentId eq assignment.id }.flatMap {
|
||||||
it[GroupFeedbacks.feedback].split('\n')
|
it[GroupFeedbacks.feedback].split('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
val forIndividuals = IndividualFeedbacks.selectAll().where { IndividualFeedbacks.groupAssignmentId eq assignment.id }.flatMap {
|
val forIndividuals = (IndividualFeedbacks innerJoin GroupAssignmentCriteria).selectAll().where { GroupAssignmentCriteria.assignmentId eq assignment.id }.flatMap {
|
||||||
it[IndividualFeedbacks.feedback].split('\n')
|
it[IndividualFeedbacks.feedback].split('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,53 +492,67 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Transaction.loadFeedback(): List<Pair<Group, LocalGFeedback>> {
|
private fun Transaction.loadFeedback(): List<Pair<Group, LocalGFeedback>> {
|
||||||
|
val allCrit = GroupAssignmentCriterion.find {
|
||||||
|
GroupAssignmentCriteria.assignmentId eq assignment.id
|
||||||
|
}
|
||||||
|
|
||||||
return Group.find {
|
return Group.find {
|
||||||
(Groups.editionId eq assignment.edition.id)
|
(Groups.editionId eq assignment.edition.id)
|
||||||
}.sortAsc(Groups.name).map { group ->
|
}.sortAsc(Groups.name).map { group ->
|
||||||
// step 1: group-level feedback, including criteria
|
val forGroup = (GroupFeedbacks innerJoin Groups).selectAll().where {
|
||||||
val forGroup = GroupFeedbacks.selectAll().where {
|
(GroupFeedbacks.assignmentId eq assignment.id) and (Groups.id eq group.id)
|
||||||
(GroupFeedbacks.groupAssignmentId eq assignment.id) and
|
}.map { row ->
|
||||||
(GroupFeedbacks.groupId eq group.id)
|
val crit = row[GroupFeedbacks.criterionId]?.let { GroupAssignmentCriterion[it] }
|
||||||
}.associate {
|
val fdbk = row[GroupFeedbacks.feedback]
|
||||||
val criterion = it[GroupFeedbacks.criterionId]?.let { id -> GroupAssignmentCriterion[id] }
|
val grade = row[GroupFeedbacks.grade]
|
||||||
val fe = FeedbackEntry(it[GroupFeedbacks.feedback], it[GroupFeedbacks.grade])
|
|
||||||
criterion to fe
|
crit to FeedbackEntry(fdbk, grade)
|
||||||
}
|
}
|
||||||
val feedback = LocalFeedback(
|
|
||||||
global = forGroup[null],
|
|
||||||
byCriterion = criteria.entities.value.map { c -> LocalCriterionFeedback(c, forGroup[c]) }
|
|
||||||
)
|
|
||||||
|
|
||||||
// step 2: individual feedback
|
val global = forGroup.firstOrNull { it.first == null }?.second
|
||||||
val individuals = group.studentRoles.map { sr ->
|
val byCrit_ = forGroup.map { it.first?.let { k -> LocalCriterionFeedback(k, it.second) } }
|
||||||
val student = sr.student
|
.filterNotNull().associateBy { it.criterion.id }
|
||||||
val role = sr.role
|
val byCrit = allCrit.map { c ->
|
||||||
|
byCrit_[c.id] ?: LocalCriterionFeedback(c, null)
|
||||||
val forStudent = IndividualFeedbacks.selectAll().where {
|
|
||||||
(IndividualFeedbacks.groupAssignmentId eq assignment.id) and
|
|
||||||
(IndividualFeedbacks.groupId eq group.id) and
|
|
||||||
(IndividualFeedbacks.studentId eq student.id)
|
|
||||||
}.associate {
|
|
||||||
val criterion = it[IndividualFeedbacks.criterionId]?.let { id -> GroupAssignmentCriterion[id] }
|
|
||||||
val fe = FeedbackEntry(it[IndividualFeedbacks.feedback], it[IndividualFeedbacks.grade])
|
|
||||||
criterion to fe
|
|
||||||
}
|
}
|
||||||
val studentFeedback = LocalFeedback(
|
|
||||||
global = forStudent[null],
|
|
||||||
byCriterion = criteria.entities.value.map { c -> LocalCriterionFeedback(c, forStudent[c]) }
|
|
||||||
)
|
|
||||||
|
|
||||||
student to (role to studentFeedback)
|
val byGroup = LocalFeedback(global, byCrit)
|
||||||
}.sortedBy { it.first.name }
|
|
||||||
|
|
||||||
group to LocalGFeedback(group, feedback, individuals)
|
val indiv = group.studentRoles.map {
|
||||||
|
val student = it.student
|
||||||
|
val role = it.role
|
||||||
|
|
||||||
|
val forSt = (IndividualFeedbacks innerJoin Groups innerJoin GroupStudents)
|
||||||
|
.selectAll().where {
|
||||||
|
(IndividualFeedbacks.assignmentId eq assignment.id) and
|
||||||
|
(GroupStudents.studentId eq student.id) and (Groups.id eq group.id)
|
||||||
|
}.map { row ->
|
||||||
|
val crit = row[IndividualFeedbacks.criterionId]?.let { id -> GroupAssignmentCriterion[id] }
|
||||||
|
val fdbk = row[IndividualFeedbacks.feedback]
|
||||||
|
val grade = row[IndividualFeedbacks.grade]
|
||||||
|
|
||||||
|
crit to FeedbackEntry(fdbk, grade)
|
||||||
|
}
|
||||||
|
|
||||||
|
val global = forSt.firstOrNull { it.first == null }?.second
|
||||||
|
val byCrit_ = forSt.map { it.first?.let { k -> LocalCriterionFeedback(k, it.second) } }
|
||||||
|
.filterNotNull().associateBy { it.criterion.id }
|
||||||
|
val byCrit = allCrit.map { c ->
|
||||||
|
byCrit_[c.id] ?: LocalCriterionFeedback(c, null)
|
||||||
|
}
|
||||||
|
val byStudent = LocalFeedback(global, byCrit)
|
||||||
|
|
||||||
|
student to (role to byStudent)
|
||||||
|
}
|
||||||
|
|
||||||
|
group to LocalGFeedback(group, byGroup, indiv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun upsertGroupFeedback(group: Group, msg: String, grd: String, criterion: GroupAssignmentCriterion? = null) {
|
fun upsertGroupFeedback(group: Group, msg: String, grd: String, criterion: GroupAssignmentCriterion? = null) {
|
||||||
transaction {
|
transaction {
|
||||||
GroupFeedbacks.upsert {
|
GroupFeedbacks.upsert {
|
||||||
it[groupAssignmentId] = assignment.id
|
it[assignmentId] = assignment.id
|
||||||
it[groupId] = group.id
|
it[groupId] = group.id
|
||||||
it[this.feedback] = msg
|
it[this.feedback] = msg
|
||||||
it[this.grade] = grd
|
it[this.grade] = grd
|
||||||
|
@ -540,7 +565,7 @@ class GroupAssignmentState(val assignment: GroupAssignment) {
|
||||||
fun upsertIndividualFeedback(student: Student, group: Group, msg: String, grd: String, criterion: GroupAssignmentCriterion? = null) {
|
fun upsertIndividualFeedback(student: Student, group: Group, msg: String, grd: String, criterion: GroupAssignmentCriterion? = null) {
|
||||||
transaction {
|
transaction {
|
||||||
IndividualFeedbacks.upsert {
|
IndividualFeedbacks.upsert {
|
||||||
it[groupAssignmentId] = assignment.id
|
it[assignmentId] = assignment.id
|
||||||
it[groupId] = group.id
|
it[groupId] = group.id
|
||||||
it[studentId] = student.id
|
it[studentId] = student.id
|
||||||
it[this.feedback] = msg
|
it[this.feedback] = msg
|
||||||
|
@ -608,34 +633,42 @@ class SoloAssignmentState(val assignment: SoloAssignment) {
|
||||||
val feedback = RawDbState { loadFeedback() }
|
val feedback = RawDbState { loadFeedback() }
|
||||||
|
|
||||||
val autofill = RawDbState {
|
val autofill = RawDbState {
|
||||||
SoloFeedbacks.selectAll().where { SoloFeedbacks.soloAssignmentId eq assignment.id }.map {
|
SoloFeedbacks.selectAll().where { SoloFeedbacks.assignmentId eq assignment.id }.map {
|
||||||
it[SoloFeedbacks.feedback].split('\n')
|
it[SoloFeedbacks.feedback].split('\n')
|
||||||
}.flatten().distinct().sorted()
|
}.flatten().distinct().sorted()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Transaction.loadFeedback(): List<Pair<Student, FullFeedback>> {
|
private fun Transaction.loadFeedback(): List<Pair<Student, FullFeedback>> {
|
||||||
return editionCourse.second.soloStudents.sortAsc(Students.name).map { student ->
|
val allCrit = SoloAssignmentCriterion.find {
|
||||||
val each = SoloFeedbacks.selectAll().where {
|
SoloAssignmentCriteria.assignmentId eq assignment.id
|
||||||
(SoloFeedbacks.soloAssignmentId eq assignment.id) and
|
|
||||||
(SoloFeedbacks.studentId eq student.id)
|
|
||||||
}.associate {
|
|
||||||
val criterion = it[SoloFeedbacks.criterionId]?.let { id -> SoloAssignmentCriterion[id] }
|
|
||||||
val fe = LocalFeedback(it[SoloFeedbacks.feedback], it[SoloFeedbacks.grade])
|
|
||||||
criterion to fe
|
|
||||||
}
|
}
|
||||||
val feedback = FullFeedback(
|
|
||||||
global = each[null],
|
|
||||||
byCriterion = criteria.entities.value.map { c -> c to each[c] }
|
|
||||||
)
|
|
||||||
|
|
||||||
student to feedback
|
return editionCourse.second.soloStudents.sortAsc(Students.name).map { student ->
|
||||||
|
val forStudent = (IndividualFeedbacks innerJoin Students).selectAll().where {
|
||||||
|
(IndividualFeedbacks.assignmentId eq assignment.id) and (Students.id eq student.id)
|
||||||
|
}.map { row ->
|
||||||
|
val crit = row[IndividualFeedbacks.criterionId]?.let { SoloAssignmentCriterion[it] }
|
||||||
|
val fdbk = row[IndividualFeedbacks.feedback]
|
||||||
|
val grade = row[IndividualFeedbacks.grade]
|
||||||
|
|
||||||
|
crit to LocalFeedback(fdbk, grade)
|
||||||
|
}
|
||||||
|
|
||||||
|
val global = forStudent.firstOrNull { it.first == null }?.second
|
||||||
|
val byCrit_ = forStudent.map { it.first?.let { k -> Pair(k, it.second) } }
|
||||||
|
.filterNotNull().associateBy { it.first.id }
|
||||||
|
val byCrit = allCrit.map { c ->
|
||||||
|
byCrit_[c.id] ?: Pair(c, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
student to FullFeedback(global, byCrit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun upsertFeedback(student: Student, msg: String?, grd: String?, criterion: SoloAssignmentCriterion? = null) {
|
fun upsertFeedback(student: Student, msg: String?, grd: String?, criterion: SoloAssignmentCriterion? = null) {
|
||||||
transaction {
|
transaction {
|
||||||
SoloFeedbacks.upsert {
|
SoloFeedbacks.upsert {
|
||||||
it[soloAssignmentId] = assignment.id
|
it[assignmentId] = assignment.id
|
||||||
it[studentId] = student.id
|
it[studentId] = student.id
|
||||||
it[this.feedback] = msg ?: ""
|
it[this.feedback] = msg ?: ""
|
||||||
it[this.grade] = grd ?: ""
|
it[this.grade] = grd ?: ""
|
||||||
|
|
Loading…
Reference in New Issue