diff --git a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java index cde2b946..d336eed4 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/dto/LabelWorkDto.java @@ -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,16 @@ 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 +96,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; } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java index dbef596a..b1fea3c1 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/label/LabelWorkRepositoryImpl.java @@ -14,6 +14,7 @@ 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.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; @@ -24,8 +25,6 @@ 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; @@ -33,29 +32,23 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; 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; - - public LabelWorkRepositoryImpl(JPAQueryFactory queryFactory) { - super(MapSheetAnalDataInferenceGeomEntity.class); - this.queryFactory = queryFactory; - } /** * 변화탐지 년도 셀렉트박스 조회 @@ -129,6 +122,34 @@ public class LabelWorkRepositoryImpl extends QuerydslRepositorySupport .and(mapSheetAnalDataInferenceGeomEntity.labelStateDttm.lt(end))); } + // labelTotCnt: pnu가 있고 pass_yn = false (부적합)인 건수만 라벨링 대상 + NumberExpression 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 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 totalAssignmentCntSubQuery = + JPAExpressions.select(labelingAssignmentEntity.count().coalesce(0L)) + .from(labelingAssignmentEntity) + .where(labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id)); + List foundContent = queryFactory .select( @@ -138,17 +159,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 +184,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 +201,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(),