Skip to content

Commit

Permalink
feat: 부전공 학생 졸업 요건 검사 기능 (#238)
Browse files Browse the repository at this point in the history
* feat: 부전공 학생 졸업 요건 조정

자유선택 졸업 학점 -> 0

* refactor: FindMajorPort 메서드 시그니처 수정

* feat: SubMajor DetailGraduationResult 생성 구현

* feat: 부전공 졸업 검사 전체 결과 response & user MajorResponse 추가

* feat: jackson - null값 필드 생략 설정 추가

* refactor: 복수전공 파싱 추가

* refactor: 불필요 어노테이션 삭제

* refactor: warning 제거

* refactor: 불필요 주석 삭제

* chore: dn-rule 워크플로우 스크립트 작성 (#237)

* chore: dn-rule 워크플로우 스크립트 작성

* chore: dn-rule 워크플로우 스크립트 수정

* chore: dn-rule 워크플로우 스크립트 수정

* chore: dn-rule 워크플로우 스크립트 수정

* chore: dn-rule 워크플로우 pull_request 적용 타입 수정

* chore: dn-rule 워크플로우 수정

* refactor: 리뷰내용 반영

* chore: 깃헙 워크플로우 action 버전 수정

* build: 불필요 의존성 제거

* chore: 깃헙 워크플로우 삭제

* chore: pr-test 워크플로우 수정

* chore: pr-test 워크플로우 수정

* chore: 워크플로우 스크립트 버전 수정

* chore: application-test.yml database dialect 명시

* chore: application-test.yml database dialect 수정

* chore: testContainer를 위한 docker 설치 step 추가

* chore: testContainer를 위한 docker 설치 step 추가

* chore: 워크플로우 수정

* chore: 워크플로우 수정

* chore: 워크플로우 수정

* chore: 워크플로우 수정

* chore: 워크플로우 수정

* chore: 워크플로우 수정

* chore: 워크플로우 테스트

* refactor: 복수전공 상세 졸업결과 카테고리 미 존재 시 exception 제거

* test: 과목 검색 오류 수정 테스트 수정
폐지 여부가 반대로 나오는 오류 수정

* test: 과목 검색 오류 수정 테스트 수정
폐지 여부가 반대로 나오는 오류 수정

* chore: dn-rule 수정

* chore: 워크플로우 스크립트 버전 수정
  • Loading branch information
5uhwann authored Feb 15, 2024
1 parent 673a36f commit 825ca3f
Show file tree
Hide file tree
Showing 24 changed files with 418 additions and 97 deletions.
70 changes: 31 additions & 39 deletions .github/workflows/dn-rule.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
name: PR Label Automation
on:
pull_request:
types: [ opened ]
schedule:
- cron: '0 0 * * *' # 매일 자정에 실행
- cron: '0 0 * * *' # Executes daily at midnight

jobs:
update-labels:
Expand All @@ -14,55 +12,49 @@ jobs:
with:
script: |
const repo = context.repo;
const prNumber = context.payload.pull_request ? context.payload.pull_request.number : context.issue.number;
// PR 레이블 가져오기
const { data: pr } = await github.rest.pulls.get({
// Fetch all open PRs
const prs = await github.rest.pulls.list({
owner: repo.owner,
repo: repo.repo,
pull_number: prNumber,
state: 'open',
});
let labels = pr.labels.map(label => label.name);
const hasDLabel = labels.some(label => label.startsWith("D-"));
for (const pr of prs.data) {
const prNumber = pr.number;
let labels = pr.labels.map(label => label.name);
// 'D-' 레이블이 없는 경우 'D-5' 레이블 추가
if (!hasDLabel) {
await github.rest.issues.addLabels({
owner: repo.owner,
repo: repo.repo,
issue_number: prNumber,
labels: ['D-5'],
});
console.log("Added D-5 label to PR");
}
// 'D-x' 레이블 확인 및 업데이트
for (let label of labels) {
if (label.startsWith("D-") && label !== "D-0") {
let day = parseInt(label.split("-")[1]);
if (day > 0) {
let newLabel = `D-${day - 1}`;
// 먼저 기존 레이블을 제거
// Function to update label
async function updateLabel(oldLabel, newLabel) {
if (oldLabel) {
await github.rest.issues.removeLabel({
owner: repo.owner,
repo: repo.repo,
issue_number: prNumber,
name: label,
name: oldLabel,
});
// 새 레이블 추가
await github.rest.issues.addLabels({
owner: repo.owner,
repo: repo.repo,
issue_number: prNumber,
labels: [newLabel],
});
console.log(`Updated label from ${label} to ${newLabel}`);
}
await github.rest.issues.addLabels({
owner: repo.owner,
repo: repo.repo,
issue_number: prNumber,
labels: [newLabel],
});
}
if (day === 1) {
// 'D-0' 처리 로직
// Check and update 'D-x' labels
let dLabel = labels.find(label => label.startsWith("D-"));
if (dLabel) {
let day = parseInt(dLabel.split("-")[1]);
if (day > 0) {
await updateLabel(dLabel, `D-${day - 1}`);
console.log(`Updated label from ${dLabel} to D-${day - 1} on PR #${prNumber}`);
} else if (day === 0) {
// Handle 'D-0' logic here
}
break;
} else {
// Add 'D-5' label if no 'D-' label is present
await updateLabel(null, 'D-5');
console.log(`Added D-5 label to PR #${prNumber}`);
}
}
2 changes: 1 addition & 1 deletion .github/workflows/pr-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
# Gradle test를 실행 후 report 추출
- name: Test with Gradle
run: ./gradlew build jacocoTestReport

# report 업로드하기
- name: Upload coverage to Codecov
uses: codecov/[email protected]
1 change: 1 addition & 0 deletions .husky/prepare-commit-msg
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ TYPE_LIST=(
style
chore
test
build
docs
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.plzgraduate.myongjigraduatebe.graduation.application.port;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

Expand All @@ -19,6 +20,7 @@
import com.plzgraduate.myongjigraduatebe.graduation.domain.service.commonculture.CommonCultureGraduationManager;
import com.plzgraduate.myongjigraduatebe.graduation.domain.service.coreculture.CoreCultureGraduationManager;
import com.plzgraduate.myongjigraduatebe.graduation.domain.service.major.MajorManager;
import com.plzgraduate.myongjigraduatebe.graduation.domain.service.submajor.SubMajorManager;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.out.FindBasicAcademicalCulturePort;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.out.FindCommonCulturePort;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.out.FindCoreCulturePort;
Expand All @@ -32,6 +34,7 @@
import com.plzgraduate.myongjigraduatebe.user.application.port.in.find.FindUserUseCase;
import com.plzgraduate.myongjigraduatebe.user.domain.model.College;
import com.plzgraduate.myongjigraduatebe.user.domain.model.GraduationRequirementType;
import com.plzgraduate.myongjigraduatebe.user.domain.model.StudentCategory;
import com.plzgraduate.myongjigraduatebe.user.domain.model.User;

import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -59,6 +62,7 @@ public GraduationResponse calculateGraduation(Long userId) {
ChapelResult chapelResult = generateChapelResult(takenLectureInventory);
List<DetailGraduationResult> detailGraduationResults = generateDetailGraduationResults(user,
takenLectureInventory, graduationRequirement);

GraduationResult graduationResult = generateGraduationResult(chapelResult, detailGraduationResults,
takenLectureInventory, graduationRequirement);

Expand All @@ -80,22 +84,23 @@ private ChapelResult generateChapelResult(TakenLectureInventory takenLectureInve

private List<DetailGraduationResult> generateDetailGraduationResults(User user,
TakenLectureInventory takenLectureInventory, GraduationRequirement graduationRequirement) {
DetailGraduationResult commonCultureGraduationResult = generateCommonCultureDetailGraduationResult(
user, takenLectureInventory, graduationRequirement);

DetailGraduationResult coreCultureDetailGraduationResult = generateCoreCultureDetailGraduationResult(
user, takenLectureInventory, graduationRequirement);

DetailGraduationResult basicAcademicalDetailGraduationResult = generteBasicAcademicalDetailGraduationResult(
user, takenLectureInventory, graduationRequirement);

DetailGraduationResult majorDetailGraduationResult = generateMajorDetailGraduationResult(
user, takenLectureInventory, graduationRequirement);
List<DetailGraduationResult> detailGraduationResults = new ArrayList<>(List.of(
generateCommonCultureDetailGraduationResult(
user, takenLectureInventory, graduationRequirement),
generateCoreCultureDetailGraduationResult(
user, takenLectureInventory, graduationRequirement),
generteBasicAcademicalDetailGraduationResult(
user, takenLectureInventory, graduationRequirement),
generateMajorDetailGraduationResult(
user, takenLectureInventory, graduationRequirement)
));

// TODO: Additional Major check - DetailGraduationResult
if (user.getStudentCategory() == StudentCategory.SUB_MAJOR) {
detailGraduationResults.add(generateSubMajorDetailGraduationResult(user, takenLectureInventory));
}

return List.of(commonCultureGraduationResult, coreCultureDetailGraduationResult,
basicAcademicalDetailGraduationResult, majorDetailGraduationResult);
return detailGraduationResults;
}

private DetailGraduationResult generateCommonCultureDetailGraduationResult(User user,
Expand Down Expand Up @@ -144,12 +149,21 @@ private GraduationManager<BasicAcademicalCultureLecture> determineBasicAcademica

private DetailGraduationResult generateMajorDetailGraduationResult(User user,
TakenLectureInventory takenLectureInventory, GraduationRequirement graduationRequirement) {
Set<MajorLecture> graduationMajorLectures = findMajorPort.findMajor(user);
Set<MajorLecture> graduationMajorLectures = findMajorPort.findMajor(user.getMajor());
GraduationManager<MajorLecture> majorGraduationManager = new MajorManager();
return majorGraduationManager.createDetailGraduationResult(user,
takenLectureInventory, graduationMajorLectures, graduationRequirement.getMajorCredit());
}

private DetailGraduationResult generateSubMajorDetailGraduationResult(User user,
TakenLectureInventory takenLectureInventory) {
int requireSubMajorCredit = 21;
Set<MajorLecture> graduationSubMajorLectures = findMajorPort.findMajor(user.getSubMajor());
GraduationManager<MajorLecture> subMajorManager = new SubMajorManager();
return subMajorManager.createDetailGraduationResult(user, takenLectureInventory, graduationSubMajorLectures,
requireSubMajorCredit);
}

private GraduationResult generateGraduationResult(ChapelResult chapelResult,
List<DetailGraduationResult> detailGraduationResults, TakenLectureInventory takenLectureInventory,
GraduationRequirement graduationRequirement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ public class BasicInfoResponse {
private final String name;
@Schema(name = "studentNumber", example = "60202000")
private final String studentNumber;
@Schema(name = "major", example = "응용소프트웨어전공")
private final String major;
private final MajorInfoResponse major;
@Schema(name = "totalCredit", example = "132")
private final int totalCredit;
@Schema(name = "takenCredit", example = "50")
private final double takenCredit;

@Builder
private BasicInfoResponse(String name, String studentNumber, String major, int totalCredit, double takenCredit) {
private BasicInfoResponse(String name, String studentNumber, MajorInfoResponse major, int totalCredit, double takenCredit) {
this.name = name;
this.studentNumber = studentNumber;
this.major = major;
Expand All @@ -34,7 +33,7 @@ public static BasicInfoResponse of(User user, GraduationResult graduationResult)
return BasicInfoResponse.builder()
.name(user.getName())
.studentNumber(user.getStudentNumber())
.major(user.getMajor())
.major(MajorInfoResponse.from(user))
.totalCredit(graduationResult.getTotalCredit())
.takenCredit(graduationResult.getTakenCredit()).build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public class GraduationResponse {
private final DetailGraduationResultResponse coreCulture;
private final DetailGraduationResultResponse basicAcademicalCulture;
private final DetailGraduationResultResponse major;
@Schema(name = "subMajor", nullable = true)
private final DetailGraduationResultResponse subMajor;
//TODO: 복수전공 response 추가
private final RestResultResponse normalCulture;
private final RestResultResponse freeElective;
@Schema(name = "graduated", example = "false")
Expand All @@ -28,13 +31,15 @@ public class GraduationResponse {
private GraduationResponse(BasicInfoResponse basicInfo, ChapelResultResponse chapelResult,
DetailGraduationResultResponse commonCulture, DetailGraduationResultResponse coreCulture,
DetailGraduationResultResponse basicAcademicalCulture, DetailGraduationResultResponse major,
RestResultResponse normalCulture, RestResultResponse freeElective, boolean graduated) {
DetailGraduationResultResponse subMajor, RestResultResponse normalCulture, RestResultResponse freeElective,
boolean graduated) {
this.basicInfo = basicInfo;
this.chapelResult = chapelResult;
this.commonCulture = commonCulture;
this.coreCulture = coreCulture;
this.basicAcademicalCulture = basicAcademicalCulture;
this.major = major;
this.subMajor = subMajor;
this.normalCulture = normalCulture;
this.freeElective = freeElective;
this.graduated = graduated;
Expand All @@ -48,6 +53,7 @@ public static GraduationResponse of(User user, GraduationResult graduationResult
.coreCulture(findDetailGraduationResultResponse(graduationResult, CORE_CULTURE))
.basicAcademicalCulture(findDetailGraduationResultResponse(graduationResult, BASIC_ACADEMICAL_CULTURE))
.major(findDetailGraduationResultResponse(graduationResult, MAJOR))
.subMajor(findDetailGraduationResultResponse(graduationResult, SUB_MAJOR))
.normalCulture(
RestResultResponse.fromNormalCultureResult(graduationResult.getNormalCultureGraduationResult()))
.freeElective(RestResultResponse.fromFreeElectiveResult(graduationResult.getFreeElectiveGraduationResult()))
Expand All @@ -62,6 +68,6 @@ private static DetailGraduationResultResponse findDetailGraduationResultResponse
.equals(graduationCategory.getName()))
.map(DetailGraduationResultResponse::from)
.findFirst()
.orElseThrow(IllegalArgumentException::new);
.orElse(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.plzgraduate.myongjigraduatebe.graduation.application.port.in.response;


import com.plzgraduate.myongjigraduatebe.user.domain.model.User;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Getter;


@Getter
public class MajorInfoResponse {

@Schema(name = "primaryMajor", example = "데이터테크놀로지전공")
private final String primaryMajor;
@Schema(name = "doubleMajor", example = "융합소프트웨어전공", nullable = true)
private final String doubleMajor;
@Schema(name = "subMajor", example = "융합소프트웨어전공", nullable = true)
private final String subMajor;

@Builder
private MajorInfoResponse(String primaryMajor, String doubleMajor, String subMajor) {
this.primaryMajor = primaryMajor;
this.doubleMajor = doubleMajor;
this.subMajor = subMajor;
}

public static MajorInfoResponse from(User user) {
return MajorInfoResponse.builder()
.primaryMajor(user.getMajor())
.subMajor(user.getSubMajor())
.doubleMajor(null) //TODO: 복수전공 추가
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum GraduationCategory {
CORE_CULTURE("핵심교양"),
BASIC_ACADEMICAL_CULTURE("학문기초교양"),
MAJOR("전공"),
SUB_MAJOR("부전공"),
NORMAL_CULTURE("일반교양"),
FREE_ELECTIVE("자유선택");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class GraduationRequirement {
private int commonCultureCredit;
private final int coreCultureCredit;
private int normalCultureCredit;
private final int freeElectiveCredit;
private int freeElectiveCredit;

@Builder
private GraduationRequirement(int totalCredit, int majorCredit, int subMajorCredit, int basicAcademicalCredit,
Expand All @@ -34,4 +34,8 @@ public void transferEnglishCreditCommonToNormal() {
commonCultureCredit -= ENGLISH.getTotalCredit();
normalCultureCredit += ENGLISH.getTotalCredit();
}

public void deleteFreeElectiveCredit() {
freeElectiveCredit = 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.plzgraduate.myongjigraduatebe.graduation.domain.service.submajor;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import com.plzgraduate.myongjigraduatebe.graduation.domain.model.DetailCategoryResult;
import com.plzgraduate.myongjigraduatebe.graduation.domain.model.DetailGraduationResult;
import com.plzgraduate.myongjigraduatebe.graduation.domain.model.GraduationCategory;
import com.plzgraduate.myongjigraduatebe.graduation.domain.service.GraduationManager;
import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture;
import com.plzgraduate.myongjigraduatebe.lecture.domain.model.MajorLecture;
import com.plzgraduate.myongjigraduatebe.takenlecture.domain.model.TakenLecture;
import com.plzgraduate.myongjigraduatebe.takenlecture.domain.model.TakenLectureInventory;
import com.plzgraduate.myongjigraduatebe.user.domain.model.User;

public class SubMajorManager implements GraduationManager<MajorLecture> {

@Override
public DetailGraduationResult createDetailGraduationResult(User user, TakenLectureInventory takenLectureInventory,
Set<MajorLecture> graduationLectures, int graduationResultTotalCredit) {
Set<TakenLecture> removedTakenLecture = new HashSet<>();
DetailCategoryResult detailCategoryResult = generateDetailCategoryResult(takenLectureInventory,
graduationLectures, removedTakenLecture, graduationResultTotalCredit);

takenLectureInventory.handleFinishedTakenLectures(removedTakenLecture);

return DetailGraduationResult.create(GraduationCategory.SUB_MAJOR, graduationResultTotalCredit,
List.of(detailCategoryResult));
}

private DetailCategoryResult generateDetailCategoryResult(TakenLectureInventory takenLectureInventory,
Set<MajorLecture> graduationLectures, Set<TakenLecture> removedTakenLecture, int graduationResultTotalCredit) {
boolean isSatisfiedMandatory = true;
Set<Lecture> subMajorGraduationLectures = graduationLectures.stream()
.map(MajorLecture::getLecture)
.collect(Collectors.toSet());
Set<Lecture> taken = new HashSet<>();

takenLectureInventory.getTakenLectures().stream()
.filter(takenLecture -> subMajorGraduationLectures.contains(takenLecture.getLecture()))
.forEach(takenLecture -> {
removedTakenLecture.add(takenLecture);
taken.add(takenLecture.getLecture());
});

DetailCategoryResult detailCategoryResult = DetailCategoryResult.create("전공선택", isSatisfiedMandatory,
graduationResultTotalCredit);
detailCategoryResult.calculate(taken, subMajorGraduationLectures);
return detailCategoryResult;
}
}
Loading

0 comments on commit 825ca3f

Please sign in to comment.