Skip to content

Commit

Permalink
feat(judgment): implement the function to apply and retrieve judgment…
Browse files Browse the repository at this point in the history
… results

Co-authored-by: cjlee38 <[email protected]>
  • Loading branch information
summerlunaa and cjlee38 authored Oct 14, 2022
1 parent e164183 commit 0f9a16c
Show file tree
Hide file tree
Showing 38 changed files with 832 additions and 209 deletions.
4 changes: 4 additions & 0 deletions src/docs/asciidoc/judgment.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

operation::judgment-judge-example-post[snippets='http-request,http-response']

== 예제 테스트 결과 조회

operation::judgment-judge-example-get[snippets='http-request,http-response']

== 자동 채점 성공

operation::judgment-success-result-post[snippets='http-request,http-response']
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/apply/application/AssignmentDtos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ data class AssignmentRequest(

@field:Pattern(
regexp = "https://github\\.com(/[\\w\\-]+){2}/pull/[1-9]\\d*",
message = "올바른 형식의 URL이어야 합니다"
message = "올바른 형식의 Pull Request URL이어야 합니다"
)
val pullRequestUrl: String,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ class EvaluationTargetService(

fun grade(evaluationTargetId: Long, request: EvaluationTargetData) {
val evaluationTarget = evaluationTargetRepository.getById(evaluationTargetId)

val evaluationAnswers = request.evaluationItemScores
.map { EvaluationAnswer(it.score, it.id) }
.toMutableList()
Expand Down
52 changes: 52 additions & 0 deletions src/main/kotlin/apply/application/GradingService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package apply.application

import apply.domain.assignment.AssignmentRepository
import apply.domain.assignment.getById
import apply.domain.evaluationtarget.EvaluationTargetRepository
import apply.domain.evaluationtarget.getByEvaluationIdAndUserId
import apply.domain.judgment.JudgmentStartedEvent
import apply.domain.judgment.JudgmentSucceededEvent
import apply.domain.judgment.JudgmentTouchedEvent
import apply.domain.judgmentitem.JudgmentItemRepository
import apply.domain.judgmentitem.getByMissionId
import apply.domain.mission.MissionRepository
import apply.domain.mission.getById
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional
import org.springframework.transaction.event.TransactionalEventListener

@Transactional
@Service
class GradingService(
private val evaluationTargetRepository: EvaluationTargetRepository,
private val missionRepository: MissionRepository,
private val judgmentItemRepository: JudgmentItemRepository,
private val assignmentRepository: AssignmentRepository
) {
@Transactional(propagation = Propagation.REQUIRES_NEW)
@TransactionalEventListener(condition = "#event.type.evaluable")
fun grade(event: JudgmentStartedEvent) {
grade(event.assignmentId, 0)
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
@TransactionalEventListener(condition = "#event.type.evaluable")
fun grade(event: JudgmentTouchedEvent) {
grade(event.assignmentId, event.passCount)
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
@TransactionalEventListener(condition = "#event.type.evaluable")
fun grade(event: JudgmentSucceededEvent) {
grade(event.assignmentId, event.passCount)
}

private fun grade(assignmentId: Long, score: Int) {
val assignment = assignmentRepository.getById(assignmentId)
val mission = missionRepository.getById(assignment.missionId)
val judgmentItem = judgmentItemRepository.getByMissionId(mission.id)
val target = evaluationTargetRepository.getByEvaluationIdAndUserId(mission.evaluationId, assignment.userId)
target.updateScore(judgmentItem.evaluationItemId, score)
}
}
27 changes: 27 additions & 0 deletions src/main/kotlin/apply/application/JudgmentAllService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package apply.application

import apply.domain.assignment.AssignmentRepository
import apply.domain.judgment.JudgmentType
import apply.domain.judgmentitem.JudgmentItemRepository
import apply.domain.mission.MissionRepository
import apply.domain.mission.getByEvaluationId
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Service

@Service
class JudgmentAllService(
private val judgmentService: JudgmentService,
private val missionRepository: MissionRepository,
private val judgmentItemRepository: JudgmentItemRepository,
private val assignmentRepository: AssignmentRepository
) {
@Async
fun judgeAll(evaluationId: Long) {
val mission = missionRepository.getByEvaluationId(evaluationId)
check(judgmentItemRepository.existsByMissionId(mission.id)) { "자동 채점을 실행할 수 없습니다." }
val assignments = assignmentRepository.findAllByMissionId(mission.id)
assignments.forEach {
runCatching { judgmentService.judge(mission, it, JudgmentType.REAL) }
}
}
}
9 changes: 7 additions & 2 deletions src/main/kotlin/apply/application/JudgmentDtos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,13 @@ data class JudgmentData(
val startedDateTime: LocalDateTime?,
val id: Long
) {
constructor(id: Long?, evaluationItemId: Long?, assignmentId: Long?, judgmentRecord: JudgmentRecord?) : this(
evaluationItemId ?: 0L,
constructor(
id: Long? = null,
evaluationItemId: Long,
assignmentId: Long? = null,
judgmentRecord: JudgmentRecord? = null
) : this(
evaluationItemId,
assignmentId ?: 0L,
judgmentRecord?.commit?.hash,
judgmentRecord?.status,
Expand Down
65 changes: 24 additions & 41 deletions src/main/kotlin/apply/application/JudgmentService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import apply.domain.assignment.Assignment
import apply.domain.assignment.AssignmentRepository
import apply.domain.assignment.getById
import apply.domain.assignment.getByUserIdAndMissionId
import apply.domain.evaluationtarget.EvaluationTargetRepository
import apply.domain.evaluationtarget.getById
import apply.domain.judgment.AssignmentArchive
import apply.domain.judgment.Commit
import apply.domain.judgment.Judgment
Expand All @@ -26,69 +24,54 @@ class JudgmentService(
private val assignmentRepository: AssignmentRepository,
private val missionRepository: MissionRepository,
private val judgmentItemRepository: JudgmentItemRepository,
private val evaluationTargetRepository: EvaluationTargetRepository,
private val assignmentArchive: AssignmentArchive
) {
fun judgeExample(userId: Long, missionId: Long): LastJudgmentResponse {
val mission = missionRepository.getById(missionId)
check(mission.isSubmitting) { "예제 테스트를 실행할 수 없습니다." }
check(mission.isSubmitting && judgmentItemRepository.existsByMissionId(mission.id)) {
"예제 테스트를 실행할 수 없습니다."
}
val assignment = assignmentRepository.getByUserIdAndMissionId(userId, missionId)
return judge(mission, assignment, JudgmentType.EXAMPLE)
}

fun judgeReal(userId: Long, missionId: Long): LastJudgmentResponse {
return judgeReal(assignmentRepository.getByUserIdAndMissionId(userId, missionId))
}

fun judgeRealByAssignmentId(assignmentId: Long): LastJudgmentResponse {
return judgeReal(assignmentRepository.getById(assignmentId))
}

private fun judgeReal(assignment: Assignment): LastJudgmentResponse {
val mission = missionRepository.getById(assignment.missionId)
return judge(mission, assignment, JudgmentType.REAL)
}

private fun judge(mission: Mission, assignment: Assignment, judgmentType: JudgmentType): LastJudgmentResponse {
check(judgmentItemRepository.existsByMissionId(mission.id)) { "예제 테스트를 실행할 수 없습니다." }
var judgment = judgmentRepository.findByAssignmentIdAndType(assignment.id, judgmentType)
?: judgmentRepository.save(Judgment(assignment.id, judgmentType))
val commit = assignmentArchive.getLastCommit(assignment.pullRequestUrl, mission.period.endDateTime)
judgment.start(commit)
judgment = judgmentRepository.save(judgment)
return LastJudgmentResponse(assignment.pullRequestUrl, judgment.lastRecord)
fun findLastExampleJudgment(userId: Long, missionId: Long): LastJudgmentResponse? {
val assignment = assignmentRepository.findByUserIdAndMissionId(userId, missionId) ?: return null
val judgment = judgmentRepository.findByAssignmentIdAndType(assignment.id, JudgmentType.EXAMPLE)
return judgment?.let { LastJudgmentResponse(assignment.pullRequestUrl, it.lastRecord) }
}

fun success(judgmentId: Long, request: SuccessJudgmentRequest) {
val judgment = judgmentRepository.getById(judgmentId)
judgment.success(Commit(request.commit), request.passCount, request.totalCount)
// TODO: reflect result to evaluation answer
judgmentRepository.save(judgment)
}

fun fail(judgmentId: Long, request: FailJudgmentRequest) {
val judgment = judgmentRepository.getById(judgmentId)
judgment.fail(Commit(request.commit), request.message)
judgmentRepository.save(judgment)
}

fun cancel(judgmentId: Long, request: CancelJudgmentRequest) {
val judgment = judgmentRepository.getById(judgmentId)
judgment.cancel(Commit(request.commit), request.message)
judgmentRepository.save(judgment)
}

fun judgeReal(assignmentId: Long): LastJudgmentResponse {
val assignment = assignmentRepository.getById(assignmentId)
val mission = missionRepository.getById(assignment.missionId)
check(judgmentItemRepository.existsByMissionId(mission.id)) { "자동 채점을 실행할 수 없습니다." }
return judge(mission, assignment, JudgmentType.REAL)
}

fun findByEvaluationTargetId(evaluationTargetId: Long, type: JudgmentType): JudgmentData? {
val evaluationTarget = evaluationTargetRepository.getById(evaluationTargetId)
val mission = missionRepository.findByEvaluationId(evaluationTarget.evaluationId) ?: return null
val judgmentItem = judgmentItemRepository.findByMissionId(mission.id) ?: return null
val assignment = assignmentRepository.findByUserIdAndMissionId(evaluationTarget.userId, mission.id)
return assignment
?.let { judgmentRepository.findByAssignmentIdAndType(it.id, type) }
.let {
JudgmentData(
id = it?.id,
evaluationItemId = judgmentItem.evaluationItemId,
assignmentId = assignment?.id,
judgmentRecord = it?.lastRecord
)
}
fun judge(mission: Mission, assignment: Assignment, judgmentType: JudgmentType): LastJudgmentResponse {
val commit = assignmentArchive.getLastCommit(assignment.pullRequestUrl, mission.period.endDateTime)
var judgment = judgmentRepository.findByAssignmentIdAndType(assignment.id, judgmentType)
?: judgmentRepository.save(Judgment(assignment.id, judgmentType))
judgment.start(commit)
judgment = judgmentRepository.save(judgment)
return LastJudgmentResponse(assignment.pullRequestUrl, judgment.lastRecord)
}
}
15 changes: 12 additions & 3 deletions src/main/kotlin/apply/application/MissionDtos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,26 @@ data class MyMissionResponse(
val submitted: Boolean,
val startDateTime: LocalDateTime,
val endDateTime: LocalDateTime,
val status: MissionStatus
val status: MissionStatus,
val runnable: Boolean,
val judgment: LastJudgmentResponse?
) {
constructor(mission: Mission, submitted: Boolean) : this(
constructor(
mission: Mission,
submitted: Boolean = false,
runnable: Boolean = false,
judgment: LastJudgmentResponse? = null
) : this(
mission.id,
mission.title,
mission.description,
mission.submittable,
submitted,
mission.period.startDateTime,
mission.period.endDateTime,
mission.status
mission.status,
runnable,
judgment
)
}

Expand Down
14 changes: 0 additions & 14 deletions src/main/kotlin/apply/application/MissionService.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package apply.application

import apply.domain.assignment.AssignmentRepository
import apply.domain.evaluation.EvaluationRepository
import apply.domain.evaluation.getById
import apply.domain.evaluationitem.EvaluationItemRepository
import apply.domain.evaluationtarget.EvaluationTargetRepository
import apply.domain.judgmentitem.JudgmentItem
import apply.domain.judgmentitem.JudgmentItemRepository
import apply.domain.mission.Mission
Expand All @@ -19,9 +17,7 @@ import org.springframework.transaction.annotation.Transactional
class MissionService(
private val missionRepository: MissionRepository,
private val evaluationRepository: EvaluationRepository,
private val evaluationTargetRepository: EvaluationTargetRepository,
private val evaluationItemRepository: EvaluationItemRepository,
private val assignmentRepository: AssignmentRepository,
private val judgmentItemRepository: JudgmentItemRepository
) {
fun save(request: MissionData): MissionResponse {
Expand Down Expand Up @@ -98,16 +94,6 @@ class MissionService(
return missions.map { MissionAndEvaluationResponse(it, evaluationsById.getValue(it.evaluationId)) }
}

fun findAllByUserIdAndRecruitmentId(userId: Long, recruitmentId: Long): List<MyMissionResponse> {
val evaluationIds = evaluationRepository.findAllByRecruitmentId(recruitmentId).map { it.id }
val includedEvaluationIds = evaluationIds
.filter { evaluationTargetRepository.existsByUserIdAndEvaluationId(userId, it) }
val assignments = assignmentRepository.findAllByUserId(userId)
return missionRepository.findAllByEvaluationIdIn(includedEvaluationIds)
.filterNot { it.hidden }
.map { mission -> MyMissionResponse(mission, assignments.any { it.missionId == mission.id }) }
}

fun deleteById(id: Long) {
val mission = missionRepository.getById(id)
check(!mission.submittable) { "제출 가능한 과제는 삭제할 수 없습니다." }
Expand Down
97 changes: 97 additions & 0 deletions src/main/kotlin/apply/application/MyMissionService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package apply.application

import apply.domain.assignment.Assignment
import apply.domain.assignment.AssignmentRepository
import apply.domain.evaluation.EvaluationRepository
import apply.domain.evaluationtarget.EvaluationTargetRepository
import apply.domain.evaluationtarget.getById
import apply.domain.judgment.Judgment
import apply.domain.judgment.JudgmentRepository
import apply.domain.judgment.JudgmentType
import apply.domain.judgmentitem.JudgmentItem
import apply.domain.judgmentitem.JudgmentItemRepository
import apply.domain.mission.Mission
import apply.domain.mission.MissionRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Transactional(readOnly = true)
@Service
class MyMissionService(
private val evaluationRepository: EvaluationRepository,
private val evaluationTargetRepository: EvaluationTargetRepository,
private val missionRepository: MissionRepository,
private val judgmentItemRepository: JudgmentItemRepository,
private val assignmentRepository: AssignmentRepository,
private val judgmentRepository: JudgmentRepository
) {
fun findAllByUserIdAndRecruitmentId(userId: Long, recruitmentId: Long): List<MyMissionResponse> {
val missions = findMissions(userId, recruitmentId)
if (missions.isEmpty()) return emptyList()

val assignments = assignmentRepository.findAllByUserId(userId)
if (assignments.isEmpty()) return missions.map(::MyMissionResponse)

val judgmentItems = judgmentItemRepository.findAllByMissionIdIn(missions.map { it.id })
if (judgmentItems.isEmpty()) return missions.mapBy(assignments)

val judgments = judgmentRepository
.findAllByAssignmentIdInAndType(assignments.map { it.id }, JudgmentType.EXAMPLE)
return missions.mapBy(assignments, judgmentItems, judgments)
}

private fun findMissions(userId: Long, recruitmentId: Long): List<Mission> {
val evaluationIds = evaluationRepository.findAllByRecruitmentId(recruitmentId).map { it.id }
val targets = evaluationTargetRepository.findAllByUserIdAndEvaluationIdIn(userId, evaluationIds)
return missionRepository.findAllByEvaluationIdIn(targets.map { it.id }).filterNot { it.hidden }
}

private fun List<Mission>.mapBy(assignments: List<Assignment>): List<MyMissionResponse> {
return map { mission ->
val assignment = assignments.find { it.missionId == mission.id }
MyMissionResponse(mission, assignment != null)
}
}

private fun List<Mission>.mapBy(
assignments: List<Assignment>,
judgmentItems: List<JudgmentItem>,
judgments: List<Judgment>
): List<MyMissionResponse> {
return map { mission ->
val assignment = assignments.find { it.missionId == mission.id }
val judgmentItem = judgmentItems.find { it.missionId == mission.id }
val judgment = judgments.findLastJudgment(assignment, judgmentItem)
MyMissionResponse(
mission = mission,
submitted = assignment != null,
runnable = assignment != null && judgmentItem != null,
judgment = judgment
)
}
}

private fun List<Judgment>.findLastJudgment(
assignment: Assignment?,
judgmentItem: JudgmentItem?
): LastJudgmentResponse? {
if (assignment == null || judgmentItem == null) return null
val judgment = find { it.assignmentId == assignment.id } ?: return null
return LastJudgmentResponse(assignment.pullRequestUrl, judgment.lastRecord)
}

fun findLastRealJudgmentByEvaluationTargetId(evaluationTargetId: Long): JudgmentData? {
val evaluationTarget = evaluationTargetRepository.getById(evaluationTargetId)
val mission = missionRepository.findByEvaluationId(evaluationTarget.evaluationId) ?: return null
val judgmentItem = judgmentItemRepository.findByMissionId(mission.id) ?: return null
val assignment = assignmentRepository.findByUserIdAndMissionId(evaluationTarget.userId, mission.id)
?: return JudgmentData(evaluationItemId = judgmentItem.evaluationItemId)
val judgment = judgmentRepository.findByAssignmentIdAndType(assignment.id, JudgmentType.REAL)
return JudgmentData(
id = judgment?.id,
evaluationItemId = judgmentItem.evaluationItemId,
assignmentId = assignment.id,
judgmentRecord = judgment?.lastRecord
)
}
}
Loading

0 comments on commit 0f9a16c

Please sign in to comment.