라벨링 종료여부 추가

This commit is contained in:
DanielLee
2026-01-09 10:10:28 +09:00
parent 980828fbd3
commit 26e58b01d5
8 changed files with 271 additions and 40 deletions

View File

@@ -6,6 +6,7 @@ import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelerDetail; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelerDetail;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelingStatDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.LabelingStatDto;
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.UpdateClosedRequest;
import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerListResponse; import com.kamco.cd.kamcoback.label.dto.WorkerStatsDto.WorkerListResponse;
import com.kamco.cd.kamcoback.label.service.LabelAllocateService; import com.kamco.cd.kamcoback.label.service.LabelAllocateService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@@ -236,4 +237,66 @@ public class LabelAllocateApiController {
String uuid) { String uuid) {
return ApiResponseDto.ok(labelAllocateService.moveAvailUserList(userId, uuid)); return ApiResponseDto.ok(labelAllocateService.moveAvailUserList(userId, uuid));
} }
@Operation(
summary = "작업현황 관리 > 라벨링/검수 종료 여부 업데이트",
description = "라벨링/검수 종료 여부를 업데이트합니다. uuid 생략 시 최신 프로젝트 대상")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "업데이트 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PostMapping("/projectinfo/closed")
public ApiResponseDto<ApiResponseDto.ResponseObj> updateClosedYn(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "종료 여부 업데이트 요청",
required = true,
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = UpdateClosedRequest.class),
examples = {
@io.swagger.v3.oas.annotations.media.ExampleObject(
name = "라벨링 종료",
value = """
{"closedType": "LABELING", "closedYn": "Y"}
"""),
@io.swagger.v3.oas.annotations.media.ExampleObject(
name = "검수 종료",
value = """
{"closedType": "INSPECTION", "closedYn": "Y"}
"""),
@io.swagger.v3.oas.annotations.media.ExampleObject(
name = "라벨링 재개",
value = """
{"closedType": "LABELING", "closedYn": "N"}
"""),
@io.swagger.v3.oas.annotations.media.ExampleObject(
name = "검수 재개",
value = """
{"closedType": "INSPECTION", "closedYn": "N"}
"""),
@io.swagger.v3.oas.annotations.media.ExampleObject(
name = "특정 프로젝트 라벨링 종료",
value = """
{"uuid": "f97dc186-e6d3-4645-9737-3173dde8dc64", "closedType": "LABELING", "closedYn": "Y"}
""")
}))
@RequestBody
@Valid
UpdateClosedRequest request) {
labelAllocateService.updateClosedYn(
request.getUuid(), request.getClosedType(), request.getClosedYn());
String typeLabel = "LABELING".equals(request.getClosedType()) ? "라벨링" : "검수";
String statusMessage =
"Y".equals(request.getClosedYn())
? typeLabel + "이(가) 종료되었습니다."
: typeLabel + "이(가) 재개되었습니다.";
return ApiResponseDto.okObject(
new ApiResponseDto.ResponseObj(ApiResponseDto.ApiResponseCode.OK, statusMessage));
}
} }

View File

