diff --git a/README.md b/README.md
index 572a180..1ed8801 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,58 @@
# java-attendance-precourse
-
-
-
-
-
-
-
-
-
-
-
-
+## 기능 구현 목록
+1. 파일 입력
+2. 오늘 날짜와 요일 출력
+3. 기능 선택 입력
+ - 예외 사항
+ 1. “1”,”2”,”3”,”4”,”Q”가 아닌 다른 값을 입력한 경우 - “잘못된 형식을 입력하였습니다.”
+4. 출석 확인
+ - 예외 사항
+ 1. 주말 또는 공휴일인 경우 - “%d월 %d일 %s요일은 등교일이 아닙니다.”
+ 1. 닉네임 입력
+ - 예외 사항
+ 1. 등록되지 않은 닉네임인 경우 - “등록되지 않은 닉네임입니다.”
+ 2. 이미 출석한 경우 - “이미 출석을 확인하였습니다. 필요한 경우 수정 기능을 이용해주세요.”
+ 2. 등교 시간 입력
+ - 예외 사항
+ 1. 시간 형식이 잘못된 경우(00:00) - “잘못된 형식을 입력하였습니다.”
+ 2. 등교 시간이 캠퍼스 운영 시간이 아닌 경우 - “캠퍼스 운영 시간에만 출석이 가능합니다.”
+ 3. 출석 등록된 날짜, 요일, 시간 정보 출력
+ - “%d월 %d일 %s요일 00:00 (출석)”
+ - “%d월 %d일 %s요일 00:00 (지각)”
+ - “%d월 %d일 %s요일 —:— (결석)”
+5. 출석 수정
+ - 예외 사항
+ 1. 주말 또는 공휴일인 경우 - “%d월 %d일 %s요일은 등교일이 아닙니다.”
+ 1. 닉네임 입력
+ - 예외 사항
+ 1. 등록되지 않은 닉네임인 경우 - “등록되지 않은 닉네임입니다.”
+ 2. 수정 날짜 입력
+ - 예외 사항
+ 1. 날짜 형식(1이상 31이하의 정수)이 아닌 경우 - “잘못된 형식을 입력하였습니다.”
+ 2. 미래 날짜인 경우 - “아직 수정할 수 없습니다.”
+ 3. 변경 시각 입력
+ - 예외 사항
+ 1. 시간 형식이 잘못된 경우(00:00) - “잘못된 형식을 입력하였습니다.”
+ 2. 등교 시간이 캠퍼스 운영 시간이 아닌 경우 - “캠퍼스 운영 시간에만 출석이 가능합니다.”
+ 4. 변경 정보 출력
+ - “%d월 %d일 %s요일 00:00 (지각) -> %d월 %d일 %s요일 00:00 (출석) 수정 완료!”
+6. 크루별 출석 기록 확인
+ 1. 닉네임 입력
+ - 예외 사항
+ 1. 등록되지 않은 닉네임인 경우 - “등록되지 않은 닉네임입니다.”
+ 2. 출석 기록 출력
+ - “이번 달 %s의 출석 기록입니다.”
+ - 평일 날짜별 출석 기록
+ - 출석, 지각, 결석 횟수
+ - 대상자 정보
+ - “경고 대상자입니다.”
+ - “면담 대상자입니다.”
+ - “제적 대상자입니다.”
+7. 제적 위험자 확인
+ - 제적 위험자 출력
+ - “제적 위험자 조회 결과”
+ - “- %s: 결석 %d회, 지각 %d회 (면담)”
+ - 제적, 면담, 경고 순으로 출력(지각 3회는 결석 1회로 계산)
+ - 출석 상태 같으면 닉네임 오름차순
+8. 종료
diff --git a/src/main/java/attendance/Application.java b/src/main/java/attendance/Application.java
index e9b866e..be71cdb 100644
--- a/src/main/java/attendance/Application.java
+++ b/src/main/java/attendance/Application.java
@@ -1,7 +1,201 @@
package attendance;
+import attendance.domain.Attendances;
+import attendance.domain.Crew;
+import attendance.time.DateTime;
+import attendance.util.InputParser;
+import attendance.util.file.FileReader;
+import attendance.view.InputView;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.TextStyle;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
public class Application {
- public static void main(String[] args) {
- // TODO: 프로그램 구현
+
+ private static Attendances attendances;
+
+ public static void main(String[] args) throws IOException {
+ FileReader reader = new FileReader("src/main/resources/attendances.csv");
+ List readLines = reader.readLines();
+ readLines.removeFirst();
+ attendances = Attendances.newInstance();
+ for (String readPromotion : readLines) {
+ String[] split = readPromotion.split(",");
+ String name = split[0];
+ String[] split1 = split[1].split(" ");
+ String dateFormat = split1[0];
+ String timeFormat = split1[1];
+ LocalDate date = LocalDate.parse(dateFormat);
+ LocalTime time = LocalTime.parse(timeFormat);
+ attendances.addAttendance(name, date, time);
+ }
+
+ while (true) {
+ LocalDate nowDate = DateTime.now();
+// LocalDate nowDate = LocalDate.of(2024, 12, 13);
+ String rawChoice = InputView.readChoice(nowDate);
+ String choice = InputParser.parseChoice(rawChoice);
+
+ List allDates = getAllDates(nowDate);
+ List allCrews = attendances.getCrews();
+ for (Crew crew : allCrews) {
+ crew.addAbsenceDate(allDates);
+ }
+
+ attendances.setDangerStatus();
+
+ if (choice.equals("Q")) {
+ break;
+ }
+
+ if (choice.equals("1")) {
+ int month = nowDate.getMonthValue();
+ int day = nowDate.getDayOfMonth();
+ String dayOfWeek = nowDate.getDayOfWeek().getDisplayName(TextStyle.NARROW, Locale.KOREAN);
+ if (dayOfWeek.matches("[토|일]") || nowDate.isEqual(LocalDate.of(2024, 12, 25))) {
+ throw new IllegalArgumentException(
+ String.format("[ERROR] %d월 %d일 %s요일은 등교일이 아닙니다.", month, day, dayOfWeek));
+ }
+
+ String name = InputView.readNameForRegisterAttendance();
+ if (!attendances.contains(name)) {
+ throw new IllegalArgumentException("[ERROR] 등록되지 않은 닉네임입니다.");
+ }
+
+ if (attendances.isAlreadyAttend(name, nowDate)) {
+ throw new IllegalArgumentException("[ERROR] 이미 출석을 확인하였습니다. 필요한 경우 수정 기능을 이용해주세요.");
+ }
+
+ String rawTime = InputView.readTime();
+ LocalTime newTime = InputParser.parseTime(rawTime);
+ LocalTime startTime = LocalTime.of(8, 0, 0);
+ LocalTime endTime = LocalTime.of(23, 0, 0);
+ if (newTime.isBefore(startTime) || newTime.isAfter(endTime)) {
+ throw new IllegalArgumentException("[ERROR] 캠퍼스 운영 시간에만 출석이 가능합니다.");
+ }
+
+ Crew registerdCrew = attendances.registerAttendance(name, newTime, nowDate);
+ System.out.println(registerdCrew.getInfoAt(nowDate));
+
+ attendances.setDangerStatus();
+
+ continue;
+ }
+
+ if (choice.equals("2")) {
+ String name = InputView.readNameForModifyAttendance();
+ if (!attendances.contains(name)) {
+ throw new IllegalArgumentException("[ERROR] 등록되지 않은 닉네임입니다.");
+ }
+
+ String rawModifiedDate = InputView.readDate();
+ LocalDate modifiedDate = InputParser.parseDate(rawModifiedDate);
+ if (modifiedDate.isAfter(nowDate)) {
+ throw new IllegalArgumentException("[ERROR] 아직 수정할 수 없습니다.");
+ }
+
+ int month = modifiedDate.getMonthValue();
+ int day = modifiedDate.getDayOfMonth();
+ String dayOfWeek = modifiedDate.getDayOfWeek().getDisplayName(TextStyle.NARROW, Locale.KOREAN);
+ if (dayOfWeek.matches("[토|일]") || modifiedDate.isEqual(LocalDate.of(2024, 12, 25))) {
+ throw new IllegalArgumentException(
+ String.format("[ERROR] %d월 %d일 %s요일은 등교일이 아닙니다.", month, day, dayOfWeek));
+ }
+
+ String rawTime = InputView.readTime();
+ LocalTime newTime = InputParser.parseTime(rawTime);
+ LocalTime startTime = LocalTime.of(8, 0, 0);
+ LocalTime endTime = LocalTime.of(23, 0, 0);
+ if (newTime.isBefore(startTime) || newTime.isAfter(endTime)) {
+ throw new IllegalArgumentException("[ERROR] 캠퍼스 운영 시간에만 출석이 가능합니다.");
+ }
+
+ List crews = attendances.modifyAttendance(name, newTime, modifiedDate);
+ System.out.println(crews.get(0).getInfoAt(modifiedDate) + " -> "
+ + crews.get(1).getInfoAt(modifiedDate) + " 수정 완료!");
+
+ attendances.setDangerStatus();
+
+ continue;
+ }
+
+ if (choice.equals("3")) {
+ String name = InputView.readNameForModifyAttendance();
+ if (!attendances.contains(name)) {
+ throw new IllegalArgumentException("[ERROR] 등록되지 않은 닉네임입니다.");
+ }
+
+ System.out.printf("이번 달 %s의 출석 기록입니다.\n", name);
+ Crew crew = attendances.getCrew(name);
+ List dates = crew.getDates();
+ for (LocalDate date : dates) {
+ System.out.print("\n" + crew.getInfoAt(date));
+ }
+ System.out.println();
+
+ int attendanceCount = crew.getAttendanceCount();
+ int lateCount = crew.getLateCount();
+ int absenceCount = crew.getAbsenceCount();
+ System.out.println("\n출석: " + attendanceCount + "회");
+ System.out.println("지각: " + lateCount + "회");
+ System.out.println("결석: " + absenceCount + "회");
+
+ System.out.println("\n" + crew.getDangerState() + " 대상자입니다.");
+
+ continue;
+ }
+
+ if (choice.equals("4")) {
+ List crews = attendances.getCrews();
+ System.out.println("제적 위험자 조회 결과");
+ for (Crew crew : crews) {
+ System.out.printf("- %s: 결석 %d회, 지각 %d회 (%s)\n", crew.getName(), crew.getAbsenceCount(),
+ crew.getLateCount(), crew.getDangerState());
+ }
+ }
+
+ }
+ }
+
+ private static List getAllDates(LocalDate nowDate) {
+ List dates = List.of(
+ LocalDate.of(2024, 12, 2),
+ LocalDate.of(2024, 12, 3),
+ LocalDate.of(2024, 12, 4),
+ LocalDate.of(2024, 12, 5),
+ LocalDate.of(2024, 12, 6),
+
+ LocalDate.of(2024, 12, 9),
+ LocalDate.of(2024, 12, 10),
+ LocalDate.of(2024, 12, 11),
+ LocalDate.of(2024, 12, 12),
+ LocalDate.of(2024, 12, 13),
+
+ LocalDate.of(2024, 12, 16),
+ LocalDate.of(2024, 12, 17),
+ LocalDate.of(2024, 12, 18),
+ LocalDate.of(2024, 12, 19),
+ LocalDate.of(2024, 12, 20),
+
+ LocalDate.of(2024, 12, 23),
+ LocalDate.of(2024, 12, 24),
+ LocalDate.of(2024, 12, 26),
+ LocalDate.of(2024, 12, 27),
+
+ LocalDate.of(2024, 12, 30),
+ LocalDate.of(2024, 12, 31)
+ );
+
+ List resultDate = new ArrayList<>();
+ for (LocalDate date : dates) {
+ if (date.isBefore(nowDate)) {
+ resultDate.add(date);
+ }
+ }
+ return resultDate;
}
}
diff --git a/src/main/java/attendance/constant/Constant.java b/src/main/java/attendance/constant/Constant.java
new file mode 100644
index 0000000..41c9da6
--- /dev/null
+++ b/src/main/java/attendance/constant/Constant.java
@@ -0,0 +1,7 @@
+package attendance.constant;
+
+public final class Constant {
+
+// public static final int = ;
+// public static final String = ;
+}
diff --git a/src/main/java/attendance/constant/ErrorMessage.java b/src/main/java/attendance/constant/ErrorMessage.java
new file mode 100644
index 0000000..fc1984f
--- /dev/null
+++ b/src/main/java/attendance/constant/ErrorMessage.java
@@ -0,0 +1,19 @@
+package attendance.constant;
+
+public enum ErrorMessage {
+
+ FORMAT_ERROR("잘못된 형식을 입력하였습니다."),
+ ERROR("%d월 %d일 %s요일은 등교일이 아닙니다."),
+ ;
+
+ private static final String ERROR_MESSAGE_PREFIX = "[ERROR] ";
+ private final String errorMessage;
+
+ ErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getErrorMessage(Object... args) {
+ return ERROR_MESSAGE_PREFIX + String.format(errorMessage, args);
+ }
+}
diff --git a/src/main/java/attendance/controller/AttendanceController.java b/src/main/java/attendance/controller/AttendanceController.java
new file mode 100644
index 0000000..19db5a6
--- /dev/null
+++ b/src/main/java/attendance/controller/AttendanceController.java
@@ -0,0 +1,19 @@
+package attendance.controller;
+
+import attendance.service.AttendanceService;
+
+public class AttendanceController {
+
+ private final AttendanceService attendanceService;
+
+ public AttendanceController(AttendanceService attendanceService) {
+ this.attendanceService = attendanceService;
+ }
+
+ public void run() {
+
+ }
+
+
+}
+
diff --git a/src/main/java/attendance/domain/Attendance.java b/src/main/java/attendance/domain/Attendance.java
new file mode 100644
index 0000000..3730b22
--- /dev/null
+++ b/src/main/java/attendance/domain/Attendance.java
@@ -0,0 +1,69 @@
+package attendance.domain;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+public class Attendance implements Comparable {
+
+ private LocalDate date;
+ private String dayOfWeek;
+ private LocalTime time;
+ private String state;
+
+ public Attendance(LocalDate date, String dayOfWeek, LocalTime time, String state) {
+ this.date = date;
+ this.dayOfWeek = dayOfWeek;
+ this.time = time;
+ this.state = state;
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ public String getDayOfWeek() {
+ return dayOfWeek;
+ }
+
+ public LocalTime getTime() {
+ return time;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ @Override
+ public String toString() {
+ return "Attendance{" +
+ "date=" + date +
+ ", dayOfWeek='" + dayOfWeek + '\'' +
+ ", time=" + time +
+ ", state='" + state + '\'' +
+ '}';
+ }
+
+ public void modify(LocalDate modifiedDate, String dayOfWeek, LocalTime newTime, String attendanceState) {
+ this.date = modifiedDate;
+ this.dayOfWeek = dayOfWeek;
+ this.time = newTime;
+ this.state = attendanceState;
+ }
+
+ public Attendance clone() {
+ return new Attendance(this.date, this.dayOfWeek, this.time, this.state);
+ }
+
+ @Override
+ public int compareTo(Attendance o) {
+ if (this.date.isBefore(o.date)) {
+ return -1;
+ }
+
+ if (this.date.isAfter(o.date)) {
+ return 1;
+ }
+
+ return 0;
+ }
+}
diff --git a/src/main/java/attendance/domain/Attendances.java b/src/main/java/attendance/domain/Attendances.java
new file mode 100644
index 0000000..efa4e3e
--- /dev/null
+++ b/src/main/java/attendance/domain/Attendances.java
@@ -0,0 +1,96 @@
+package attendance.domain;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class Attendances {
+
+ private List crews;
+
+ public Attendances() {
+ crews = new ArrayList<>();
+ }
+
+ public static Attendances newInstance() {
+ return new Attendances();
+ }
+
+ public void addAttendance(String name, LocalDate date, LocalTime time) {
+ Crew crew = new Crew(name);
+ for (Crew crew1 : crews) {
+ if (crew1.getName().equals(name)) {
+ crew = crew1;
+ }
+ }
+
+ crew.registerDateAndTime(date, time);
+
+ if (!crews.contains(crew)) {
+ crews.add(crew);
+ }
+ }
+
+ public boolean contains(String name) {
+ Crew crew = new Crew(name);
+ return crews.contains(crew);
+ }
+
+ public boolean isAlreadyAttend(String name, LocalDate nowDate) {
+ Crew checkCrew = new Crew(name);
+ for (Crew crew : crews) {
+ if (crew.equals(checkCrew) && crew.containsDate(nowDate)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Crew registerAttendance(String name, LocalTime newTime, LocalDate nowDate) {
+ Crew checkCrew = new Crew(name);
+ for (Crew crew : crews) {
+ if (crew.equals(checkCrew)) {
+ return crew.registerAttendance(newTime, nowDate);
+ }
+ }
+ return null;
+ }
+
+ public List getCrews() {
+ Collections.sort(crews);
+ return crews;
+ }
+
+ public Crew getCrew(String name) {
+ for (Crew crew : crews) {
+ if (crew.equals(new Crew(name))) {
+ return crew;
+ }
+ }
+ return null;
+ }
+
+ public List modifyAttendance(String name, LocalTime newTime, LocalDate modifiedDate) {
+ List crews = new ArrayList<>();
+ Crew checkCrew = new Crew(name);
+ for (Crew crew : this.crews) {
+ if (crew.equals(checkCrew)) {
+ crews.add(crew.clone());
+ crew.modifyAttendance(newTime, modifiedDate);
+ crews.add(crew);
+ return crews;
+ }
+ }
+ return null;
+ }
+
+ public void setDangerStatus() {
+ for (Crew crew : crews) {
+ crew.setDangerStatus();
+ }
+ }
+}
diff --git a/src/main/java/attendance/domain/Crew.java b/src/main/java/attendance/domain/Crew.java
new file mode 100644
index 0000000..adb3830
--- /dev/null
+++ b/src/main/java/attendance/domain/Crew.java
@@ -0,0 +1,237 @@
+package attendance.domain;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.TextStyle;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+
+public class Crew implements Comparable {
+
+ private String name;
+ private String dangerState;
+ private List attendances;
+ private int attendanceCount;
+ private int lateCount;
+ private int absenceCount;
+
+ public Crew(String name) {
+ this.name = name;
+ attendances = new ArrayList<>();
+ }
+
+ public Crew(String name, List attendances, int lateCount, int absenceCount, String dangerState) {
+ this.name = name;
+ this.attendances = attendances;
+ this.lateCount = lateCount;
+ this.absenceCount = absenceCount;
+ this.dangerState = dangerState;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+ Crew crew = (Crew) object;
+ return Objects.equals(name, crew.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name);
+ }
+
+ public void registerDateAndTime(LocalDate date, LocalTime time) {
+ String dayOfWeek = date.getDayOfWeek().getDisplayName(TextStyle.NARROW, Locale.KOREAN);
+
+ String attendanceState = getAttendanceState(time, dayOfWeek);
+
+ attendances.add(new Attendance(date, dayOfWeek, time, attendanceState));
+ }
+
+ @Override
+ public String toString() {
+ return "Crew{" +
+ "name='" + name + '\'' +
+ ", state=" + dangerState +
+ ", attendances=" + attendances +
+ ", lateCount=" + lateCount +
+ ", absenceCount=" + absenceCount +
+ '}';
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean containsDate(LocalDate date) {
+ for (Attendance attendance : attendances) {
+ if (attendance.getDate().isEqual(date)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Crew registerAttendance(LocalTime newTime, LocalDate nowDate) {
+ String dayOfWeek = nowDate.getDayOfWeek().getDisplayName(TextStyle.NARROW, Locale.KOREAN);
+ attendances.add(new Attendance(nowDate, dayOfWeek, newTime, getAttendanceState(newTime, dayOfWeek)));
+ return this;
+ }
+
+ private String getAttendanceState(LocalTime time, String dayOfWeek) {
+ LocalTime lateTime = LocalTime.parse("10:05");
+ LocalTime absenceTime = LocalTime.parse("10:30");
+ if (dayOfWeek.equals("월")) {
+ lateTime = LocalTime.parse("13:05");
+ absenceTime = LocalTime.parse("13:30");
+ }
+
+ String attendanceState = "출석";
+
+ if (time.isAfter(absenceTime)) {
+ attendanceState = "결석";
+ absenceCount++;
+ return attendanceState;
+ }
+
+ if (time.isAfter(lateTime)) {
+ attendanceState = "지각";
+ lateCount++;
+ return attendanceState;
+ }
+
+ attendanceCount++;
+
+ return attendanceState;
+ }
+
+ public String getInfoAt(LocalDate nowDate) {
+ for (Attendance attendance : attendances) {
+ if (attendance.getDate().isEqual(nowDate)) {
+ int month = attendance.getDate().getMonthValue();
+ int day = attendance.getDate().getDayOfMonth();
+ String dayOfWeek = attendance.getDayOfWeek();
+ int hour = attendance.getTime().getHour();
+ int minute = attendance.getTime().getMinute();
+ String state = attendance.getState();
+
+ if (state.equals("결석")) {
+ return month + "월 " + day + "일 " + dayOfWeek + "요일 "
+ + "--:--" + " (" + state + ")";
+ }
+
+ return month + "월 " + day + "일 " + dayOfWeek + "요일 "
+ + String.format("%02d:%02d", hour, minute) + " (" + state + ")";
+ }
+ }
+ return null;
+ }
+
+ public Crew clone() {
+ List copyAttendances = new ArrayList<>();
+ for (Attendance attendance : attendances) {
+ copyAttendances.add(attendance.clone());
+ }
+ return new Crew(this.name, copyAttendances, this.lateCount, this.absenceCount, this.dangerState);
+ }
+
+ public void modifyAttendance(LocalTime newTime, LocalDate modifiedDate) {
+ String dayOfWeek = modifiedDate.getDayOfWeek().getDisplayName(TextStyle.NARROW, Locale.KOREAN);
+ for (Attendance attendance : attendances) {
+ if (attendance.getDate().isEqual(modifiedDate)) {
+ attendance.modify(modifiedDate, dayOfWeek, newTime, getAttendanceState(newTime, dayOfWeek));
+ return;
+ }
+ }
+ attendances.add(new Attendance(modifiedDate, dayOfWeek, newTime, getAttendanceState(newTime, dayOfWeek)));
+ }
+
+ public int getLateCount() {
+ return lateCount;
+ }
+
+ public int getAbsenceCount() {
+ return absenceCount;
+ }
+
+ public int getAttendanceCount() {
+ return attendanceCount;
+ }
+
+ public String getDangerState() {
+ return dangerState;
+ }
+
+ public List getDates() {
+ List dates = new ArrayList<>();
+ Collections.sort(attendances);
+ for (Attendance attendance : attendances) {
+ dates.add(attendance.getDate());
+ }
+ return dates;
+ }
+
+ public void addAbsenceDate(List allDates) {
+ for (LocalDate date : allDates) {
+ boolean isContain = false;
+ for (Attendance attendance : attendances) {
+ if (attendance.getDate().isEqual(date)) {
+ isContain = true;
+ break;
+ }
+ }
+ if (!isContain) {
+ this.absenceCount++;
+ String dayOfWeek = date.getDayOfWeek().getDisplayName(TextStyle.NARROW, Locale.KOREAN);
+ attendances.add(new Attendance(date, dayOfWeek, LocalTime.of(0,0), "결석"));
+ }
+ }
+ }
+
+ public void setDangerStatus() {
+ int absenceCount = this.absenceCount + lateCount / 3;
+
+ if (absenceCount > 5) {
+ dangerState = "제적";
+ return;
+ }
+
+ if (absenceCount >= 3) {
+ dangerState = "면담";
+ return;
+ }
+
+ if (absenceCount >= 2) {
+ dangerState = "경고";
+ return;
+ }
+
+ dangerState = "";
+ }
+
+ @Override
+ public int compareTo(Crew o) {
+ if (this.absenceCount > o.absenceCount) {
+ return -1;
+ }
+
+ if (this.absenceCount == o.absenceCount) {
+ if (this.lateCount > o.lateCount) {
+ return -1;
+ }
+
+ if (this.lateCount < o.lateCount) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ return 0;
+ }
+}
diff --git a/src/main/java/attendance/domain/DangerState.java b/src/main/java/attendance/domain/DangerState.java
new file mode 100644
index 0000000..13ccf85
--- /dev/null
+++ b/src/main/java/attendance/domain/DangerState.java
@@ -0,0 +1,8 @@
+package attendance.domain;
+
+public enum DangerState {
+ 위험,
+ 출석,
+ 지각,
+ ;
+}
diff --git a/src/main/java/attendance/service/AttendanceService.java b/src/main/java/attendance/service/AttendanceService.java
new file mode 100644
index 0000000..4d56247
--- /dev/null
+++ b/src/main/java/attendance/service/AttendanceService.java
@@ -0,0 +1,8 @@
+package attendance.service;
+
+public class AttendanceService {
+
+ // 도메인 객체 인스턴스 변수로 저장
+
+ // 메서드
+}
diff --git a/src/main/java/attendance/time/DateTime.java b/src/main/java/attendance/time/DateTime.java
new file mode 100644
index 0000000..ab20062
--- /dev/null
+++ b/src/main/java/attendance/time/DateTime.java
@@ -0,0 +1,11 @@
+package attendance.time;
+
+import camp.nextstep.edu.missionutils.DateTimes;
+import java.time.LocalDate;
+
+public class DateTime {
+
+ public static LocalDate now() {
+ return DateTimes.now().toLocalDate();
+ }
+}
diff --git a/src/main/java/attendance/util/InputParser.java b/src/main/java/attendance/util/InputParser.java
new file mode 100644
index 0000000..f771986
--- /dev/null
+++ b/src/main/java/attendance/util/InputParser.java
@@ -0,0 +1,65 @@
+package attendance.util;
+
+import static attendance.constant.ErrorMessage.FORMAT_ERROR;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class InputParser {
+
+ private static final String FIRST_DELIMITER = ",";
+ private static final String SECOND_DELIMITER = "-";
+ private static final String YES = "Y";
+
+ private InputParser() {
+ }
+
+// public static List parsePurchaseProducts(String rawInput) {
+// Validator.validateNullOrBlank(rawInput);
+// rawInput = rawInput.strip();
+//
+// Validator.validateCsvFormat(rawInput);
+//
+// List purchaseProducts = new ArrayList<>();
+// String[] split = rawInput.split(FIRST_DELIMITER);
+// for (String s : split) {
+// String[] order = s.strip().substring(1, s.length() - 1).split(SECOND_DELIMITER);
+// String name = order[0];
+// int quantity = NumberConvertor.convertToNumber(order[1]);
+//
+// Validator.validateQuantity(quantity);
+//
+// for (int i = 0; i < quantity; i++) {
+// purchaseProducts.add(name);
+// }
+// }
+//
+// return purchaseProducts;
+// }
+
+ public static String parseChoice(String rawChoice) {
+ rawChoice = rawChoice.strip();
+ Validator.validateChoiceFormat(rawChoice);
+ return rawChoice;
+ }
+
+ public static LocalTime parseTime(String rawTime) {
+ rawTime = rawTime.strip();
+ Validator.validateTimeFormat(rawTime);
+ return LocalTime.parse(rawTime);
+ }
+
+ public static LocalDate parseDate(String rawModifiedDate) {
+ rawModifiedDate = rawModifiedDate.strip();
+ if (!rawModifiedDate.matches("\\d+")) {
+ throw new IllegalArgumentException(FORMAT_ERROR.getErrorMessage());
+ }
+ int date = NumberConvertor.convertToNumber(rawModifiedDate);
+ if (date < 1 || date > 31) {
+ throw new IllegalArgumentException(FORMAT_ERROR.getErrorMessage());
+ }
+ return LocalDate.of(2024, 12, date);
+ }
+}
diff --git a/src/main/java/attendance/util/NumberConvertor.java b/src/main/java/attendance/util/NumberConvertor.java
new file mode 100644
index 0000000..908f5c1
--- /dev/null
+++ b/src/main/java/attendance/util/NumberConvertor.java
@@ -0,0 +1,12 @@
+package attendance.util;
+
+public final class NumberConvertor {
+
+ public static Integer convertToNumber(String input) {
+ try {
+ return Integer.parseInt(input);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException();
+ }
+ }
+}
diff --git a/src/main/java/attendance/util/Validator.java b/src/main/java/attendance/util/Validator.java
new file mode 100644
index 0000000..8633b9f
--- /dev/null
+++ b/src/main/java/attendance/util/Validator.java
@@ -0,0 +1,38 @@
+package attendance.util;
+
+import static attendance.constant.ErrorMessage.FORMAT_ERROR;
+
+public final class Validator {
+
+ private static final String CSV_FORMAT = "^ *(\\[[가-힣a-zA-Z]+-\\d+])+ *(, *(\\[[가-힣a-zA-Z]+-\\d+])+ *)*$";
+ private static final String CHOICE = " *[1234Q] *";
+ private static final String TIME_FORMAT = "\\d{2}:\\d{2}";
+
+ private Validator() {
+ }
+
+ public static void validateCsvFormat(String input) {
+ if (!input.matches(CSV_FORMAT)) {
+ throw new IllegalArgumentException(FORMAT_ERROR.getErrorMessage());
+ }
+ }
+
+ public static void validateChoiceFormat(String rawChoice) {
+ if (!rawChoice.matches(CHOICE)) {
+ throw new IllegalArgumentException(FORMAT_ERROR.getErrorMessage());
+ }
+ }
+
+ public static void validateTimeFormat(String rawTime) {
+ if (!rawTime.matches(TIME_FORMAT)) {
+ throw new IllegalArgumentException(FORMAT_ERROR.getErrorMessage());
+ }
+
+ String[] split = rawTime.split(":");
+ int hour = Integer.parseInt(split[0]);
+ int min = Integer.parseInt(split[1]);
+ if (hour < 0 || hour > 23 || min < 0 || min > 59) {
+ throw new IllegalArgumentException(FORMAT_ERROR.getErrorMessage());
+ }
+ }
+}
diff --git a/src/main/java/attendance/util/file/FileReader.java b/src/main/java/attendance/util/file/FileReader.java
new file mode 100644
index 0000000..a77afe2
--- /dev/null
+++ b/src/main/java/attendance/util/file/FileReader.java
@@ -0,0 +1,30 @@
+package attendance.util.file;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileReader {
+
+ private final java.io.FileReader fr;
+
+ public FileReader(String fileName) throws IOException {
+ fr = new java.io.FileReader(fileName, UTF_8);
+ }
+
+ public List readLines() throws IOException {
+ List contents = new ArrayList<>();
+ BufferedReader br = new BufferedReader(fr);
+
+ String line;
+ while ((line = br.readLine()) != null) {
+ contents.add(line);
+ }
+ br.close();
+
+ return new ArrayList<>(contents);
+ }
+}
diff --git a/src/main/java/attendance/util/file/FileWriter.java b/src/main/java/attendance/util/file/FileWriter.java
new file mode 100644
index 0000000..fea7e9e
--- /dev/null
+++ b/src/main/java/attendance/util/file/FileWriter.java
@@ -0,0 +1,43 @@
+package attendance.util.file;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.IOException;
+
+public class FileWriter {
+
+ private final String fileName;
+ private java.io.FileWriter fw;
+
+ public FileWriter(String fileName) {
+ this.fileName = fileName;
+ }
+
+ // 파일에 내용 한 번에 쓰기 - append 기본값(false)
+ public void writeAll(String contents) throws IOException {
+ fw = new java.io.FileWriter(fileName, UTF_8, false);
+ fw.write(contents);
+ fw.close();
+ }
+
+ // 파일에 내용 한 번에 쓰기 - append 선택
+ public void writeAll(String contents, boolean append) throws IOException {
+ fw = new java.io.FileWriter(fileName, UTF_8, append);
+ fw.write(contents);
+ fw.close();
+ }
+
+ // 파일에 내용 한 줄씩 쓰기 - append 기본값(false)
+ public void writeLine(String contents) throws IOException {
+ fw = new java.io.FileWriter(fileName, UTF_8, false);
+ fw.write(contents);
+ fw.close();
+ }
+
+ // 파일에 내용 한 줄씩 쓰기 - append 선택
+ public void writeLine(String contents, boolean append) throws IOException {
+ fw = new java.io.FileWriter(fileName, UTF_8, append);
+ fw.write(contents);
+ fw.close();
+ }
+}
diff --git a/src/main/java/attendance/view/InputView.java b/src/main/java/attendance/view/InputView.java
new file mode 100644
index 0000000..68e75ee
--- /dev/null
+++ b/src/main/java/attendance/view/InputView.java
@@ -0,0 +1,47 @@
+package attendance.view;
+
+import static camp.nextstep.edu.missionutils.Console.readLine;
+
+import camp.nextstep.edu.missionutils.Console;
+import java.time.LocalDate;
+import java.time.format.TextStyle;
+import java.util.Locale;
+
+public class InputView {
+
+ private static final String CHOICE_REQUEST = "";
+
+ public static String readChoice(LocalDate nowDate) {
+ System.out.println(CHOICE_REQUEST);
+ int month = nowDate.getMonth().getValue();
+ int day = nowDate.getDayOfMonth();
+ String dayOfWeek = nowDate.getDayOfWeek().getDisplayName(TextStyle.NARROW, Locale.KOREAN);
+ System.out.printf("오늘은 %d월 %d일 %s요일입니다. 기능을 선택해주세요.\n", month, day, dayOfWeek);
+ System.out.println("1. 출석 확인");
+ System.out.println("2. 출석 수정");
+ System.out.println("3. 크루별 출석 기록 확인");
+ System.out.println("4. 제적 위험자 확인");
+ System.out.println("Q. 종료");
+ return Console.readLine();
+ }
+
+ public static String readNameForRegisterAttendance() {
+ System.out.println("닉네임을 입력해주세요.");
+ return Console.readLine();
+ }
+
+ public static String readNameForModifyAttendance() {
+ System.out.println("출석을 수정하려는 크루의 닉네임을 입력해주세요.");
+ return Console.readLine();
+ }
+
+ public static String readTime() {
+ System.out.println("등교 시간을 입력해 주세요.");
+ return Console.readLine();
+ }
+
+ public static String readDate() {
+ System.out.println("수정하려는 날짜(일)를 입력해 주세요.");
+ return Console.readLine();
+ }
+}
diff --git a/src/main/java/attendance/view/OutputView.java b/src/main/java/attendance/view/OutputView.java
new file mode 100644
index 0000000..977aaf7
--- /dev/null
+++ b/src/main/java/attendance/view/OutputView.java
@@ -0,0 +1,58 @@
+//package attendance.view;
+//
+//import java.util.Map;
+//import store.domain.Product;
+//import store.dto.ReceiptDto;
+//import store.dto.StockDto;
+//
+//public class OutputView {
+//
+// private static final String WELL_COME_MESSAGE = "\n안녕하세요. W편의점입니다.\n현재 보유하고 있는 상품입니다.\n";
+//
+// public static void printStock(StockDto stock) {
+// System.out.println(WELL_COME_MESSAGE);
+// System.out.println(stock.stock());
+// }
+//
+// public static void printErrorMessage(IllegalArgumentException e) {
+// System.out.println(e.getMessage());
+// }
+//
+// public static void printReceipt(ReceiptDto receiptDto) {
+// Map purchaseProducts = receiptDto.purchaseProducts();
+// Map presentProducts = receiptDto.presentProducts();
+// int membershipDiscountAmount = receiptDto.membershipDiscountAmount();
+//
+// int totalPurchaseQuantity = 0;
+// int totalPurchasePrice = 0;
+// int totalPresentPrice = 0;
+//
+//
+// System.out.println("\n============W 편의점============");
+// System.out.println("상품명 수량 금액");
+// for (Product product : purchaseProducts.keySet()) {
+// int quantity = purchaseProducts.get(product);
+// int price = product.getPrice(quantity);
+// System.out.printf("%-16s%-8s%,d\n", product.getName(), quantity, price);
+//
+// totalPurchaseQuantity += quantity;
+// totalPurchasePrice += price;
+// }
+//
+// System.out.println("============증 정============");
+// for (Product product : presentProducts.keySet()) {
+// int quantity = presentProducts.get(product);
+// int price = product.getPrice(quantity);
+// System.out.printf("%-16s%d\n", product.getName(), quantity);
+//
+// totalPresentPrice += price;
+// }
+//
+// int finalPrice = totalPurchasePrice - totalPresentPrice - membershipDiscountAmount;
+// System.out.println("==============================");
+// System.out.printf("%-15s%-8s%,d\n", "총구매액", totalPurchaseQuantity, totalPurchasePrice);
+// System.out.printf("%-23s-%,d\n", "행사할인", totalPresentPrice);
+// System.out.printf("%-23s-%,d\n", "멤버십할인", membershipDiscountAmount);
+// System.out.printf("%-24s%,d\n", "내실돈", finalPrice);
+// }
+//}
diff --git a/src/test/java/attendance/ApplicationTest.java b/src/test/java/attendance/ApplicationTest.java
index c1b4349..0cf183f 100644
--- a/src/test/java/attendance/ApplicationTest.java
+++ b/src/test/java/attendance/ApplicationTest.java
@@ -1,6 +1,7 @@
package attendance;
import camp.nextstep.edu.missionutils.test.NsTest;
+import java.io.IOException;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
@@ -10,6 +11,7 @@
import static org.assertj.core.api.Assertions.assertThatThrownBy;
class ApplicationTest extends NsTest {
+
@Test
void 잘못된_형식_예외_테스트() {
assertNowTest(
@@ -79,6 +81,10 @@ class ApplicationTest extends NsTest {
@Override
protected void runMain() {
- Application.main(new String[]{});
+ try {
+ Application.main(new String[]{});
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
}