Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package inu.codin.codin.domain.lecture.controller;

import inu.codin.codin.domain.lecture.dto.MetaMode;
import inu.codin.codin.domain.lecture.service.LectureUploadService;
import inu.codin.codin.global.common.response.SingleResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -49,5 +51,26 @@ public ResponseEntity<SingleResponse<?>> uploadNewSemesterRooms(@RequestParam("e

}

@Operation(
summary = "강의 메타(키워드/태그/선수과목) 엑셀 업로드",
description = "'단과대약어_연도_학기_meta'로 설정하여 업로드 ex) info_25_2_meta.xlxs. 기본값은 모두 true (즉 전체 처리)."
)
@PostMapping(value = "/meta", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_MANAGER')")
public ResponseEntity<SingleResponse<?>> uploadLectureMeta(
@Parameter(description = "업로드할 엑셀 파일 (.xlsx)")
@RequestParam("excelFile") MultipartFile file,

@Parameter(description = "처리 모드 (ALL | KEYWORDS | TAGS | PRE_COURSES). 기본값: ALL")
@RequestParam(name = "mode", defaultValue = "ALL") MetaMode mode
) {
lectureUploadService.uploadLectureMeta(file, mode);
return ResponseEntity.status(HttpStatus.CREATED)
.body(new SingleResponse<>(201,
file.getOriginalFilename()+"의 태그,키워드,선수 과목등 메타데이터 포함 엑셀파일 업데이트",
null));

}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package inu.codin.codin.domain.lecture.dto;

public enum MetaMode {
ALL, // 키워드+태그+선수과목
KEYWORDS, // 키워드만
TAGS, // 태그만
PRE_COURSES // 선수과목만
}
21 changes: 21 additions & 0 deletions src/main/java/inu/codin/codin/domain/lecture/entity/Keyword.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package inu.codin.codin.domain.lecture.entity;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Keyword {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String keywordDescription;

@OneToMany(mappedBy = "keyword", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<LectureKeyword> lectureKeywords;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package inu.codin.codin.domain.lecture.entity;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class LectureKeyword {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "lecture_id")
private Lecture lecture;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "keyword_id")
private Keyword keyword;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package inu.codin.codin.domain.lecture.service;

import inu.codin.codin.domain.elasticsearch.indexer.LectureStartupIndexer;
import inu.codin.codin.domain.lecture.dto.MetaMode;
import inu.codin.codin.domain.lecture.exception.LectureErrorCode;
import inu.codin.codin.domain.lecture.exception.LectureUploadException;
import lombok.RequiredArgsConstructor;
Expand All @@ -12,6 +13,9 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
Expand All @@ -28,6 +32,8 @@ public class LectureUploadService {

private String ROOM_PROGRAM = "dayTimeOfRoom.py";
private String LECTURE_PROGRAM = "infoOfLecture.py";
private static final String META_PROGRAM = "load_metadata.py";


public void uploadNewSemesterLectures(MultipartFile file){
try {
Expand Down Expand Up @@ -68,6 +74,63 @@ public void uploadNewSemesterRooms(MultipartFile file) {
}
}

public void uploadLectureMeta(MultipartFile file, MetaMode mode) {
try {
saveFile(file); // 기존 그대로 사용
executeMetaLoader(file, mode);
log.info("[uploadLectureMeta] {} 메타 업데이트 완료", file.getOriginalFilename());

try { indexer.lectureIndex(); } catch (Exception e) {
log.warn("[uploadLectureMeta] 색인 갱신 경고: {}", e.getMessage());
}

} catch (LectureUploadException e) {
throw e;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new LectureUploadException(LectureErrorCode.LECTURE_UPLOAD_FAIL, e.getMessage());
}
}


/** 메타 로더 실행 헬퍼: load_metadata.py <excel> [--keywords] [--tags] [--pre-courses] | --all */
private void executeMetaLoader(MultipartFile file,MetaMode mode) {
String scriptPath = Paths.get(UPLOAD_DIR, META_PROGRAM).toString();
String excelPath = Paths.get(UPLOAD_DIR, file.getOriginalFilename()).toString();

List<String> cmd = new ArrayList<>();
cmd.add(PYTHON_DIR); // ex) /usr/bin/python3 or venv/bin/python
cmd.add(scriptPath); // load_metadata.py 절대/상대 경로
cmd.add(excelPath); // 업로드된 엑셀 파일 경로

switch (mode) {
case ALL -> cmd.add("--all");
case KEYWORDS -> cmd.add("--keywords");
case TAGS -> cmd.add("--tags");
case PRE_COURSES -> cmd.add("--pre-courses");
}
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);

try {
Process p = pb.start();
StringBuilder out = new StringBuilder();
try (var br = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) {
out.append(line).append('\n');
log.debug("[Python Output] {}", line);
}
}
int exit = p.waitFor();
if (exit != 0) {
throw new LectureUploadException(LectureErrorCode.LECTURE_UPLOAD_FAIL, out.toString());
}
} catch (IOException | InterruptedException e) {
throw new LectureUploadException(LectureErrorCode.LECTURE_UPLOAD_FAIL, e.getMessage());
}
}

private void saveFile(MultipartFile file) {
String originalName = file.getOriginalFilename();
if (originalName == null || originalName.isBlank()) {
Expand Down