검수완료 geosjon 생성 스케줄러 수정

This commit is contained in:
2026-02-24 19:52:51 +09:00
parent 135c6b5892
commit 6bec4d017f
282 changed files with 1913 additions and 4824 deletions

7
label/review-to-geojson/.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KubernetesApiPersistence">{}</component>
<component name="KubernetesApiProvider">{
&quot;isMigrated&quot;: true
}</component>
</project>

View File

@@ -0,0 +1,26 @@
# Getting Started
### Reference Documentation
For further reference, please consider the following sections:
* [Official Gradle documentation](https://docs.gradle.org)
* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/4.0.3/gradle-plugin)
* [Create an OCI image](https://docs.spring.io/spring-boot/4.0.3/gradle-plugin/packaging-oci-image.html)
* [Spring Boot DevTools](https://docs.spring.io/spring-boot/4.0.3/reference/using/devtools.html)
* [Spring Web](https://docs.spring.io/spring-boot/4.0.3/reference/web/servlet.html)
### Guides
The following guides illustrate how to use some features concretely:
* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
### Additional Links
These additional references should also help you:
* [Gradle Build Scans insights for your project's build](https://scans.gradle.com#gradle)

View File

@@ -0,0 +1,26 @@
# REVIEW TO GEOJSON
> 어제 검수완료된 것 geojson 생성 schedule
## 📋 프로젝트 소개
**review-to-down**는 어제 검수 완료된 것을 geojson 파일로 생성하는 schedule 입니다.
## 🚀 시작하기
TrainingDataReviewJobService 의 exportGeojsonLabelingGeom 메소드가 매일 02:00분에 schedule 실행됨
```bash
./gradlew spotlessApply
```
```bash
./gradlew clean build
```
```bash
Java -jar review-to-geojson.jar --spring.profiles.active=dev \
```
### 필수 요구사항
- Java 21 (JDK 21)
- PostgreSQL 12+ (PostGIS 확장 필요)
- Gradle 8.x (또는 Gradle Wrapper 사용)
- Docker & Docker Compose (선택사항)

View File

@@ -0,0 +1,54 @@
plugins {
id 'java'
id 'org.springframework.boot' version '4.0.3'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.kamcoback.cd'
version = '0.0.1-SNAPSHOT'
description = 'TrainingDataReview'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
bootJar {
archiveFileName = "review-to-geojson.jar"
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'org.postgresql:postgresql'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta"
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
implementation 'org.locationtech.jts:jts-core:1.19.0'
}
tasks.named('test') {
useJUnitPlatform()
}

View File

@@ -0,0 +1,39 @@
package com.kamcoback.cd.review.postgres.entity;
import static com.querydsl.core.types.PathMetadataFactory.*;
import com.querydsl.core.types.dsl.*;
import com.querydsl.core.types.PathMetadata;
import javax.annotation.processing.Generated;
import com.querydsl.core.types.Path;
/**
* QCommonDateEntity is a Querydsl query type for CommonDateEntity
*/
@Generated("com.querydsl.codegen.DefaultSupertypeSerializer")
public class QCommonDateEntity extends EntityPathBase<CommonDateEntity> {
private static final long serialVersionUID = 887355116L;
public static final QCommonDateEntity commonDateEntity = new QCommonDateEntity("commonDateEntity");
public final DateTimePath<java.time.ZonedDateTime> createdDate = createDateTime("createdDate", java.time.ZonedDateTime.class);
public final DateTimePath<java.time.ZonedDateTime> modifiedDate = createDateTime("modifiedDate", java.time.ZonedDateTime.class);
public QCommonDateEntity(String variable) {
super(CommonDateEntity.class, forVariable(variable));
}
public QCommonDateEntity(Path<? extends CommonDateEntity> path) {
super(path.getType(), path.getMetadata());
}
public QCommonDateEntity(PathMetadata metadata) {
super(CommonDateEntity.class, metadata);
}
}

View File

@@ -0,0 +1,67 @@
package com.kamcoback.cd.review.postgres.entity;
import static com.querydsl.core.types.PathMetadataFactory.*;
import com.querydsl.core.types.dsl.*;
import com.querydsl.core.types.PathMetadata;
import javax.annotation.processing.Generated;
import com.querydsl.core.types.Path;
/**
* QLabelingAssignmentEntity is a Querydsl query type for LabelingAssignmentEntity
*/
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QLabelingAssignmentEntity extends EntityPathBase<LabelingAssignmentEntity> {
private static final long serialVersionUID = 1552947982L;
public static final QLabelingAssignmentEntity labelingAssignmentEntity = new QLabelingAssignmentEntity("labelingAssignmentEntity");
public final QCommonDateEntity _super = new QCommonDateEntity(this);
public final NumberPath<Long> analUid = createNumber("analUid", Long.class);
public final StringPath assignGroupId = createString("assignGroupId");
public final ComparablePath<java.util.UUID> assignmentUid = createComparable("assignmentUid", java.util.UUID.class);
//inherited
public final DateTimePath<java.time.ZonedDateTime> createdDate = _super.createdDate;
public final NumberPath<Long> inferenceGeomUid = createNumber("inferenceGeomUid", Long.class);
public final StringPath inspectorUid = createString("inspectorUid");
public final DateTimePath<java.time.ZonedDateTime> inspectStatDttm = createDateTime("inspectStatDttm", java.time.ZonedDateTime.class);
public final StringPath inspectState = createString("inspectState");
public final NumberPath<Long> learnGeomUid = createNumber("learnGeomUid", Long.class);
//inherited
public final DateTimePath<java.time.ZonedDateTime> modifiedDate = _super.modifiedDate;
public final ComparablePath<Character> stagnationYn = createComparable("stagnationYn", Character.class);
public final StringPath workerUid = createString("workerUid");
public final DateTimePath<java.time.ZonedDateTime> workStatDttm = createDateTime("workStatDttm", java.time.ZonedDateTime.class);
public final StringPath workState = createString("workState");
public QLabelingAssignmentEntity(String variable) {
super(LabelingAssignmentEntity.class, forVariable(variable));
}
public QLabelingAssignmentEntity(Path<? extends LabelingAssignmentEntity> path) {
super(path.getType(), path.getMetadata());
}
public QLabelingAssignmentEntity(PathMetadata metadata) {
super(LabelingAssignmentEntity.class, metadata);
}
}

View File

@@ -0,0 +1,49 @@
package com.kamcoback.cd.review.postgres.entity;
import static com.querydsl.core.types.PathMetadataFactory.*;
import com.querydsl.core.types.dsl.*;
import com.querydsl.core.types.PathMetadata;
import javax.annotation.processing.Generated;
import com.querydsl.core.types.Path;
/**
* QLabelingInspectorEntity is a Querydsl query type for LabelingInspectorEntity
*/
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QLabelingInspectorEntity extends EntityPathBase<LabelingInspectorEntity> {
private static final long serialVersionUID = 1160555516L;
public static final QLabelingInspectorEntity labelingInspectorEntity = new QLabelingInspectorEntity("labelingInspectorEntity");
public final QCommonDateEntity _super = new QCommonDateEntity(this);
public final NumberPath<Long> analUid = createNumber("analUid", Long.class);
//inherited
public final DateTimePath<java.time.ZonedDateTime> createdDate = _super.createdDate;
public final StringPath inspectorUid = createString("inspectorUid");
//inherited
public final DateTimePath<java.time.ZonedDateTime> modifiedDate = _super.modifiedDate;
public final ComparablePath<java.util.UUID> operatorUid = createComparable("operatorUid", java.util.UUID.class);
public QLabelingInspectorEntity(String variable) {
super(LabelingInspectorEntity.class, forVariable(variable));
}
public QLabelingInspectorEntity(Path<? extends LabelingInspectorEntity> path) {
super(path.getType(), path.getMetadata());
}
public QLabelingInspectorEntity(PathMetadata metadata) {
super(LabelingInspectorEntity.class, metadata);
}
}

View File

@@ -0,0 +1,110 @@
package com.kamcoback.cd.review.postgres.entity;
import static com.querydsl.core.types.PathMetadataFactory.*;
import com.querydsl.core.types.dsl.*;
import com.querydsl.core.types.PathMetadata;
import javax.annotation.processing.Generated;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.dsl.PathInits;
/**
* QMapSheetAnalInferenceEntity is a Querydsl query type for MapSheetAnalInferenceEntity
*/
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QMapSheetAnalInferenceEntity extends EntityPathBase<MapSheetAnalInferenceEntity> {
private static final long serialVersionUID = 2038127441L;
public static final QMapSheetAnalInferenceEntity mapSheetAnalInferenceEntity = new QMapSheetAnalInferenceEntity("mapSheetAnalInferenceEntity");
public final NumberPath<Double> accuracy = createNumber("accuracy", Double.class);
public final DateTimePath<java.time.ZonedDateTime> analEndDttm = createDateTime("analEndDttm", java.time.ZonedDateTime.class);
public final NumberPath<Long> analPredSec = createNumber("analPredSec", Long.class);
public final NumberPath<Long> analSec = createNumber("analSec", Long.class);
public final StringPath analState = createString("analState");
public final DateTimePath<java.time.ZonedDateTime> analStrtDttm = createDateTime("analStrtDttm", java.time.ZonedDateTime.class);
public final StringPath analTargetType = createString("analTargetType");
public final StringPath analTitle = createString("analTitle");
public final StringPath baseMapSheetNum = createString("baseMapSheetNum");
public final NumberPath<Integer> compareYyyy = createNumber("compareYyyy", Integer.class);
public final DateTimePath<java.time.ZonedDateTime> createdDttm = createDateTime("createdDttm", java.time.ZonedDateTime.class);
public final NumberPath<Long> createdUid = createNumber("createdUid", Long.class);
public final NumberPath<Long> detectingCnt = createNumber("detectingCnt", Long.class);
public final StringPath detectingDescription = createString("detectingDescription");
public final StringPath detectionDataOption = createString("detectionDataOption");
public final DateTimePath<java.time.ZonedDateTime> gukyuinApplyDttm = createDateTime("gukyuinApplyDttm", java.time.ZonedDateTime.class);
public final StringPath gukyuinUsed = createString("gukyuinUsed");
public final MapPath<String, Object, SimplePath<Object>> hyperParams = this.<String, Object, SimplePath<Object>>createMap("hyperParams", String.class, Object.class, SimplePath.class);
public final NumberPath<Long> id = createNumber("id", Long.class);
public final StringPath inspectionClosedYn = createString("inspectionClosedYn");
public final StringPath labelingClosedYn = createString("labelingClosedYn");
public final NumberPath<Long> learnId = createNumber("learnId", Long.class);
public final StringPath modelM1Ver = createString("modelM1Ver");
public final StringPath modelM2Ver = createString("modelM2Ver");
public final StringPath modelM3Ver = createString("modelM3Ver");
public final NumberPath<Long> modelUid = createNumber("modelUid", Long.class);
public final NumberPath<Long> modelVerUid = createNumber("modelVerUid", Long.class);
public final StringPath resultUrl = createString("resultUrl");
public final StringPath serverIds = createString("serverIds");
public final NumberPath<Integer> stage = createNumber("stage", Integer.class);
public final NumberPath<Integer> targetYyyy = createNumber("targetYyyy", Integer.class);
public final StringPath testRate = createString("testRate");
public final ListPath<Double, NumberPath<Double>> tranningRate = this.<Double, NumberPath<Double>>createList("tranningRate", Double.class, NumberPath.class, PathInits.DIRECT2);
public final DateTimePath<java.time.ZonedDateTime> updatedDttm = createDateTime("updatedDttm", java.time.ZonedDateTime.class);
public final NumberPath<Long> updatedUid = createNumber("updatedUid", Long.class);
public final ComparablePath<java.util.UUID> uuid = createComparable("uuid", java.util.UUID.class);
public final ListPath<Double, NumberPath<Double>> validationRate = this.<Double, NumberPath<Double>>createList("validationRate", Double.class, NumberPath.class, PathInits.DIRECT2);
public QMapSheetAnalInferenceEntity(String variable) {
super(MapSheetAnalInferenceEntity.class, forVariable(variable));
}
public QMapSheetAnalInferenceEntity(Path<? extends MapSheetAnalInferenceEntity> path) {
super(path.getType(), path.getMetadata());
}
public QMapSheetAnalInferenceEntity(PathMetadata metadata) {
super(MapSheetAnalInferenceEntity.class, metadata);
}
}

View File

@@ -0,0 +1,59 @@
package com.kamcoback.cd.review.postgres.entity;
import static com.querydsl.core.types.PathMetadataFactory.*;
import com.querydsl.core.types.dsl.*;
import com.querydsl.core.types.PathMetadata;
import javax.annotation.processing.Generated;
import com.querydsl.core.types.Path;
/**
* QMapSheetLearnDataGeomEntity is a Querydsl query type for MapSheetLearnDataGeomEntity
*/
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QMapSheetLearnDataGeomEntity extends EntityPathBase<MapSheetLearnDataGeomEntity> {
private static final long serialVersionUID = 886149914L;
public static final QMapSheetLearnDataGeomEntity mapSheetLearnDataGeomEntity = new QMapSheetLearnDataGeomEntity("mapSheetLearnDataGeomEntity");
public final QCommonDateEntity _super = new QCommonDateEntity(this);
public final NumberPath<Integer> afterYyyy = createNumber("afterYyyy", Integer.class);
public final NumberPath<Double> area = createNumber("area", Double.class);
public final NumberPath<Integer> beforeYyyy = createNumber("beforeYyyy", Integer.class);
public final StringPath classAfterCd = createString("classAfterCd");
public final StringPath classBeforeCd = createString("classBeforeCd");
//inherited
public final DateTimePath<java.time.ZonedDateTime> createdDate = _super.createdDate;
public final BooleanPath fileCreateYn = createBoolean("fileCreateYn");
public final ComparablePath<org.locationtech.jts.geom.Geometry> geom = createComparable("geom", org.locationtech.jts.geom.Geometry.class);
public final NumberPath<Long> geoUid = createNumber("geoUid", Long.class);
//inherited
public final DateTimePath<java.time.ZonedDateTime> modifiedDate = _super.modifiedDate;
public QMapSheetLearnDataGeomEntity(String variable) {
super(MapSheetLearnDataGeomEntity.class, forVariable(variable));
}
public QMapSheetLearnDataGeomEntity(Path<? extends MapSheetLearnDataGeomEntity> path) {
super(path.getType(), path.getMetadata());
}
public QMapSheetLearnDataGeomEntity(PathMetadata metadata) {
super(MapSheetLearnDataGeomEntity.class, metadata);
}
}

View File

@@ -0,0 +1,141 @@
package com.kamcoback.cd.review.postgres.entity;
import static com.querydsl.core.types.PathMetadataFactory.*;
import com.querydsl.core.types.dsl.*;
import com.querydsl.core.types.PathMetadata;
import javax.annotation.processing.Generated;
import com.querydsl.core.types.Path;
/**
* QMapSheetLearnEntity is a Querydsl query type for MapSheetLearnEntity
*/
@Generated("com.querydsl.codegen.DefaultEntitySerializer")
public class QMapSheetLearnEntity extends EntityPathBase<MapSheetLearnEntity> {
private static final long serialVersionUID = 1102645876L;
public static final QMapSheetLearnEntity mapSheetLearnEntity = new QMapSheetLearnEntity("mapSheetLearnEntity");
public final DateTimePath<java.time.ZonedDateTime> applyDttm = createDateTime("applyDttm", java.time.ZonedDateTime.class);
public final StringPath applyStatus = createString("applyStatus");
public final DateTimePath<java.time.ZonedDateTime> applyStatusDttm = createDateTime("applyStatusDttm", java.time.ZonedDateTime.class);
public final BooleanPath applyYn = createBoolean("applyYn");
public final StringPath chnDtctMstId = createString("chnDtctMstId");
public final NumberPath<Integer> compareYyyy = createNumber("compareYyyy", Integer.class);
public final DateTimePath<java.time.ZonedDateTime> createdDttm = createDateTime("createdDttm", java.time.ZonedDateTime.class);
public final NumberPath<Long> createdUid = createNumber("createdUid", Long.class);
public final NumberPath<Long> detectEndCnt = createNumber("detectEndCnt", Long.class);
public final NumberPath<Long> detectingCnt = createNumber("detectingCnt", Long.class);
public final StringPath detectOption = createString("detectOption");
public final DateTimePath<java.time.ZonedDateTime> elapsedTime = createDateTime("elapsedTime", java.time.ZonedDateTime.class);
public final NumberPath<Long> id = createNumber("id", Long.class);
public final DateTimePath<java.time.ZonedDateTime> inferEndDttm = createDateTime("inferEndDttm", java.time.ZonedDateTime.class);
public final DateTimePath<java.time.ZonedDateTime> inferStartDttm = createDateTime("inferStartDttm", java.time.ZonedDateTime.class);
public final NumberPath<Integer> m1CompletedJobs = createNumber("m1CompletedJobs", Integer.class);
public final NumberPath<Integer> m1FailedJobs = createNumber("m1FailedJobs", Integer.class);
public final NumberPath<Long> m1ModelBatchId = createNumber("m1ModelBatchId", Long.class);
public final DateTimePath<java.time.ZonedDateTime> m1ModelEndDttm = createDateTime("m1ModelEndDttm", java.time.ZonedDateTime.class);
public final DateTimePath<java.time.ZonedDateTime> m1ModelStartDttm = createDateTime("m1ModelStartDttm", java.time.ZonedDateTime.class);
public final ComparablePath<java.util.UUID> m1ModelUuid = createComparable("m1ModelUuid", java.util.UUID.class);
public final NumberPath<Integer> m1PendingJobs = createNumber("m1PendingJobs", Integer.class);
public final NumberPath<Integer> m1RunningJobs = createNumber("m1RunningJobs", Integer.class);
public final NumberPath<Integer> m2CompletedJobs = createNumber("m2CompletedJobs", Integer.class);
public final NumberPath<Integer> m2FailedJobs = createNumber("m2FailedJobs", Integer.class);
public final NumberPath<Long> m2ModelBatchId = createNumber("m2ModelBatchId", Long.class);
public final DateTimePath<java.time.ZonedDateTime> m2ModelEndDttm = createDateTime("m2ModelEndDttm", java.time.ZonedDateTime.class);
public final DateTimePath<java.time.ZonedDateTime> m2ModelStartDttm = createDateTime("m2ModelStartDttm", java.time.ZonedDateTime.class);
public final ComparablePath<java.util.UUID> m2ModelUuid = createComparable("m2ModelUuid", java.util.UUID.class);
public final NumberPath<Integer> m2PendingJobs = createNumber("m2PendingJobs", Integer.class);
public final NumberPath<Integer> m2RunningJobs = createNumber("m2RunningJobs", Integer.class);
public final NumberPath<Integer> m3CompletedJobs = createNumber("m3CompletedJobs", Integer.class);
public final NumberPath<Integer> m3FailedJobs = createNumber("m3FailedJobs", Integer.class);
public final NumberPath<Long> m3ModelBatchId = createNumber("m3ModelBatchId", Long.class);
public final DateTimePath<java.time.ZonedDateTime> m3ModelEndDttm = createDateTime("m3ModelEndDttm", java.time.ZonedDateTime.class);
public final DateTimePath<java.time.ZonedDateTime> m3ModelStartDttm = createDateTime("m3ModelStartDttm", java.time.ZonedDateTime.class);
public final ComparablePath<java.util.UUID> m3ModelUuid = createComparable("m3ModelUuid", java.util.UUID.class);
public final NumberPath<Integer> m3PendingJobs = createNumber("m3PendingJobs", Integer.class);
public final NumberPath<Integer> m3RunningJobs = createNumber("m3RunningJobs", Integer.class);
public final StringPath mapSheetCnt = createString("mapSheetCnt");
public final StringPath mapSheetScope = createString("mapSheetScope");
public final StringPath modelComparePath = createString("modelComparePath");
public final StringPath modelTargetPath = createString("modelTargetPath");
public final StringPath runningModelType = createString("runningModelType");
public final NumberPath<Integer> stage = createNumber("stage", Integer.class);
public final StringPath status = createString("status");
public final NumberPath<Integer> targetYyyy = createNumber("targetYyyy", Integer.class);
public final StringPath title = createString("title");
public final NumberPath<Long> totalJobs = createNumber("totalJobs", Long.class);
public final StringPath uid = createString("uid");
public final DateTimePath<java.time.ZonedDateTime> updatedDttm = createDateTime("updatedDttm", java.time.ZonedDateTime.class);
public final NumberPath<Long> updatedUid = createNumber("updatedUid", Long.class);
public final ComparablePath<java.util.UUID> uuid = createComparable("uuid", java.util.UUID.class);
public QMapSheetLearnEntity(String variable) {
super(MapSheetLearnEntity.class, forVariable(variable));
}
public QMapSheetLearnEntity(Path<? extends MapSheetLearnEntity> path) {
super(path.getType(), path.getMetadata());
}
public QMapSheetLearnEntity(PathMetadata metadata) {
super(MapSheetLearnEntity.class, metadata);
}
}

View File

@@ -0,0 +1,111 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
<title>Test results - contextLoads()</title>
<link href="../css/base-style.css" rel="stylesheet" type="text/css"/>
<link href="../css/style.css" rel="stylesheet" type="text/css"/>
<script src="../js/report.js" type="text/javascript"></script>
</head>
<body>
<div id="content">
<div class="breadcrumbs">
<a class="breadcrumb" href="../index.html">all</a> &gt;
<a class="breadcrumb" href="index.html">com.kamcoback.cd.review.TrainingDataReviewApplicationTests</a> &gt;
<span class="breadcrumb">contextLoads()</span>
</div>
<div class="tab-container">
<ul class="tabLinks">
<li>
<a class="successGroup" href="#">Gradle Test Run :test</a>
</li>
</ul>
<div class="tab">
<h2>Gradle Test Run :test</h2>
<h1>contextLoads()</h1>
<div class="tab-container">
<ul class="tabLinks">
<li>
<a class="" href="#">summary</a>
</li>
<li>
<a class="" href="#">error output</a>
</li>
</ul>
<div class="tab">
<h2>summary</h2>
<div>
<div class="summary">
<table>
<tr>
<td>
<div class="summaryGroup">
<table>
<tr>
<td>
<div class="infoBox">
<div class="counter">1</div>
<p>tests</p>
</div>
</td>
<td>
<div class="infoBox">
<div class="counter">0</div>
<p>failures</p>
</div>
</td>
<td>
<div class="infoBox">
<div class="counter">0</div>
<p>skipped</p>
</div>
</td>
<td>
<div class="infoBox duration">
<div class="counter">0.235s</div>
<p>duration</p>
</div>
</td>
</tr>
</table>
</div>
</td>
<td>
<div class="infoBox success successRate">
<div class="percent">100%</div>
<p>successful</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="tab">
<h2>error output</h2>
<span class="code">
<pre id="root-0-test-stderr-contextLoads()">Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
WARNING: A Java agent has been loaded dynamically (/Users/bokmin/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.17.8/f09415827a71be7ed621c7bd02550678f28bc81c/byte-buddy-agent-1.17.8.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
</pre>
<button class="clipboard-copy-btn" aria-label="Copy to clipboard" data-copy-element-id="root-0-test-stderr-contextLoads()">Copy</button>
</span>
</div>
</div>
</div>
</div>
<div id="footer">
<p>
<div>
<label class="hidden" id="label-for-line-wrapping-toggle" for="line-wrapping-toggle">Wrap lines
<input id="line-wrapping-toggle" type="checkbox" autocomplete="off"/>
</label>
</div>Generated by
<a href="https://www.gradle.org">Gradle 9.3.1</a> at 2026. 2. 24. 오후 7:11:40</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,173 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
<title>Test results - com.kamcoback.cd.review.TrainingDataReviewApplicationTests</title>
<link href="../css/base-style.css" rel="stylesheet" type="text/css"/>
<link href="../css/style.css" rel="stylesheet" type="text/css"/>
<script src="../js/report.js" type="text/javascript"></script>
</head>
<body>
<div id="content">
<div class="breadcrumbs">
<a class="breadcrumb" href="../index.html">all</a> &gt;
<span class="breadcrumb">com.kamcoback.cd.review.TrainingDataReviewApplicationTests</span>
</div>
<div class="tab-container">
<ul class="tabLinks">
<li>
<a class="successGroup" href="#">Gradle Test Run :test</a>
</li>
</ul>
<div class="tab">
<h2>Gradle Test Run :test</h2>
<h1>TrainingDataReviewApplicationTests</h1>
<div class="tab-container">
<ul class="tabLinks">
<li>
<a class="" href="#">summary</a>
</li>
<li>
<a class="" href="#">standard output</a>
</li>
</ul>
<div class="tab">
<h2>summary</h2>
<div>
<div class="summary">
<table>
<tr>
<td>
<div class="summaryGroup">
<table>
<tr>
<td>
<div class="infoBox">
<div class="counter">1</div>
<p>tests</p>
</div>
</td>
<td>
<div class="infoBox">
<div class="counter">0</div>
<p>failures</p>
</div>
</td>
<td>
<div class="infoBox">
<div class="counter">0</div>
<p>skipped</p>
</div>
</td>
<td>
<div class="infoBox duration">
<div class="counter">2.344s</div>
<p>duration</p>
</div>
</td>
</tr>
</table>
</div>
</td>
<td>
<div class="infoBox success successRate">
<div class="percent">100%</div>
<p>successful</p>
</div>
</td>
</tr>
</table>
</div>
<table class="test-results">
<thead>
<tr>
<th>Child</th>
<th hidden="">Name</th>
<th>Tests</th>
<th>Failures</th>
<th>Skipped</th>
<th>Duration</th>
<th>Success rate</th>
</tr>
</thead>
<tr>
<td class="success">
<a href="contextLoads().html">contextLoads()</a>
</td>
<td class="path" hidden="">contextLoads()</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>0.235s</td>
<td class="success">100%</td>
</tr>
</table>
</div>
</div>
<div class="tab">
<h2>standard output</h2>
<span class="code">
<pre id="root-0-test-stdout-com.kamcoback.cd.review.TrainingDataReviewApplicationTests">19:11:37.735 [Test worker] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [com.kamcoback.cd.review.TrainingDataReviewApplicationTests]: TrainingDataReviewApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
19:11:37.777 [Test worker] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration com.kamcoback.cd.review.TrainingDataReviewApplication for test class com.kamcoback.cd.review.TrainingDataReviewApplicationTests
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v4.0.3)
2026-02-24T19:11:37.923+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] k.c.r.TrainingDataReviewApplicationTests : Starting TrainingDataReviewApplicationTests using Java 21.0.9 with PID 37834 (started by bokmin in /Users/bokmin/workspace/scheduler/training_data_review/TrainingDataReview)
2026-02-24T19:11:37.923+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] k.c.r.TrainingDataReviewApplicationTests : The following 1 profile is active: &quot;local&quot;
2026-02-24T19:11:37.935+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] o.s.w.c.s.GenericWebApplicationContext : Refreshing org.springframework.web.context.support.GenericWebApplicationContext@70730db
2026-02-24T19:11:38.119+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2026-02-24T19:11:38.143+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 17 ms. Found 1 JPA repository interface.
2026-02-24T19:11:38.284+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.jpa : HHH008540: Processing PersistenceUnitInfo [name: default]
2026-02-24T19:11:38.300+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.core : HHH000001: Hibernate ORM core version 7.2.4.Final
2026-02-24T19:11:38.435+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
2026-02-24T19:11:38.447+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-02-24T19:11:38.517+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@75c0cd39
2026-02-24T19:11:38.518+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-02-24T19:11:38.579+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.connections.pooling : HHH10001005: Database info:
Database JDBC URL [jdbc:postgresql://192.168.2.127:15432/kamco_cds]
Database driver: PostgreSQL JDBC Driver
Database dialect: PostgreSQLDialect
Database version: 15.4
Default catalog/schema: kamco_cds/public
Autocommit mode: undefined/unknown
Isolation level: READ_COMMITTED [default READ_COMMITTED]
JDBC fetch size: none
Pool: DataSourceConnectionProvider
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-24T19:11:38.998+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.core : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2026-02-24T19:11:39.000+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.jdbc.batch : HHH100501: Automatic JDBC statement batching enabled (maximum batch size 50)
2026-02-24T19:11:39.147+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2026-02-24T19:11:39.206+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] o.s.d.j.r.query.QueryEnhancerFactories : Hibernate is in classpath; If applicable, HQL parser will be used.
2026-02-24T19:11:39.695+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] s.w.s.m.m.a.RequestMappingHandlerMapping : 3 mappings in 'requestMappingHandlerMapping'
2026-02-24T19:11:39.705+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] o.s.w.s.handler.SimpleUrlHandlerMapping : Patterns [/webjars/**, /**] in 'resourceHandlerMapping'
2026-02-24T19:11:39.717+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 2 RequestBodyAdvice, 1 ResponseBodyAdvice
2026-02-24T19:11:39.733+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice
2026-02-24T19:11:39.785+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] k.c.r.TrainingDataReviewApplicationTests : Started TrainingDataReviewApplicationTests in 1.964 seconds (process running for 2.4)
</pre>
<button class="clipboard-copy-btn" aria-label="Copy to clipboard" data-copy-element-id="root-0-test-stdout-com.kamcoback.cd.review.TrainingDataReviewApplicationTests">Copy</button>
</span>
</div>
</div>
</div>
</div>
<div id="footer">
<p>
<div>
<label class="hidden" id="label-for-line-wrapping-toggle" for="line-wrapping-toggle">Wrap lines
<input id="line-wrapping-toggle" type="checkbox" autocomplete="off"/>
</label>
</div>Generated by
<a href="https://www.gradle.org">Gradle 9.3.1</a> at 2026. 2. 24. 오후 7:11:40</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,175 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
font-size: 12pt;
}
body, a, a:visited {
color: #303030;
}
#content {
padding: 30px 50px;
}
#content h1 {
font-size: 160%;
margin-bottom: 10px;
}
#footer {
margin-top: 100px;
font-size: 80%;
white-space: nowrap;
}
#footer, #footer a {
color: #a0a0a0;
}
#line-wrapping-toggle {
vertical-align: middle;
}
#label-for-line-wrapping-toggle {
vertical-align: middle;
}
ul {
margin-left: 0;
}
h1, h2, h3 {
white-space: nowrap;
}
h2 {
font-size: 120%;
}
.tab-container .tab-container {
margin-left: 8px;
}
ul.tabLinks {
padding: 0;
margin-bottom: 0;
overflow: auto;
min-width: 800px;
width: auto;
border-bottom: solid 1px #aaa;
}
ul.tabLinks li {
float: left;
height: 100%;
list-style: none;
padding: 5px 10px;
border-radius: 7px 7px 0 0;
border: solid 1px transparent;
border-bottom: none;
margin-right: 6px;
background-color: #f0f0f0;
cursor: pointer;
}
ul.tabLinks li.deselected > a {
color: #6d6d6d;
}
ul.tabLinks li:hover {
background-color: #fafafa;
}
ul.tabLinks li.selected {
background-color: #c5f0f5;
border-color: #aaa;
}
ul.tabLinks a {
font-size: 120%;
display: block;
outline: none;
text-decoration: none;
margin: 0;
padding: 0;
}
ul.tabLinks li h2 {
margin: 0;
padding: 0;
}
div.tab {
}
div.selected {
display: block;
}
div.deselected {
display: none;
}
div.tab table {
min-width: 350px;
width: auto;
border-collapse: collapse;
}
div.tab th, div.tab table {
border-bottom: solid 1px #d0d0d0;
}
div.tab th {
text-align: left;
white-space: nowrap;
padding-left: 6em;
}
div.tab th:first-child {
padding-left: 0;
}
div.tab td {
white-space: nowrap;
padding-left: 6em;
padding-top: 5px;
padding-bottom: 5px;
}
div.tab td:first-child {
padding-left: 0;
}
div.tab td.numeric, div.tab th.numeric {
text-align: right;
}
span.code {
display: inline-block;
margin-top: 0;
margin-bottom: 1em;
}
span.code pre {
font-size: 11pt;
padding: 10px;
margin: 0;
background-color: #f7f7f7;
border: solid 1px #d0d0d0;
min-width: 700px;
width: auto;
}
span.wrapped pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: break-all;
}
label.hidden {
display: none;
}

View File

@@ -0,0 +1,154 @@
.summary {
margin-top: 30px;
margin-bottom: 40px;
}
.summary table {
border-collapse: collapse;
}
.summary td {
vertical-align: top;
}
div.tab table.test-results td {
padding-left: 1em;
}
div.tab table.test-results th {
padding-top: 0.5em;
padding-left: 1em;
}
.breadcrumbs, .breadcrumbs a {
color: #606060;
}
.infoBox {
width: 110px;
padding-top: 15px;
padding-bottom: 15px;
text-align: center;
}
.infoBox p {
margin: 0;
}
.counter, .percent {
font-size: 120%;
font-weight: bold;
margin-bottom: 8px;
}
.duration {
width: 125px;
}
.successRate, .summaryGroup {
border: solid 2px #d0d0d0;
-moz-border-radius: 10px;
border-radius: 10px;
}
.successRate {
width: 140px;
margin-left: 35px;
}
.successRate .percent {
font-size: 180%;
}
.success, .success a {
color: #008000;
}
div.success, .successRate.success {
background-color: #bbd9bb;
border-color: #008000;
}
.failures, .failures a {
color: #b60808;
}
.skipped, .skipped a {
color: #c09853;
}
div.failures, .successRate.failures {
background-color: #ecdada;
border-color: #b60808;
}
ul.linkList {
padding-left: 0;
}
ul.linkList li {
list-style: none;
margin-bottom: 5px;
}
div.metadata td:first-child {
padding-left: 5px;
}
div.metadata td {
padding-left: 5px;
}
.metadata tr.odd {
background-color: #f7f7f7;
border: solid 1px #d0d0d0;
}
.metadata tr.even {
border: solid 1px #d0d0d0;
}
.metadata th, .metadata td {
padding: 5px;
text-align: left;
}
.metadata a {
color: blue;
}
.metadata .unrenderable {
color: darkred;
}
.code {
position: relative;
}
.clipboard-copy-btn {
position: absolute;
top: 8px;
right: 8px;
padding: 4px 8px;
font-size: 0.9em;
cursor: pointer;
}
.successGroup::before {
content: "\23FA";
margin-right: 8px;
color: #008000;
display: inline-block;
}
.failureGroup::before {
content: "\2297";
margin-right: 8px;
color: #b60808;
display: inline-block;
}
.skippedGroup::before {
content: "\2296";
margin-right: 8px;
color: #c09853;
display: inline-block;
}

View File

@@ -0,0 +1,116 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
<title>Test results - All Results</title>
<link href="css/base-style.css" rel="stylesheet" type="text/css"/>
<link href="css/style.css" rel="stylesheet" type="text/css"/>
<script src="js/report.js" type="text/javascript"></script>
</head>
<body>
<div id="content">
<h1>All Results</h1>
<div class="tab-container">
<ul class="tabLinks">
<li>
<a class="successGroup" href="#">Gradle Test Run :test</a>
</li>
</ul>
<div class="tab">
<h2>Gradle Test Run :test</h2>
<h1>Gradle Test Run :test</h1>
<div class="tab-container">
<ul class="tabLinks">
<li>
<a class="" href="#">summary</a>
</li>
</ul>
<div class="tab">
<h2>summary</h2>
<div>
<div class="summary">
<table>
<tr>
<td>
<div class="summaryGroup">
<table>
<tr>
<td>
<div class="infoBox">
<div class="counter">1</div>
<p>tests</p>
</div>
</td>
<td>
<div class="infoBox">
<div class="counter">0</div>
<p>failures</p>
</div>
</td>
<td>
<div class="infoBox">
<div class="counter">0</div>
<p>skipped</p>
</div>
</td>
<td>
<div class="infoBox duration">
<div class="counter">2.679s</div>
<p>duration</p>
</div>
</td>
</tr>
</table>
</div>
</td>
<td>
<div class="infoBox success successRate">
<div class="percent">100%</div>
<p>successful</p>
</div>
</td>
</tr>
</table>
</div>
<table class="test-results">
<thead>
<tr>
<th>Child</th>
<th>Name</th>
<th>Tests</th>
<th>Failures</th>
<th>Skipped</th>
<th>Duration</th>
<th>Success rate</th>
</tr>
</thead>
<tr>
<td class="success">
<a href="com.kamcoback.cd.review.TrainingDataReviewApplicationTests/index.html">TrainingDataReviewApplicationTests</a>
</td>
<td class="path">com.kamcoback.cd.review.TrainingDataReviewApplicationTests</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>2.344s</td>
<td class="success">100%</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<p>
<div>
<label class="hidden" id="label-for-line-wrapping-toggle" for="line-wrapping-toggle">Wrap lines
<input id="line-wrapping-toggle" type="checkbox" autocomplete="off"/>
</label>
</div>Generated by
<a href="https://www.gradle.org">Gradle 9.3.1</a> at 2026. 2. 24. 오후 7:11:40</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,228 @@
(function (window, document) {
"use strict";
function changeElementClass(element, classValue) {
if (element.getAttribute("className")) {
element.setAttribute("className", classValue);
} else {
element.setAttribute("class", classValue);
}
}
function getClassAttribute(element) {
if (element.getAttribute("className")) {
return element.getAttribute("className");
} else {
return element.getAttribute("class");
}
}
function addClass(element, classValue) {
changeElementClass(element, getClassAttribute(element) + " " + classValue);
}
function removeClass(element, classValue) {
changeElementClass(element, getClassAttribute(element).replace(classValue, ""));
}
function getCheckBox() {
return document.getElementById("line-wrapping-toggle");
}
function getLabelForCheckBox() {
return document.getElementById("label-for-line-wrapping-toggle");
}
function findCodeBlocks() {
const codeBlocks = [];
const tabContainers = getTabContainers();
for (let i = 0; i < tabContainers.length; i++) {
const spans = tabContainers[i].getElementsByTagName("span");
for (let i = 0; i < spans.length; ++i) {
if (spans[i].className.indexOf("code") >= 0) {
codeBlocks.push(spans[i]);
}
}
}
return codeBlocks;
}
function forAllCodeBlocks(operation) {
const codeBlocks = findCodeBlocks();
for (let i = 0; i < codeBlocks.length; ++i) {
operation(codeBlocks[i], "wrapped");
}
}
function toggleLineWrapping() {
const checkBox = getCheckBox();
if (checkBox.checked) {
forAllCodeBlocks(addClass);
} else {
forAllCodeBlocks(removeClass);
}
}
function initClipboardCopyButton() {
document.querySelectorAll(".clipboard-copy-btn").forEach((button) => {
const copyElementId = button.getAttribute("data-copy-element-id");
const elementWithCodeToSelect = document.getElementById(copyElementId);
button.addEventListener("click", () => {
const text = elementWithCodeToSelect.innerText.trim();
navigator.clipboard
.writeText(text)
.then(() => {
button.textContent = "Copied!";
setTimeout(() => {
button.textContent = "Copy";
}, 1500);
})
.catch((err) => {
alert("Failed to copy to the clipboard: '" + err.message + "'. Check JavaScript console for more details.")
console.warn("Failed to copy to the clipboard", err);
});
});
});
}
function initControls() {
if (findCodeBlocks().length > 0) {
const checkBox = getCheckBox();
const label = getLabelForCheckBox();
checkBox.onclick = toggleLineWrapping;
checkBox.checked = false;
removeClass(label, "hidden");
}
initClipboardCopyButton()
}
class TabManager {
baseId;
tabs;
titles;
headers;
constructor(baseId, tabs, titles, headers) {
this.baseId = baseId;
this.tabs = tabs;
this.titles = titles;
this.headers = headers;
this.init();
}
init() {
for (let i = 0; i < this.headers.length; i++) {
const header = this.headers[i];
header.onclick = () => {
this.select(i);
return false;
};
}
}
select(i) {
this.deselectAll();
changeElementClass(this.tabs[i], "tab selected");
changeElementClass(this.headers[i], "selected");
}
deselectAll() {
for (let i = 0; i < this.tabs.length; i++) {
changeElementClass(this.tabs[i], "tab deselected");
changeElementClass(this.headers[i], "deselected");
}
}
}
function getTabContainers() {
const tabContainers = Array.from(document.getElementsByClassName("tab-container"));
// Used by existing TabbedPageRenderer users, which have not adjusted to use TabsRenderer yet.
const legacyContainer = document.getElementById("tabs");
if (legacyContainer) {
tabContainers.push(legacyContainer);
}
return tabContainers;
}
function initTabs() {
let tabGroups = 0;
function createTab(num, container) {
const tabElems = findTabs(container);
const tabManager = new TabManager("tabs" + num, tabElems, findTitles(tabElems), findHeaders(container));
tabManager.select(0);
}
const tabContainers = getTabContainers();
for (let i = 0; i < tabContainers.length; i++) {
createTab(tabGroups, tabContainers[i]);
tabGroups++;
}
return true;
}
function findTabs(container) {
return findChildElements(container, "DIV", "tab");
}
function findHeaders(container) {
const owner = findChildElements(container, "UL", "tabLinks");
return findChildElements(owner[0], "LI", null);
}
function findTitles(tabs) {
const titles = [];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
const header = findChildElements(tab, "H2", null)[0];
header.parentNode.removeChild(header);
if (header.innerText) {
titles.push(header.innerText);
} else {
titles.push(header.textContent);
}
}
return titles;
}
function findChildElements(container, name, targetClass) {
const elements = [];
const children = container.childNodes;
for (let i = 0; i < children.length; i++) {
const child = children.item(i);
if (child.nodeType === 1 && child.nodeName === name) {
if (targetClass && child.className.indexOf(targetClass) < 0) {
continue;
}
elements.push(child);
}
}
return elements;
}
// Entry point.
window.onload = function() {
initTabs();
initControls();
};
} (window, window.document));

View File

@@ -0,0 +1 @@
com.kamcoback.cd.review.TrainingDataReviewApplication

View File

@@ -0,0 +1,66 @@
spring:
config:
activate:
on-profile: dev
jpa:
show-sql: false
hibernate:
ddl-auto: validate
properties:
hibernate:
default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지
order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지
order_inserts: true
use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용)
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
jdbc:
batch_size: 1000 # ✅ 추가 (JDBC batch)
open-in-view: false
mvc:
async:
request-timeout: 300s # 5분 (예: 30s, 120s, 10m 등도 가능)
datasource:
url: jdbc:postgresql://192.168.2.127:15432/kamco_cds
#url: jdbc:postgresql://localhost:15432/kamco_cds
username: kamco_cds
password: kamco_cds_Q!W@E#R$
hikari:
minimum-idle: 10
maximum-pool-size: 20
connection-timeout: 60000 # 60초 연결 타임아웃
idle-timeout: 300000 # 5분 유휴 타임아웃
max-lifetime: 1800000 # 30분 최대 수명
leak-detection-threshold: 60000 # 연결 누수 감지
transaction:
default-timeout: 300 # 5분 트랜잭션 타임아웃
data:
redis:
host: 192.168.2.109
port: 6379
password: kamco
servlet:
multipart:
enabled: true
max-file-size: 4GB
max-request-size: 4GB
file-size-threshold: 10MB
server:
tomcat:
max-swallow-size: 4GB
max-http-form-post-size: 4GB
logging:
level:
root: INFO
org.springframework.web: DEBUG
training-data:
geojson-dir: /kamco-nfs/dataset/request/

View File

@@ -0,0 +1,51 @@
spring:
config:
activate:
on-profile: local
jpa:
show-sql: false
hibernate:
ddl-auto: update # 로컬만 완화(시킬려면 update으로 변경)
properties:
hibernate:
default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지
order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지
use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용)
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
open-in-view: false
datasource:
url: jdbc:postgresql://192.168.2.127:15432/kamco_cds
#url: jdbc:postgresql://localhost:5432/kamco_cds # 로컬호스트
username: kamco_cds
password: kamco_cds_Q!W@E#R$
hikari:
minimum-idle: 1
maximum-pool-size: 5
data:
redis:
host: 192.168.2.109
port: 6379
password: kamco
servlet:
multipart:
enabled: true
max-file-size: 4GB
max-request-size: 4GB
file-size-threshold: 10MB
server:
tomcat:
max-swallow-size: 4GB
max-http-form-post-size: 4GB
logging:
level:
org.hibernate.SQL: debug
org.hibernate.orm.jdbc.bind: trace
training-data:
geojson-dir: /Users/bokmin/kamco-nfs/model_output/labeling/

View File

@@ -0,0 +1,64 @@
spring:
config:
activate:
on-profile: prod
jpa:
show-sql: true
hibernate:
ddl-auto: validate
properties:
hibernate:
default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지
order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지
order_inserts: true
use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용)
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
jdbc:
batch_size: 1000 # ✅ 추가 (JDBC batch)
open-in-view: false
mvc:
async:
request-timeout: 300s # 5분 (예: 30s, 120s, 10m 등도 가능)
datasource:
url: jdbc:postgresql://127.0.0.1:15432/kamco_cds
#url: jdbc:postgresql://localhost:15432/kamco_cds
username: kamco_cds
password: kamco_cds_Q!W@E#R$
hikari:
minimum-idle: 10
maximum-pool-size: 20
connection-timeout: 60000 # 60초 연결 타임아웃
idle-timeout: 300000 # 5분 유휴 타임아웃
max-lifetime: 1800000 # 30분 최대 수명
leak-detection-threshold: 60000 # 연결 누수 감지
transaction:
default-timeout: 300 # 5분 트랜잭션 타임아웃
data:
redis:
host: 127.0.0.1
port: 16379
password: kamco
servlet:
multipart:
enabled: true
max-file-size: 4GB
max-request-size: 4GB
file-size-threshold: 10MB
server:
tomcat:
max-swallow-size: 4GB
max-http-form-post-size: 4GB
logging:
level:
root: INFO
org.springframework.web: DEBUG
training-data:
geojson-dir: /kamco-nfs/dataset/request/

View File

@@ -0,0 +1,40 @@
server:
port: 8080
spring:
application:
name: kamco-change-detection-api
profiles:
active: local # 사용할 프로파일 지정 (ex. dev, prod, test)
datasource:
driver-class-name: org.postgresql.Driver
hikari:
jdbc:
time_zone: UTC
batch_size: 50
# 권장 설정
minimum-idle: 2
maximum-pool-size: 2
connection-timeout: 20000
idle-timeout: 300000
max-lifetime: 1800000
leak-detection-threshold: 60000
data:
redis:
host: localhost
port: 6379
password:
jpa:
hibernate:
ddl-auto: update # 테이블이 없으면 생성, 있으면 업데이트
properties:
hibernate:
jdbc:
batch_size: 50
default_batch_fetch_size: 100
logging:
level:
root: INFO
org.springframework.web: DEBUG

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="com.kamcoback.cd.review.TrainingDataReviewApplicationTests" tests="1" skipped="0" failures="0" errors="0" timestamp="2026-02-24T10:11:37.681Z" hostname="gimbogmins-MacBook-Pro.local" time="2.343">
<properties/>
<testcase name="contextLoads()" classname="com.kamcoback.cd.review.TrainingDataReviewApplicationTests" time="0.235"/>
<system-out><![CDATA[19:11:37.735 [Test worker] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [com.kamcoback.cd.review.TrainingDataReviewApplicationTests]: TrainingDataReviewApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
19:11:37.777 [Test worker] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration com.kamcoback.cd.review.TrainingDataReviewApplication for test class com.kamcoback.cd.review.TrainingDataReviewApplicationTests
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v4.0.3)
2026-02-24T19:11:37.923+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] k.c.r.TrainingDataReviewApplicationTests : Starting TrainingDataReviewApplicationTests using Java 21.0.9 with PID 37834 (started by bokmin in /Users/bokmin/workspace/scheduler/training_data_review/TrainingDataReview)
2026-02-24T19:11:37.923+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] k.c.r.TrainingDataReviewApplicationTests : The following 1 profile is active: "local"
2026-02-24T19:11:37.935+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] o.s.w.c.s.GenericWebApplicationContext : Refreshing org.springframework.web.context.support.GenericWebApplicationContext@70730db
2026-02-24T19:11:38.119+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2026-02-24T19:11:38.143+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 17 ms. Found 1 JPA repository interface.
2026-02-24T19:11:38.284+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.jpa : HHH008540: Processing PersistenceUnitInfo [name: default]
2026-02-24T19:11:38.300+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.core : HHH000001: Hibernate ORM core version 7.2.4.Final
2026-02-24T19:11:38.435+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer
2026-02-24T19:11:38.447+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-02-24T19:11:38.517+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@75c0cd39
2026-02-24T19:11:38.518+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-02-24T19:11:38.579+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.connections.pooling : HHH10001005: Database info:
Database JDBC URL [jdbc:postgresql://192.168.2.127:15432/kamco_cds]
Database driver: PostgreSQL JDBC Driver
Database dialect: PostgreSQLDialect
Database version: 15.4
Default catalog/schema: kamco_cds/public
Autocommit mode: undefined/unknown
Isolation level: READ_COMMITTED [default READ_COMMITTED]
JDBC fetch size: none
Pool: DataSourceConnectionProvider
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
2026-02-24T19:11:38.998+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.core : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2026-02-24T19:11:39.000+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] org.hibernate.orm.jdbc.batch : HHH100501: Automatic JDBC statement batching enabled (maximum batch size 50)
2026-02-24T19:11:39.147+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2026-02-24T19:11:39.206+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] o.s.d.j.r.query.QueryEnhancerFactories : Hibernate is in classpath; If applicable, HQL parser will be used.
2026-02-24T19:11:39.695+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] s.w.s.m.m.a.RequestMappingHandlerMapping : 3 mappings in 'requestMappingHandlerMapping'
2026-02-24T19:11:39.705+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] o.s.w.s.handler.SimpleUrlHandlerMapping : Patterns [/webjars/**, /**] in 'resourceHandlerMapping'
2026-02-24T19:11:39.717+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 2 RequestBodyAdvice, 1 ResponseBodyAdvice
2026-02-24T19:11:39.733+09:00 DEBUG 37834 --- [kamco-change-detection-api] [ Test worker] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice
2026-02-24T19:11:39.785+09:00 INFO 37834 --- [kamco-change-detection-api] [ Test worker] k.c.r.TrainingDataReviewApplicationTests : Started TrainingDataReviewApplicationTests in 1.964 seconds (process running for 2.4)
]]></system-out>
<system-err><![CDATA[Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
WARNING: A Java agent has been loaded dynamically (/Users/bokmin/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.17.8/f09415827a71be7ed621c7bd02550678f28bc81c/byte-buddy-agent-1.17.8.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
]]></system-err>
</testsuite>

View File

@@ -0,0 +1,12 @@
Manifest-Version: 1.0
Main-Class: org.springframework.boot.loader.launch.JarLauncher
Start-Class: com.kamcoback.cd.review.TrainingDataReviewApplication
Spring-Boot-Version: 4.0.3
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Build-Jdk-Spec: 21
Implementation-Title: TrainingDataReview
Implementation-Version: 0.0.1-SNAPSHOT

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0

Binary file not shown.

View File

@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

248
label/review-to-geojson/gradlew vendored Executable file
View File

@@ -0,0 +1,248 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

93
label/review-to-geojson/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,93 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1 @@
rootProject.name = 'TrainingDataReview'

View File

@@ -0,0 +1,13 @@
package com.kamcoback.cd.review;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TrainingDataReviewApplication {
public static void main(String[] args) {
SpringApplication.run(TrainingDataReviewApplication.class, args);
}
}

View File

@@ -0,0 +1,15 @@
package com.kamcoback.cd.review.config;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuerydslConfig {
@Bean
public JPAQueryFactory jpaQueryFactory(EntityManager em) {
return new JPAQueryFactory(em);
}
}

View File

@@ -0,0 +1,22 @@
package com.kamcoback.cd.review.controller;
import com.kamcoback.cd.review.dto.ApiResponseDto;
import com.kamcoback.cd.review.service.TrainingDataReviewJobService;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class TrainingDataReviewController {
private final TrainingDataReviewJobService trainingDataReviewJobService;
@GetMapping("/review-to-geojson")
public ApiResponseDto<Long> runExportGeojsonLabelingGeom(@RequestParam(required = false) LocalDate baseDate) {
trainingDataReviewJobService.exportGeojsonLabelingGeom(baseDate);
return ApiResponseDto.ok(0L);
}
}

View File

@@ -0,0 +1,223 @@
package com.kamcoback.cd.review.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.kamcoback.cd.review.enums.EnumType;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.http.HttpStatus;
@Getter
@ToString
public class ApiResponseDto<T> {
private T data;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Error error;
@JsonInclude(JsonInclude.Include.NON_NULL)
private T errorData;
@JsonIgnore private HttpStatus httpStatus;
@JsonIgnore private Long errorLogUid;
public ApiResponseDto(T data) {
this.data = data;
}
private ApiResponseDto(T data, HttpStatus httpStatus) {
this.data = data;
this.httpStatus = httpStatus;
}
public ApiResponseDto(ApiResponseCode code) {
this.error = new Error(code.getId(), code.getMessage());
}
public ApiResponseDto(ApiResponseCode code, String message) {
this.error = new Error(code.getId(), message);
}
public ApiResponseDto(ApiResponseCode code, String message, HttpStatus httpStatus) {
this.error = new Error(code.getId(), message);
this.httpStatus = httpStatus;
}
public ApiResponseDto(
ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) {
this.error = new Error(code.getId(), message);
this.httpStatus = httpStatus;
this.errorLogUid = errorLogUid;
}
public ApiResponseDto(ApiResponseCode code, String message, T errorData) {
this.error = new Error(code.getId(), message);
this.errorData = errorData;
}
// HTTP 상태 코드가 내장된 ApiResponseDto 반환 메서드들
public static <T> ApiResponseDto<T> createOK(T data) {
return new ApiResponseDto<>(data, HttpStatus.CREATED);
}
public static <T> ApiResponseDto<T> ok(T data) {
return new ApiResponseDto<>(data, HttpStatus.OK);
}
public static <T> ApiResponseDto<ResponseObj> okObject(ResponseObj data) {
if (data.getCode().equals(ApiResponseCode.OK)) {
return new ApiResponseDto<>(data, HttpStatus.NO_CONTENT);
} else {
return new ApiResponseDto<>(data.getCode(), data.getMessage(), HttpStatus.CONFLICT);
}
}
public static <T> ApiResponseDto<T> deleteOk(T data) {
return new ApiResponseDto<>(data, HttpStatus.NO_CONTENT);
}
public static ApiResponseDto<String> createException(ApiResponseCode code) {
return new ApiResponseDto<>(code);
}
public static ApiResponseDto<String> createException(ApiResponseCode code, String message) {
return new ApiResponseDto<>(code, message);
}
public static ApiResponseDto<String> createException(
ApiResponseCode code, String message, HttpStatus httpStatus) {
return new ApiResponseDto<>(code, message, httpStatus);
}
public static ApiResponseDto<String> createException(
ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) {
return new ApiResponseDto<>(code, message, httpStatus, errorLogUid);
}
public static <T> ApiResponseDto<T> createException(
ApiResponseCode code, String message, T data) {
return new ApiResponseDto<>(code, message, data);
}
@Getter
public static class Error {
private final String code;
private final String message;
public Error(String code, String message) {
this.code = code;
this.message = message;
}
}
/** Error가 아닌 Business상 성공이거나 실패인 경우, 메세지 함께 전달하기 위한 object */
@Getter
public static class ResponseObj {
private final ApiResponseCode code;
private final String message;
public ResponseObj(ApiResponseCode code, String message) {
this.code = code;
this.message = message;
}
}
@Getter
@RequiredArgsConstructor
public enum ApiResponseCode implements EnumType {
// @formatter:off
OK("요청이 성공하였습니다."),
BAD_REQUEST("요청 파라미터가 잘못되었습니다."),
BAD_GATEWAY("네트워크 상태가 불안정합니다."),
ALREADY_EXIST_MALL("이미 등록된 쇼핑센터입니다."),
NOT_FOUND_MAP("지도를 찾을 수 없습니다."),
UNAUTHORIZED("권한이 없습니다."),
CONFLICT("이미 등록된 컨텐츠입니다."),
NOT_FOUND("Resource를 찾을 수 없습니다."),
NOT_FOUND_DATA("데이터를 찾을 수 없습니다."),
NOT_FOUND_WEATHER_DATA("날씨 데이터를 찾을 수 없습니다."),
FAIL_SEND_MESSAGE("메시지를 전송하지 못했습니다."),
TOO_MANY_CONNECTED_MACHINES("연결된 기기가 너무 많습니다."),
UNAUTHENTICATED("인증에 실패하였습니다."),
INVALID_TOKEN("잘못된 토큰입니다."),
EXPIRED_TOKEN("만료된 토큰입니다."),
INTERNAL_SERVER_ERROR("서버에 문제가 발생 하였습니다."),
FORBIDDEN("권한을 확인해주세요."),
INVALID_PASSWORD("잘못된 비밀번호 입니다."),
NOT_FOUND_CAR_IN("입차정보가 없습니다."),
WRONG_STATUS("잘못된 상태입니다."),
FAIL_VERIFICATION("인증에 실패하였습니다."),
INVALID_EMAIL("잘못된 형식의 이메일입니다."),
REQUIRED_EMAIL("이메일은 필수 항목입니다."),
WRONG_PASSWORD("잘못된 패스워드입니다."),
DUPLICATE_EMAIL("이미 가입된 이메일입니다."),
DUPLICATE_DATA("이미 등록되어 있습니다."),
DATA_INTEGRITY_ERROR("데이터 무결성이 위반되어 요청을 처리할수 없습니다."),
FOREIGN_KEY_ERROR("참조 중인 데이터가 있어 삭제할 수 없습니다."),
DUPLICATE_EMPLOYEEID("이미 가입된 사번입니다."),
NOT_FOUND_USER_FOR_EMAIL("이메일로 유저를 찾을 수 없습니다."),
NOT_FOUND_USER("사용자를 찾을 수 없습니다."),
UNPROCESSABLE_ENTITY("이 데이터는 삭제할 수 없습니다."),
LOGIN_ID_NOT_FOUND("아이디를 잘못 입력하셨습니다."),
LOGIN_PASSWORD_MISMATCH("비밀번호를 잘못 입력하셨습니다."),
LOGIN_PASSWORD_EXCEEDED("비밀번호 오류 횟수를 초과하여 이용하실 수 없습니다.\n로그인 오류에 대해 관리자에게 문의하시기 바랍니다."),
INACTIVE_ID("사용할 수 없는 계정입니다."),
INVALID_EMAIL_TOKEN(
"You can only reset your password within 24 hours from when the email was sent.\n"
+ "To reset your password again, please submit a new request through \"Forgot"
+ " Password.\""),
PAYLOAD_TOO_LARGE("업로드 용량 제한을 초과했습니다."),
NOT_FOUND_TARGET_YEAR("기준년도 도엽을 찾을 수 없습니다."),
NOT_FOUND_COMPARE_YEAR("비교년도 도엽을 찾을 수 없습니다."),
FAIL_SAVE_MAP_SHEET("도엽 저장 중 오류가 발생했습니다."),
FAIL_CREATE_MAP_SHEET_FILE("도엽 설정파일 생성 중 오류가 발생했습니다."),
;
// @formatter:on
private final String message;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return message;
}
public static ApiResponseCode getCode(String name) {
return ApiResponseCode.valueOf(name.toUpperCase());
}
public static String getMessage(String name) {
return ApiResponseCode.valueOf(name.toUpperCase()).getText();
}
public static ApiResponseCode from(String codeName, HttpStatus status) {
if (codeName != null && !codeName.isBlank()) {
try {
return ApiResponseCode.valueOf(codeName.toUpperCase());
} catch (IllegalArgumentException ignore) {
// fallback
}
}
if (status != null) {
try {
return ApiResponseCode.valueOf(status.name());
} catch (IllegalArgumentException ignore) {
// fallback
}
}
return INTERNAL_SERVER_ERROR;
}
}
}

