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

use LLM to extract time data from schedule title #236

Merged
merged 8 commits into from
Mar 3, 2025
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
11 changes: 10 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
kotlin("plugin.spring") version "2.1.10"
kotlin("plugin.jpa") version "2.1.10"
id("io.spring.dependency-management") version "1.1.7"
id("org.springframework.boot") version "3.3.5"
id("org.springframework.boot") version "3.4.3"
id("org.asciidoctor.jvm.convert") version "3.3.2"
}

Expand All @@ -20,6 +20,8 @@ repositories {
mavenCentral()
}

extra["springAiVersion"] = "1.0.0-M6"

val asciidoctorExt: Configuration by configurations.creating
dependencies {
// Kotlin
Expand All @@ -36,6 +38,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-webflux")
runtimeOnly("io.netty:netty-resolver-dns-native-macos:4.1.93.Final:osx-aarch_64")
implementation("org.springframework.ai:spring-ai-openai-spring-boot-starter")

// Monitoring
implementation("io.micrometer:micrometer-registry-prometheus")
Expand Down Expand Up @@ -68,6 +71,12 @@ dependencies {
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
}

dependencyManagement {
imports {
mavenBom("org.springframework.ai:spring-ai-bom:${property("springAiVersion")}")
}
}

tasks.withType<Test> {
useJUnitPlatform()
failFast = true
Expand Down
2 changes: 1 addition & 1 deletion dutypark_secret
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.tistory.shanepark.dutypark

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.boot.runApplication
import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.annotation.Bean
import org.springframework.data.jpa.repository.config.EnableJpaAuditing
import org.springframework.scheduling.annotation.EnableScheduling

Expand All @@ -19,6 +22,13 @@ fun main(args: Array<String>) {
runApplication<DutyparkApplication>(*args)
}

@Bean
fun objectMapper(): ObjectMapper {
return ObjectMapper().apply {
registerModule(JavaTimeModule())
}
}




Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ abstract class BaseTimeEntity {

@LastModifiedDate
@Column(name = "modified_date", updatable = true)
var modifiedDate: LocalDateTime? = null
var lastModifiedDate: LocalDateTime = LocalDateTime.now()

@CreatedDate
@Column(name = "created_date", updatable = false)
var createdDate: LocalDateTime? = null
var createdDate: LocalDateTime = LocalDateTime.now()

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ import jakarta.persistence.*
import org.hibernate.annotations.JdbcTypeCode
import org.hibernate.proxy.HibernateProxy
import org.hibernate.type.SqlTypes
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate
import org.springframework.data.domain.Persistable
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.time.LocalDateTime
import java.util.*
import kotlin.jvm.Transient

@MappedSuperclass
@EntityListeners(AuditingEntityListener::class)
class EntityBase : Persistable<UUID> {

@Id
Expand Down Expand Up @@ -56,4 +61,12 @@ class EntityBase : Persistable<UUID> {
_isNew = false
}

@LastModifiedDate
@Column(name = "modified_date", updatable = true)
var lastModifiedDate: LocalDateTime = LocalDateTime.now()

@CreatedDate
@Column(name = "created_date", updatable = false)
var createdDate: LocalDateTime = LocalDateTime.now()

}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ data class DepartmentDto(
dutyTypes = sortedTypes,
members = members.map { MemberDto.ofSimple(it) },
createdDate = department.createdDate.toString(),
lastModifiedDate = department.modifiedDate.toString(),
lastModifiedDate = department.lastModifiedDate.toString(),
manager = department.manager?.name,
dutyBatchTemplate = department.dutyBatchTemplate?.let { DutyBatchTemplateDto(it) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ class DepartmentService(
if (department.members.isNotEmpty()) {
throw IllegalStateException("Department has members")
}
val dutyTypes = department.dutyTypes
dutyRepository.setDutyTypeNullIfDutyTypeIn(dutyTypes)

dutyRepository.deleteAllByDutyTypeIn(department.dutyTypes)
departmentRepository.deleteById(id)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,10 @@ interface DutyRepository : JpaRepository<Duty, Long> {
@Query("update Duty d set d.dutyType = null where d.dutyType = :dutyType")
fun setDutyTypeNullIfDutyTypeIs(dutyType: DutyType)

@Modifying
@Query("update Duty d set d.dutyType = null where d.dutyType in :dutyTypes")
fun setDutyTypeNullIfDutyTypeIn(dutyTypes: Iterable<DutyType>)

fun deleteDutiesByMemberAndDutyDateBetween(member: Member, start: LocalDate, end: LocalDate)

fun findByDutyDateAndMemberIn(dutyDate: LocalDate, members: List<Member>): List<Duty>

fun deleteAllByDutyTypeIn(dutyTypes: List<DutyType>)

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ data class ScheduleDto(
fun ofSimple(member: Member, schedule: Schedule): ScheduleDto {
return ScheduleDto(
id = schedule.id,
content = schedule.content,
content = schedule.content(),
position = schedule.position,
year = schedule.startDateTime.year,
month = schedule.startDateTime.monthValue,
Expand All @@ -57,7 +57,7 @@ data class ScheduleDto(
return validDays.map {
ScheduleDto(
id = schedule.id,
content = schedule.content,
content = schedule.content(),
description = schedule.description,
position = schedule.position,
year = it.year,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.tistory.shanepark.dutypark.schedule.domain.entity
import com.tistory.shanepark.dutypark.common.domain.entity.EntityBase
import com.tistory.shanepark.dutypark.member.domain.entity.Member
import com.tistory.shanepark.dutypark.member.domain.enums.Visibility
import com.tistory.shanepark.dutypark.schedule.domain.enums.ParsingTimeStatus
import jakarta.persistence.*
import java.time.LocalDateTime

Expand All @@ -25,13 +26,20 @@ class Schedule(
var endDateTime: LocalDateTime,

@Column(name = "position", nullable = false)
var position: Int,
var position: Int = -1,

@Column(name = "visibility", nullable = false)
@Enumerated(EnumType.STRING)
var visibility: Visibility = Visibility.FRIENDS
var visibility: Visibility = Visibility.FRIENDS,

) : EntityBase() {
@Column(name = "parsing_time_status")
@Enumerated(EnumType.STRING)
var parsingTimeStatus: ParsingTimeStatus = ParsingTimeStatus.WAIT,

) : EntityBase() {

@Column(name = "content_without_time")
var contentWithoutTime: String = ""

@OneToMany(mappedBy = "schedule", fetch = FetchType.LAZY, cascade = [CascadeType.ALL], orphanRemoval = true)
val tags: MutableList<ScheduleTag> = mutableListOf()
Expand All @@ -52,4 +60,12 @@ class Schedule(
tags.remove(scheduleTag)
}

fun hasTimeInfo(): Boolean {
return startDateTime.hour != 0 || startDateTime.minute != 0
}

fun content(): String {
return contentWithoutTime.ifBlank { content }
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tistory.shanepark.dutypark.schedule.domain.enums

enum class ParsingTimeStatus {
WAIT,
SKIP,
PARSED,
NO_TIME_INFO,
ALREADY_HAVE_TIME_INFO,
FAILED,
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.tistory.shanepark.dutypark.schedule.repository
import com.tistory.shanepark.dutypark.member.domain.entity.Member
import com.tistory.shanepark.dutypark.member.domain.enums.Visibility
import com.tistory.shanepark.dutypark.schedule.domain.entity.Schedule
import com.tistory.shanepark.dutypark.schedule.domain.enums.ParsingTimeStatus
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository
Expand Down Expand Up @@ -97,5 +98,7 @@ interface ScheduleRepository : JpaRepository<Schedule, UUID> {
@Param("endOfDay") endOfDay: LocalDateTime = LocalDateTime.now().toLocalDate().atTime(23, 59, 59)
): List<Schedule>

fun findAllByParsingTimeStatus(parsingTimeStatus: ParsingTimeStatus): List<Schedule>

}

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import com.tistory.shanepark.dutypark.member.service.FriendService
import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleDto
import com.tistory.shanepark.dutypark.schedule.domain.dto.ScheduleUpdateDto
import com.tistory.shanepark.dutypark.schedule.domain.entity.Schedule
import com.tistory.shanepark.dutypark.schedule.domain.enums.ParsingTimeStatus
import com.tistory.shanepark.dutypark.schedule.repository.ScheduleRepository
import com.tistory.shanepark.dutypark.schedule.timeparsing.service.ScheduleTimeParsingQueueManager
import com.tistory.shanepark.dutypark.security.domain.dto.LoginMember
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
Expand All @@ -21,7 +23,8 @@ import java.util.*
class ScheduleService(
private val scheduleRepository: ScheduleRepository,
private val memberRepository: MemberRepository,
private val friendService: FriendService
private val friendService: FriendService,
private val scheduleTimeParsingQueueManager: ScheduleTimeParsingQueueManager,
) {
private val log = org.slf4j.LoggerFactory.getLogger(this.javaClass)

Expand Down Expand Up @@ -74,7 +77,6 @@ class ScheduleService(
return array
}


fun createSchedule(loginMember: LoginMember, scheduleUpdateDto: ScheduleUpdateDto): Schedule {
val scheduleMember = memberRepository.findById(scheduleUpdateDto.memberId).orElseThrow()
checkScheduleCreateAuthority(loginMember, scheduleMember)
Expand All @@ -92,7 +94,9 @@ class ScheduleService(
)

log.info("create schedule: $scheduleUpdateDto")
return scheduleRepository.save(schedule)
scheduleRepository.save(schedule)
scheduleTimeParsingQueueManager.addTask(schedule)
return schedule
}

private fun findNextPosition(
Expand All @@ -109,9 +113,12 @@ class ScheduleService(
schedule.content = scheduleUpdateDto.content
schedule.description = scheduleUpdateDto.description
schedule.visibility = scheduleUpdateDto.visibility
schedule.parsingTimeStatus = ParsingTimeStatus.WAIT

log.info("update schedule: $scheduleUpdateDto")
return scheduleRepository.save(schedule)
scheduleRepository.save(schedule)
scheduleTimeParsingQueueManager.addTask(schedule)
return schedule
}

fun swapSchedulePosition(loginMember: LoginMember, schedule1Id: UUID, schedule2Id: UUID) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.tistory.shanepark.dutypark.schedule.timeparsing.domain

import java.time.LocalDate

data class ScheduleTimeParsingRequest(
val date: LocalDate,
val content: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.tistory.shanepark.dutypark.schedule.timeparsing.domain

data class ScheduleTimeParsingResponse(
val result: Boolean = false,
val hasTime: Boolean = false,
val startDateTime: String? = null,
val endDateTime: String? = null,
val content: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.tistory.shanepark.dutypark.schedule.timeparsing.domain

import com.tistory.shanepark.dutypark.schedule.domain.entity.Schedule
import java.time.LocalDateTime
import java.util.*

data class ScheduleTimeParsingTask(
val scheduleId: UUID,
) {
private val requestDateTime: LocalDateTime = LocalDateTime.now()

fun isExpired(schedule: Schedule): Boolean {
if (schedule.id != scheduleId) {
throw IllegalArgumentException("Schedule ID does not match: ${schedule.id} != $scheduleId")
}
return schedule.lastModifiedDate
.minusSeconds(1)
.isAfter(requestDateTime)
}

}

Loading
Loading