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 -스크린샷 2025-11-27 20 49 53 -스크린샷 2025-11-27 20 49 55 -스크린샷 2025-11-27 20 49 56 -스크린샷 2025-11-27 20 49 57 -스크린샷 2025-11-27 20 49 58 -스크린샷 2025-11-27 20 50 05 -스크린샷 2025-11-27 20 50 06 -스크린샷 2025-11-27 20 50 08 -스크린샷 2025-11-27 20 50 09 -스크린샷 2025-11-27 20 50 10 -스크린샷 2025-11-27 20 50 11 -스크린샷 2025-11-27 20 51 49 +## 기능 구현 목록 +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); + } } }