@@ -2,6 +2,8 @@ package com.kamco.cd.kamcoback.label.dto;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
@@ -35,6 +37,43 @@ public class WorkerStatsDto {
@Schema(description = "프로젝트 UUID") @Schema(description = "프로젝트 UUID")
private String uuid; private String uuid;
@Schema(description = "라벨링 종료 여부 (Y: 종료, N: 진행중)")
private String labelingClosedYn;
@Schema(description = "검수 종료 여부 (Y: 종료, N: 진행중)")
private String inspectionClosedYn;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "프로젝트 종료 여부 업데이트 요청")
public static class UpdateClosedRequest {
@Schema(
description = "프로젝트 UUID (선택) - 미입력 시 현재 진행중인 최신 프로젝트가 대상",
example = "f97dc186-e6d3-4645-9737-3173dde8dc64")
private String uuid;
@NotBlank(message = "종료 유형은 필수입니다.")
@Pattern(regexp = "^(LABELING|INSPECTION)$", message = "종료 유형은 LABELING 또는 INSPECTION이어야 합니다.")
@Schema(
description = "종료 유형 (LABELING: 라벨링, INSPECTION: 검수)",
example = "LABELING",
allowableValues = {"LABELING", "INSPECTION"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String closedType;
@NotBlank(message = "종료 여부는 필수입니다.")
@Pattern(regexp = "^[YN]$", message = "종료 여부는 Y 또는 N이어야 합니다.")
@Schema(
description = "종료 여부 (Y: 종료, N: 진행중)",
example = "Y",
allowableValues = {"Y", "N"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String closedYn;
} }
@Getter @Getter
@@ -144,7 +183,7 @@ public class WorkerStatsDto {
@Schema(description = "검수 작업 상태 (진행중/완료)") @Schema(description = "검수 작업 상태 (진행중/완료)")
private String inspectionStatus; private String inspectionStatus;
@Schema(description = "검수 전체 대상 건수") @Schema(description = "검수 대상 건수 (라벨링 대상과 동일)")
private Long inspectionTotalCount; private Long inspectionTotalCount;
@Schema(description = "검수 완료 건수 (DONE)") @Schema(description = "검수 완료 건수 (DONE)")
@@ -179,10 +218,6 @@ public class WorkerStatsDto {
@Deprecated @Deprecated
@Schema(description = "[Deprecated] inspectionRemainingCount 사용 권장") @Schema(description = "[Deprecated] inspectionRemainingCount 사용 권장")
private Long remainingInspectCount; private Long remainingInspectCount;
@Deprecated
@Schema(description = "[Deprecated] labelingStatus/inspectionStatus 사용 권장")
private String workStatus;
} }
@Getter @Getter

View File

@@ -195,4 +195,27 @@ public class LabelAllocateService {
public MoveInfo moveAvailUserList(String userId, String uuid) { public MoveInfo moveAvailUserList(String userId, String uuid) {
return labelAllocateCoreService.moveAvailUserList(userId, uuid); return labelAllocateCoreService.moveAvailUserList(userId, uuid);
} }
/**
* 프로젝트 종료 여부 업데이트
*
* @param uuid 프로젝트 UUID (선택, 미입력 시 최신 프로젝트 대상)
* @param closedType 종료 유형 (LABELING/INSPECTION)
* @param closedYn 종료 여부 (Y/N)
*/
@Transactional
public void updateClosedYn(String uuid, String closedType, String closedYn) {
String targetUuid = uuid;
// uuid가 없으면 최신 프로젝트 uuid 조회
if (targetUuid == null || targetUuid.isBlank()) {
var latestProjectInfo = labelAllocateCoreService.findLatestProjectInfo();
if (latestProjectInfo == null || latestProjectInfo.getUuid() == null) {
throw new IllegalArgumentException("진행중인 프로젝트가 없습니다.");
}
targetUuid = latestProjectInfo.getUuid();
}
labelAllocateCoreService.updateClosedYnByUuid(targetUuid, closedType, closedYn);
}
} }

View File

@@ -133,4 +133,9 @@ public class LabelAllocateCoreService {
String uuid, String userId, String paramUserId, Long assignCount) { String uuid, String userId, String paramUserId, Long assignCount) {
labelAllocateRepository.assignOwnerReAllocate(uuid, userId, paramUserId, assignCount); labelAllocateRepository.assignOwnerReAllocate(uuid, userId, paramUserId, assignCount);
} }
public void updateClosedYnByUuid(String uuid, String closedType, String closedYn) {
labelAllocateRepository.updateClosedYnByUuid(uuid, closedType, closedYn);
}
} }

View File

@@ -149,4 +149,14 @@ public class MapSheetAnalInferenceEntity {
@Column(name = "stage") @Column(name = "stage")
private Integer 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";
} }

View File

