Skip to content
68 changes: 56 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,58 @@
# java-attendance-precourse

<img width="800" height="1100" alt="스크린샷 2025-11-27 20 49 53" src="https://github.com/user-attachments/assets/2d22a1eb-c88c-4874-be38-522ca8f39ef0" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 49 55" src="https://github.com/user-attachments/assets/3e800b13-b514-46ca-ab04-f7b96a91e3d3" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 49 56" src="https://github.com/user-attachments/assets/2612dc78-c368-43d4-a889-c96635bb81e2" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 49 57" src="https://github.com/user-attachments/assets/91b8f32d-d8eb-4afb-8618-2db857046be5" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 49 58" src="https://github.com/user-attachments/assets/a60dff46-b1ba-4a9c-b553-b2d33b6f83c3" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 50 05" src="https://github.com/user-attachments/assets/ca7622c4-a817-42e6-bd51-b82ea4e64687" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 50 06" src="https://github.com/user-attachments/assets/41ba608f-32d7-4cd5-81b8-4ce6815a8df0" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 50 08" src="https://github.com/user-attachments/assets/01a2d89f-8974-472b-bc3c-66aa38860e20" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 50 09" src="https://github.com/user-attachments/assets/6af5f4c8-d713-484d-925b-612c4c1dd28e" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 50 10" src="https://github.com/user-attachments/assets/902e6200-fa14-43aa-b0f1-be86d9740973" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 50 11" src="https://github.com/user-attachments/assets/d123d70f-ade3-4f0a-afd8-3b92c95f6d44" />
<img width="800" height="1100" alt="스크린샷 2025-11-27 20 51 49" src="https://github.com/user-attachments/assets/3889a8a8-86e2-4bc8-aca0-8577a5b8253e" />
## 기능 구현 목록
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. 종료
198 changes: 196 additions & 2 deletions src/main/java/attendance/Application.java
Original file line number Diff line number Diff line change
@@ -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<String> 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<LocalDate> allDates = getAllDates(nowDate);
List<Crew> 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<Crew> 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<LocalDate> 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<Crew> 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<LocalDate> getAllDates(LocalDate nowDate) {
List<LocalDate> 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<LocalDate> resultDate = new ArrayList<>();
for (LocalDate date : dates) {
if (date.isBefore(nowDate)) {
resultDate.add(date);
}
}
return resultDate;
}
}
7 changes: 7 additions & 0 deletions src/main/java/attendance/constant/Constant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package attendance.constant;

public final class Constant {

// public static final int = ;
// public static final String = ;
}
19 changes: 19 additions & 0 deletions src/main/java/attendance/constant/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
19 changes: 19 additions & 0 deletions src/main/java/attendance/controller/AttendanceController.java
Original file line number Diff line number Diff line change
@@ -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() {

}


}

69 changes: 69 additions & 0 deletions src/main/java/attendance/domain/Attendance.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package attendance.domain;

import java.time.LocalDate;
import java.time.LocalTime;

public class Attendance implements Comparable<Attendance> {

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;
}
}
Loading