Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release #104

Merged
merged 4 commits into from
Apr 13, 2024
Merged
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
@@ -1,6 +1,5 @@
package com.wafflestudio.snuttev.config

import jakarta.servlet.Filter
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
Expand All @@ -9,8 +8,8 @@ import org.springframework.context.annotation.Configuration
class FilterConfig {

@Bean
fun filterRegistrationBean(): FilterRegistrationBean<Filter> {
val filterRegistrationBean = FilterRegistrationBean<Filter>(SnuttUserFilter())
fun filterRegistrationBean(): FilterRegistrationBean<SnuttUserFilter> {
val filterRegistrationBean = FilterRegistrationBean(SnuttUserFilter())
filterRegistrationBean.addUrlPatterns("/v1/*")
return filterRegistrationBean
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
package com.wafflestudio.snuttev.config

import jakarta.servlet.Filter
import jakarta.servlet.FilterChain
import jakarta.servlet.ServletRequest
import jakarta.servlet.ServletResponse
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.web.filter.OncePerRequestFilter

class SnuttUserFilter : Filter {
override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
val httpRequest = request as HttpServletRequest
val httpResponse = response as HttpServletResponse

val snuttUserId = httpRequest.getHeader("Snutt-User-Id")
class SnuttUserFilter : OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain,
) {
val snuttUserId = request.getHeader("Snutt-User-Id")
if (snuttUserId?.isNotBlank() == true) {
request.setAttribute("UserId", snuttUserId)
chain?.doFilter(request, response)
filterChain.doFilter(request, response)
} else {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED)
response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
}
}

override fun shouldNotFilter(request: HttpServletRequest): Boolean {
val path = request.requestURI
val excludePath = listOf("/v1/lectures/snutt-summary")
return excludePath.contains(path)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class EvaluationController(
@GetMapping("/v1/lectures/{id}/evaluations")
fun getLectureEvaluations(
@PathVariable(value = "id") lectureId: Long,
@RequestParam("cursor") cursor: String?,
@RequestParam cursor: String?,
@RequestAttribute(value = "UserId") userId: String,
): CursorPaginationResponse<EvaluationWithSemesterResponse> {
return evaluationService.getEvaluationsOfLecture(userId, lectureId, cursor)
Expand All @@ -74,7 +74,7 @@ class EvaluationController(

@GetMapping("/v1/evaluations/users/me")
fun getEvaluationsOfMe(
@RequestParam("cursor") cursor: String?,
@RequestParam cursor: String?,
@RequestAttribute(value = "UserId") userId: String,
): CursorPaginationResponse<EvaluationWithLectureResponse> {
return evaluationService.getMyEvaluations(userId, cursor)
Expand All @@ -83,7 +83,7 @@ class EvaluationController(
@GetMapping("/v1/tags/main/{id}/evaluations")
fun getMainTagEvaluations(
@PathVariable(value = "id") tagId: Long,
@RequestParam("cursor") cursor: String?,
@RequestParam cursor: String?,
@RequestAttribute(value = "UserId") userId: String,
): CursorPaginationResponse<EvaluationWithLectureResponse> {
return evaluationService.getMainTagEvaluations(userId, tagId, cursor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.wafflestudio.snuttev.core.common.dto.common.ListResponse
import com.wafflestudio.snuttev.core.common.dto.common.PaginationResponse
import com.wafflestudio.snuttev.core.domain.lecture.dto.EvLectureSummaryForSnutt
import com.wafflestudio.snuttev.core.domain.lecture.dto.LectureAndSemesterLecturesResponse
import com.wafflestudio.snuttev.core.domain.lecture.dto.LectureDto
import com.wafflestudio.snuttev.core.domain.lecture.dto.LectureIdResponse
Expand Down Expand Up @@ -41,17 +42,25 @@ class LectureController(
@GetMapping("/v1/lectures/id")
fun getLectureId(
@RequestParam("course_number") courseNumber: String,
@RequestParam("instructor") instructor: String,
@RequestParam instructor: String,
): LectureIdResponse {
return lectureService.getLectureIdFromCourseNumber(courseNumber, instructor)
}

@GetMapping("/v1/lectures/snutt-summary")
fun getEvLectureSummaryForSnutt(
@RequestParam semesterLectureSnuttIds: List<String>,
): ListResponse<EvLectureSummaryForSnutt> {
val evLectureSummary = lectureService.getEvLectureSummaryForSnutt(semesterLectureSnuttIds)
return ListResponse(evLectureSummary)
}

@GetMapping("/v1/users/me/lectures/latest")
fun getLecturesTakenByCurrentUser(
@Parameter(hidden = true)
@RequestParam("snutt_lecture_info")
snuttLectureInfoString: String? = "",
@RequestParam("filter")
@RequestParam
filter: String?,
@RequestAttribute(value = "UserId")
userId: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.wafflestudio.snuttev.sync

import com.wafflestudio.snuttev.core.common.type.LectureClassification
import com.wafflestudio.snuttev.core.common.util.SemesterUtils
import com.wafflestudio.snuttev.core.domain.lecture.model.Lecture
import com.wafflestudio.snuttev.core.domain.lecture.model.SemesterLecture
import com.wafflestudio.snuttev.core.domain.lecture.model.SnuttLectureIdMap
import com.wafflestudio.snuttev.core.domain.lecture.repository.LectureRepository
import com.wafflestudio.snuttev.core.domain.lecture.repository.SemesterLectureRepository
import com.wafflestudio.snuttev.core.domain.lecture.repository.SnuttLectureIdMapRepository
import com.wafflestudio.snuttev.sync.model.SnuttSemesterLecture
import com.wafflestudio.snuttev.sync.repository.SnuttSemesterLectureRepository
import jakarta.persistence.EntityManagerFactory
import org.springframework.batch.core.Job
import org.springframework.batch.core.Step
Expand All @@ -16,8 +16,8 @@ import org.springframework.batch.core.repository.JobRepository
import org.springframework.batch.core.step.builder.StepBuilder
import org.springframework.batch.item.ItemProcessor
import org.springframework.batch.item.ItemWriter
import org.springframework.batch.item.data.MongoItemReader
import org.springframework.batch.item.data.builder.MongoItemReaderBuilder
import org.springframework.batch.item.data.MongoCursorItemReader
import org.springframework.batch.item.data.builder.MongoCursorItemReaderBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
Expand All @@ -36,8 +36,7 @@ class SnuttLectureSyncJobConfig(
private val mongoTemplate: MongoTemplate,
private val semesterLectureRepository: SemesterLectureRepository,
private val lectureRepository: LectureRepository,
private val semesterUtils: SemesterUtils,
private val snuttSemesterLectureRepository: SnuttSemesterLectureRepository,
private val snuttLectureIdMapRepository: SnuttLectureIdMapRepository,
) {
companion object {
private const val JOB_NAME = "SYNC_JOB"
Expand All @@ -48,6 +47,7 @@ class SnuttLectureSyncJobConfig(

private var lecturesMap: MutableMap<String, Lecture> = mutableMapOf()
private var semesterLecturesMap: MutableMap<String, SemesterLecture> = mutableMapOf()
private var snuttLectureIdMap: MutableMap<String, SnuttLectureIdMap> = mutableMapOf()

@Bean
fun syncJobNextSemester(jobRepository: JobRepository): Job {
Expand All @@ -63,6 +63,9 @@ class SnuttLectureSyncJobConfig(
semesterLectureRepository.findAllByYearAndSemesterWithLecture(targetYear, targetSemester)
.associateBy { "${it.lecture.courseNumber},${it.lecture.instructor},${it.year},${it.semester}" }
.toMutableMap()
snuttLectureIdMap = snuttLectureIdMapRepository.findAll()
.associateBy { it.snuttId }
.toMutableMap()

return JobBuilder(NEXT_SEMESTER_JOB_NAME, jobRepository)
.start(
Expand All @@ -85,14 +88,17 @@ class SnuttLectureSyncJobConfig(
semesterLectureRepository.findAllWithLecture()
.associateBy { "${it.lecture.courseNumber},${it.lecture.instructor},${it.year},${it.semester}" }
.toMutableMap()
snuttLectureIdMap = snuttLectureIdMapRepository.findAll()
.associateBy { it.snuttId }
.toMutableMap()
return JobBuilder(JOB_NAME, jobRepository)
.start(customReaderStep(jobRepository, Query()))
.build()
}

private fun customReaderStep(jobRepository: JobRepository, query: Query): Step {
return StepBuilder(CUSTOM_READER_JOB_STEP, jobRepository)
.chunk<SnuttSemesterLecture, SemesterLecture>(
.chunk<SnuttSemesterLecture, SyncProcessResult>(
CHUNK_SIZE,
JpaTransactionManager().apply {
this.entityManagerFactory = [email protected]
Expand All @@ -104,18 +110,18 @@ class SnuttLectureSyncJobConfig(
.build()
}

private fun reader(query: Query): MongoItemReader<SnuttSemesterLecture> {
return MongoItemReaderBuilder<SnuttSemesterLecture>()
private fun reader(query: Query): MongoCursorItemReader<SnuttSemesterLecture> {
return MongoCursorItemReaderBuilder<SnuttSemesterLecture>()
.template(mongoTemplate)
.collection("lectures").query(query)
.sorts(mapOf("courseNumber" to Sort.DEFAULT_DIRECTION))
.targetType(SnuttSemesterLecture::class.java).pageSize(CHUNK_SIZE)
.sorts(mapOf("_id" to Sort.DEFAULT_DIRECTION))
.targetType(SnuttSemesterLecture::class.java)
.name(this::reader.name)
.build()
}

private fun processor(): ItemProcessor<SnuttSemesterLecture, SemesterLecture> {
return ItemProcessor<SnuttSemesterLecture, SemesterLecture> { item: SnuttSemesterLecture ->
private fun processor(): ItemProcessor<SnuttSemesterLecture, SyncProcessResult> {
return ItemProcessor<SnuttSemesterLecture, SyncProcessResult> { item: SnuttSemesterLecture ->
val lecture: Lecture = lecturesMap["${item.courseNumber},${item.instructor}"]?.apply {
this.academicYear = item.academicYear
this.credit = item.credit
Expand All @@ -131,7 +137,7 @@ class SnuttLectureSyncJobConfig(
item.category,
LectureClassification.customValueOf(item.classification)!!,
).also { lecturesMap["${item.courseNumber},${item.instructor}"] = it }
semesterLecturesMap["${item.courseNumber},${item.instructor},${item.year},${item.semester}"]?.apply {
val semesterLecture = semesterLecturesMap["${item.courseNumber},${item.instructor},${item.year},${item.semester}"]?.apply {
this.academicYear = item.academicYear
this.category = item.category
this.classification = LectureClassification.customValueOf(item.classification)!!
Expand All @@ -148,13 +154,25 @@ class SnuttLectureSyncJobConfig(
item.category,
LectureClassification.customValueOf(item.classification)!!,
).also { semesterLecturesMap["${item.courseNumber},${item.instructor},${item.year},${item.semester}"] = it }
val snuttIdToLecture = snuttLectureIdMap[item.id] ?: SnuttLectureIdMap(
item.id,
semesterLecture,
).also { snuttLectureIdMap[item.id] = it }
SyncProcessResult(lecture, semesterLecture, snuttIdToLecture)
}
}

private fun writer(): ItemWriter<SemesterLecture> {
private fun writer(): ItemWriter<SyncProcessResult> {
return ItemWriter { items ->
lectureRepository.saveAll(items.map { it.lecture }.toSet())
semesterLectureRepository.saveAll(items.toSet())
semesterLectureRepository.saveAll(items.map { it.semesterLecture }.toSet())
snuttLectureIdMapRepository.saveAll(items.map { it.snuttLectureIdMap })
}
}
}

data class SyncProcessResult(
val lecture: Lecture,
val semesterLecture: SemesterLecture,
val snuttLectureIdMap: SnuttLectureIdMap,
)
10 changes: 5 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar
import java.io.ByteArrayOutputStream

plugins {
id("org.springframework.boot") version "3.1.4" apply false
id("org.springframework.boot") version "3.2.4" apply false
id("io.spring.dependency-management") version "1.1.3"
kotlin("jvm") version "1.9.10"
kotlin("plugin.spring") version "1.8.21"
kotlin("plugin.allopen") version "1.8.21"
kotlin("plugin.noarg") version "1.8.21"
kotlin("jvm") version "1.9.22"
kotlin("plugin.spring") version "1.9.22"
kotlin("plugin.allopen") version "1.9.22"
kotlin("plugin.noarg") version "1.9.22"
id("org.jlleitschuh.gradle.ktlint") version "11.3.2"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ data class LectureTakenByUserResponse(
val takenYear: Int,
val takenSemester: Int,
)

data class EvLectureSummaryForSnutt(
val snuttId: String,
val evLectureId: Long,
val avgRating: Double?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,8 @@ data class LectureEvaluationSummaryDao(
val avgLifeBalance: Double?,
val avgRating: Double?,
)

data class LectureRatingDao(
val id: Long,
val avgRating: Double?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import jakarta.persistence.Column
import jakarta.persistence.Convert
import jakarta.persistence.Entity
import jakarta.persistence.FetchType
import jakarta.persistence.Index
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.OneToMany
import jakarta.persistence.Table
import jakarta.persistence.UniqueConstraint

@Entity
@Table(uniqueConstraints = [UniqueConstraint(columnNames = ["lecture_id", "year", "semester"])])
@Table(
uniqueConstraints = [UniqueConstraint(columnNames = ["lecture_id", "year", "semester"])],
indexes = [Index(name = "semester_lecture_snutt_id_index", columnList = "snutt_id")],
)
class SemesterLecture(
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "lecture_id", nullable = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.wafflestudio.snuttev.core.domain.lecture.model

import com.wafflestudio.snuttev.core.common.model.BaseEntity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.FetchType
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne

@Entity
class SnuttLectureIdMap(
@Column(name = "snutt_id", columnDefinition = "char(24)")
var snuttId: String,

@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "semester_lecture_id", nullable = false, unique = true)
var semesterLecture: SemesterLecture,
) : BaseEntity()
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.wafflestudio.snuttev.core.domain.lecture.repository

import com.wafflestudio.snuttev.core.domain.lecture.model.Lecture
import com.wafflestudio.snuttev.core.domain.lecture.model.LectureEvaluationSummaryDao
import com.wafflestudio.snuttev.core.domain.lecture.model.LectureRatingDao
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query

Expand All @@ -22,4 +23,13 @@ interface LectureRepository : JpaRepository<Lecture, Long?>, LectureRepositoryCu
""",
)
fun findLectureWithAvgEvById(id: Long): LectureEvaluationSummaryDao

@Query(
"""
select new com.wafflestudio.snuttev.core.domain.lecture.model.LectureRatingDao(
sl.lecture.id, avg(le.rating)
) from LectureEvaluation le right join le.semesterLecture sl where sl.lecture.id in :ids and le.isHidden = false group by sl.lecture.id
""",
)
fun findAllRatingsByLectureIds(ids: Iterable<Long>): List<LectureRatingDao>
}
Loading
Loading