View File

@@ -0,0 +1,670 @@
package com.kamcoback.cd.review.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.kamcoback.cd.review.enums.EnumType;
import com.kamcoback.cd.review.interfaces.EnumValid;
import com.kamcoback.cd.review.interfaces.JsonFormatDttm;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.Duration;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
public class InferenceResultDto {
/** 분석대상 도엽 enum */
@Getter
@AllArgsConstructor
public enum MapSheetScope implements EnumType {
ALL("전체"),
PART("부분"),
;
private final String desc;
public static MapSheetScope fromCode(String code) {
return Arrays.stream(values()).filter(v -> v.name().equals(code)).findFirst().orElse(null);
}
public static String getDescByCode(String code) {
return fromCode(code).getDesc();
}
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
/** 탐지 데이터 옵션 dto */
@Getter
@AllArgsConstructor
public enum DetectOption implements EnumType {
EXCL("추론제외"),
PREV("이전 년도 도엽 사용"),
;
private final String desc;
public static DetectOption fromCode(String code) {
return Arrays.stream(values()).filter(v -> v.name().equals(code)).findFirst().orElse(null);
}
public static String getDescByCode(String code) {
return fromCode(code).getDesc();
}
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@AllArgsConstructor
public enum Status implements EnumType {
READY("대기"),
IN_PROGRESS("진행중"),
END("완료"),
FORCED_END("강제종료");
private final String desc;
public static Status fromCode(String code) {
return Arrays.stream(values()).filter(v -> v.name().equals(code)).findFirst().orElse(null);
}
public static String getDescByCode(String code) {
return fromCode(code).getDesc();
}
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@AllArgsConstructor
public enum ServerStatus implements EnumType {
SAFETY("원활"),
CAUTION("주의"),
FAILUR("장애"),
;
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
/** 목록조회 dto */
// 추론관리 목록
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class ResultList {
private UUID uuid;
// 제목
private String title;
// 회차
private Integer stage;
// 상태
private String status;
// 분석 도엽
private String mapSheetCnt;
// 탐지건수
private Long detectingCnt;
// 시작일시
@JsonFormatDttm
private ZonedDateTime startTime;
// 종료일시
@JsonFormatDttm
private ZonedDateTime endTime;
// 반영여부
private Boolean applyYn;
// 반영일시
@JsonFormatDttm
private ZonedDateTime applyDttm;
// 비교년도
private Integer compareYyyy;
// 기준년도
private Integer targetYyyy;
// uid
private String uid;
// uid 앞 8자리
@JsonProperty("subUid")
public String subUid() {
return this.uid.substring(0, 8).toUpperCase();
}
// 상태명
@JsonProperty("statusName")
public String statusName() {
return Status.getDescByCode(this.status);
}
// 소요시간
@JsonProperty("elapsedTim")
public String getElapsedTime() {
if (this.startTime == null || this.endTime == null) {
return null;
}
ZonedDateTime start = this.startTime;
ZonedDateTime end = this.endTime;
Duration d = Duration.between(start, end);
if (d.isNegative()) {
d = d.negated();
}
long s = d.getSeconds();
long h = s / 3600;
long m = (s % 3600) / 60;
long sec = s % 60;
return String.format("%d시간 %d분 %d초", h, m, sec);
}
}
/** 목록조회 검색 조건 dto */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchListReq {
// 검색 조건
private String applyYn;
private LocalDate strtDttm;
private LocalDate endDttm;
private String title;
// 페이징 파라미터
private int page = 0;
private int size = 20;
public Pageable toPageable() {
return PageRequest.of(page, size);
}
}
/** 변화탐지 실행 정보 저장 요청 정보 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class RegReq {
// 제목
@NotBlank
private String title;
// G1
@NotNull
private UUID model1Uuid;
// G2
@NotNull
private UUID model2Uuid;
// G3
@NotNull
private UUID model3Uuid;
// 비교년도
@NotNull
private Integer compareYyyy;
// 탐지년도
@NotNull
private Integer targetYyyy;
// 분석대상 도엽 - 전체(ALL), 부분(PART)
@NotBlank
@EnumValid(enumClass = MapSheetScope.class, message = "분석대상 도엽 옵션은 '전체', '부분' 만 사용 가능합니다.")
private String mapSheetScope;
// 탐지 데이터 옵션 - 추론제외(EXCL), 이전 년도 도엽 사용(PREV)
@NotBlank
@EnumValid(
enumClass = DetectOption.class,
message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.")
private String detectOption;
// 5k 도협 번호 목록
@NotNull
private List<String> mapSheetNum;
}
@Getter
@Setter
public static class MapSheetNumDto {
private String mapSheetNum;
private String mapSheetName;
}
@Getter
@Setter
@NoArgsConstructor
// 추론(변화탐지) 진행상태
public static class InferenceStatusDetailDto {
// 모델1 사용시간 시작일시
@JsonFormatDttm
ZonedDateTime m1ModelStartDttm;
// 모델2 사용시간 시작일시
@JsonFormatDttm
ZonedDateTime m2ModelStartDttm;
// 모델3 사용시간 시작일시
@JsonFormatDttm
ZonedDateTime m3ModelStartDttm;
// 모델1 사용시간 종료일시
@JsonFormatDttm
ZonedDateTime m1ModelEndDttm;
// 모델2 사용시간 종료일시
@JsonFormatDttm
ZonedDateTime m2ModelEndDttm;
// 모델3 사용시간 종료일시
@JsonFormatDttm
ZonedDateTime m3ModelEndDttm;
// 탐지대상 도엽수
private Long detectingCnt;
// 모델1 분석 대기
private Integer m1PendingJobs;
// 모델2 분석 대기
private Integer m2PendingJobs;
// 모델3 분석 대기
private Integer m3PendingJobs;
// 모델1 분석 진행중
private Integer m1RunningJobs;
// 모델2 분석 진행중
private Integer m2RunningJobs;
// 모델3 분석 진행중
private Integer m3RunningJobs;
// 모델1 분석 완료
private Integer m1CompletedJobs;
// 모델2 분석 완료
private Integer m2CompletedJobs;
// 모델3 분석 완료
private Integer m3CompletedJobs;
// 모델1 분석 실패
private Integer m1FailedJobs;
// 모델2 분석 실패
private Integer m2FailedJobs;
// 모델3 분석 실패
private Integer m3FailedJobs;
// 변화탐지 제목
private String title;
// 비교년도
private Integer compareYyyy;
// 기준년도
private Integer targetYyyy;
// 회차
private Integer stage;
// 변화탐지 시작
@JsonFormatDttm
private ZonedDateTime inferStartDttm;
// 변화탐지 종료
@JsonFormatDttm
private ZonedDateTime inferEndDttm;
// 변화탐지 옵션
private String detectOption;
// 분석도엽
private String mapSheetScope;
// 모델1 버전
private String modelVer1;
// 모델2 버전
private String modelVer2;
// 모델3 버전
private String modelVer3;
// 탑지 도엽 수
@JsonIgnore
private Long totalJobs;
public InferenceStatusDetailDto(
Long detectingCnt,
Integer m1PendingJobs,
Integer m2PendingJobs,
Integer m3PendingJobs,
Integer m1RunningJobs,
Integer m2RunningJobs,
Integer m3RunningJobs,
Integer m1CompletedJobs,
Integer m2CompletedJobs,
Integer m3CompletedJobs,
Integer m1FailedJobs,
Integer m2FailedJobs,
Integer m3FailedJobs,
ZonedDateTime m1ModelStartDttm,
ZonedDateTime m2ModelStartDttm,
ZonedDateTime m3ModelStartDttm,
ZonedDateTime m1ModelEndDttm,
ZonedDateTime m2ModelEndDttm,
ZonedDateTime m3ModelEndDttm,
String title,
Integer compareYyyy,
Integer targetYyyy,
Integer stage,
ZonedDateTime inferStartDttm,
ZonedDateTime inferEndDttm,
String detectOption,
String mapSheetScope,
String modelVer1,
String modelVer2,
String modelVer3,
Long totalJobs) {
this.detectingCnt = detectingCnt;
this.m1PendingJobs = m1PendingJobs;
this.m2PendingJobs = m2PendingJobs;
this.m3PendingJobs = m3PendingJobs;
this.m1RunningJobs = m1RunningJobs;
this.m2RunningJobs = m2RunningJobs;
this.m3RunningJobs = m3RunningJobs;
this.m1CompletedJobs = m1CompletedJobs;
this.m2CompletedJobs = m2CompletedJobs;
this.m3CompletedJobs = m3CompletedJobs;
this.m1FailedJobs = m1FailedJobs;
this.m2FailedJobs = m2FailedJobs;
this.m3FailedJobs = m3FailedJobs;
this.m1ModelStartDttm = m1ModelStartDttm;
this.m2ModelStartDttm = m2ModelStartDttm;
this.m3ModelStartDttm = m3ModelStartDttm;
this.m1ModelEndDttm = m1ModelEndDttm;
this.m2ModelEndDttm = m2ModelEndDttm;
this.m3ModelEndDttm = m3ModelEndDttm;
this.title = title;
this.compareYyyy = compareYyyy;
this.targetYyyy = targetYyyy;
this.stage = stage;
this.inferStartDttm = inferStartDttm;
this.inferEndDttm = inferEndDttm;
this.detectOption = detectOption;
this.mapSheetScope = mapSheetScope;
this.modelVer1 = modelVer1;
this.modelVer2 = modelVer2;
this.modelVer3 = modelVer3;
this.totalJobs = totalJobs;
}
// 진행률
@JsonProperty("progress")
private int getProgress() {
long tiles = this.totalJobs == null ? 0L : this.totalJobs; // 도엽수
int models = 3; // 모델 개수
int completed =
this.m1CompletedJobs
+ this.m2CompletedJobs
+ this.m3CompletedJobs
+ this.m1FailedJobs
+ this.m2FailedJobs
+ this.m3FailedJobs; // 완료수
long total = tiles * models; // 전체 작업량
if (completed >= total) {
return 100;
}
return (int) ((completed * 100L) / total);
}
// 변화탐지 옵션명
@JsonProperty("detectOptionName")
private String getDetectOptionName() {
return DetectOption.getDescByCode(this.detectOption);
}
// 분석도엽 명
@JsonProperty("mapSheetScopeName")
private String getMapSheetScopeName() {
return MapSheetScope.getDescByCode(this.mapSheetScope);
}
// G1 사용시간
@JsonProperty("m1ElapsedTim")
public String getM1ElapsedTime() {
return formatElapsedTime(this.m1ModelStartDttm, this.m1ModelEndDttm);
}
// G2 사용시간
@JsonProperty("m2ElapsedTim")
public String getM2ElapsedTime() {
return formatElapsedTime(this.m2ModelStartDttm, this.m2ModelEndDttm);
}
// G3 사용시간
@JsonProperty("m3ElapsedTim")
public String getM3ElapsedTime() {
return formatElapsedTime(this.m3ModelStartDttm, this.m3ModelEndDttm);
}
private String formatElapsedTime(ZonedDateTime start, ZonedDateTime end) {
if (start == null || end == null) {
return null;
}
Duration d = Duration.between(start, end);
if (d.isNegative()) {
d = d.negated();
}
long s = d.getSeconds();
long h = s / 3600;
long m = (s % 3600) / 60;
long sec = s % 60;
return String.format("%d시간 %d분 %d초", h, m, sec);
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class InferenceServerStatusDto {
private String serverName;
@JsonIgnore private float cpu_user;
@JsonIgnore private float cpu_system;
@JsonIgnore private float memused;
private Long kbmemused;
private float gpuUtil;
public float getCpuUseRate() {
return this.cpu_user + this.cpu_system;
}
public String getServerStatus() {
String enumId = "SAFETY";
if (this.cpu_user + this.cpu_system + this.gpuUtil + this.kbmemused == 0) {
enumId = "FAILUR";
}
return enumId;
}
public String getServerStatusName() {
String enumStr = ServerStatus.SAFETY.getText();
if (this.cpu_user + this.cpu_system + this.gpuUtil + this.kbmemused == 0) {
enumStr = ServerStatus.FAILUR.getText();
}
return enumStr;
}
public String getCpuStatus() {
String enumId = "SAFETY";
if (this.cpu_user + this.cpu_system >= 80) {
enumId = "CAUTION";
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
enumId = "FAILUR";
}
return enumId;
}
public String getGpuStatus() {
String enumId = "SAFETY";
if (this.gpuUtil >= 80) {
enumId = "CAUTION";
} else if (this.cpu_user + this.cpu_system == 0) {
enumId = "FAILUR";
}
return enumId;
}
public String getMemStatus() {
String enumId = "SAFETY";
if (this.memused >= 80) {
enumId = "CAUTION";
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
enumId = "FAILUR";
}
return enumId;
}
public String getCpuStatusName() {
if (this.cpu_user + this.cpu_system >= 80) {
return ServerStatus.CAUTION.getText();
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
return ServerStatus.FAILUR.getText();
}
return ServerStatus.SAFETY.getText();
}
public String getGpuStatusName() {
if (this.gpuUtil >= 80) {
return ServerStatus.CAUTION.getText();
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
return ServerStatus.FAILUR.getText();
}
return ServerStatus.SAFETY.getText();
}
public String getMemStatusName() {
if (this.memused >= 80) {
return ServerStatus.CAUTION.getText();
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
return ServerStatus.FAILUR.getText();
}
return ServerStatus.SAFETY.getText();
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class InferenceStatusDetailDto2 {
InferenceServerStatusDto serverStatus;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class SaveInferenceAiDto {
private UUID uuid;
private Long batchId;
private String status;
private String type;
private ZonedDateTime inferStartDttm;
private ZonedDateTime inferEndDttm;
private Long detectEndCnt;
private String modelComparePath;
private String modelTargetPath;
private String modelModelPath;
private ZonedDateTime modelStartDttm;
private ZonedDateTime modelEndDttm;
private Long updateUid;
private String runningModelType;
private Integer pendingJobs;
private Integer runningJobs;
private Integer completedJobs;
private Integer failedJobs;
}
@Getter
@Setter
public static class InferenceLearnDto {
private String uid;
private Long m1ModelBatchId;
private Long m2ModelBatchId;
private Long m3ModelBatchId;
}
}

View File

@@ -0,0 +1,347 @@
package com.kamcoback.cd.review.dto;
import com.kamcoback.cd.review.enums.EnumType;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
public class LabelAllocateDto {
@Getter
@AllArgsConstructor
public enum LabelMngState implements EnumType {
PENDING("작업대기"),
ASSIGNED("작업할당"),
ING("진행중"),
FINISH("종료");
private String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@AllArgsConstructor
public enum LabelState implements EnumType {
WAIT("대기"),
ASSIGNED("배정"),
SKIP("스킵"),
DONE("완료");
private String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@AllArgsConstructor
public enum InspectState implements EnumType {
UNCONFIRM("미확인"),
EXCEPT("제외"),
COMPLETE("완료");
private String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@Setter
@AllArgsConstructor
public static class AllocateDto {
// 회차 마스터 key
private UUID uuid;
// 비교년도-기준년도
private String yyyy;
// 회차
private Integer stage;
// 라벨러 할당 목록
private List<TargetUser> labelers;
// 검수자 할당 목록
private List<String> inspectors;
}
@Getter
public static class TargetUser {
// 라벨러 사번
private final String userId;
// 할당 건수
private final int demand;
public TargetUser(String userId, int demand) {
this.userId = userId;
this.demand = demand;
}
}
@Getter
@AllArgsConstructor
public static class TargetInspector {
// 검수자 사번
private final String inspectorUid;
// 할당 명수
private int userCount;
}
@Getter
@Setter
@AllArgsConstructor
public static class Basic {
private UUID assignmentUid;
private Long inferenceGeomUid;
private String workerUid;
private String inspectorUid;
private String workState;
private Character stagnationYn;
private String assignGroupId;
private Long learnGeomUid;
private Long analUid;
private ZonedDateTime createdDttm;
private ZonedDateTime updatedDttm;
private String inspectState;
private ZonedDateTime workStatDttm;
private ZonedDateTime inspectStatDttm;
}
@Getter
@Setter
@AllArgsConstructor
public static class UserList {
private String userRole;
private String employeeNo;
private String name;
}
@Getter
@Setter
@AllArgsConstructor
public static class InferenceDetail {
private String analTitle;
private Integer stage;
private ZonedDateTime gukyuinDttm;
private Long count;
}
@Getter
@Setter
@AllArgsConstructor
public static class LabelerDetail {
private String roleType;
private String name;
private String userId; // 사번
private Long count;
private Long completeCnt;
private Long skipCnt;
private Double percent;
private Integer ranking;
private ZonedDateTime createdDttm;
private String ownerName;
private Long remainCnt;
}
@Getter
@Setter
@AllArgsConstructor
public static class AllocateMoveDto {
// 총 잔여 건수
private Integer totalCnt;
// 이관할 라벨러
private List<String> labelers;
// 회차 마스터 key
private String uuid;
// 대상 사번
private String userId;
}
@Getter
@Setter
@AllArgsConstructor
public static class AllocateInfoDto {
private Long geoUid;
private Long mapSheetNum;
private Long pnu;
}
@Getter
@Setter
@AllArgsConstructor
public static class LabelingStatDto {
private String workDate;
private Long dailyTotalCnt;
private Long totalCnt;
private Long assignedCnt;
private Long skipCnt;
private Long completeCnt;
private Long remainCnt;
}
// 일자별 작업 목록 요청
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class searchReq {
// 페이징 파라미터
private int page = 0;
private int size = 20;
private String sort;
public Pageable toPageable() {
if (sort != null && !sort.isEmpty()) {
String[] sortParams = sort.split(",");
String property = sortParams[0];
Sort.Direction direction =
sortParams.length > 1 ? Sort.Direction.fromString(sortParams[1]) : Sort.Direction.ASC;
return PageRequest.of(page, size, Sort.by(direction, property));
}
return PageRequest.of(page, size);
}
}
@Getter
@Setter
@AllArgsConstructor
public static class MoveUserList {
private String userRole;
private String employeeNo;
private String name;
private Long remainCnt;
private Double percent;
}
@Getter
@Setter
@AllArgsConstructor
public static class MoveInfo {
private Long totalCnt;
private List<MoveUserList> moveUserList;
}
// WorkHistoryDto
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class WorkHistoryDto {
// 행 번호
private Integer rowNum;
// 변화탐지년도
private String changeDetectionYear;
// 국유IN 회차
private Long stage;
// 반영일
private ZonedDateTime gukyuinApplyDttm;
// 할당건수
private Long assignedCnt;
// 완료건수
private Long completeCnt;
// Skip건수
private Long skipCnt;
// 잔여건수
private Long remainCnt;
// 상태 (진행중/완료)
private String status;
// 진행률 (%)
private Double percent;
// 작업기간 시작일
private ZonedDateTime createdDttm;
// 작업기간 종료일
private ZonedDateTime projectCloseDttm;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class InferenceLearnDto {
private UUID analUuid;
private String learnUid;
private String analState;
private Long analId;
}
@Getter
@Setter
@AllArgsConstructor
public static class AllocateAddStbltDto {
// 총 잔여 건수
private Integer totalCnt;
// 추가할당할 라벨러
private List<String> labelers;
// 회차 마스터 key
private UUID uuid;
// 기준일자
private LocalDate baseDate;
}
}

View File

@@ -0,0 +1,22 @@
package com.kamcoback.cd.review.dto;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
public class LabelInspectorDto {
@Getter
@Setter
@AllArgsConstructor
public static class Basic {
private UUID operatorUid;
private Long analUid;
private String inspectorUid;
private ZonedDateTime createdDttm;
private ZonedDateTime updatedDttm;
}
}

View File

@@ -0,0 +1,132 @@
package com.kamcoback.cd.review.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ObjectNode;
public class TrainingDataReviewJobDto {
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class Tasks {
private UUID assignmentUid;
private Long inferenceUid;
private Long analUid;
}
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class InspectorPendingDto {
String inspectorUid;
Long pendingCount;
}
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class AnalCntInfo {
Long analUid;
String resultUid;
Long allCnt;
Long completeCnt;
Long fileCnt;
}
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class AnalMapSheetList {
private Integer compareYyyy;
private Integer targetYyyy;
private String mapSheetNum;
}
@Getter
@Setter
@JsonPropertyOrder({"type", "features"})
public static class FeatureCollection {
private final String type = "FeatureCollection";
private List<CompleteLabelData.GeoJsonFeature> features;
public FeatureCollection(List<CompleteLabelData.GeoJsonFeature> features) {
this.features = features;
}
}
@Getter
@Setter
@JsonPropertyOrder({"type", "geometry", "properties"})
public static class CompleteLabelData {
private Long geoUid;
private String type;
@JsonIgnore private String geomStr;
private JsonNode geometry;
private Properties properties;
public CompleteLabelData(Long geoUid, String type, String geomStr, Properties properties) {
this.geoUid = geoUid;
this.type = type;
this.geomStr = geomStr;
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = null;
if (geomStr != null) {
jsonNode = mapper.readTree(this.geomStr);
}
this.geometry = jsonNode;
if (jsonNode != null && jsonNode.isObject()) {
((ObjectNode) jsonNode).remove("crs");
}
this.properties = properties;
}
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class Properties {
private String modelId;
private String before;
private String after;
}
@Getter
@AllArgsConstructor
public static class GeoJsonFeature {
private String type;
private JsonNode geometry;
private Properties properties;
public static GeoJsonFeature from(CompleteLabelData data) {
return new GeoJsonFeature(
data.getType(),
data.getGeometry(), // geoUid 없음
data.getProperties());
}
}
}
}

View File

@@ -0,0 +1,8 @@
package com.kamcoback.cd.review.enums;
public interface EnumType {
String getId();
String getText();
}

View File

@@ -0,0 +1,26 @@
package com.kamcoback.cd.review.enums;
import com.kamcoback.cd.review.interfaces.EnumValid;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
public class EnumValidator implements ConstraintValidator<EnumValid, String> {
private Set<String> acceptedValues;
@Override
public void initialize(EnumValid constraintAnnotation) {
acceptedValues =
Arrays.stream(constraintAnnotation.enumClass().getEnumConstants())
.map(Enum::name)
.collect(Collectors.toSet());
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && acceptedValues.contains(value);
}
}

View File

@@ -0,0 +1,28 @@
package com.kamcoback.cd.review.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum GukYuinStatus implements EnumType {
PENDING("대기"),
IN_PROGRESS("진행중"),
GUK_COMPLETED("국유인 매핑 완료"),
PNU_COMPLETED("PNU 싱크 완료"),
PNU_FAILED("PNU 싱크 중 에러"),
END("종료"),
CANCELED("취소");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}

View File

@@ -0,0 +1,24 @@
package com.kamcoback.cd.review.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ModelType implements EnumType {
G1("G1"),
G2("G2"),
G3("G3");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}

View File

@@ -0,0 +1,23 @@
package com.kamcoback.cd.review.interfaces;
import com.kamcoback.cd.review.enums.EnumValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)
public @interface EnumValid {
String message() default "올바르지 않은 값입니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends Enum<?>> enumClass();
}

View File

@@ -0,0 +1,19 @@
package com.kamcoback.cd.review.interfaces;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonFormat(
shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX",
timezone = "Asia/Seoul")
public @interface JsonFormatDttm {}

View File

@@ -0,0 +1,35 @@
package com.kamcoback.cd.review.postgres.core;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.AnalCntInfo;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.AnalMapSheetList;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.CompleteLabelData;
import com.kamcoback.cd.review.postgres.repository.TrainingDataReviewJobRepository;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class TrainingDataReviewJobCoreService {
private final TrainingDataReviewJobRepository trainingDataReviewJobRepository;
public List<CompleteLabelData> findCompletedYesterdayLabelingList(
Long analUid, String mapSheetNum, LocalDate baseDate) {
return trainingDataReviewJobRepository.findCompletedYesterdayLabelingList(
analUid, mapSheetNum, baseDate);
}
public List<AnalMapSheetList> findCompletedAnalMapSheetList(Long analUid, LocalDate baseDate) {
return trainingDataReviewJobRepository.findCompletedAnalMapSheetList(analUid, baseDate);
}
public List<AnalCntInfo> findAnalCntInfoList() {
return trainingDataReviewJobRepository.findAnalCntInfoList();
}
public void updateLearnDataGeomFileCreateYn(List<Long> geoUids) {
trainingDataReviewJobRepository.updateLearnDataGeomFileCreateYn(geoUids);
}
}

View File

@@ -0,0 +1,34 @@
package com.kamcoback.cd.review.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import java.time.ZonedDateTime;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
@Getter
@MappedSuperclass
public class CommonDateEntity {
@CreatedDate
@Column(name = "created_dttm", updatable = false, nullable = false)
private ZonedDateTime createdDate;
@LastModifiedDate
@Column(name = "updated_dttm", nullable = false)
private ZonedDateTime modifiedDate;
@PrePersist
protected void onPersist() {
this.createdDate = ZonedDateTime.now();
this.modifiedDate = ZonedDateTime.now();
}
@PreUpdate
protected void onUpdate() {
this.modifiedDate = ZonedDateTime.now();
}
}

View File

@@ -0,0 +1,69 @@
package com.kamcoback.cd.review.postgres.entity;
import com.kamcoback.cd.review.dto.LabelAllocateDto;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.ZonedDateTime;
import java.util.UUID;
@Entity
@Table(name = "tb_labeling_assignment")
public class LabelingAssignmentEntity extends CommonDateEntity {
@Id
@Column(name = "assignment_uid")
private UUID assignmentUid;
@Column(name = "inference_geom_uid")
private Long inferenceGeomUid;
@Column(name = "worker_uid")
private String workerUid;
@Column(name = "inspector_uid")
private String inspectorUid;
@Column(name = "work_state")
private String workState;
@Column(name = "stagnation_yn")
private Character stagnationYn;
@Column(name = "assign_group_id")
private String assignGroupId;
@Column(name = "learn_geom_uid")
private Long learnGeomUid;
@Column(name = "anal_uid")
private Long analUid;
@Column(name = "inspect_state")
private String inspectState;
@Column(name = "work_stat_dttm")
private ZonedDateTime workStatDttm;
@Column(name = "inspect_stat_dttm")
private ZonedDateTime inspectStatDttm;
public LabelAllocateDto.Basic toDto() {
return new LabelAllocateDto.Basic(
this.assignmentUid,
this.inferenceGeomUid,
this.workerUid,
this.inspectorUid,
this.workState,
this.stagnationYn,
this.assignGroupId,
this.learnGeomUid,
this.analUid,
super.getCreatedDate(),
super.getModifiedDate(),
this.inspectState,
this.workStatDttm,
this.inspectStatDttm);
}
}

View File

@@ -0,0 +1,32 @@
package com.kamcoback.cd.review.postgres.entity;
import com.kamcoback.cd.review.dto.LabelInspectorDto;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.UUID;
@Entity
@Table(name = "tb_labeling_inspector")
public class LabelingInspectorEntity extends CommonDateEntity {
@Id
@Column(name = "operator_uid")
private UUID operatorUid;
@Column(name = "anal_uid")
private Long analUid;
@Column(name = "inspector_uid")
private String inspectorUid;
public LabelInspectorDto.Basic toDto() {
return new LabelInspectorDto.Basic(
this.operatorUid,
this.analUid,
this.inspectorUid,
super.getCreatedDate(),
super.getModifiedDate());
}
}

View File

@@ -0,0 +1,165 @@
package com.kamcoback.cd.review.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Size;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
@Getter
@Setter
@Entity
@Table(name = "tb_map_sheet_anal_inference")
public class MapSheetAnalInferenceEntity {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "tb_map_sheet_anal_inference_id_gen")
@SequenceGenerator(
name = "tb_map_sheet_anal_inference_id_gen",
sequenceName = "tb_map_sheet_anal_inference_uid",
allocationSize = 1)
@Column(name = "anal_uid", nullable = false)
private Long id;
@Column(name = "compare_yyyy")
private Integer compareYyyy;
@Column(name = "target_yyyy")
private Integer targetYyyy;
@Column(name = "model_uid")
private Long modelUid;
@Size(max = 100)
@Column(name = "server_ids", length = 100)
private String serverIds;
@Column(name = "anal_strt_dttm")
private ZonedDateTime analStrtDttm;
@Column(name = "anal_end_dttm")
private ZonedDateTime analEndDttm;
@Column(name = "anal_sec")
private Long analSec;
@Size(max = 20)
@Column(name = "anal_state", length = 20)
private String analState;
@Size(max = 20)
@Column(name = "gukyuin_used", length = 20)
private String gukyuinUsed;
@Column(name = "accuracy")
private Double accuracy;
@Size(max = 255)
@Column(name = "result_url")
private String resultUrl;
@ColumnDefault("now()")
@Column(name = "created_dttm")
private ZonedDateTime createdDttm;
@Column(name = "created_uid")
private Long createdUid;
@ColumnDefault("now()")
@Column(name = "updated_dttm")
private ZonedDateTime updatedDttm;
@Column(name = "updated_uid")
private Long updatedUid;
@Size(max = 255)
@Column(name = "anal_title")
private String analTitle;
@Column(name = "detecting_cnt")
private Long detectingCnt;
@Column(name = "anal_pred_sec")
private Long analPredSec;
@Column(name = "model_ver_uid")
private Long modelVerUid;
@Column(name = "hyper_params")
@JdbcTypeCode(SqlTypes.JSON)
private Map<String, Object> hyperParams;
@Column(name = "tranning_rate")
private List<Double> tranningRate;
@Column(name = "validation_rate")
private List<Double> validationRate;
@Column(name = "test_rate", length = Integer.MAX_VALUE)
private String testRate;
@Size(max = 128)
@Column(name = "detecting_description", length = 128)
private String detectingDescription;
@Size(max = 12)
@Column(name = "base_map_sheet_num", length = 12)
private String baseMapSheetNum;
@ColumnDefault("gen_random_uuid()")
@Column(name = "uuid")
private UUID uuid;
@Size(max = 50)
@Column(name = "model_m1_ver", length = 50)
private String modelM1Ver;
@Size(max = 50)
@Column(name = "model_m2_ver", length = 50)
private String modelM2Ver;
@Size(max = 50)
@Column(name = "model_m3_ver", length = 50)
private String modelM3Ver;
@Size(max = 20)
@Column(name = "anal_target_type", length = 20)
private String analTargetType;
@Column(name = "gukyuin_apply_dttm")
private ZonedDateTime gukyuinApplyDttm;
@Size(max = 20)
@Column(name = "detection_data_option", length = 20)
private String detectionDataOption;
@Column(name = "stage")
private Integer stage;
@Size(max = 1)
@ColumnDefault("'N'")
@Column(name = "labeling_closed_yn", length = 1)
private String labelingClosedYn = "N";
@Size(max = 1)
@ColumnDefault("'N'")
@Column(name = "inspection_closed_yn", length = 1)
private String inspectionClosedYn = "N";
@Column(name = "learn_id")
private Long learnId;
}

View File

@@ -0,0 +1,45 @@
package com.kamcoback.cd.review.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.locationtech.jts.geom.Geometry;
/** 라벨링 툴에서 그린 폴리곤 저장 테이블 */
@Getter
@Setter
@Entity
@Table(name = "tb_map_sheet_learn_data_geom")
public class MapSheetLearnDataGeomEntity extends CommonDateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "geo_uid", nullable = false)
private Long geoUid;
@Column(name = "after_yyyy")
private Integer afterYyyy;
@Column(name = "area")
private Double area;
@Column(name = "before_yyyy")
private Integer beforeYyyy;
@Column(name = "class_after_cd")
private String classAfterCd;
@Column(name = "class_before_cd")
private String classBeforeCd;
@Column(name = "geom")
private Geometry geom;
@Column(name = "file_create_yn")
private Boolean fileCreateYn;
}

View File

@@ -0,0 +1,221 @@
package com.kamcoback.cd.review.postgres.entity;
import com.kamcoback.cd.review.dto.InferenceResultDto;
import com.kamcoback.cd.review.enums.GukYuinStatus;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "tb_map_sheet_learn")
public class MapSheetLearnEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_sheet_learn_id_gen")
@SequenceGenerator(
name = "tb_map_sheet_learn_id_gen",
sequenceName = "tb_map_sheet_learn_uid",
allocationSize = 1)
@Column(name = "id", nullable = false)
private Long id;
@ColumnDefault("gen_random_uuid()")
@Column(name = "uuid")
private UUID uuid = UUID.randomUUID();
@Size(max = 200)
@NotNull
@Column(name = "title", nullable = false, length = 200)
private String title;
@Size(max = 20)
@Column(name = "status", length = 10)
private String status;
@Column(name = "m1_model_uuid")
private UUID m1ModelUuid;
@Column(name = "m2_model_uuid")
private UUID m2ModelUuid;
@Column(name = "m3_model_uuid")
private UUID m3ModelUuid;
@Column(name = "compare_yyyy")
private Integer compareYyyy;
@Column(name = "target_yyyy")
private Integer targetYyyy;
@Size(max = 20)
@Column(name = "detect_option", length = 20)
private String detectOption;
@Size(max = 100)
@Column(name = "map_sheet_cnt", length = 100)
private String mapSheetCnt;
@Size(max = 20)
@Column(name = "map_sheet_scope", length = 20)
private String mapSheetScope;
@Column(name = "detecting_cnt")
private Long detectingCnt;
@Column(name = "infer_start_dttm")
private ZonedDateTime inferStartDttm;
@Column(name = "infer_end_dttm")
private ZonedDateTime inferEndDttm;
@Column(name = "elapsed_time")
private ZonedDateTime elapsedTime;
@Column(name = "apply_yn")
private Boolean applyYn;
@Column(name = "apply_dttm")
private ZonedDateTime applyDttm;
@org.hibernate.annotations.CreationTimestamp
@Column(name = "created_dttm")
private ZonedDateTime createdDttm;
@Column(name = "created_uid")
private Long createdUid;
@ColumnDefault("now()")
@Column(name = "updated_dttm")
private ZonedDateTime updatedDttm;
@Column(name = "updated_uid")
private Long updatedUid;
@Column(name = "running_model_type")
private String runningModelType;
@Column(name = "detect_end_cnt")
private Long detectEndCnt;
@Column(name = "model_compare_path")
private String modelComparePath;
@Column(name = "model_target_path")
private String modelTargetPath;
@Column(name = "stage")
private Integer stage;
/* ===================== M1 ===================== */
@Column(name = "m1_model_batch_id")
private Long m1ModelBatchId;
@Column(name = "m1_model_start_dttm")
private ZonedDateTime m1ModelStartDttm;
@Column(name = "m1_model_end_dttm")
private ZonedDateTime m1ModelEndDttm;
@Column(name = "m1_pending_jobs", nullable = false)
private int m1PendingJobs = 0;
@Column(name = "m1_running_jobs", nullable = false)
private int m1RunningJobs = 0;
@Column(name = "m1_completed_jobs", nullable = false)
private int m1CompletedJobs = 0;
@Column(name = "m1_failed_jobs", nullable = false)
private int m1FailedJobs = 0;
/* ===================== M2 ===================== */
@Column(name = "m2_model_batch_id")
private Long m2ModelBatchId;
@Column(name = "m2_model_start_dttm")
private ZonedDateTime m2ModelStartDttm;
@Column(name = "m2_model_end_dttm")
private ZonedDateTime m2ModelEndDttm;
@Column(name = "m2_pending_jobs", nullable = false)
private int m2PendingJobs = 0;
@Column(name = "m2_running_jobs", nullable = false)
private int m2RunningJobs = 0;
@Column(name = "m2_completed_jobs", nullable = false)
private int m2CompletedJobs = 0;
@Column(name = "m2_failed_jobs", nullable = false)
private int m2FailedJobs = 0;
/* ===================== M3 ===================== */
@Column(name = "m3_model_batch_id")
private Long m3ModelBatchId;
@Column(name = "m3_model_start_dttm")
private ZonedDateTime m3ModelStartDttm;
@Column(name = "m3_model_end_dttm")
private ZonedDateTime m3ModelEndDttm;
@Column(name = "m3_pending_jobs", nullable = false)
private int m3PendingJobs = 0;
@Column(name = "m3_running_jobs", nullable = false)
private int m3RunningJobs = 0;
@Column(name = "m3_completed_jobs", nullable = false)
private int m3CompletedJobs = 0;
@Column(name = "m3_failed_jobs", nullable = false)
private int m3FailedJobs = 0;
@Column(name = "apply_status")
private String applyStatus = GukYuinStatus.PENDING.getId();
@Column(name = "apply_status_dttm")
private ZonedDateTime applyStatusDttm;
@Column(name = "uid", nullable = false)
private String uid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
@Column(name = "total_jobs")
private Long totalJobs;
@Column(name = "chn_dtct_mst_id")
private String chnDtctMstId;
public InferenceResultDto.ResultList toDto() {
return new InferenceResultDto.ResultList(
this.uuid,
this.title,
this.stage,
this.status,
this.mapSheetCnt,
this.detectingCnt,
this.inferStartDttm,
this.inferEndDttm,
this.applyYn,
this.applyDttm,
this.compareYyyy,
this.targetYyyy,
this.uid);
}
}

View File

@@ -0,0 +1,23 @@
package com.kamcoback.cd.review.postgres.repository;
import com.kamcoback.cd.review.postgres.entity.LabelingInspectorEntity;
import jakarta.persistence.LockModeType;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
public interface TrainingDataReviewJobRepository
extends JpaRepository<LabelingInspectorEntity, UUID>, TrainingDataReviewJobRepositoryCustom {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query(
"""
select r
from LabelingInspectorEntity r
where r.analUid = :analUid
and r.inspectorUid in :inspectorUids
""")
List<LabelingInspectorEntity> lockInspectors(Long analUid, List<String> inspectorUids);
}

View File

@@ -0,0 +1,19 @@
package com.kamcoback.cd.review.postgres.repository;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.AnalCntInfo;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.AnalMapSheetList;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.CompleteLabelData;
import java.time.LocalDate;
import java.util.List;
public interface TrainingDataReviewJobRepositoryCustom {
List<CompleteLabelData> findCompletedYesterdayLabelingList(
Long analUid, String mapSheetNum, LocalDate baseDate);
List<AnalMapSheetList> findCompletedAnalMapSheetList(Long analUid, LocalDate baseDate);
List<AnalCntInfo> findAnalCntInfoList();
void updateLearnDataGeomFileCreateYn(List<Long> geoUids);
}

View File

@@ -0,0 +1,173 @@
package com.kamcoback.cd.review.postgres.repository;
import static com.kamcoback.cd.review.postgres.entity.QLabelingAssignmentEntity.labelingAssignmentEntity;
import static com.kamcoback.cd.review.postgres.entity.QMapSheetLearnDataGeomEntity.mapSheetLearnDataGeomEntity;
import static com.kamcoback.cd.review.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import static com.kamcoback.cd.review.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import com.kamcoback.cd.review.dto.LabelAllocateDto.InspectState;
import com.kamcoback.cd.review.dto.LabelAllocateDto.LabelMngState;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.AnalCntInfo;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.AnalMapSheetList;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.CompleteLabelData;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.CompleteLabelData.Properties;
import com.kamcoback.cd.review.enums.ModelType;
import com.kamcoback.cd.review.postgres.entity.LabelingAssignmentEntity;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.transaction.Transactional;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;
@Repository
public class TrainingDataReviewJobRepositoryImpl extends QuerydslRepositorySupport
implements TrainingDataReviewJobRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");
public TrainingDataReviewJobRepositoryImpl(JPAQueryFactory queryFactory) {
super(LabelingAssignmentEntity.class);
this.queryFactory = queryFactory;
}
@Override
public List<CompleteLabelData> findCompletedYesterdayLabelingList(
Long analUid, String mapSheetNum, LocalDate baseDate) {
ZoneId zoneId = ZoneId.of("Asia/Seoul");
// baseDate가 null이면 기존처럼 오늘 기준
LocalDate targetDate = (baseDate != null) ? baseDate : LocalDate.now(zoneId);
ZonedDateTime end = targetDate.plusDays(1).atStartOfDay(zoneId);
return queryFactory
.select(
Projections.constructor(
CompleteLabelData.class,
mapSheetLearnDataGeomEntity.geoUid,
Expressions.stringTemplate("{0}", "Feature").as("type"),
Expressions.stringTemplate("ST_AsGeoJSON({0})", mapSheetLearnDataGeomEntity.geom),
Projections.constructor(
Properties.class,
new CaseBuilder()
.when(mapSheetLearnDataGeomEntity.classAfterCd.in("building", "container"))
.then(ModelType.G1.getId())
.when(mapSheetLearnDataGeomEntity.classAfterCd.eq("waste"))
.then(ModelType.G2.getId())
.otherwise(ModelType.G3.getId()),
mapSheetLearnDataGeomEntity.classBeforeCd,
mapSheetLearnDataGeomEntity.classAfterCd)))
.from(labelingAssignmentEntity)
.leftJoin(mapSheetLearnDataGeomEntity)
.on(labelingAssignmentEntity.inferenceGeomUid.eq(mapSheetLearnDataGeomEntity.geoUid))
.where(
labelingAssignmentEntity.analUid.eq(analUid),
labelingAssignmentEntity.assignGroupId.eq(mapSheetNum),
labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()),
labelingAssignmentEntity.inspectStatDttm.lt(end))
.fetch();
}
@Override
public List<AnalMapSheetList> findCompletedAnalMapSheetList(Long analUid, LocalDate baseDate) {
ZoneId zoneId = ZoneId.of("Asia/Seoul");
// baseDate가 null이면 기존처럼 오늘 기준
LocalDate targetDate = (baseDate != null) ? baseDate : LocalDate.now(zoneId);
ZonedDateTime end = targetDate.plusDays(1).atStartOfDay(zoneId);
return queryFactory
.select(
Projections.constructor(
AnalMapSheetList.class,
mapSheetAnalInferenceEntity.compareYyyy,
mapSheetAnalInferenceEntity.targetYyyy,
labelingAssignmentEntity.assignGroupId))
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalInferenceEntity)
.on(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id))
.where(
labelingAssignmentEntity.analUid.eq(analUid),
labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()),
labelingAssignmentEntity.inspectStatDttm.lt(end))
.groupBy(
mapSheetAnalInferenceEntity.compareYyyy,
mapSheetAnalInferenceEntity.targetYyyy,
labelingAssignmentEntity.assignGroupId)
.fetch();
}
@Override
public List<AnalCntInfo> findAnalCntInfoList() {
// 검수 제외(EXCEPT)를 뺀 나머지 cnt
NumberExpression<Long> allCnt =
new CaseBuilder()
.when(
labelingAssignmentEntity
.inspectState
.eq(InspectState.UNCONFIRM.getId())
.or(labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()))
.or(labelingAssignmentEntity.inspectState.isNull()))
.then(1L)
.otherwise(0L)
.sum();
// file_cnt
NumberExpression<Long> fileCnt =
new CaseBuilder()
.when(mapSheetLearnDataGeomEntity.fileCreateYn.isTrue())
.then(1L)
.otherwise(0L)
.sum();
NumberExpression<Long> completeCnt =
new CaseBuilder()
.when(labelingAssignmentEntity.inspectState.eq(InspectState.COMPLETE.getId()))
.then(1L)
.otherwise(0L)
.sum();
return queryFactory
.select(
Projections.constructor(
AnalCntInfo.class,
labelingAssignmentEntity.analUid,
mapSheetLearnEntity.uid,
allCnt,
completeCnt,
fileCnt))
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalInferenceEntity)
.on(
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
mapSheetAnalInferenceEntity.analState.eq(LabelMngState.ING.getId()))
.leftJoin(mapSheetLearnEntity)
.on(mapSheetAnalInferenceEntity.learnId.eq(mapSheetLearnEntity.id))
.leftJoin(mapSheetLearnDataGeomEntity)
.on(labelingAssignmentEntity.inferenceGeomUid.eq(mapSheetLearnDataGeomEntity.geoUid))
.groupBy(labelingAssignmentEntity.analUid, mapSheetLearnEntity.uid)
.having(completeCnt.gt(0L))
.fetch();
}
@Override
@Transactional
public void updateLearnDataGeomFileCreateYn(List<Long> geoUids) {
queryFactory
.update(mapSheetLearnDataGeomEntity)
.set(mapSheetLearnDataGeomEntity.fileCreateYn, true)
.set(mapSheetLearnDataGeomEntity.modifiedDate, ZonedDateTime.now())
.where(mapSheetLearnDataGeomEntity.geoUid.in(geoUids))
.execute();
}
}

