Skip to content

Commit

Permalink
Merge pull request #135 from 4m9d/feat/#132/develop-api-for-convertin…
Browse files Browse the repository at this point in the history
…g-materials-to-pdf(swm-386)

Feat/#132/develop api for converting materials to pdf(swm 386)
  • Loading branch information
D-w-nJ authored Nov 4, 2023
2 parents c850323 + 68d14e9 commit 5e5de3c
Show file tree
Hide file tree
Showing 16 changed files with 223 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.m9d.sroom.course.dto.VideoInfoForSchedule;
import com.m9d.sroom.common.entity.CourseVideoEntity;
import com.m9d.sroom.search.dto.response.LastVideoInfo;
import com.m9d.sroom.search.dto.response.VideoInfo;
import com.m9d.sroom.search.dto.response.VideoWatchInfo;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
Expand Down Expand Up @@ -115,8 +115,8 @@ public Optional<CourseVideoEntity> findByCourseIdAndPrevIndex(Long courseId, int
}

@Override
public LastVideoInfo getLastInfoByCourseId(Long courseId) {
return jdbcTemplate.queryForObject(CourseVideoRepositorySql.GET_LAST_INFO_BY_COURSE_ID, (rs, rowNum) -> LastVideoInfo.builder()
public VideoInfo getLastInfoByCourseId(Long courseId) {
return jdbcTemplate.queryForObject(CourseVideoRepositorySql.GET_LAST_INFO_BY_COURSE_ID, (rs, rowNum) -> VideoInfo.builder()
.videoId(rs.getLong("video_id"))
.videoTitle(rs.getString("title"))
.videoCode(rs.getString("video_code"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.m9d.sroom.course.dto.VideoInfoForSchedule;
import com.m9d.sroom.common.entity.CourseVideoEntity;
import com.m9d.sroom.search.dto.response.LastVideoInfo;
import com.m9d.sroom.search.dto.response.VideoInfo;
import com.m9d.sroom.search.dto.response.VideoWatchInfo;

import java.util.List;
Expand Down Expand Up @@ -31,8 +31,8 @@ public interface CourseVideoRepository {
void deleteByCourseId(Long courseId);

Optional<CourseVideoEntity> findByCourseIdAndPrevIndex(Long courseId, int videoIndex);

LastVideoInfo getLastInfoByCourseId(Long courseId);
VideoInfo getLastInfoByCourseId(Long courseId);

List<VideoWatchInfo> getWatchInfoListByCourseIdAndSection(Long courseId, int section);

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/m9d/sroom/course/CourseService.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ public LectureStatus updateLectureTime(Long memberId, Long courseVideoId, int vi
VideoCompletionStatus status = courseServiceHelper
.getVideoCompletionStatus(courseVideoEntity.toCourseVideo(), viewDuration, isMarkedAsCompleted);

learningActivityUpdater.updateCourseVideoStatus(courseVideoEntity, viewDuration, status.isCompleted());
learningActivityUpdater.updateCourseVideoStatus(courseVideoEntity, status.getViewDuration(),
status.isCompleted());

if (!status.isRewound()) {
learningActivityUpdater.updateCourseDailyLog(memberId, courseVideoEntity.getCourseId(), status);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.m9d.sroom.course.vo.CourseVideo;
import com.m9d.sroom.search.dto.VideoCompletionStatus;
import com.m9d.sroom.material.exception.CourseQuizNotFoundException;
import com.m9d.sroom.search.dto.response.VideoInfo;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.m9d.sroom.course.dto.response;

import com.m9d.sroom.common.entity.CourseEntity;
import com.m9d.sroom.course.vo.Course;
import com.m9d.sroom.search.dto.response.LastVideoInfo;
import com.m9d.sroom.search.dto.response.VideoInfo;
import com.m9d.sroom.search.dto.response.Section;
import com.m9d.sroom.search.dto.response.VideoWatchInfo;
import io.swagger.v3.oas.annotations.media.Schema;
Expand Down Expand Up @@ -52,13 +51,13 @@ public class CourseDetail {
private int progress;

@Schema(description = "강의에서 마지막으로 시청한 비디오 정보")
private LastVideoInfo lastViewVideo;
private VideoInfo lastViewVideo;

@Schema(description = "강의 일정의 주 리스트")
private List<Section> sections;

public CourseDetail(CourseEntity courseEntity, Set<String> channels, List<Section> sectionList,
LastVideoInfo lastVideoInfo) {
VideoInfo videoInfo) {
this.courseId = courseEntity.getCourseId();
this.courseTitle = courseEntity.getCourseTitle();
this.useSchedule = courseEntity.isScheduled();
Expand All @@ -77,7 +76,7 @@ public CourseDetail(CourseEntity courseEntity, Set<String> channels, List<Sectio
.count())
.sum();
this.progress = courseEntity.getProgress();
this.lastViewVideo = lastVideoInfo;
this.lastViewVideo = videoInfo;
this.sections = sectionList;
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/m9d/sroom/course/vo/Course.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,8 @@ public int getSumOfMaxDuration() {
.mapToInt(CourseVideo::getMaxDuration)
.sum();
}

public boolean hasUnpreparedVideo(){
return courseVideoList.stream().anyMatch(video -> video.getSummaryId() == 0);
}
}
8 changes: 7 additions & 1 deletion src/main/java/com/m9d/sroom/course/vo/CourseVideo.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,13 @@ public VideoCompletionStatus getCompletionStatus(int viewDuration, int videoDura
status.setFullyWatched(false);
}

status.setTimeGap(Math.max(viewDuration - maxDuration, 0));
if (isMarkedAsCompleted) {
status.setTimeGap(videoDuration - maxDuration);
status.setViewDuration(videoDuration - 1);
} else {
status.setTimeGap(Math.max(viewDuration - maxDuration, 0));
status.setViewDuration(viewDuration);
}

return status;
}
Expand Down
24 changes: 15 additions & 9 deletions src/main/java/com/m9d/sroom/material/MaterialController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import com.m9d.sroom.course.CourseServiceHelper;
import com.m9d.sroom.material.dto.request.SubmittedQuizRequest;
import com.m9d.sroom.material.dto.request.SummaryEditRequest;
import com.m9d.sroom.material.dto.response.Material;
import com.m9d.sroom.material.dto.response.ScrapResult;
import com.m9d.sroom.material.dto.response.SubmittedQuizInfoResponse;
import com.m9d.sroom.material.dto.response.SummaryId;
import com.m9d.sroom.material.dto.response.*;
import com.m9d.sroom.util.JwtUtil;
import com.m9d.sroom.util.annotation.Auth;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -22,7 +19,6 @@

@RestController
@RequiredArgsConstructor
@RequestMapping("/materials")
@Slf4j
public class MaterialController {

Expand All @@ -31,7 +27,7 @@ public class MaterialController {
private final CourseServiceHelper courseServiceHelper;

@Auth
@GetMapping("/{courseVideoId}")
@GetMapping("/materials/{courseVideoId}")
@Tag(name = "강의 수강")
@Operation(summary = "강의자료 불러오기", description = "영상 ID를 사용해 저장된 강의노트, 퀴즈를 불러옵니다.")
@ApiResponse(responseCode = "200", description = "성공적으로 강의 자료를 불러왔습니다.", content = @Content(schema = @Schema(implementation = Material.class)))
Expand All @@ -40,7 +36,7 @@ public Material getMaterials(@PathVariable("courseVideoId") Long courseVideoId)
}

@Auth
@PutMapping("/summaries/{courseVideoId}")
@PutMapping("/materials/summaries/{courseVideoId}")
@Tag(name = "강의 수강")
@Operation(summary = "강의 노트 수정하기", description = "영상 ID를 사용해 저장된 강의노트를 수정합니다.")
@ApiResponse(responseCode = "200", description = "성공적으로 강의 노트를 업데이트 했습니다.", content = @Content(schema = @Schema(implementation = SummaryId.class)))
Expand All @@ -49,7 +45,7 @@ public SummaryId updateSummaries(@PathVariable("courseVideoId") Long courseVideo
}

@Auth
@PostMapping("/quizzes/{courseVideoId}")
@PostMapping("/materials/quizzes/{courseVideoId}")
@Tag(name = "강의 수강")
@Operation(summary = "퀴즈 채점 결과 저장", description = "courseVideoId 를 받아 채점 결과를 courseQuiz 테이블에 저장합니다.")
@ApiResponse(responseCode = "200", description = "성공적으로 채점 결과를 저장했습니다.", content = @Content(schema = @Schema(implementation = SubmittedQuizInfoResponse.class)))
Expand All @@ -59,7 +55,7 @@ public List<SubmittedQuizInfoResponse> submitQuizResults(@PathVariable("courseVi
}

@Auth
@PutMapping("/quizzes/{courseQuizId}/scrap")
@PutMapping("/materials/quizzes/{courseQuizId}/scrap")
@Tag(name = "강의 수강")
@Operation(summary = "퀴즈 오답노트 등록, 취소", description = "courseQuizId 를 받아 해당 퀴즈를 오답노트에 등록하거나 취소합니다.")
@ApiResponse(responseCode = "200", description = "성공적으로 오답노트에 등록/취소하였습니다.", content = @Content(schema = @Schema(implementation = ScrapResult.class)))
Expand All @@ -68,4 +64,14 @@ public ScrapResult switchScrapFlag(@PathVariable("courseQuizId") Long courseQuiz
courseServiceHelper.validateCourseQuizForMember(memberId, courseQuizId);
return materialService.switchScrapFlag(courseQuizId);
}

@Auth
@GetMapping("/courses/materials/{courseId}")
@Tag(name = "내 강의실")
@Operation(summary = "pdf 변환을 위한 강의자료 불러오기", description = "코스의 모든 강의자료를 pdf 변환하기 위해 해당 자료를 불러옵니다.")
@ApiResponse(responseCode = "200", description = "성공적으로 강의자료를 불러왔습니다.", content = @Content(schema = @Schema(implementation = Material4PdfResponse.class)))
public Material4PdfResponse getMaterialsForConvertingPdf(@PathVariable("courseId") Long courseId) {
courseServiceHelper.validateCourseForMember(jwtUtil.getMemberIdFromRequest(), courseId);
return materialService.getCourseMaterials(courseId);
}
}
46 changes: 45 additions & 1 deletion src/main/java/com/m9d/sroom/material/MaterialService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import com.m9d.sroom.common.LearningActivityUpdater;
import com.m9d.sroom.common.entity.CourseQuizEntity;
import com.m9d.sroom.common.entity.CourseVideoEntity;
import com.m9d.sroom.common.repository.video.VideoRepository;
import com.m9d.sroom.course.CourseServiceHelper;
import com.m9d.sroom.course.vo.Course;
import com.m9d.sroom.course.vo.CourseVideo;
import com.m9d.sroom.material.dto.request.SubmittedQuizRequest;
import com.m9d.sroom.material.dto.response.*;
import com.m9d.sroom.material.exception.CourseQuizDuplicationException;
import com.m9d.sroom.material.model.MaterialStatus;
import com.m9d.sroom.quiz.QuizService;
import com.m9d.sroom.quiz.vo.Quiz;
import com.m9d.sroom.summary.SummaryService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -24,13 +27,16 @@ public class MaterialService {
private final SummaryService summaryService;
private final QuizService quizService;
private final LearningActivityUpdater activityUpdater;
private final VideoRepository videoRepository;

public MaterialService(CourseServiceHelper courseServiceHelper, SummaryService summaryService,
QuizService quizService, LearningActivityUpdater activityUpdater) {
QuizService quizService, LearningActivityUpdater activityUpdater,
VideoRepository videoRepository) {
this.courseServiceHelper = courseServiceHelper;
this.summaryService = summaryService;
this.quizService = quizService;
this.activityUpdater = activityUpdater;
this.videoRepository = videoRepository;
}


Expand Down Expand Up @@ -104,4 +110,42 @@ public ScrapResult switchScrapFlag(Long courseQuizId) {
.scrapped(quizService.scrap(courseQuizId))
.build();
}

public Material4PdfResponse getCourseMaterials(Long courseId) {
Course course = courseServiceHelper.getCourse(courseId);

if (course.hasUnpreparedVideo()) {
return Material4PdfResponse.createUnprepared(course.getTitle(), course.getCourseVideoList().size());
}

List<Content4PdfResponse> contentList = new ArrayList<>();
List<Answer4PdfResponse> answerList = new ArrayList<>();

course.getCourseVideoList()
.forEach(courseVideo -> addMaterialAndQuizAnswer(courseVideo, answerList, contentList));

return Material4PdfResponse.createPrepared(course.getTitle(), course.getCourseVideoList().size(), contentList,
answerList);
}

private void addMaterialAndQuizAnswer(CourseVideo courseVideo, List<Answer4PdfResponse> answerList,
List<Content4PdfResponse> contentList) {
int quizIndex = 1;
SummaryBrief summaryBrief = null;
List<Quiz4PdfResponse> quizList = new ArrayList<>();

if (courseVideo.getMaterialStatus().equals(MaterialStatus.CREATED)) {
summaryBrief = new SummaryBrief(summaryService.getSummary(courseVideo.getSummaryId()));

for (Quiz quiz : quizService.getQuizList(courseVideo.getVideoId())) {
quizList.add(new Quiz4PdfResponse(quiz, quizIndex));
answerList.add(new Answer4PdfResponse(courseVideo.getVideoIndex(), quizIndex, quiz.getAnswer(),
quiz.getOptionStrList().get(Integer.parseInt(quiz.getAnswer()) - 1)));
quizIndex++;
}
}

contentList.add(Content4PdfResponse.create(videoRepository.getById(courseVideo.getVideoId()), courseVideo,
summaryBrief, quizList));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.m9d.sroom.material.dto.response;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Answer4PdfResponse {
private int videoIndex;
private int quizIndex;
private String answer;
private String answerStr;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.m9d.sroom.material.dto.response;

import com.m9d.sroom.common.entity.VideoEntity;
import com.m9d.sroom.course.vo.CourseVideo;
import com.m9d.sroom.material.model.MaterialStatus;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

import java.util.List;

@Data
@Builder
@AllArgsConstructor
public class Content4PdfResponse {

private String videoTitle;

private int index;

private String channel;

private boolean usable;

private SummaryBrief summaryBrief;

private List<Quiz4PdfResponse> quizzes;


public static Content4PdfResponse create(VideoEntity videoEntity, CourseVideo courseVideo,
SummaryBrief summaryBrief, List<Quiz4PdfResponse> quizzeList) {
return Content4PdfResponse.builder()
.videoTitle(videoEntity.getTitle())
.index(courseVideo.getVideoIndex())
.channel(videoEntity.getChannel())
.usable(courseVideo.getMaterialStatus().equals(MaterialStatus.CREATED))
.summaryBrief(summaryBrief)
.quizzes(quizzeList)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.m9d.sroom.material.dto.response;

import com.m9d.sroom.material.model.MaterialStatus;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

import java.util.List;

@Data
@Builder
@AllArgsConstructor
public class Material4PdfResponse {

private String courseTitle;

private int totalVideoCount;

private int status;

private List<Content4PdfResponse> materials;

private List<Answer4PdfResponse> answers;

public static Material4PdfResponse createUnprepared(String title, int videoCount) {
return Material4PdfResponse.builder()
.courseTitle(title)
.totalVideoCount(videoCount)
.status(MaterialStatus.CREATING.getValue())
.build();
}

public static Material4PdfResponse createPrepared(String title, int videoCount,
List<Content4PdfResponse> contentList,
List<Answer4PdfResponse> answerList) {
return Material4PdfResponse.builder()
.courseTitle(title)
.totalVideoCount(videoCount)
.status(MaterialStatus.CREATED.getValue())
.materials(contentList)
.answers(answerList)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.m9d.sroom.material.dto.response;

import com.m9d.sroom.quiz.vo.Quiz;
import lombok.Data;

import java.util.List;

@Data
public class Quiz4PdfResponse {

private int type;

private int index;

private String question;

private List<String> options;

public Quiz4PdfResponse(Quiz quiz, int index) {
this.type = quiz.getType().getValue();
this.index = index;
this.question = quiz.getQuestion();
this.options = quiz.getOptionStrList();
}
}
Loading

0 comments on commit 5e5de3c

Please sign in to comment.