Merge branch 'feat/infer_dev_260107' of https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice into feat/infer_dev_260107

# Conflicts:
#	src/main/resources/application.yml
This commit is contained in:
Moon
2026-01-08 18:52:49 +09:00
39 changed files with 1051 additions and 769 deletions

View File

@@ -19,6 +19,7 @@ services:
- /mnt/nfs_share/model_output:/app/model-outputs
- /mnt/nfs_share/train_dataset:/app/train-dataset
- /mnt/nfs_share/tmp:/app/tmp
- /kamco-nfs:/kamco-nfs
networks:
- kamco-cds
restart: unless-stopped

View File

@@ -1,7 +1,7 @@
package com.kamco.cd.kamcoback.common.api;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.LearningModelResultDto.BatchProcessResponse;
import com.kamco.cd.kamcoback.inference.service.InferenceResultService;
import io.swagger.v3.oas.annotations.Operation;

View File

@@ -0,0 +1,20 @@
package com.kamco.cd.kamcoback.common.utils;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class DateRange {
private static final ZoneId KST = ZoneId.of("Asia/Seoul");
private DateRange() {}
public static ZonedDateTime start(LocalDate date) {
return date == null ? null : date.atStartOfDay(KST);
}
public static ZonedDateTime end(LocalDate date) {
return date == null ? null : date.plusDays(1).atStartOfDay(KST);
}
}

View File