@@ -77,4 +77,8 @@ public interface LabelAllocateRepositoryCustom {
void insertLabelerUser(Long analUid, String userId, int demand); void insertLabelerUser(Long analUid, String userId, int demand);
void assignOwnerReAllocate(String uuid, String userId, String paramUserId, Long assignCount); void assignOwnerReAllocate(String uuid, String userId, String paramUserId, Long assignCount);
// 프로젝트 종료 여부 업데이트 (uuid 기반)
void updateClosedYnByUuid(String uuid, String closedType, String closedYn);
} }

View File

@@ -335,10 +335,50 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
@Override @Override
public WorkProgressInfo findWorkProgressInfo(Long analUid) { public WorkProgressInfo findWorkProgressInfo(Long analUid) {
BooleanExpression analUidCondition = // analUid가 null이면 최신 프로젝트의 analUid 조회
analUid != null ? labelingAssignmentEntity.analUid.eq(analUid) : null; Long effectiveAnalUid = analUid;
if (effectiveAnalUid == null) {
UUID latestUuid = findLastLabelWorkState();
if (latestUuid != null) {
effectiveAnalUid =
queryFactory
.select(mapSheetAnalInferenceEntity.id)
.from(mapSheetAnalInferenceEntity)
.where(mapSheetAnalInferenceEntity.uuid.eq(latestUuid))
.fetchOne();
}
}
// 전체 배정 건수 BooleanExpression analUidCondition =
effectiveAnalUid != null ? labelingAssignmentEntity.analUid.eq(effectiveAnalUid) : null;
// analUid로 분석 정보(compareYyyy, targetYyyy, stage) 조회
MapSheetAnalInferenceEntity analEntity = null;
if (effectiveAnalUid != null) {
analEntity =
queryFactory
.selectFrom(mapSheetAnalInferenceEntity)
.where(mapSheetAnalInferenceEntity.id.eq(effectiveAnalUid))
.fetchOne();
}
// 라벨링 대상 건수: tb_map_sheet_anal_data_inference_geom에서 pnu > 0 AND pass_yn = false(부적합)인 건수
Long labelingTargetCount = 0L;
if (analEntity != null) {
labelingTargetCount =
queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid.count())
.from(mapSheetAnalDataInferenceGeomEntity)
.where(
mapSheetAnalDataInferenceGeomEntity.compareYyyy.eq(analEntity.getCompareYyyy()),
mapSheetAnalDataInferenceGeomEntity.targetYyyy.eq(analEntity.getTargetYyyy()),
mapSheetAnalDataInferenceGeomEntity.stage.eq(analEntity.getStage()),
mapSheetAnalDataInferenceGeomEntity.pnu.gt(0L),
mapSheetAnalDataInferenceGeomEntity.passYn.isFalse())
.fetchOne();
}
// 전체 배정 건수 (tb_labeling_assignment 기준)
Long totalAssigned = Long totalAssigned =
queryFactory queryFactory
.select(labelingAssignmentEntity.count()) .select(labelingAssignmentEntity.count())
@@ -390,47 +430,69 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.where(analUidCondition, labelingAssignmentEntity.inspectorUid.isNotNull()) .where(analUidCondition, labelingAssignmentEntity.inspectorUid.isNotNull())
.fetchOne(); .fetchOne();
// 남은 작업 건수 계산 // 라벨링 대상 건수 (pass_yn = false인 부적합 데이터 기준)
long total = totalAssigned != null ? totalAssigned : 0L; long labelingTotal = labelingTargetCount != null ? labelingTargetCount : 0L;
long assignedTotal = totalAssigned != null ? totalAssigned : 0L;
long labelCompleted = labelingCompleted != null ? labelingCompleted : 0L; long labelCompleted = labelingCompleted != null ? labelingCompleted : 0L;
long inspectCompleted = inspectionCompleted != null ? inspectionCompleted : 0L; long inspectCompleted = inspectionCompleted != null ? inspectionCompleted : 0L;
long skipped = skipCount != null ? skipCount : 0L; long skipped = skipCount != null ? skipCount : 0L;
long labelingRemaining = total - labelCompleted - skipped; // 라벨링 남은 건수: 라벨링 대상 건수 - 완료 - 스킵
long inspectionRemaining = total - inspectCompleted - skipped; long labelingRemaining = labelingTotal - labelCompleted - skipped;
if (labelingRemaining < 0) labelingRemaining = 0;
// 진행률 계산 // 검수 대상 건수: 라벨링 대상 건수와 동일 (기획서 기준)
double labelingRate = total > 0 ? (double) labelCompleted / total * 100 : 0.0; long inspectionTotal = labelingTotal;
double inspectionRate = total > 0 ? (double) inspectCompleted / total * 100 : 0.0; // 검수 남은 건수: 검수 대상 건수 - 검수완료(DONE) - 스킵
long inspectionRemaining = inspectionTotal - inspectCompleted - skipped;
if (inspectionRemaining < 0) inspectionRemaining = 0;
// 상태 판단 // 진행률 계산 (라벨링 대상 건수 기준)
String labelingStatus = labelingRemaining > 0 ? "진행중" : "완료"; double labelingRate = labelingTotal > 0 ? (double) labelCompleted / labelingTotal * 100 : 0.0;
String inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료"; double inspectionRate =
inspectionTotal > 0 ? (double) inspectCompleted / inspectionTotal * 100 : 0.0;
// 상태 판단 (각각의 closedYn이 "Y"이면 "종료", 아니면 진행중/완료)
String labelingStatus;
String inspectionStatus;
// 라벨링 상태 판단
if (analEntity != null && "Y".equals(analEntity.getLabelingClosedYn())) {
labelingStatus = "종료";
} else {
labelingStatus = labelingRemaining > 0 ? "진행중" : "완료";
}
// 검수 상태 판단
if (analEntity != null && "Y".equals(analEntity.getInspectionClosedYn())) {
inspectionStatus = "종료";
} else {
inspectionStatus = inspectionRemaining > 0 ? "진행중" : "완료";
}
return WorkProgressInfo.builder() return WorkProgressInfo.builder()
// 라벨링 // 라벨링 (pass_yn = false인 부적합 데이터 기준)
.labelingProgressRate(labelingRate) .labelingProgressRate(labelingRate)
.labelingStatus(labelingStatus) .labelingStatus(labelingStatus)
.labelingTotalCount(total) .labelingTotalCount(labelingTotal)
.labelingCompletedCount(labelCompleted) .labelingCompletedCount(labelCompleted)
.labelingSkipCount(skipped) .labelingSkipCount(skipped)
.labelingRemainingCount(labelingRemaining) .labelingRemainingCount(labelingRemaining)
.labelerCount(labelerCount != null ? labelerCount : 0L) .labelerCount(labelerCount != null ? labelerCount : 0L)
// 검수 // 검수 (라벨링 완료 건수 기준)
.inspectionProgressRate(inspectionRate) .inspectionProgressRate(inspectionRate)
.inspectionStatus(inspectionStatus) .inspectionStatus(inspectionStatus)
.inspectionTotalCount(total) .inspectionTotalCount(inspectionTotal)
.inspectionCompletedCount(inspectCompleted) .inspectionCompletedCount(inspectCompleted)
.inspectionSkipCount(skipped) .inspectionSkipCount(skipped)
.inspectionRemainingCount(inspectionRemaining) .inspectionRemainingCount(inspectionRemaining)
.inspectorCount(inspectorCount != null ? inspectorCount : 0L) .inspectorCount(inspectorCount != null ? inspectorCount : 0L)
// 레거시 호환 필드 (Deprecated) // 레거시 호환 필드 (Deprecated)
.progressRate(labelingRate) .progressRate(labelingRate)
.totalAssignedCount(total) .totalAssignedCount(labelingTotal)
.completedCount(labelCompleted) .completedCount(labelCompleted)
.remainingLabelCount(labelingRemaining) .remainingLabelCount(labelingRemaining)
.remainingInspectCount(inspectionRemaining) .remainingInspectCount(inspectionRemaining)
.workStatus(labelingStatus)
.build(); .build();
} }
@@ -677,7 +739,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
mapSheetAnalInferenceEntity.stage, mapSheetAnalInferenceEntity.stage,
mapSheetAnalInferenceEntity.gukyuinApplyDttm, mapSheetAnalInferenceEntity.gukyuinApplyDttm,
mapSheetAnalInferenceEntity.createdDttm, mapSheetAnalInferenceEntity.createdDttm,
mapSheetAnalInferenceEntity.uuid) mapSheetAnalInferenceEntity.uuid,
mapSheetAnalInferenceEntity.labelingClosedYn,
mapSheetAnalInferenceEntity.inspectionClosedYn)
.from(mapSheetAnalInferenceEntity) .from(mapSheetAnalInferenceEntity)
.where(mapSheetAnalInferenceEntity.id.eq(analUid)) .where(mapSheetAnalInferenceEntity.id.eq(analUid))
.fetchOne(); .fetchOne();
@@ -692,6 +756,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
ZonedDateTime gukyuinApplyDttm = result.get(mapSheetAnalInferenceEntity.gukyuinApplyDttm); ZonedDateTime gukyuinApplyDttm = result.get(mapSheetAnalInferenceEntity.gukyuinApplyDttm);
ZonedDateTime createdDttm = result.get(mapSheetAnalInferenceEntity.createdDttm); ZonedDateTime createdDttm = result.get(mapSheetAnalInferenceEntity.createdDttm);
UUID uuid = result.get(mapSheetAnalInferenceEntity.uuid); UUID uuid = result.get(mapSheetAnalInferenceEntity.uuid);
String labelingClosedYn = result.get(mapSheetAnalInferenceEntity.labelingClosedYn);
String inspectionClosedYn = result.get(mapSheetAnalInferenceEntity.inspectionClosedYn);
// 변화탐지년도 생성 // 변화탐지년도 생성
String detectionYear = String detectionYear =
@@ -706,6 +772,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.gukyuinApplyDttm(gukyuinApplyDttm) .gukyuinApplyDttm(gukyuinApplyDttm)
.startDttm(createdDttm) .startDttm(createdDttm)
.uuid(uuid != null ? uuid.toString() : null) .uuid(uuid != null ? uuid.toString() : null)
.labelingClosedYn(labelingClosedYn)
.inspectionClosedYn(inspectionClosedYn)
.build(); .build();
} }
@@ -727,7 +795,9 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
mapSheetAnalInferenceEntity.stage, mapSheetAnalInferenceEntity.stage,
mapSheetAnalInferenceEntity.gukyuinApplyDttm, mapSheetAnalInferenceEntity.gukyuinApplyDttm,
mapSheetAnalInferenceEntity.createdDttm, mapSheetAnalInferenceEntity.createdDttm,
mapSheetAnalInferenceEntity.uuid) mapSheetAnalInferenceEntity.uuid,
mapSheetAnalInferenceEntity.labelingClosedYn,
mapSheetAnalInferenceEntity.inspectionClosedYn)
.from(mapSheetAnalInferenceEntity) .from(mapSheetAnalInferenceEntity)
.where(mapSheetAnalInferenceEntity.uuid.eq(uuid)) .where(mapSheetAnalInferenceEntity.uuid.eq(uuid))
.fetchOne(); .fetchOne();
@@ -741,6 +811,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
Integer stage = result.get(mapSheetAnalInferenceEntity.stage); Integer stage = result.get(mapSheetAnalInferenceEntity.stage);
ZonedDateTime gukyuinApplyDttm = result.get(mapSheetAnalInferenceEntity.gukyuinApplyDttm); ZonedDateTime gukyuinApplyDttm = result.get(mapSheetAnalInferenceEntity.gukyuinApplyDttm);
ZonedDateTime createdDttm = result.get(mapSheetAnalInferenceEntity.createdDttm); ZonedDateTime createdDttm = result.get(mapSheetAnalInferenceEntity.createdDttm);
String labelingClosedYn = result.get(mapSheetAnalInferenceEntity.labelingClosedYn);
String inspectionClosedYn = result.get(mapSheetAnalInferenceEntity.inspectionClosedYn);
// 변화탐지년도 생성 // 변화탐지년도 생성
String detectionYear = String detectionYear =
@@ -755,6 +827,8 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
.gukyuinApplyDttm(gukyuinApplyDttm) .gukyuinApplyDttm(gukyuinApplyDttm)
.startDttm(createdDttm) .startDttm(createdDttm)
.uuid(uuid.toString()) .uuid(uuid.toString())
.labelingClosedYn(labelingClosedYn)
.inspectionClosedYn(inspectionClosedYn)
.build(); .build();
} }
@@ -1160,4 +1234,24 @@ public class LabelAllocateRepositoryImpl implements LabelAllocateRepositoryCusto
labelingLabelerEntity.workerUid.eq(paramUserId)) labelingLabelerEntity.workerUid.eq(paramUserId))
.execute(); .execute();
} }
@Override
public void updateClosedYnByUuid(String uuid, String closedType, String closedYn) {
var updateQuery = queryFactory.update(mapSheetAnalInferenceEntity);
if ("LABELING".equals(closedType)) {
updateQuery.set(mapSheetAnalInferenceEntity.labelingClosedYn, closedYn);
} else if ("INSPECTION".equals(closedType)) {
updateQuery.set(mapSheetAnalInferenceEntity.inspectionClosedYn, closedYn);
}
updateQuery
.set(mapSheetAnalInferenceEntity.updatedDttm, ZonedDateTime.now())
.where(mapSheetAnalInferenceEntity.uuid.eq(UUID.fromString(uuid)))
.execute();
em.flush();
em.clear();
}
} }

