diff --git a/src/main/java/com/kamco/cd/kamcoback/model/ModelMgmtApiController.java b/src/main/java/com/kamco/cd/kamcoback/model/ModelMgmtApiController.java new file mode 100644 index 00000000..1b9bc0db --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/model/ModelMgmtApiController.java @@ -0,0 +1,43 @@ +package com.kamco.cd.kamcoback.model; + +import com.kamco.cd.kamcoback.config.api.ApiResponseDto; +import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto; +import com.kamco.cd.kamcoback.model.service.ModelMgmtService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.transaction.Transactional; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "모델 관리", description = "모델 관리 API") +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/model") +@Transactional +public class ModelMgmtApiController { + + private final ModelMgmtService modelMgmtService; + + @Operation(summary = "모델관리 목록") + @GetMapping + public ApiResponseDto> findModelMgmtList( + @RequestParam(required = false) LocalDate startDate, + @RequestParam(required = false) LocalDate endDate, + @RequestParam(required = false, defaultValue = "createCompleteDttm") String sortColumn, + @RequestParam(required = false) String modelType, + @RequestParam(required = false) String searchVal, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int size) { + ModelMgmtDto.searchReq searchReq = new ModelMgmtDto.searchReq(page, size, sortColumn + ",desc"); + + Page result = + modelMgmtService.findModelMgmtList(searchReq, startDate, endDate, modelType, searchVal); + + return ApiResponseDto.ok(result); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java b/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java index 042cb152..3c6312f2 100644 --- a/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java @@ -22,8 +22,9 @@ import org.springframework.web.bind.annotation.*; @Tag(name = "모델 관리", description = "모델 관리 API") @RequiredArgsConstructor @RestController -@RequestMapping("/api/model") +@RequestMapping("/api/model/deprecated") @Transactional +@Deprecated public class ModelMngApiController { private final ModelMngService modelMngService; diff --git a/src/main/java/com/kamco/cd/kamcoback/model/dto/ModelMgmtDto.java b/src/main/java/com/kamco/cd/kamcoback/model/dto/ModelMgmtDto.java new file mode 100644 index 00000000..b2fee42b --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/model/dto/ModelMgmtDto.java @@ -0,0 +1,113 @@ +package com.kamco.cd.kamcoback.model.dto; + +import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.ZonedDateTime; +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 ModelMgmtDto { + + @Schema(name = "ModelMgmtDto Basic", description = "모델관리 엔티티 기본 정보") + @Getter + @NoArgsConstructor + public static class Basic { + + private String modelVer; + private String hyperVer; + private String epochVer; + private String dockerFileNm; + + @JsonFormatDttm private ZonedDateTime createCompleteDttm; + @JsonFormatDttm private ZonedDateTime recentUseDttm; + private Boolean deleted; + @JsonFormatDttm private ZonedDateTime createdDttm; + private Long createdUid; + + @JsonFormatDttm private ZonedDateTime updatedDttm; + private Long updatedUid; + + public Basic( + String modelVer, + String hyperVer, + String epochVer, + String dockerFileNm, + ZonedDateTime createCompleteDttm, + ZonedDateTime recentUseDttm, + Boolean deleted, + ZonedDateTime createdDttm, + Long createdUid, + ZonedDateTime updatedDttm, + Long updatedUid) { + this.modelVer = modelVer; + this.hyperVer = hyperVer; + this.epochVer = epochVer; + this.dockerFileNm = dockerFileNm; + this.createCompleteDttm = createCompleteDttm; + this.recentUseDttm = recentUseDttm; + this.deleted = deleted; + this.createdDttm = createdDttm; + this.createdUid = createdUid; + this.updatedDttm = updatedDttm; + this.updatedUid = updatedUid; + } + } + + @Schema(name = "ModelList", description = "모델관리 목록") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class ModelList { + private Integer rowNum; + private String modelVer; + private String dockerFileNm; + private String modelType; + private String createCompleteDttm; + private String recentUseDttm; + private Boolean deleted; + } + + @Schema(name = "ModelAddReq", description = "모델 등록 req") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class AddReq { + + private String modelType; + private String dockerFileNm; + private String modelVer; + private String hyperVer; + private String epochVer; + } + + @Schema(name = "searchReq", description = "검색 요청") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class searchReq { + + // 페이징 파라미터 + private int page = 0; + private int size = 20; + private String sort; + + public Pageable toPageable() { + if (sort != null && !sort.isEmpty()) { + String[] sortParams = sort.split(","); + String property = sortParams[0]; + Sort.Direction direction = + sortParams.length > 1 ? Sort.Direction.fromString(sortParams[1]) : Sort.Direction.ASC; + return PageRequest.of(page, size, Sort.by(direction, property)); + } + return PageRequest.of(page, size); + } + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/model/service/ModelMgmtService.java b/src/main/java/com/kamco/cd/kamcoback/model/service/ModelMgmtService.java new file mode 100644 index 00000000..51895638 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/model/service/ModelMgmtService.java @@ -0,0 +1,25 @@ +package com.kamco.cd.kamcoback.model.service; + +import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto; +import com.kamco.cd.kamcoback.postgres.core.ModelMgmtCoreService; +import java.time.LocalDate; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ModelMgmtService { + + private final ModelMgmtCoreService modelMgmtCoreService; + + public Page findModelMgmtList( + ModelMgmtDto.searchReq searchReq, + LocalDate startDate, + LocalDate endDate, + String modelType, + String searchVal) { + return modelMgmtCoreService.findModelMgmtList( + searchReq, startDate, endDate, modelType, searchVal); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/ModelMgmtCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/ModelMgmtCoreService.java new file mode 100644 index 00000000..62a78174 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/ModelMgmtCoreService.java @@ -0,0 +1,25 @@ +package com.kamco.cd.kamcoback.postgres.core; + +import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto; +import com.kamco.cd.kamcoback.postgres.repository.model.ModelMgmtRepository; +import java.time.LocalDate; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ModelMgmtCoreService { + + private final ModelMgmtRepository modelMgmtRepository; + + public Page findModelMgmtList( + ModelMgmtDto.searchReq searchReq, + LocalDate startDate, + LocalDate endDate, + String modelType, + String searchVal) { + return modelMgmtRepository.findModelMgmtList( + searchReq, startDate, endDate, modelType, searchVal); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelClassCountEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelClassCountEntity.java new file mode 100644 index 00000000..70b72292 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelClassCountEntity.java @@ -0,0 +1,25 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "tb_model_class_count") +public class ModelClassCountEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "count_uid") + private Integer countUid; + + @Column(name = "model_uid") + private Integer modelUid; + + @Column(name = "class_cd") + private String classCd; + + @Column(name = "obj_cnt") + private Integer objCnt; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelDatasetMappEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelDatasetMappEntity.java new file mode 100644 index 00000000..dde32a60 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelDatasetMappEntity.java @@ -0,0 +1,20 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "tb_model_dataset_mapp") +public class ModelDatasetMappEntity { + + @EmbeddedId private ModelDatasetMappEntityId id; + + @Column(name = "dataset_type") + private String datasetType; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelDatasetMappEntityId.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelDatasetMappEntityId.java new file mode 100644 index 00000000..c77cf0df --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelDatasetMappEntityId.java @@ -0,0 +1,42 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.Hibernate; + +@Getter +@Setter +@Embeddable +public class ModelDatasetMappEntityId implements Serializable { + + @NotNull + @Column(name = "model_uid", nullable = false) + private Integer modelUid; + + @NotNull + @Column(name = "dataset_uid", nullable = false) + private Integer datasetUid; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) { + return false; + } + ModelDatasetMappEntityId entity = (ModelDatasetMappEntityId) o; + return Objects.equals(this.modelUid, entity.modelUid) + && Objects.equals(this.datasetUid, entity.datasetUid); + } + + @Override + public int hashCode() { + return Objects.hash(modelUid, modelUid); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelHyperParamEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelHyperParamEntity.java new file mode 100644 index 00000000..164a7b06 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelHyperParamEntity.java @@ -0,0 +1,40 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.time.ZonedDateTime; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "tb_model_hyper_param") +public class ModelHyperParamEntity { + @Id + @Column(name = "hyper_ver") + private String hyperVer; + + @Column(name = "learning_rate") + private Double learningRate; + + @Column(name = "batch_size") + private Integer batchSize; + + @Column(name = "dropout_ratio") + private Double dropoutRatio; + + @Column(name = "cnn_filter_cnt") + private Integer cnnFilterCnt; + + @Column(name = "memo", columnDefinition = "TEXT") + private String memo; + + @Column(name = "del_yn") + private Character delYn; + + @Column(name = "created_dttm") + private ZonedDateTime createdDttm; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelMgmtEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelMgmtEntity.java new file mode 100644 index 00000000..2d98f4f1 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelMgmtEntity.java @@ -0,0 +1,45 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import com.kamco.cd.kamcoback.postgres.CommonDateEntity; +import jakarta.persistence.*; +import java.time.ZonedDateTime; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "tb_model_mgmt") +public class ModelMgmtEntity extends CommonDateEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "model_uid") + private Integer modelUid; + + @Column(name = "model_ver") + private String modelVer; + + @Column(name = "hyper_ver") + private String hyperVer; + + @Column(name = "epoch_ver") + private String epochVer; + + @Column(name = "docker_file_nm") + private String dockerFileNm; + + @Column(name = "create_complete_dttm") + private ZonedDateTime createCompleteDttm; + + @Column(name = "recent_use_dttm") + private ZonedDateTime recentUseDttm; + + @Column(name = "deleted") + private Boolean deleted; + + @Column(name = "created_uid") + private Long createdUid; + + @Column(name = "updated_uid") + private Long updatedUid; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelTrainMasterEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelTrainMasterEntity.java new file mode 100644 index 00000000..496aceda --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelTrainMasterEntity.java @@ -0,0 +1,66 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import com.kamco.cd.kamcoback.postgres.CommonDateEntity; +import jakarta.persistence.*; +import java.time.ZonedDateTime; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "tb_model_train_master") +public class ModelTrainMasterEntity extends CommonDateEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "model_uid", nullable = false) + private Integer modelUid; + + @Column(name = "model_ver") + private String modelVer; + + @Column(name = "hyper_ver") + private String hyperVer; + + @Column(name = "epoch_ver") + private String epochVer; + + @Column(name = "process_step") + private String processStep; + + @Column(name = "status_cd") + private String statsusCd; + + @Column(name = "train_start_dttm") + private ZonedDateTime trainStartDttm; + + @Column(name = "epoch_cnt") + private Integer epochCnt; + + @Column(name = "dataset_ratio") + private String datasetRatio; + + @Column(name = "best_epoch") + private Integer bestEpoch; + + @Column(name = "step1_end_dttm") + private ZonedDateTime step1EndDttm; + + @Column(name = "step1_duration") + private String step1Duration; + + @Column(name = "step2_end_dttm") + private ZonedDateTime step2EndDttm; + + @Column(name = "step2_duration") + private String step2Duration; + + @Column(name = "del_yn") + private Character delYn; + + @Column(name = "created_uid") + private Long createdUid; + + @Column(name = "updated_uid") + private Long updatedUid; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMgmtRepository.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMgmtRepository.java new file mode 100644 index 00000000..a9d27dfa --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMgmtRepository.java @@ -0,0 +1,7 @@ +package com.kamco.cd.kamcoback.postgres.repository.model; + +import com.kamco.cd.kamcoback.postgres.entity.ModelMgmtEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ModelMgmtRepository + extends JpaRepository, ModelMgmtRepositoryCustom {} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMgmtRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMgmtRepositoryCustom.java new file mode 100644 index 00000000..f3055470 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMgmtRepositoryCustom.java @@ -0,0 +1,15 @@ +package com.kamco.cd.kamcoback.postgres.repository.model; + +import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto; +import java.time.LocalDate; +import org.springframework.data.domain.Page; + +public interface ModelMgmtRepositoryCustom { + + Page findModelMgmtList( + ModelMgmtDto.searchReq searchReq, + LocalDate startDate, + LocalDate endDate, + String modelType, + String searchVal); +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMgmtRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMgmtRepositoryImpl.java new file mode 100644 index 00000000..ed72986c --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMgmtRepositoryImpl.java @@ -0,0 +1,135 @@ +package com.kamco.cd.kamcoback.postgres.repository.model; + +import com.kamco.cd.kamcoback.model.dto.ModelMgmtDto; +import com.kamco.cd.kamcoback.postgres.QuerydslOrderUtil; +import com.kamco.cd.kamcoback.postgres.entity.ModelMgmtEntity; +import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.types.Expression; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.core.types.dsl.StringExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.kamco.cd.kamcoback.postgres.entity.QModelMgmtEntity.modelMgmtEntity; + +public class ModelMgmtRepositoryImpl extends QuerydslRepositorySupport + implements ModelMgmtRepositoryCustom { + + private final JPAQueryFactory queryFactory; + private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)"); + + public ModelMgmtRepositoryImpl(JPAQueryFactory queryFactory) { + super(ModelMgmtEntity.class); + this.queryFactory = queryFactory; + } + + @Override + public Page findModelMgmtList( + ModelMgmtDto.searchReq searchReq, + LocalDate startDate, + LocalDate endDate, + String modelType, + String searchVal) { + Pageable pageable = searchReq.toPageable(); + Sort sort = pageable.getSort(); + String property = "createCompleteDttm"; // 기본으로 생성완료일 기준 + + Map> sortColumnMap = + Map.of( + "createCompleteDttm", modelMgmtEntity.createCompleteDttm, + "recentUseDttm", modelMgmtEntity.recentUseDttm); + + if (sort.isSorted()) { + Sort.Order order = sort.iterator().next(); + property = order.getProperty(); + } + + Expression sortColumn = sortColumnMap.get(property); + + List foundContent = + queryFactory + .select( + Projections.constructor( + ModelMgmtDto.ModelList.class, + Expressions.numberTemplate( + Integer.class, "row_number() over(order by {0} desc)", sortColumn), + modelMgmtEntity.modelVer, + modelMgmtEntity.dockerFileNm, + modelMgmtEntity.modelVer.as("modelType"), + Expressions.stringTemplate( + "to_char({0}, 'YYYY-MM-DD')", modelMgmtEntity.createCompleteDttm), + Expressions.stringTemplate( + "to_char({0}, 'YYYY-MM-DD')", modelMgmtEntity.recentUseDttm), + modelMgmtEntity.deleted)) + .from(modelMgmtEntity) + .where( + eventEndedAtBetween(startDate, endDate, property), + searchModelVersion(modelType, searchVal)) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .orderBy( + QuerydslOrderUtil.getOrderSpecifiers( + pageable, ModelMgmtEntity.class, "modelMgmtEntity")) + .fetch(); + + Long countQuery = + queryFactory + .select(modelMgmtEntity.modelUid.count()) + .from(modelMgmtEntity) + .where( + eventEndedAtBetween(startDate, endDate, property), + searchModelVersion(modelType, searchVal)) + .fetchOne(); + + return new PageImpl<>(foundContent, pageable, countQuery); + } + + private BooleanExpression eventEndedAtBetween( + LocalDate startDate, LocalDate endDate, String sortColumn) { + if (Objects.isNull(startDate) || Objects.isNull(endDate)) { + return null; + } + ZoneId zone = ZoneId.systemDefault(); + LocalDateTime startDateTime = startDate.atStartOfDay(); + LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay(); + + if (sortColumn.equals("createCompleteDttm")) { + return modelMgmtEntity + .createCompleteDttm + .goe(startDateTime.atZone(zone)) + .and(modelMgmtEntity.createCompleteDttm.lt(endDateTime.atZone(zone))); + } else { + return modelMgmtEntity + .recentUseDttm + .goe(startDateTime.atZone(zone)) + .and(modelMgmtEntity.recentUseDttm.lt(endDateTime.atZone(zone))); + } + } + + private BooleanBuilder searchModelVersion(String modelType, String searchVal) { + BooleanBuilder builder = new BooleanBuilder(); + + if (Objects.nonNull(modelType)) { + builder.and(modelMgmtEntity.modelVer.eq(modelType)); + } + + if (Objects.nonNull(searchVal)) { + builder.and(modelMgmtEntity.dockerFileNm.likeIgnoreCase("%" + searchVal + "%")); + } + + return builder; + } +}