View File

@@ -0,0 +1,117 @@
package com.kamcoback.cd.review.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.AnalCntInfo;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.AnalMapSheetList;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.CompleteLabelData;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.CompleteLabelData.GeoJsonFeature;
import com.kamcoback.cd.review.dto.TrainingDataReviewJobDto.FeatureCollection;
import com.kamcoback.cd.review.postgres.core.TrainingDataReviewJobCoreService;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Log4j2
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class TrainingDataReviewJobService {
private final TrainingDataReviewJobCoreService trainingDataReviewJobCoreService;
@Value("${spring.profiles.active}")
private String profile;
@Value("${training-data.geojson-dir}")
private String trainingDataDir;
private boolean isLocalProfile() {
return "local".equalsIgnoreCase(profile);
}
@Scheduled(cron = "0 0 2 * * *")
public void runTask() {
exportGeojsonLabelingGeom(null);
}
@Transactional
public void exportGeojsonLabelingGeom(LocalDate baseDate) {
//
// if (isLocalProfile()) {
// return;
// }
// 1) 경로/파일명 결정
String targetDir = "local".equals(profile) ? System.getProperty("user.home") + "/geojson" : trainingDataDir;
// 2) 진행중인 회차 중, complete_cnt 가 존재하는 회차 목록 가져오기
List<AnalCntInfo> analList = trainingDataReviewJobCoreService.findAnalCntInfoList();
for (AnalCntInfo info : analList) {
if (Objects.equals(info.getAllCnt(), info.getFileCnt())) {
continue;
}
String resultUid = info.getResultUid(); // 회차의 대문자 uid (폴더명으로 사용)
// 3) 회차 + 어제까지 검수 완료된 총 데이터의 도엽별 목록 가져오기
List<AnalMapSheetList> analMapList =
trainingDataReviewJobCoreService.findCompletedAnalMapSheetList(
info.getAnalUid(), baseDate);
if (analMapList.isEmpty()) {
continue;
}
for (AnalMapSheetList mapSheet : analMapList) {
// 4) 도엽별 geom 데이터 가지고 와서 geojson 만들기
List<CompleteLabelData> completeList =
trainingDataReviewJobCoreService.findCompletedYesterdayLabelingList(
info.getAnalUid(), mapSheet.getMapSheetNum(), baseDate);
if (!completeList.isEmpty()) {
List<Long> geoUids = completeList.stream().map(CompleteLabelData::getGeoUid).toList();
List<GeoJsonFeature> features = completeList.stream().map(GeoJsonFeature::from).toList();
// 5) 파일서버에 uid 폴더 생성 후 업로드 하기
FeatureCollection collection = new FeatureCollection(features);
String filename =
String.format(
"%s_%s_%s_%s_D15.geojson",
resultUid.substring(0, 8),
mapSheet.getCompareYyyy(),
mapSheet.getTargetYyyy(),
mapSheet.getMapSheetNum());
Path outputPath = Paths.get(targetDir + "/" + resultUid, filename);
try {
Files.createDirectories(outputPath.getParent());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.writeValue(outputPath.toFile(), collection);
// geoUids : file_create_yn = true 로 업데이트
trainingDataReviewJobCoreService.updateLearnDataGeomFileCreateYn(geoUids);
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
}
}
}

View File

@@ -0,0 +1,66 @@
spring:
config:
activate:
on-profile: dev
jpa:
show-sql: false
hibernate:
ddl-auto: validate
properties:
hibernate:
default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지
order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지
order_inserts: true
use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용)
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
jdbc:
batch_size: 1000 # ✅ 추가 (JDBC batch)
open-in-view: false
mvc:
async:
request-timeout: 300s # 5분 (예: 30s, 120s, 10m 등도 가능)
datasource:
url: jdbc:postgresql://192.168.2.127:15432/kamco_cds
#url: jdbc:postgresql://localhost:15432/kamco_cds
username: kamco_cds
password: kamco_cds_Q!W@E#R$
hikari:
minimum-idle: 10
maximum-pool-size: 20
connection-timeout: 60000 # 60초 연결 타임아웃
idle-timeout: 300000 # 5분 유휴 타임아웃
max-lifetime: 1800000 # 30분 최대 수명
leak-detection-threshold: 60000 # 연결 누수 감지
transaction:
default-timeout: 300 # 5분 트랜잭션 타임아웃
data:
redis:
host: 192.168.2.109
port: 6379
password: kamco
servlet:
multipart:
enabled: true
max-file-size: 4GB
max-request-size: 4GB
file-size-threshold: 10MB
server:
tomcat:
max-swallow-size: 4GB
max-http-form-post-size: 4GB
logging:
level:
root: INFO
org.springframework.web: DEBUG
training-data:
geojson-dir: /kamco-nfs/dataset/request/

View File

@@ -0,0 +1,51 @@
spring:
config:
activate:
on-profile: local
jpa:
show-sql: false
hibernate:
ddl-auto: update # 로컬만 완화(시킬려면 update으로 변경)
properties:
hibernate:
default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지
order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지
use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용)
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
open-in-view: false
datasource:
url: jdbc:postgresql://192.168.2.127:15432/kamco_cds
#url: jdbc:postgresql://localhost:5432/kamco_cds # 로컬호스트
username: kamco_cds
password: kamco_cds_Q!W@E#R$
hikari:
minimum-idle: 1
maximum-pool-size: 5
data:
redis:
host: 192.168.2.109
port: 6379
password: kamco
servlet:
multipart:
enabled: true
max-file-size: 4GB
max-request-size: 4GB
file-size-threshold: 10MB
server:
tomcat:
max-swallow-size: 4GB
max-http-form-post-size: 4GB
logging:
level:
org.hibernate.SQL: debug
org.hibernate.orm.jdbc.bind: trace
training-data:
geojson-dir: /Users/bokmin/kamco-nfs/model_output/labeling/

View File

@@ -0,0 +1,64 @@
spring:
config:
activate:
on-profile: prod
jpa:
show-sql: true
hibernate:
ddl-auto: validate
properties:
hibernate:
default_batch_fetch_size: 100 # ✅ 성능 - N+1 쿼리 방지
order_updates: true # ✅ 성능 - 업데이트 순서 정렬로 데드락 방지
order_inserts: true
use_sql_comments: true # ⚠️ 선택 - SQL에 주석 추가 (디버깅용)
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
jdbc:
batch_size: 1000 # ✅ 추가 (JDBC batch)
open-in-view: false
mvc:
async:
request-timeout: 300s # 5분 (예: 30s, 120s, 10m 등도 가능)
datasource:
url: jdbc:postgresql://127.0.0.1:15432/kamco_cds
#url: jdbc:postgresql://localhost:15432/kamco_cds
username: kamco_cds
password: kamco_cds_Q!W@E#R$
hikari:
minimum-idle: 10
maximum-pool-size: 20
connection-timeout: 60000 # 60초 연결 타임아웃
idle-timeout: 300000 # 5분 유휴 타임아웃
max-lifetime: 1800000 # 30분 최대 수명
leak-detection-threshold: 60000 # 연결 누수 감지
transaction:
default-timeout: 300 # 5분 트랜잭션 타임아웃
data:
redis:
host: 127.0.0.1
port: 16379
password: kamco
servlet:
multipart:
enabled: true
max-file-size: 4GB
max-request-size: 4GB
file-size-threshold: 10MB
server:
tomcat:
max-swallow-size: 4GB
max-http-form-post-size: 4GB
logging:
level:
root: INFO
org.springframework.web: DEBUG
training-data:
geojson-dir: /kamco-nfs/dataset/request/

View File

@@ -0,0 +1,40 @@
server:
port: 8080
spring:
application:
name: kamco-change-detection-api
profiles:
active: local # 사용할 프로파일 지정 (ex. dev, prod, test)
datasource:
driver-class-name: org.postgresql.Driver
hikari:
jdbc:
time_zone: UTC
batch_size: 50
# 권장 설정
minimum-idle: 2
maximum-pool-size: 2
connection-timeout: 20000
idle-timeout: 300000
max-lifetime: 1800000
leak-detection-threshold: 60000
data:
redis:
host: localhost
port: 6379
password:
jpa:
hibernate:
ddl-auto: update # 테이블이 없으면 생성, 있으면 업데이트
properties:
hibernate:
jdbc:
batch_size: 50
default_batch_fetch_size: 100
logging:
level:
root: INFO
org.springframework.web: DEBUG

View File

@@ -0,0 +1,13 @@
package com.kamcoback.cd.review;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class TrainingDataReviewApplicationTests {
@Test
void contextLoads() {
}
}