View File

@@ -267,32 +267,29 @@ public class LabelWorkRepositoryImpl implements LabelWorkRepositoryCustom {
whereSubBuilder.and(labelingAssignmentEntity.workerUid.eq(memberEntity.userId)); whereSubBuilder.and(labelingAssignmentEntity.workerUid.eq(memberEntity.userId));
// 공통 조건 추출
BooleanExpression doneStateCondition =
labelingAssignmentEntity.workState.eq(LabelState.DONE.name());
NumberExpression<Long> assignedCnt = labelingAssignmentEntity.workerUid.count(); NumberExpression<Long> assignedCnt = labelingAssignmentEntity.workerUid.count();
NumberExpression<Long> doneCnt = NumberExpression<Long> doneCnt = this.caseSumExpression(doneStateCondition);
this.caseSumExpression(labelingAssignmentEntity.workState.eq(LabelState.DONE.name()));
NumberExpression<Long> skipCnt = NumberExpression<Long> skipCnt =
this.caseSumExpression(labelingAssignmentEntity.workState.eq(LabelState.SKIP.name())); this.caseSumExpression(labelingAssignmentEntity.workState.eq(LabelState.SKIP.name()));
NumberExpression<Long> day3AgoDoneCnt = NumberExpression<Long> day3AgoDoneCnt =
this.caseSumExpression( this.caseSumExpression(
labelingAssignmentEntity doneStateCondition.and(
.workState this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -3)));
.eq(LabelState.DONE.name())
.and(this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -3)));
NumberExpression<Long> day2AgoDoneCnt = NumberExpression<Long> day2AgoDoneCnt =
this.caseSumExpression( this.caseSumExpression(
labelingAssignmentEntity doneStateCondition.and(
.workState this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -2)));
.eq(LabelState.DONE.name())
.and(this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -2)));
NumberExpression<Long> day1AgoDoneCnt = NumberExpression<Long> day1AgoDoneCnt =
this.caseSumExpression( this.caseSumExpression(
labelingAssignmentEntity doneStateCondition.and(
.workState this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -1)));
.eq(LabelState.DONE.name())
.and(this.fromDateEqExpression(labelingAssignmentEntity.modifiedDate, -1)));
NumberExpression<Long> remainingCnt = assignedCnt.subtract(doneCnt); NumberExpression<Long> remainingCnt = assignedCnt.subtract(doneCnt);