@@ -3,7 +3,7 @@ package com.kamco.cd.kamcoback.common.utils;
import java.util.regex.Pattern;
import org.mindrot.jbcrypt.BCrypt;
public class CommonStringUtils {
public class StringUtils {
private static final int BCRYPT_COST = 10;

View File

@@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.inference;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
import com.kamco.cd.kamcoback.inference.service.InferenceResultService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -10,11 +11,10 @@ import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@@ -27,7 +27,7 @@ public class InferenceResultApiController {
private final InferenceResultService inferenceResultService;
@Operation(summary = "추론관리 분석결과 목록 조회", description = "분석상태, 제목으로 분석결과를 조회 합니다.")
@Operation(summary = "추론관리 목록", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록")
@ApiResponses(
value = {
@ApiResponse(
@@ -41,93 +41,95 @@ public class InferenceResultApiController {
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/list")
public ApiResponseDto<Page<InferenceResultDto.AnalResList>> getInferenceResultList(
@Parameter(description = "분석상태", example = "0002") @RequestParam(required = false)
String statCode,
public ApiResponseDto<Page<ResultList>> getInferenceResultList(
@Parameter(description = "국유인 반영 여부", example = "Y") @RequestParam(required = false)
String applyYn,
@Parameter(description = "반영일", example = "2025-01-01") @RequestParam(required = false)
LocalDate strtDttm,
@Parameter(description = "반영일", example = "2026-01-01") @RequestParam(required = false)
LocalDate endDttm,
@Parameter(description = "제목", example = "변화탐지") @RequestParam(required = false) String title,
@Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0")
int page,
@Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20")
int size) {
InferenceResultDto.SearchReq searchReq =
new InferenceResultDto.SearchReq(statCode, title, page, size);
Page<InferenceResultDto.AnalResList> analResList =
inferenceResultService.getInferenceResultList(searchReq);
InferenceResultDto.SearchListReq req =
new InferenceResultDto.SearchListReq(applyYn, strtDttm, endDttm, title, page, size);
Page<ResultList> analResList = inferenceResultService.getInferenceResultList(req);
return ApiResponseDto.ok(analResList);
}
@Operation(summary = "추론관리 분석결과 요약정보", description = "분석결과 요약정보를 조회합니다.")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = InferenceResultDto.AnalResSummary.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/summary/{id}")
public ApiResponseDto<InferenceResultDto.AnalResSummary> getInferenceResultSummary(
@Parameter(description = "목록 id", example = "53") @PathVariable Long id) {
return ApiResponseDto.ok(inferenceResultService.getInferenceResultSummary(id));
}
@Operation(summary = "추론관리 분석결과 상세", description = "분석결과 상제 정보 Summary, DashBoard")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = InferenceResultDto.Detail.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/detail/{id}")
public ApiResponseDto<InferenceResultDto.Detail> getInferenceDetail(
@Parameter(description = "목록 id", example = "53") @PathVariable Long id) {
return ApiResponseDto.ok(inferenceResultService.getDetail(id));
}
@Operation(summary = "추론관리 분석결과 상세 목록", description = "추론관리 분석결과 상세 목록 geojson 데이터 조회")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "검색 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Page.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/geom/{id}")
public ApiResponseDto<Page<InferenceResultDto.Geom>> getInferenceResultGeomList(
@Parameter(description = "분석결과 id", example = "53") @PathVariable Long id,
@Parameter(description = "기준년도 분류", example = "land") @RequestParam(required = false)
String targetClass,
@Parameter(description = "비교년도 분류", example = "waste") @RequestParam(required = false)
String compareClass,
@Parameter(description = "5000:1 도협번호 37801011,37801012") @RequestParam(required = false)
List<Long> mapSheetNum,
@Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0")
int page,
@Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20")
int size,
@Parameter(description = "정렬 조건 (형식: 필드명,방향)", example = "name,asc")
@RequestParam(required = false)
String sort) {
InferenceResultDto.SearchGeoReq searchGeoReq =
new InferenceResultDto.SearchGeoReq(
targetClass, compareClass, mapSheetNum, page, size, sort);
Page<InferenceResultDto.Geom> geomList =
inferenceResultService.getInferenceResultGeomList(id, searchGeoReq);
return ApiResponseDto.ok(geomList);
}
// @ApiResponses(
// value = {
// @ApiResponse(
// responseCode = "200",
// description = "검색 성공",
// content =
// @Content(
// mediaType = "application/json",
// schema = @Schema(implementation = InferenceDetailDto.AnalResSummary.class))),
// @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
// @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
// })
// @GetMapping("/summary/{id}")
// public ApiResponseDto<InferenceDetailDto.AnalResSummary> getInferenceResultSummary(
// @Parameter(description = "목록 id", example = "53") @PathVariable Long id) {
// return ApiResponseDto.ok(inferenceResultService.getInferenceResultSummary(id));
// }
//
// @Operation(summary = "추론관리 분석결과 상세", description = "분석결과 상제 정보 Summary, DashBoard")
// @ApiResponses(
// value = {
// @ApiResponse(
// responseCode = "200",
// description = "검색 성공",
// content =
// @Content(
// mediaType = "application/json",
// schema = @Schema(implementation = InferenceDetailDto.Detail.class))),
// @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
// @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
// })
// @GetMapping("/detail/{id}")
// public ApiResponseDto<InferenceDetailDto.Detail> getInferenceDetail(
// @Parameter(description = "목록 id", example = "53") @PathVariable Long id) {
// return ApiResponseDto.ok(inferenceResultService.getDetail(id));
// }
//
// @Operation(summary = "추론관리 분석결과 상세 목록", description = "추론관리 분석결과 상세 목록 geojson 데이터 조회")
// @ApiResponses(
// value = {
// @ApiResponse(
// responseCode = "200",
// description = "검색 성공",
// content =
// @Content(
// mediaType = "application/json",
// schema = @Schema(implementation = Page.class))),
// @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
// @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
// })
// @GetMapping("/geom/{id}")
// public ApiResponseDto<Page<InferenceDetailDto.Geom>> getInferenceResultGeomList(
// @Parameter(description = "분석결과 id", example = "53") @PathVariable Long id,
// @Parameter(description = "기준년도 분류", example = "land") @RequestParam(required = false)
// String targetClass,
// @Parameter(description = "비교년도 분류", example = "waste") @RequestParam(required = false)
// String compareClass,
// @Parameter(description = "5000:1 도협번호 37801011,37801012") @RequestParam(required = false)
// List<Long> mapSheetNum,
// @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0")
// int page,
// @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20")
// int size,
// @Parameter(description = "정렬 조건 (형식: 필드명,방향)", example = "name,asc")
// @RequestParam(required = false)
// String sort) {
// InferenceDetailDto.SearchGeoReq searchGeoReq =
// new InferenceDetailDto.SearchGeoReq(
// targetClass, compareClass, mapSheetNum, page, size, sort);
// Page<InferenceDetailDto.Geom> geomList =
// inferenceResultService.getInferenceResultGeomList(id, searchGeoReq);
// return ApiResponseDto.ok(geomList);
// }
}

View File

@@ -1,7 +1,7 @@
package com.kamco.cd.kamcoback.inference;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.service.InferenceResultService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -43,7 +43,7 @@ public class InferenceResultApiV2Controller {
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/geom/{id}")
public ApiResponseDto<Page<InferenceResultDto.DetailListEntity>> listInferenceResultWithGeom(
public ApiResponseDto<Page<InferenceDetailDto.DetailListEntity>> listInferenceResultWithGeom(
@Parameter(description = "분석결과 id", example = "1") @PathVariable Long id,
@Parameter(description = "기준년도 분류", example = "land") @RequestParam(required = false)
String targetClass,
@@ -58,11 +58,11 @@ public class InferenceResultApiV2Controller {
@Parameter(description = "정렬 조건 (형식: 필드명,방향)", example = "name,asc")
@RequestParam(required = false)
String sort) {
InferenceResultDto.SearchGeoReq searchGeoReq =
new InferenceResultDto.SearchGeoReq(
InferenceDetailDto.SearchGeoReq searchGeoReq =
new InferenceDetailDto.SearchGeoReq(
targetClass, compareClass, mapSheetNum, page, size, sort);
Page<InferenceResultDto.DetailListEntity> geomList =
Page<InferenceDetailDto.DetailListEntity> geomList =
inferenceResultService.listInferenceResultWithGeom(id, searchGeoReq);
return ApiResponseDto.ok(geomList);
}

View File

@@ -0,0 +1,394 @@
package com.kamco.cd.kamcoback.inference.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.common.enums.DetectionClassification;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
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 InferenceDetailDto {
@Schema(name = "InferenceResultBasic", description = "분석결과 기본 정보")
@Getter
public static class Basic {
private Long id;
private String dataName;
private Long mapSheepNum;
private Long detectingCnt;
@JsonFormatDttm private ZonedDateTime analStrtDttm;
@JsonFormatDttm private ZonedDateTime analEndDttm;
private Long analSec;
private String analState;
public Basic(
Long id,
String dataName,
Long mapSheepNum,
Long detectingCnt,
ZonedDateTime analStrtDttm,
ZonedDateTime analEndDttm,
Long analSec,
String analState) {
this.id = id;
this.dataName = dataName;
this.mapSheepNum = mapSheepNum;
this.detectingCnt = detectingCnt;
this.analStrtDttm = analStrtDttm;
this.analEndDttm = analEndDttm;
this.analSec = analSec;
this.analState = analState;
}
}
@Schema(name = "AnalysisResultList", description = "분석결과 목록")
@Getter
public static class AnalResList {
private Long id;
private String analTitle;
private Long detectingCnt;
@JsonFormatDttm private ZonedDateTime analStrtDttm;
@JsonFormatDttm private ZonedDateTime analEndDttm;
private Long analSec;
private Long analPredSec;
private String analState;
private String analStateNm;
private String gukyuinUsed;
public AnalResList(
Long id,
String analTitle,
Long detectingCnt,
ZonedDateTime analStrtDttm,
ZonedDateTime analEndDttm,
Long analSec,
Long analPredSec,
String analState,
String analStateNm,
String gukyuinUsed) {
this.id = id;
this.analTitle = analTitle;
this.detectingCnt = detectingCnt;
this.analStrtDttm = analStrtDttm;
this.analEndDttm = analEndDttm;
this.analSec = analSec;
this.analPredSec = analPredSec;
this.analState = analState;
this.analStateNm = analStateNm;
this.gukyuinUsed = gukyuinUsed;
}
}
@Schema(name = "AnalysisResultSummary", description = "분석결과 요약정보")
@Getter
public static class AnalResSummary {
private Long id;
private String analTitle;
private String modelInfo;
private Integer targetYyyy;
private Integer compareYyyy;
@JsonFormatDttm private ZonedDateTime analStrtDttm;
@JsonFormatDttm private ZonedDateTime analEndDttm;
private Long analSec;
private Long analPredSec;
private String resultUrl;
private Long detectingCnt;
private Double accuracy;
private String analState;
private String analStateNm;
public AnalResSummary(
Long id,
String analTitle,
String modelInfo,
Integer targetYyyy,
Integer compareYyyy,
ZonedDateTime analStrtDttm,
ZonedDateTime analEndDttm,
Long analSec,
Long analPredSec,
String resultUrl,
Long detectingCnt,
Double accuracy,
String analState,
String analStateNm) {
this.id = id;
this.analTitle = analTitle;
this.modelInfo = modelInfo;
this.targetYyyy = targetYyyy;
this.compareYyyy = compareYyyy;
this.analStrtDttm = analStrtDttm;
this.analEndDttm = analEndDttm;
this.analSec = analSec;
this.analPredSec = analPredSec;
this.resultUrl = resultUrl;
this.detectingCnt = detectingCnt;
this.accuracy = accuracy;
this.analState = analState;
this.analStateNm = analStateNm;
}
}
@Getter
public static class Dashboard {
String classAfterCd;
String classAfterName;
Long classAfterCnt;
public Dashboard(String classAfterCd, Long classAfterCnt) {
this.classAfterCd = classAfterCd;
this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc();
this.classAfterCnt = classAfterCnt;
}
}
@Getter
public static class Detail {
AnalResSummary summary;
List<Dashboard> dashboard;
Long totalCnt;
public Detail(AnalResSummary summary, List<Dashboard> dashboard, Long totalCnt) {
this.summary = summary;
this.dashboard = dashboard;
this.totalCnt = totalCnt;
}
}
// 분석 상세 ROW
@Getter
@AllArgsConstructor
public static class DetailListEntity {
private Uid code;
private Double detectionScore;
private Clazzes compare;
private Clazzes target;
private MapSheet mapSheet;
private Coordinate center;
@JsonFormatDttm private ZonedDateTime updatedDttm;
public DetailListEntity(
UUID uuid,
Double detectionScore,
Clazzes compare,
Clazzes target,
MapSheet mapSheet,
Coordinate center,
ZonedDateTime updatedDttm) {
this.code = new Uid(uuid);
this.detectionScore = detectionScore;
this.compare = compare;
this.target = target;
this.mapSheet = mapSheet;
this.center = center;
this.updatedDttm = updatedDttm;
}
}
@Getter
@AllArgsConstructor
public static class Uid {
private String shortCode;
private String code;
public Uid(UUID uuid) {
if (uuid != null) {
this.shortCode = uuid.toString().substring(0, 8).toUpperCase();
this.code = uuid.toString();
}
}
}
// MAP NO
@Getter
@AllArgsConstructor
public static class MapSheet {
private String number;
private String name;
}
// classification info
@Getter
public static class Clazz {
private String code;
private String name;
@JsonIgnore private Double score;
public Clazz(String code, Double score) {
this.code = code;
this.score = score;
this.name = DetectionClassification.fromString(code).getDesc();
}
public Clazz(String code) {
this.code = code;
this.name = DetectionClassification.fromString(code).getDesc();
}
}
// classification info
@Getter
public static class Clazzes {
private DetectionClassification code;
private String name;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Double score;
private Integer order;
public Clazzes(DetectionClassification classification, Double score) {
this.code = classification;
this.name = classification.getDesc();
this.order = classification.getOrder();
this.score = score;
}
public Clazzes(DetectionClassification classification) {
this.code = classification;
this.name = classification.getDesc();
this.order = classification.getOrder();
}
}
// 좌표 정보 point
@Getter
public static class Coordinate {
private Double lon; // 경도(Longitude)
private Double lat; // 위도(Latitude)
private String srid; // Spatial Reference ID의 약자로, 데이터베이스에서 좌표계를 식별하는 고유 번호 추후enum으로
public Coordinate(Double lon, Double lat) {
this.lon = lon;
this.lat = lat;
this.srid = "EPSG:4326";
}
}
@Getter
public static class Geom {
Integer compareYyyy;
Integer targetYyyy;
String classBeforeCd;
String classBeforeName;
Double classBeforeProb;
String classAfterCd;
String classAfterName;
Double classAfterProb;
Long mapSheetNum;
@JsonIgnore String gemoStr;
@JsonIgnore String geomCenterStr;
JsonNode gemo;
JsonNode geomCenter;
public Geom(
Integer compareYyyy,
Integer targetYyyy,
String classBeforeCd,
Double classBeforeProb,
String classAfterCd,
Double classAfterProb,
Long mapSheetNum,
String gemoStr,
String geomCenterStr) {
this.compareYyyy = compareYyyy;
this.targetYyyy = targetYyyy;
this.classBeforeCd = classBeforeCd;
this.classBeforeName = DetectionClassification.fromString(classBeforeCd).getDesc();
this.classBeforeProb = classBeforeProb;
this.classAfterCd = classAfterCd;
this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc();
this.classAfterProb = classAfterProb;
this.mapSheetNum = mapSheetNum;
this.gemoStr = gemoStr;
this.geomCenterStr = geomCenterStr;
ObjectMapper mapper = new ObjectMapper();
JsonNode geomJson;
JsonNode geomCenterJson;
try {
geomJson = mapper.readTree(gemoStr);
geomCenterJson = mapper.readTree(geomCenterStr);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
this.gemo = geomJson;
this.geomCenter = geomCenterJson;
}
}
@Schema(name = "InferenceResultSearchReq", description = "분석결과 목록 요청 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchReq {
// 검색 조건
private String statCode;
private String title;
// 페이징 파라미터
private int page = 0;
private int size = 20;
public Pageable toPageable() {
return PageRequest.of(page, size);
}
}
@Schema(name = "InferenceResultSearchReq", description = "분석결과 목록 요청 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchGeoReq {
// 기준년도
private String targetClass;
// 비교년도
private String compareClass;
// 분석도엽
private List<Long> mapSheetNum;
// 페이징 파라미터
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);
}
}
}

View File

@@ -1,15 +1,9 @@
package com.kamco.cd.kamcoback.inference.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.common.enums.DetectionClassification;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -17,339 +11,37 @@ 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 InferenceResultDto {
@Schema(name = "InferenceResultBasic", description = "분석결과 기본 정보")
@Getter
public static class Basic {
private Long id;
private String dataName;
private Long mapSheepNum;
private Long detectingCnt;
@JsonFormatDttm private ZonedDateTime analStrtDttm;
@JsonFormatDttm private ZonedDateTime analEndDttm;
private Long analSec;
private String analState;
public Basic(
Long id,
String dataName,
Long mapSheepNum,
Long detectingCnt,
ZonedDateTime analStrtDttm,
ZonedDateTime analEndDttm,
Long analSec,
String analState) {
this.id = id;
this.dataName = dataName;
this.mapSheepNum = mapSheepNum;
this.detectingCnt = detectingCnt;
this.analStrtDttm = analStrtDttm;
this.analEndDttm = analEndDttm;
this.analSec = analSec;
this.analState = analState;
}
}
@Schema(name = "AnalysisResultList", description = "분석결과 목록")
@Getter
public static class AnalResList {
private Long id;
private String analTitle;
private Long detectingCnt;
@JsonFormatDttm private ZonedDateTime analStrtDttm;
@JsonFormatDttm private ZonedDateTime analEndDttm;
private Long analSec;
private Long analPredSec;
private String analState;
private String analStateNm;
private String gukyuinUsed;
public AnalResList(
Long id,
String analTitle,
Long detectingCnt,
ZonedDateTime analStrtDttm,
ZonedDateTime analEndDttm,
Long analSec,
Long analPredSec,
String analState,
String analStateNm,
String gukyuinUsed) {
this.id = id;
this.analTitle = analTitle;
this.detectingCnt = detectingCnt;
this.analStrtDttm = analStrtDttm;
this.analEndDttm = analEndDttm;
this.analSec = analSec;
this.analPredSec = analPredSec;
this.analState = analState;
this.analStateNm = analStateNm;
this.gukyuinUsed = gukyuinUsed;
}
}
@Schema(name = "AnalysisResultSummary", description = "분석결과 요약정보")
@Getter
public static class AnalResSummary {
private Long id;
private String analTitle;
private String modelInfo;
private Integer targetYyyy;
private Integer compareYyyy;
@JsonFormatDttm private ZonedDateTime analStrtDttm;
@JsonFormatDttm private ZonedDateTime analEndDttm;
private Long analSec;
private Long analPredSec;
private String resultUrl;
private Long detectingCnt;
private Double accuracy;
private String analState;
private String analStateNm;
public AnalResSummary(
Long id,
String analTitle,
String modelInfo,
Integer targetYyyy,
Integer compareYyyy,
ZonedDateTime analStrtDttm,
ZonedDateTime analEndDttm,
Long analSec,
Long analPredSec,
String resultUrl,
Long detectingCnt,
Double accuracy,
String analState,
String analStateNm) {
this.id = id;
this.analTitle = analTitle;
this.modelInfo = modelInfo;
this.targetYyyy = targetYyyy;
this.compareYyyy = compareYyyy;
this.analStrtDttm = analStrtDttm;
this.analEndDttm = analEndDttm;
this.analSec = analSec;
this.analPredSec = analPredSec;
this.resultUrl = resultUrl;
this.detectingCnt = detectingCnt;
this.accuracy = accuracy;
this.analState = analState;
this.analStateNm = analStateNm;
}
}
@Getter
public static class Dashboard {
String classAfterCd;
String classAfterName;
Long classAfterCnt;
public Dashboard(String classAfterCd, Long classAfterCnt) {
this.classAfterCd = classAfterCd;
this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc();
this.classAfterCnt = classAfterCnt;
}
}
@Getter
public static class Detail {
AnalResSummary summary;
List<Dashboard> dashboard;
Long totalCnt;
public Detail(AnalResSummary summary, List<Dashboard> dashboard, Long totalCnt) {
this.summary = summary;
this.dashboard = dashboard;
this.totalCnt = totalCnt;
}
}
// 분석 상세 ROW
@Getter
@Setter
@AllArgsConstructor
public static class DetailListEntity {
@NoArgsConstructor
public static class ResultList {
private Uid code;
private Double detectionScore;
private Clazzes compare;
private Clazzes target;
private MapSheet mapSheet;
private Coordinate center;
@JsonFormatDttm private ZonedDateTime updatedDttm;
public DetailListEntity(
UUID uuid,
Double detectionScore,
Clazzes compare,
Clazzes target,
MapSheet mapSheet,
Coordinate center,
ZonedDateTime updatedDttm) {
this.code = new Uid(uuid);
this.detectionScore = detectionScore;
this.compare = compare;
this.target = target;
this.mapSheet = mapSheet;
this.center = center;
this.updatedDttm = updatedDttm;
}
private UUID uuid;
private String title;
private String status;
private String mapSheetCnt;
private Long detectingCnt;
@JsonFormatDttm private ZonedDateTime startTime;
@JsonFormatDttm private ZonedDateTime endTime;
@JsonFormatDttm private ZonedDateTime elapsedTime;
private Boolean applyYn;
@JsonFormatDttm private ZonedDateTime applyDttm;
}
@Getter
@AllArgsConstructor
public static class Uid {
private String shortCode;
private String code;
public Uid(UUID uuid) {
if (uuid != null) {
this.shortCode = uuid.toString().substring(0, 8).toUpperCase();
this.code = uuid.toString();
}
}
}
// MAP NO
@Getter
@AllArgsConstructor
public static class MapSheet {
private String number;
private String name;
}
// classification info
@Getter
public static class Clazz {
private String code;
private String name;
@JsonIgnore private Double score;
public Clazz(String code, Double score) {
this.code = code;
this.score = score;
this.name = DetectionClassification.fromString(code).getDesc();
}
public Clazz(String code) {
this.code = code;
this.name = DetectionClassification.fromString(code).getDesc();
}
}
// classification info
@Getter
public static class Clazzes {
private DetectionClassification code;
private String name;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Double score;
private Integer order;
public Clazzes(DetectionClassification classification, Double score) {
this.code = classification;
this.name = classification.getDesc();
this.order = classification.getOrder();
this.score = score;
}
public Clazzes(DetectionClassification classification) {
this.code = classification;
this.name = classification.getDesc();
this.order = classification.getOrder();
}
}
// 좌표 정보 point
@Getter
public static class Coordinate {
private Double lon; // 경도(Longitude)
private Double lat; // 위도(Latitude)
private String srid; // Spatial Reference ID의 약자로, 데이터베이스에서 좌표계를 식별하는 고유 번호 추후enum으로
public Coordinate(Double lon, Double lat) {
this.lon = lon;
this.lat = lat;
this.srid = "EPSG:4326";
}
}
@Getter
public static class Geom {
Integer compareYyyy;
Integer targetYyyy;
String classBeforeCd;
String classBeforeName;
Double classBeforeProb;
String classAfterCd;
String classAfterName;
Double classAfterProb;
Long mapSheetNum;
@JsonIgnore String gemoStr;
@JsonIgnore String geomCenterStr;
JsonNode gemo;
JsonNode geomCenter;
public Geom(
Integer compareYyyy,
Integer targetYyyy,
String classBeforeCd,
Double classBeforeProb,
String classAfterCd,
Double classAfterProb,
Long mapSheetNum,
String gemoStr,
String geomCenterStr) {
this.compareYyyy = compareYyyy;
this.targetYyyy = targetYyyy;
this.classBeforeCd = classBeforeCd;
this.classBeforeName = DetectionClassification.fromString(classBeforeCd).getDesc();
this.classBeforeProb = classBeforeProb;
this.classAfterCd = classAfterCd;
this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc();
this.classAfterProb = classAfterProb;
this.mapSheetNum = mapSheetNum;
this.gemoStr = gemoStr;
this.geomCenterStr = geomCenterStr;
ObjectMapper mapper = new ObjectMapper();
JsonNode geomJson;
JsonNode geomCenterJson;
try {
geomJson = mapper.readTree(gemoStr);
geomCenterJson = mapper.readTree(geomCenterStr);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
this.gemo = geomJson;
this.geomCenter = geomCenterJson;
}
}
@Schema(name = "InferenceResultSearchReq", description = "분석결과 목록 요청 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchReq {
public static class SearchListReq {
// 검색 조건
private String statCode;
private String applyYn;
private LocalDate strtDttm;
private LocalDate endDttm;
private String title;
// 페이징 파라미터
@@ -361,34 +53,41 @@ public class InferenceResultDto {
}
}
@Schema(name = "InferenceResultSearchReq", description = "분석결과 목록 요청 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchGeoReq {
public enum DetectOption implements EnumType {
ALL("전체"),
PART("부분"),
;
private final String desc;
// 기준년도
private String targetClass;
// 비교년도
private String compareClass;
// 분석도엽
private List<Long> mapSheetNum;
@Override
public String getId() {
return name();
}
// 페이징 파라미터
private int page = 0;
private int size = 20;
private String sort;
@Override
public String getText() {
return desc;
}
}
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));
@Getter
@AllArgsConstructor
public enum MapSheetScope implements EnumType {
EXCL("추론제외"),
PREV("이전 년도 도엽 사용"),
;
private final String desc;
@Override
public String getId() {
return name();
}
return PageRequest.of(page, size);
@Override
public String getText() {
return desc;
}
}
}

View File

@@ -1,72 +0,0 @@
package com.kamco.cd.kamcoback.inference.dto;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import io.swagger.v3.oas.annotations.media.Schema;
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 InferenceResultMngDto {
@Schema(name = "SearchListReq", description = "분석결과 목록 검색 조건")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchListReq {
// 검색 조건
private String applyYn;
private String strtDttm;
private String endDttm;
// 페이징 파라미터
private int page = 0;
private int size = 20;
public Pageable toPageable() {
return PageRequest.of(page, size);
}
}
@Getter
@AllArgsConstructor
public enum DetectOption implements EnumType {
ALL("전체"),
PART("부분"),
;
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@AllArgsConstructor
public enum MapSheetScope implements EnumType {
EXCL("추론제외"),
PREV("이전 년도 도엽 사용"),
;
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
}

View File

@@ -1,10 +0,0 @@
package com.kamco.cd.kamcoback.inference.service;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class InferenceResultMngService {}

View File

@@ -1,9 +1,11 @@
package com.kamco.cd.kamcoback.inference.service;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Detail;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Detail;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
import jakarta.validation.constraints.NotNull;
import java.util.List;
@@ -20,14 +22,13 @@ public class InferenceResultService {
private final InferenceResultCoreService inferenceResultCoreService;
/**
* 추론관리 > 분석결과 목록 조회
* 추론관리 목록
*
* @param searchReq
* @param req
* @return
*/
public Page<InferenceResultDto.AnalResList> getInferenceResultList(
InferenceResultDto.SearchReq searchReq) {
return inferenceResultCoreService.getInferenceResultList(searchReq);
public Page<ResultList> getInferenceResultList(InferenceResultDto.SearchListReq req) {
return inferenceResultCoreService.getInferenceResultList(req);
}
/**
@@ -36,7 +37,7 @@ public class InferenceResultService {
* @param id
* @return
*/
public InferenceResultDto.AnalResSummary getInferenceResultSummary(Long id) {
public InferenceDetailDto.AnalResSummary getInferenceResultSummary(Long id) {
return inferenceResultCoreService.getInferenceResultSummary(id);
}
@@ -56,8 +57,8 @@ public class InferenceResultService {
* @param searchGeoReq
* @return
*/
public Page<InferenceResultDto.Geom> getInferenceResultGeomList(
Long id, InferenceResultDto.SearchGeoReq searchGeoReq) {
public Page<InferenceDetailDto.Geom> getInferenceResultGeomList(
Long id, InferenceDetailDto.SearchGeoReq searchGeoReq) {
return inferenceResultCoreService.getInferenceResultGeomList(id, searchGeoReq);
}
@@ -67,8 +68,8 @@ public class InferenceResultService {
* @param searchReq
* @return
*/
public Page<InferenceResultDto.DetailListEntity> listInferenceResultWithGeom(
@NotNull Long id, InferenceResultDto.SearchGeoReq searchReq) {
public Page<InferenceDetailDto.DetailListEntity> listInferenceResultWithGeom(
@NotNull Long id, InferenceDetailDto.SearchGeoReq searchReq) {
return inferenceResultCoreService.listInferenceResultWithGeom(id, searchReq);
}
@@ -81,7 +82,7 @@ public class InferenceResultService {
*/
public Detail getDetail(Long id) {
// summary
InferenceResultDto.AnalResSummary summary = this.getInferenceResultSummary(id);
InferenceDetailDto.AnalResSummary summary = this.getInferenceResultSummary(id);
// Dashboard
List<Dashboard> dashboards = this.getDashboard(id);

View File

@@ -49,8 +49,7 @@ public class LabelAllocateDto {
WAIT("대기"),
ASSIGNED("배정"),
SKIP("스킵"),
DONE("완료"),
COMPLETE("완료");
DONE("완료");
private String desc;
@@ -202,8 +201,6 @@ public class LabelAllocateDto {
private Integer stage;
private ZonedDateTime gukyuinDttm;
private Long count;
private Long labelCnt;
private Long inspectorCnt;
}
@Getter

View File

@@ -37,7 +37,7 @@ public class LabelWorkDto {
private UUID uuid;
private Integer compareYyyy;
private Integer targetYyyy;
private int stage;
private Integer stage;
@JsonFormatDttm private ZonedDateTime createdDttm;
private Long detectionTotCnt;
private Long labelTotCnt;
@@ -47,6 +47,15 @@ public class LabelWorkDto {
private Long labelCompleteTotCnt;
@JsonFormatDttm private ZonedDateTime labelStartDttm;
// tb_map_sheet_anal_inference.anal_state 컬럼 값
private String analState;
// tb_labeling_assignment 테이블에서 stagnation_yn = 'N'인 정상 진행 건수
private Long normalProgressCnt;
// tb_labeling_assignment 테이블에서 총 배정 건수
private Long totalAssignmentCnt;
@JsonProperty("detectYear")
public String getDetectYear() {
if (compareYyyy == null || targetYyyy == null) {
@@ -55,8 +64,14 @@ public class LabelWorkDto {
return compareYyyy + "-" + targetYyyy;
}
/** 라벨링 상태 반환 (tb_map_sheet_anal_inference.anal_state 기준) */
public String getLabelState() {
// anal_state 값이 있으면 해당 값 사용
if (this.analState != null && !this.analState.isEmpty()) {
return this.analState;
}
// anal_state 값이 없으면 기존 로직으로 폴백
String mngState = "PENDING";
if (this.labelTotCnt == 0) {
@@ -79,14 +94,25 @@ public class LabelWorkDto {
}
LabelMngState type = Enums.fromId(LabelMngState.class, enumId);
// type이 null인 경우 (enum에 정의되지 않은 상태값) 상태값 자체를 반환
if (type == null) {
return enumId;
}
return type.getText();
}
/**
* 작업 진행률 반환 (tb_labeling_assignment.stagnation_yn = 'N'인 정상 진행율 기준) 계산식: (정상 진행 건수 / 총 배정 건수) *
* 100
*/
public double getLabelRate() {
if (this.labelTotCnt == null || this.labelCompleteTotCnt == 0) {
if (this.totalAssignmentCnt == null || this.totalAssignmentCnt == 0) {
return 0.0;
}
return (double) this.labelTotCnt / this.labelCompleteTotCnt * 100.0;
if (this.normalProgressCnt == null) {
return 0.0;
}
return (double) this.normalProgressCnt / this.totalAssignmentCnt * 100.0;
}
}
@@ -168,14 +194,14 @@ public class LabelWorkDto {
@Schema(description = "1일전처리개수")
private Long day1AgoDoneCnt;
public Long getremindCnt() {
public Long getRemainCnt() {
return this.assignedCnt - this.doneCnt;
}
public double getDoneRate() {
Long dayDoneCnt = this.day3AgoDoneCnt + this.day2AgoDoneCnt + this.day1AgoDoneCnt;
long dayDoneCnt = this.day3AgoDoneCnt + this.day2AgoDoneCnt + this.day1AgoDoneCnt;
if (dayDoneCnt == null || dayDoneCnt == 0) {
if (dayDoneCnt == 0) {
return 0.0;
}
return (double) dayDoneCnt / 3;

View File

@@ -12,9 +12,7 @@ import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.TargetUser;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.UserList;
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerListResponse;
import com.kamco.cd.kamcoback.postgres.core.LabelAllocateCoreService;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
@@ -123,7 +121,6 @@ public class LabelAllocateService {
public ApiResponseDto.ResponseObj allocateMove(
Integer totalCnt, String uuid, List<String> targetUsers, String userId) {
Map<String, Integer> result = new LinkedHashMap<>();
int userCount = targetUsers.size();
if (userCount <= 0) {
return new ApiResponseDto.ResponseObj(ApiResponseCode.BAD_REQUEST, "재할당할 라벨러를 선택해주세요.");
@@ -132,6 +129,11 @@ public class LabelAllocateService {
int base = totalCnt / userCount;
int remainder = totalCnt % userCount;
Long lastId = null;
List<Long> allIds =
labelAllocateCoreService.fetchNextMoveIds(lastId, totalCnt.longValue(), uuid, userId);
int index = 0;
for (int i = 0; i < userCount; i++) {
int assignCount = base;
@@ -140,11 +142,15 @@ public class LabelAllocateService {
assignCount += remainder;
}
result.put(targetUsers.get(i), assignCount);
// TODO: 재할당 테이블에 update 까지만 하고 나머지는 배치에서 처리하기?
labelAllocateCoreService.assignOwnerReAllocate(
uuid, userId, targetUsers.get(i), (long) assignCount);
// labelAllocateCoreService.assignOwnerReAllocate(
// uuid, userId, targetUsers.get(i), (long) assignCount);
int end = index + assignCount;
List<Long> sub = allIds.subList(index, end);
labelAllocateCoreService.assignOwnerMove(sub, targetUsers.get(i));
index = end;
}
// Long lastId = null;

View File

@@ -1,7 +1,7 @@
package com.kamco.cd.kamcoback.members.service;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.common.utils.CommonStringUtils;
import com.kamco.cd.kamcoback.common.utils.StringUtils;
import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.postgres.core.MembersCoreService;
import java.util.UUID;
@@ -25,7 +25,7 @@ public class AdminService {
*/
@Transactional
public Long saveMember(MembersDto.AddReq addReq) {
if (!CommonStringUtils.isValidPassword(addReq.getPassword())) {
if (!StringUtils.isValidPassword(addReq.getPassword())) {
throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST);
}

View File

@@ -1,7 +1,7 @@
package com.kamco.cd.kamcoback.members.service;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.common.utils.CommonStringUtils;
import com.kamco.cd.kamcoback.common.utils.StringUtils;
import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
import com.kamco.cd.kamcoback.postgres.core.MembersCoreService;
@@ -37,7 +37,7 @@ public class MembersService {
@Transactional
public void resetPassword(String id, MembersDto.InitReq initReq) {
if (!CommonStringUtils.isValidPassword(initReq.getNewPassword())) {
if (!StringUtils.isValidPassword(initReq.getNewPassword())) {
throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST);
}
membersCoreService.resetPassword(id, initReq);

View File

@@ -1,12 +1,16 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
import com.kamco.cd.kamcoback.postgres.entity.MapInkx5kEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetAnalDataInferenceRepository;
import com.kamco.cd.kamcoback.postgres.repository.Inference.MapSheetLearnRepository;
import com.kamco.cd.kamcoback.postgres.repository.scene.MapInkx5kRepository;
import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.constraints.NotNull;
@@ -21,27 +25,30 @@ import org.springframework.transaction.annotation.Transactional;
public class InferenceResultCoreService {
private final MapSheetAnalDataInferenceRepository mapSheetAnalDataRepository;
private final MapSheetLearnRepository mapSheetLearnRepository;
private final MapInkx5kRepository mapInkx5kRepository;
/**
* 추론관리 > 분석결과 목록 조회
* 추론관리 목록
*
* @param searchReq
* @param req
* @return
*/
public Page<InferenceResultDto.AnalResList> getInferenceResultList(
InferenceResultDto.SearchReq searchReq) {
return mapSheetAnalDataRepository.getInferenceResultList(searchReq);
public Page<ResultList> getInferenceResultList(InferenceResultDto.SearchListReq req) {
Page<MapSheetLearnEntity> list = mapSheetLearnRepository.getInferenceMgnResultList(req);
return list.map(MapSheetLearnEntity::toDto);
}
/****/
/**
* 분석결과 요약정보
*
* @param id
* @return
*/
public InferenceResultDto.AnalResSummary getInferenceResultSummary(Long id) {
InferenceResultDto.AnalResSummary summary =
public InferenceDetailDto.AnalResSummary getInferenceResultSummary(Long id) {
InferenceDetailDto.AnalResSummary summary =
mapSheetAnalDataRepository
.getInferenceResultSummary(id)
.orElseThrow(() -> new EntityNotFoundException("요약정보를 찾을 수 없습니다. " + id));
@@ -64,8 +71,8 @@ public class InferenceResultCoreService {
* @param searchGeoReq
* @return
*/
public Page<InferenceResultDto.Geom> getInferenceResultGeomList(
Long id, InferenceResultDto.SearchGeoReq searchGeoReq) {
public Page<InferenceDetailDto.Geom> getInferenceResultGeomList(
Long id, InferenceDetailDto.SearchGeoReq searchGeoReq) {
return mapSheetAnalDataRepository.getInferenceGeomList(id, searchGeoReq);
}
@@ -76,8 +83,8 @@ public class InferenceResultCoreService {
* @return
*/
@Transactional(readOnly = true)
public Page<InferenceResultDto.DetailListEntity> listInferenceResultWithGeom(
@NotNull Long analyId, InferenceResultDto.SearchGeoReq searchReq) {
public Page<InferenceDetailDto.DetailListEntity> listInferenceResultWithGeom(
@NotNull Long analyId, InferenceDetailDto.SearchGeoReq searchReq) {
// 분석 ID 에 해당하는 dataids를 가져온다.
List<Long> dataIds =
mapSheetAnalDataRepository.listAnalyGeom(analyId).stream()

View File

@@ -1,8 +0,0 @@
package com.kamco.cd.kamcoback.postgres.core;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class InferenceResultMngCoreService {}

View File

@@ -87,15 +87,8 @@ public class LabelAllocateCoreService {
return labelAllocateRepository.findInferenceDetail(uuid);
}
public List<Long> fetchNextMoveIds(
Long lastId,
Long batchSize,
Integer compareYyyy,
Integer targetYyyy,
Integer stage,
String userId) {
return labelAllocateRepository.fetchNextMoveIds(
lastId, batchSize, compareYyyy, targetYyyy, stage, userId);
public List<Long> fetchNextMoveIds(Long lastId, Long batchSize, String uuid, String userId) {
return labelAllocateRepository.fetchNextMoveIds(lastId, batchSize, uuid, userId);
}
public void assignOwnerMove(List<Long> sub, String userId) {

View File

@@ -3,7 +3,7 @@ package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.common.enums.CommonUseStatus;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ApiResponseCode;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ResponseObj;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.postgres.entity.MapInkx50kEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapInkx5kEntity;
import com.kamco.cd.kamcoback.postgres.repository.scene.MapInkx50kRepository;

View File

@@ -3,7 +3,7 @@ package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.common.enums.StatusType;
import com.kamco.cd.kamcoback.common.enums.error.AuthErrorCode;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.common.utils.CommonStringUtils;
import com.kamco.cd.kamcoback.common.utils.StringUtils;
import com.kamco.cd.kamcoback.common.utils.UserUtil;
import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.AddReq;
@@ -17,7 +17,6 @@ import com.kamco.cd.kamcoback.postgres.repository.members.MembersRepository;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
@@ -43,7 +42,7 @@ public class MembersCoreService {
}
// 패스워드 암호화, 초기 패스워드 고정
String hashedPassword = CommonStringUtils.hashPassword(addReq.getPassword());
String hashedPassword = StringUtils.hashPassword(addReq.getPassword());
MemberEntity memberEntity = new MemberEntity();
memberEntity.setUserId(addReq.getEmployeeNo());
@@ -67,22 +66,22 @@ public class MembersCoreService {
MemberEntity memberEntity =
membersRepository.findByUUID(uuid).orElseThrow(MemberNotFoundException::new);
if (StringUtils.isNotBlank(updateReq.getName())) {
if (org.apache.commons.lang3.StringUtils.isNotBlank(updateReq.getName())) {
memberEntity.setName(updateReq.getName());
}
if (StringUtils.isNotBlank(updateReq.getStatus())) {
if (org.apache.commons.lang3.StringUtils.isNotBlank(updateReq.getStatus())) {
memberEntity.changeStatus(updateReq.getStatus());
}
if (StringUtils.isNotBlank(updateReq.getPassword())) {
if (org.apache.commons.lang3.StringUtils.isNotBlank(updateReq.getPassword())) {
// 패스워드 유효성 검사
if (!CommonStringUtils.isValidPassword(updateReq.getPassword())) {
if (!StringUtils.isValidPassword(updateReq.getPassword())) {
throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST);
}
String password = CommonStringUtils.hashPassword(updateReq.getPassword());
String password = StringUtils.hashPassword(updateReq.getPassword());
memberEntity.setStatus(StatusType.PENDING.getId());
memberEntity.setLoginFailCount(0);
@@ -107,7 +106,7 @@ public class MembersCoreService {
throw new CustomApiException(AuthErrorCode.LOGIN_PASSWORD_MISMATCH);
}
String password = CommonStringUtils.hashPassword(initReq.getNewPassword());
String password = StringUtils.hashPassword(initReq.getNewPassword());
memberEntity.setPassword(password);
memberEntity.setStatus(StatusType.ACTIVE.getId());

View File

@@ -1,6 +1,6 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;

View File

@@ -1,8 +1,8 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.common.enums.CommonUseStatus;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
import com.kamco.cd.kamcoback.scene.dto.MapInkxMngDto.MapListEntity;
import jakarta.persistence.Column;
@@ -71,7 +71,7 @@ public class MapInkx5kEntity extends CommonDateEntity {
this.useInference = useInference;
}
public InferenceResultDto.MapSheet toEntity() {
public InferenceDetailDto.MapSheet toEntity() {
return new MapSheet(mapidcdNo, mapidNm);
}

View File

@@ -1,8 +1,8 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.common.enums.DetectionClassification;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Clazzes;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Clazzes;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
@@ -160,20 +160,20 @@ public class MapSheetAnalDataInferenceGeomEntity {
@JoinColumn(name = "map_5k_id", referencedColumnName = "fid")
private MapInkx5kEntity map5k;
public InferenceResultDto.DetailListEntity toEntity() {
public InferenceDetailDto.DetailListEntity toEntity() {
DetectionClassification classification = DetectionClassification.fromString(classBeforeCd);
Clazzes comparedClazz = new Clazzes(classification, classBeforeProb);
DetectionClassification classification1 = DetectionClassification.fromString(classAfterCd);
Clazzes targetClazz = new Clazzes(classification1, classAfterProb);
InferenceResultDto.MapSheet mapSheet = map5k != null ? map5k.toEntity() : null;
InferenceDetailDto.MapSheet mapSheet = map5k != null ? map5k.toEntity() : null;
InferenceResultDto.Coordinate coordinate = null;
InferenceDetailDto.Coordinate coordinate = null;
if (geomCenter != null) {
org.locationtech.jts.geom.Point point = (org.locationtech.jts.geom.Point) geomCenter;
coordinate = new InferenceResultDto.Coordinate(point.getX(), point.getY());
coordinate = new InferenceDetailDto.Coordinate(point.getX(), point.getY());
}
return new InferenceResultDto.DetailListEntity(
return new InferenceDetailDto.DetailListEntity(
uuid, cdProb, comparedClazz, targetClazz, mapSheet, coordinate, createdDttm);
}
}

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
@@ -7,6 +8,7 @@ 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;
@@ -31,7 +33,16 @@ public class MapSheetLearnEntity {
@ColumnDefault("gen_random_uuid()")
@Column(name = "uuid")
private UUID uuid = UUID.randomUUID();
private UUID uuid;
@Size(max = 200)
@NotNull
@Column(name = "title", nullable = false, length = 200)
private String title;
@Size(max = 10)
@Column(name = "status", length = 10)
private String status;
@Column(name = "m1_model_uid")
private Long m1ModelUid;
@@ -52,6 +63,10 @@ public class MapSheetLearnEntity {
@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;
@@ -59,6 +74,12 @@ public class MapSheetLearnEntity {
@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;
@@ -70,7 +91,7 @@ public class MapSheetLearnEntity {
@ColumnDefault("now()")
@Column(name = "created_dttm")
private ZonedDateTime createdDttm = ZonedDateTime.now();
private ZonedDateTime createdDttm;
@Column(name = "created_uid")
private Long createdUid;
@@ -81,4 +102,18 @@ public class MapSheetLearnEntity {
@Column(name = "updated_uid")
private Long updatedUid;
public InferenceResultDto.ResultList toDto() {
return new InferenceResultDto.ResultList(
this.uuid,
this.title,
this.status,
this.mapSheetCnt,
this.detectingCnt,
this.inferStartDttm,
this.inferEndDttm,
this.elapsedTime,
this.applyYn,
this.applyDttm);
}
}

View File

@@ -1,8 +0,0 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
public interface InferenceResultMngRepositoryCustom {
MapSheetLearnEntity getInferenceMgnResultList();
}

View File

@@ -1,18 +0,0 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class InferenceResultMngRepositoryImpl implements InferenceResultMngRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public MapSheetLearnEntity getInferenceMgnResultList() {
return null;
}
}

View File

@@ -1,10 +1,10 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.AnalResList;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.AnalResSummary;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SearchGeoReq;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.AnalResList;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.AnalResSummary;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.SearchGeoReq;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import jakarta.validation.constraints.NotNull;
@@ -14,12 +14,12 @@ import org.springframework.data.domain.Page;
public interface MapSheetAnalDataInferenceRepositoryCustom {
Page<AnalResList> getInferenceResultList(InferenceResultDto.SearchReq searchReq);
Page<AnalResList> getInferenceResultList(InferenceDetailDto.SearchReq searchReq);
Optional<AnalResSummary> getInferenceResultSummary(Long id);
Page<InferenceResultDto.Geom> getInferenceGeomList(
Long id, InferenceResultDto.SearchGeoReq searchGeoReq);
Page<InferenceDetailDto.Geom> getInferenceGeomList(
Long id, InferenceDetailDto.SearchGeoReq searchGeoReq);
Page<MapSheetAnalDataInferenceGeomEntity> listInferenceResultWithGeom(
List<Long> dataIds, SearchGeoReq searchReq);

View File

@@ -2,11 +2,11 @@ package com.kamco.cd.kamcoback.postgres.repository.Inference;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.AnalResList;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.AnalResSummary;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SearchGeoReq;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.AnalResList;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.AnalResSummary;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Dashboard;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.SearchGeoReq;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceEntity;
@@ -58,7 +58,7 @@ public class MapSheetAnalDataInferenceRepositoryImpl
* @return
*/
@Override
public Page<AnalResList> getInferenceResultList(InferenceResultDto.SearchReq searchReq) {
public Page<AnalResList> getInferenceResultList(InferenceDetailDto.SearchReq searchReq) {
Pageable pageable = searchReq.toPageable();
// "0000" 전체조회
BooleanBuilder builder = new BooleanBuilder();
@@ -75,7 +75,7 @@ public class MapSheetAnalDataInferenceRepositoryImpl
queryFactory
.select(
Projections.constructor(
InferenceResultDto.AnalResList.class,
InferenceDetailDto.AnalResList.class,
mapSheetAnalInferenceEntity.id,
mapSheetAnalInferenceEntity.analTitle,
mapSheetAnalInferenceEntity.detectingCnt,
@@ -117,12 +117,12 @@ public class MapSheetAnalDataInferenceRepositoryImpl
JPQLQuery<Long> latestVerUidSub =
JPAExpressions.select(tmv.id.max()).from(tmv).where(tmv.modelUid.eq(tmm.id));
Optional<InferenceResultDto.AnalResSummary> content =
Optional<InferenceDetailDto.AnalResSummary> content =
Optional.ofNullable(
queryFactory
.select(
Projections.constructor(
InferenceResultDto.AnalResSummary.class,
InferenceDetailDto.AnalResSummary.class,
mapSheetAnalInferenceEntity.id,
mapSheetAnalInferenceEntity.analTitle,
tmm.modelNm.concat(" ").concat(tmv.modelVer).as("modelInfo"),
@@ -228,7 +228,7 @@ public class MapSheetAnalDataInferenceRepositoryImpl
* @return
*/
@Override
public Page<InferenceResultDto.Geom> getInferenceGeomList(Long id, SearchGeoReq searchGeoReq) {
public Page<InferenceDetailDto.Geom> getInferenceGeomList(Long id, SearchGeoReq searchGeoReq) {
Pageable pageable = searchGeoReq.toPageable();
BooleanBuilder builder = new BooleanBuilder();
@@ -257,11 +257,11 @@ public class MapSheetAnalDataInferenceRepositoryImpl
builder.and(MapSheetAnalDataInferenceGeomEntity.mapSheetNum.in(mapSheetNum));
}
List<InferenceResultDto.Geom> content =
List<InferenceDetailDto.Geom> content =
queryFactory
.select(
Projections.constructor(
InferenceResultDto.Geom.class,
InferenceDetailDto.Geom.class,
MapSheetAnalDataInferenceGeomEntity.compareYyyy,
MapSheetAnalDataInferenceGeomEntity.targetYyyy,
MapSheetAnalDataInferenceGeomEntity.classBeforeCd,

View File

@@ -3,5 +3,5 @@ package com.kamco.cd.kamcoback.postgres.repository.Inference;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface InferenceResultMngRepository
extends JpaRepository<MapSheetLearnEntity, Long>, InferenceResultMngRepositoryCustom {}
public interface MapSheetLearnRepository
extends JpaRepository<MapSheetLearnEntity, Long>, MapSheetLearnRepositoryCustom {}

View File

@@ -0,0 +1,10 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import org.springframework.data.domain.Page;
public interface MapSheetLearnRepositoryCustom {
Page<MapSheetLearnEntity> getInferenceMgnResultList(InferenceResultDto.SearchListReq req);
}

View File

@@ -0,0 +1,76 @@
package com.kamco.cd.kamcoback.postgres.repository.Inference;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import com.kamco.cd.kamcoback.common.utils.DateRange;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class MapSheetLearnRepositoryImpl implements MapSheetLearnRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public Page<MapSheetLearnEntity> getInferenceMgnResultList(InferenceResultDto.SearchListReq req) {
Pageable pageable = req.toPageable();
BooleanBuilder builder = new BooleanBuilder();
NumberExpression<Integer> statusOrder =
new CaseBuilder().when(mapSheetLearnEntity.status.eq("Y")).then(0).otherwise(1);
// 국유인 반영 여부
if (StringUtils.isNotBlank(req.getApplyYn())) {
if ("Y".equalsIgnoreCase(req.getApplyYn())) {
builder.and(mapSheetLearnEntity.applyYn.isTrue());
} else if ("N".equalsIgnoreCase(req.getApplyYn())) {
builder.and(mapSheetLearnEntity.applyYn.isNull().or(mapSheetLearnEntity.applyYn.isFalse()));
}
}
// 국유인 반영일
if (req.getStrtDttm() != null && req.getEndDttm() != null) {
builder.and(
mapSheetLearnEntity
.applyDttm
.goe(DateRange.start(req.getStrtDttm()))
.and(mapSheetLearnEntity.applyDttm.lt(DateRange.end(req.getEndDttm()))));
}
// 제목
if (StringUtils.isNotBlank(req.getTitle())) {
builder.and(mapSheetLearnEntity.title.equalsIgnoreCase(req.getTitle()));
}
List<MapSheetLearnEntity> content =
queryFactory
.select(mapSheetLearnEntity)
.from(mapSheetLearnEntity)
.where(builder)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(statusOrder.asc())
.fetch();
Long total =
queryFactory
.select(mapSheetLearnEntity.count())
.from(mapSheetLearnEntity)
.where(builder)
.fetchOne();
return new PageImpl<>(content, pageable, total == null ? 0L : total);
}
}

View File

@@ -54,13 +54,7 @@ public interface LabelAllocateRepositoryCustom {
InferenceDetail findInferenceDetail(String uuid);
List<Long> fetchNextMoveIds(
Long lastId,
Long batchSize,
Integer compareYyyy,
Integer targetYyyy,
Integer stage,
String userId);
List<Long> fetchNextMoveIds(Long lastId, Long batchSize, String uuid, String userId);
void assignOwnerMove(List<Long> sub, String userId);

View File

@@ -7,6 +7,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceG
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity;
import com.kamco.cd.kamcoback.common.enums.StatusType;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.AllocateInfoDto;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail;
@@ -493,38 +494,35 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
Projections.constructor(
InferenceDetail.class,
mapSheetAnalInferenceEntity.analTitle,
Expressions.numberTemplate(Integer.class, "{0}", 4),
mapSheetAnalInferenceEntity.stage,
mapSheetAnalInferenceEntity.gukyuinApplyDttm,
mapSheetAnalInferenceEntity.detectingCnt,
labelingAssignmentEntity.workerUid.countDistinct(),
labelingAssignmentEntity.inspectorUid.countDistinct()))
mapSheetAnalDataInferenceGeomEntity.geoUid.count()))
.from(mapSheetAnalInferenceEntity)
.innerJoin(labelingAssignmentEntity)
.on(mapSheetAnalInferenceEntity.id.eq(labelingAssignmentEntity.analUid))
.leftJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
mapSheetAnalInferenceEntity.compareYyyy.eq(
mapSheetAnalDataInferenceGeomEntity.compareYyyy),
mapSheetAnalInferenceEntity.targetYyyy.eq(
mapSheetAnalDataInferenceGeomEntity.targetYyyy),
mapSheetAnalInferenceEntity.stage.eq(mapSheetAnalDataInferenceGeomEntity.stage),
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0),
mapSheetAnalDataInferenceGeomEntity.passYn.isFalse())
.where(mapSheetAnalInferenceEntity.id.eq(analEntity.getId()))
.groupBy(
mapSheetAnalInferenceEntity.analTitle,
mapSheetAnalInferenceEntity.stage,
mapSheetAnalInferenceEntity.gukyuinApplyDttm,
mapSheetAnalInferenceEntity.detectingCnt)
.fetchOne();
}
@Override
public List<Long> fetchNextMoveIds(
Long lastId,
Long batchSize,
Integer compareYyyy,
Integer targetYyyy,
Integer stage,
String userId) {
public List<Long> fetchNextMoveIds(Long lastId, Long batchSize, String uuid, String userId) {
MapSheetAnalInferenceEntity analEntity =
queryFactory
.selectFrom(mapSheetAnalInferenceEntity)
.where(
mapSheetAnalInferenceEntity.compareYyyy.eq(compareYyyy),
mapSheetAnalInferenceEntity.targetYyyy.eq(targetYyyy),
mapSheetAnalInferenceEntity.stage.eq(stage))
.where(mapSheetAnalInferenceEntity.uuid.eq(UUID.fromString(uuid)))
.fetchOne();
if (Objects.isNull(analEntity)) {
@@ -539,7 +537,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
labelingAssignmentEntity.workState.eq(LabelState.ASSIGNED.getId()),
labelingAssignmentEntity.analUid.eq(analEntity.getId()),
lastId == null ? null : labelingAssignmentEntity.inferenceGeomUid.gt(lastId))
.orderBy(labelingAssignmentEntity.inferenceGeomUid.asc())
.orderBy(
labelingAssignmentEntity.assignGroupId.asc(),
labelingAssignmentEntity.inferenceGeomUid.asc())
.limit(batchSize)
.fetch();
}
@@ -574,7 +574,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
NumberExpression<Long> completeCnt =
new CaseBuilder()
.when(labelingAssignmentEntity.workState.eq(LabelState.COMPLETE.getId()))
.when(labelingAssignmentEntity.workState.eq(LabelState.DONE.getId()))
.then(1L)
.otherwise((Long) null)
.count();
@@ -1050,7 +1050,7 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.when(
labelingAssignmentEntity
.workState
.eq(LabelState.COMPLETE.getId())
.eq(LabelState.DONE.getId())
.or(labelingAssignmentEntity.workState.eq(LabelState.SKIP.getId())))
.then(1L)
.otherwise(0L)
@@ -1103,7 +1103,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
percent))
.from(labelingAssignmentEntity)
.innerJoin(memberEntity)
.on(labelingAssignmentEntity.workerUid.eq(memberEntity.employeeNo))
.on(
labelingAssignmentEntity.workerUid.eq(memberEntity.employeeNo),
memberEntity.status.eq(StatusType.ACTIVE.getId()))
.where(
labelingAssignmentEntity.analUid.eq(analEntity.getId()),
labelingAssignmentEntity.workerUid.ne(userId))
@@ -1112,7 +1114,10 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
completeCnt
.multiply(2)
.goe(totalCnt)) // 진행률 평균 이상인 것들만 조회 => percent 를 바로 쓰면 having절에 무리가 갈 수 있다고 함
.orderBy(completeCnt.desc()) // TODO: 도엽번호? PNU? 로 정렬하여 보여주기?
.orderBy(
completeCnt
.desc()) // TODO: 현재는 잔여건수가 제일 적은(=완료건수가 높은) 순서로 desc, 추후 도엽번호? PNU? 로 정렬하여
// 보여주기?
.fetch();
return new MoveInfo(userChargeCnt, list);

View File

@@ -1,19 +1,18 @@
package com.kamco.cd.kamcoback.postgres.repository.label;
import static com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity.labelingAssignmentEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceEntity.mapSheetAnalDataInferenceEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelState;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMng;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.LabelWorkMngDetail;
import com.kamco.cd.kamcoback.label.dto.LabelWorkDto.WorkerState;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.QLabelingAssignmentEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalInferenceEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
@@ -21,46 +20,48 @@ import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.core.types.dsl.DateTimePath;
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.JPAExpressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;
@Slf4j
@Repository
public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
implements LabelWorkRepositoryCustom {
@RequiredArgsConstructor
public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");
@PersistenceContext private EntityManager em;
// Q클래스 필드 선언
private final QMapSheetAnalInferenceEntity mapSheetAnalInferenceEntity =
QMapSheetAnalInferenceEntity.mapSheetAnalInferenceEntity;
private final QMapSheetAnalDataInferenceEntity mapSheetAnalDataInferenceEntity =
QMapSheetAnalDataInferenceEntity.mapSheetAnalDataInferenceEntity;
private final QMapSheetAnalDataInferenceGeomEntity mapSheetAnalDataInferenceGeomEntity =
QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
private final QLabelingAssignmentEntity labelingAssignmentEntity =
QLabelingAssignmentEntity.labelingAssignmentEntity;
private final QMemberEntity memberEntity = QMemberEntity.memberEntity;
public LabelWorkRepositoryImpl(JPAQueryFactory queryFactory) {
super(MapSheetAnalDataInferenceGeomEntity.class);
this.queryFactory = queryFactory;
}
/**
* 변화탐지 년도 셀렉트박스 조회
*
* @return
* @return 변화탐지 년도 목록 (Entity 반환)
*/
@Override
public List<MapSheetAnalInferenceEntity> findChangeDetectYearList() {
@@ -81,9 +82,10 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
/**
* 라벨링 작업관리 목록 조회
* (복잡한 집계 쿼리로 인해 DTO 직접 반환)
*
* @param searchReq
* @return
* @param searchReq 검색 조건
* @return 라벨링 작업관리 목록 페이지
*/
@Override
public Page<LabelWorkMng> labelWorkMngList(LabelWorkDto.LabelWorkMngSearchReq searchReq) {
@@ -129,6 +131,37 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
.and(mapSheetAnalDataInferenceGeomEntity.labelStateDttm.lt(end)));
}
// labelTotCnt: pnu가 있고 pass_yn = false (부적합)인 건수만 라벨링 대상
NumberExpression<Long> labelTotCntExpr =
new CaseBuilder()
.when(
mapSheetAnalDataInferenceGeomEntity
.pnu
.isNotNull()
.and(mapSheetAnalDataInferenceGeomEntity.pnu.ne(0L))
.and(mapSheetAnalDataInferenceGeomEntity.passYn.eq(Boolean.FALSE)))
.then(1L)
.otherwise(0L)
.sum();
// stagnation_yn = 'N'인 정상 진행 건수 (서브쿼리로 별도 집계)
Expression<Long> normalProgressCntSubQuery =
JPAExpressions.select(
new CaseBuilder()
.when(labelingAssignmentEntity.stagnationYn.eq('N'))
.then(1L)
.otherwise(0L)
.sum()
.coalesce(0L))
.from(labelingAssignmentEntity)
.where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id));
// 총 배정 건수 (서브쿼리로 별도 집계)
Expression<Long> totalAssignmentCntSubQuery =
JPAExpressions.select(labelingAssignmentEntity.count().coalesce(0L))
.from(labelingAssignmentEntity)
.where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id));
List<LabelWorkMng> foundContent =
queryFactory
.select(
@@ -138,17 +171,11 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
mapSheetAnalInferenceEntity.compareYyyy,
mapSheetAnalInferenceEntity.targetYyyy,
mapSheetAnalInferenceEntity.stage,
mapSheetAnalDataInferenceEntity.createdDttm.min(),
// createdDttm: tb_map_sheet_anal_inference.created_dttm 사용
mapSheetAnalInferenceEntity.createdDttm,
mapSheetAnalDataInferenceGeomEntity.dataUid.count(),
new CaseBuilder()
.when(
mapSheetAnalDataInferenceGeomEntity
.pnu
.isNotNull()
.and(mapSheetAnalDataInferenceGeomEntity.pnu.ne(0L)))
.then(1L)
.otherwise(0L)
.sum(),
// labelTotCnt: pnu 있고 pass_yn = false인 건수
labelTotCntExpr,
new CaseBuilder()
.when(mapSheetAnalDataInferenceGeomEntity.labelState.eq("ASSIGNED"))
.then(1L)
@@ -169,7 +196,13 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
.then(1L)
.otherwise(0L)
.sum(),
mapSheetAnalDataInferenceGeomEntity.labelStateDttm.min()))
mapSheetAnalDataInferenceGeomEntity.labelStateDttm.min(),
// analState: tb_map_sheet_anal_inference.anal_state
mapSheetAnalInferenceEntity.analState,
// normalProgressCnt: stagnation_yn = 'N'인 건수 (서브쿼리)
normalProgressCntSubQuery,
// totalAssignmentCnt: 총 배정 건수 (서브쿼리)
totalAssignmentCntSubQuery))
.from(mapSheetAnalInferenceEntity)
.innerJoin(mapSheetAnalDataInferenceEntity)
.on(whereSubDataBuilder)
@@ -180,7 +213,10 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
mapSheetAnalInferenceEntity.uuid,
mapSheetAnalInferenceEntity.compareYyyy,
mapSheetAnalInferenceEntity.targetYyyy,
mapSheetAnalInferenceEntity.stage)
mapSheetAnalInferenceEntity.stage,
mapSheetAnalInferenceEntity.createdDttm,
mapSheetAnalInferenceEntity.analState,
mapSheetAnalInferenceEntity.id)
.orderBy(
mapSheetAnalInferenceEntity.targetYyyy.desc(),
mapSheetAnalInferenceEntity.compareYyyy.desc(),
@@ -189,7 +225,9 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
.limit(pageable.getPageSize())
.fetch();
Long total =
// Count 쿼리 별도 실행 (null safe handling)
long total =
Optional.ofNullable(
queryFactory
.select(mapSheetAnalInferenceEntity.uuid.countDistinct())
.from(mapSheetAnalInferenceEntity)
@@ -198,7 +236,8 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(whereSubBuilder)
.where(whereBuilder)
.fetchOne();
.fetchOne())
.orElse(0L);
return new PageImpl<>(foundContent, pageable, total);
}
@@ -295,7 +334,9 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
.limit(pageable.getPageSize())
.fetch();
Long countQuery =
// Count 쿼리 별도 실행 (null safe handling)
long countQuery =
Optional.ofNullable(
queryFactory
.select(labelingAssignmentEntity.workerUid.countDistinct())
.from(labelingAssignmentEntity)
@@ -308,8 +349,8 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
.innerJoin(memberEntity)
.on(whereSubBuilder)
.where(whereBuilder)
// .groupBy(memberEntity.userRole, memberEntity.name, memberEntity.userId)
.fetchOne();
.fetchOne())
.orElse(0L);
return new PageImpl<>(foundContent, pageable, countQuery);
}
@@ -350,9 +391,10 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
/**
* 작업배정 상세조회
* (복잡한 집계 쿼리로 인해 DTO 직접 반환)
*
* @param uuid
* @return
* @param uuid 작업배정 UUID
* @return 작업배정 상세 정보
*/
@Override
public LabelWorkMngDetail findLabelWorkMngDetail(UUID uuid) {
@@ -394,19 +436,13 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport
}
public NumberExpression<Long> caseSumExpression(BooleanExpression condition) {
NumberExpression<Long> sumExp = new CaseBuilder().when(condition).then(1L).otherwise(0L).sum();
return sumExp;
return new CaseBuilder().when(condition).then(1L).otherwise(0L).sum();
}
public BooleanExpression fromDateEqExpression(DateTimePath<ZonedDateTime> path, int addDayCnt) {
LocalDate threeDaysAgo = LocalDate.now().plusDays(addDayCnt);
String toDate = threeDaysAgo.format(DateTimeFormatter.ofPattern("YYYY-MM-DD"));
String toDate = threeDaysAgo.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
BooleanExpression condition =
Expressions.stringTemplate("to_char({0}, 'YYYY-MM-DD')", path).eq(toDate);
return condition;
return Expressions.stringTemplate("to_char({0}, 'yyyy-MM-dd')", path).eq(toDate);
}
}

View File

@@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.JsonNode;
import com.kamco.cd.kamcoback.common.enums.ApiConfigEnum.EnumDto;
import com.kamco.cd.kamcoback.common.enums.CommonUseStatus;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.EntityNotFoundException;
import java.time.ZoneId;
@@ -62,8 +62,8 @@ public class MapInkxMngDto {
@Schema(name = "MapListEntity", description = "목록 항목")
public static class MapListEntity {
private InferenceResultDto.MapSheet scene50k;
private InferenceResultDto.MapSheet scene5k;
private InferenceDetailDto.MapSheet scene50k;
private InferenceDetailDto.MapSheet scene5k;
private CommonUseStatus useInference;
@JsonFormat(
@@ -85,8 +85,8 @@ public class MapInkxMngDto {
@Builder
public MapListEntity(
InferenceResultDto.MapSheet scene50k,
InferenceResultDto.MapSheet scene5k,
InferenceDetailDto.MapSheet scene50k,
InferenceDetailDto.MapSheet scene5k,
CommonUseStatus useInference,
ZonedDateTime createdDttm,
ZonedDateTime updatedDttm) {

View File

@@ -2,7 +2,7 @@ package com.kamco.cd.kamcoback.scene.service;
import com.kamco.cd.kamcoback.common.enums.CommonUseStatus;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ResponseObj;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheet;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import com.kamco.cd.kamcoback.postgres.core.MapInkxMngCoreService;
import com.kamco.cd.kamcoback.scene.dto.MapInkxMngDto;
import com.kamco.cd.kamcoback.scene.dto.MapInkxMngDto.MapList;

View File

@@ -68,7 +68,7 @@ management:
file:
#sync-root-dir: D:/app/original-images/
sync-root-dir: /app/original-images/
sync-tmp-dir: ${file.sync-root-dir}tmp/
sync-tmp-dir: ${file.sync-root-dir}/tmp
sync-file-extention: tfw,tif
#dataset-dir: D:/app/dataset/

View File

@@ -0,0 +1,97 @@
# create job
POST /jobs HTTP/1.1
Host: 10.100.0.11:8000
accept: application/json
Content-Type: application/json
Content-Length: 565
{
"pred_requests_areas": {
"input1_year": 2023,
"input2_year": 2024,
"input1_scene_path": "/kamco-nfs/requests/2023_local.geojson",
"input2_scene_path": "/kamco-nfs/requests/2024_local.geojson"
},
"model_version": "v1-2-test",
"cd_model_path": "/data/shared-dir/cd-checkpoints/v5/cdv5-model3/best_changed_fscore_epoch_11.pth",
"cd_model_config": "/data/shared-dir/cd-checkpoints/v5/cdv5-model3/rvsa-base-and-large-unet-mae-kamco.py",
"cls_model_path": "",
"cls_model_version": "v1-2-test",
"cd_model_type": "G1",
"priority": 0
}
//G1
//{
// "pred_requests_areas": {
// "input1_year": "2022",
// "input2_year": "2023",
// "areas": ["34602060", "35615072", "35813026"]
// },
// "model_version": "v1-2-test",
// "cd_model_path": "/data/shared-dir/cd-checkpoints/sample-checkpoints/G1/best_changed_fscore_epoch_53.pth",
z// "cd_model_path": "/kamco-nfs/ckpt/cd-checkpoints/sample-checkpoints/G1/best_changed_fscore_epoch_53.pth",
// "cd_model_config": "/data/shared-dir/cd-checkpoints/sample-checkpoints/G1/rvsa-base-and-large-unet-mae-kamco.py",
// "cls_model_path": "",
// "cls_model_version": "v1-2-test",
// "cd_model_type": "G1",
// "priority": 0
//}
//G2:
//{
//"pred_requests_areas": {
//"input1_year": "2022",
//"input2_year": "2023",
//"areas": ["34602060", "35615072", "35813026"]
//},
//"model_version": "v1-2-test",
//"cd_model_path": "/data/shared-dir/cd-checkpoints/sample-checkpoints/G2/best_changed_fscore_epoch_40.pth",
//"cd_model_config": "/data/shared-dir/cd-checkpoints/sample-checkpoints/G2/rvsa-base-and-large-unet-mae-kamco.py",
//"cls_model_path": "",
//"cls_model_version": "v1-2-test",
//"cd_model_type": "G2",
//"priority": 0
//}
//
//G3:
//{
//"pred_requests_areas": {
//"input1_year": "2022",
//"input2_year": "2023",
//"areas": ["34602060", "35615072", "35813026"]
//},
//"model_version": "v1-2-test",
//"cd_model_path": "/data/shared-dir/cd-checkpoints/sample-checkpoints/G3/best_changed_fscore_epoch_13.pth",
//"cd_model_config": "/data/shared-dir/cd-checkpoints/sample-checkpoints/G3/rvsa-base-and-large-unet-mae-kamco.py",
//"cls_model_path": "",
//"cls_model_version": "v1-2-test",
//"cd_model_type": "G3",
//"priority": 0
//}
//2. GET BATCH
GET /batches/188 HTTP/1.1
Host: 10.100.0.11: 8000
accept: application/json
Content-Type: application/json
//3-1. get Detail QUEUED
GET /jobs?status=QUEUED&limit=50 HTTP/1.1
Host: 10.100.0.11: 8000
//3-2. get Detail PROCESSING
GET /jobs?status=PROCESSING&limit=50 HTTP/1.1
Host: 10.100.0.11: 8000
//3-3. get Detail PROCESSING
GET /jobs?status=COMPLETED&limit=50 HTTP/1.1
Host: 10.100.0.11: 8000