From 26f124bd0fb7c03f705131e4adc006d759f26f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Mon, 31 Aug 2026 22:17:35 +0400 Subject: [PATCH 1/4] build: add Apache Commons CSV dependency - add commons-csv for CSV read and write support - configure version 1.14.1 in app/pom.xml --- app/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/pom.xml b/app/pom.xml index 5afca24..3094443 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -48,6 +48,12 @@ 3.1.5 + + org.apache.commons + commons-csv + 1.14.1 + + From 4dadefd87f644426d53ef8a47581a4e3a197997e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Mon, 31 Aug 2026 22:17:51 +0400 Subject: [PATCH 2/4] feat(io): add StudentCsvWriter - add CSV writer for individual students - support sequential writing of student lists - preserve existing CSV data and avoid duplicate headers - reject missing files with IOException --- .../java/ru/project/io/StudentCsvWriter.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 app/src/main/java/ru/project/io/StudentCsvWriter.java diff --git a/app/src/main/java/ru/project/io/StudentCsvWriter.java b/app/src/main/java/ru/project/io/StudentCsvWriter.java new file mode 100644 index 0000000..8795fbc --- /dev/null +++ b/app/src/main/java/ru/project/io/StudentCsvWriter.java @@ -0,0 +1,88 @@ +package ru.project.io; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import ru.project.collection.MyList; +import ru.project.student.Student; + +public class StudentCsvWriter { + + private static final String GROUP_NUMBER_HEADER = "groupNumber"; + private static final String AVERAGE_GRADE_HEADER = "averageGrade"; + private static final String RECORD_BOOK_NUMBER_HEADER = "recordBookNumber"; + + private final String absoluteFilePath; + + public StudentCsvWriter(String absoluteFilePath) { + this.absoluteFilePath = absoluteFilePath; + } + + public void write(Student student) throws IOException { + ensureFileExists(); + + boolean fileIsEmpty = isFileEmpty(); + + try (CSVPrinter printer = createPrinter()) { + if (fileIsEmpty) { + writeHeader(printer); + } + + writeStudent(printer, student); + } + } + + public void write(MyList[] students) throws IOException { + ensureFileExists(); + + boolean fileIsEmpty = isFileEmpty(); + + try (CSVPrinter printer = createPrinter()) { + if (fileIsEmpty) { + writeHeader(printer); + } + + for (MyList studentList : students) { + for (int i = 0; i < studentList.size(); i++) { + writeStudent(printer, studentList.get(i)); + } + } + } + } + + private CSVPrinter createPrinter() throws IOException { + BufferedWriter writer = + new BufferedWriter( + new OutputStreamWriter( + new FileOutputStream(absoluteFilePath, true), StandardCharsets.UTF_8)); + + return new CSVPrinter(writer, CSVFormat.DEFAULT); + } + + private void writeHeader(CSVPrinter printer) throws IOException { + printer.printRecord(GROUP_NUMBER_HEADER, AVERAGE_GRADE_HEADER, RECORD_BOOK_NUMBER_HEADER); + } + + private void writeStudent(CSVPrinter printer, Student student) throws IOException { + + printer.printRecord( + student.getGroupNumber(), student.getAverageGrade(), student.getRecordBookNumber()); + } + + private void ensureFileExists() throws IOException { + File file = new File(absoluteFilePath); + + if (!file.isFile()) { + throw new IOException("CSV file does not exist: " + absoluteFilePath); + } + } + + private boolean isFileEmpty() { + return new File(absoluteFilePath).length() == 0; + } +} From be73b0fbf85eda561fce03e891ebfb1fc867ced2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BE=D0=BB=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=93=D0=B5?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D0=BD?= Date: Mon, 31 Aug 2026 22:18:22 +0400 Subject: [PATCH 3/4] test(io): add StudentCsvWriter tests - test writing to empty and existing CSV files - verify append behavior and header handling - test writing arrays of student lists - verify student order and empty lists - test IOException for missing files --- .../ru/project/io/StudentCsvWriterTest.java | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 app/src/test/java/ru/project/io/StudentCsvWriterTest.java diff --git a/app/src/test/java/ru/project/io/StudentCsvWriterTest.java b/app/src/test/java/ru/project/io/StudentCsvWriterTest.java new file mode 100644 index 0000000..d5da367 --- /dev/null +++ b/app/src/test/java/ru/project/io/StudentCsvWriterTest.java @@ -0,0 +1,194 @@ +package ru.project.io; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import ru.project.collection.MyLinkedList; +import ru.project.collection.MyList; +import ru.project.student.Student; + +class StudentCsvWriterTest { + + @TempDir Path tempDir; + + @Test + void writeShouldWriteHeaderAndStudentToEmptyFile() throws Exception { + Path file = tempDir.resolve("students.csv"); + Files.createFile(file); + + Student student = new Student("A12", 4.5, 10001); + + StudentCsvWriter writer = new StudentCsvWriter(file.toAbsolutePath().toString()); + + writer.write(student); + + List lines = Files.readAllLines(file); + + assertEquals(List.of("groupNumber,averageGrade,recordBookNumber", "A12,4.5,10001"), lines); + } + + @Test + void writeShouldAppendStudentToExistingFile() throws Exception { + Path file = tempDir.resolve("students.csv"); + + Files.write(file, List.of("groupNumber,averageGrade,recordBookNumber", "A12,4.5,10001")); + + Student student = new Student("B07", 3.8, 10002); + + StudentCsvWriter writer = new StudentCsvWriter(file.toAbsolutePath().toString()); + + writer.write(student); + + List lines = Files.readAllLines(file); + + assertEquals( + List.of("groupNumber,averageGrade,recordBookNumber", "A12,4.5,10001", "B07,3.8,10002"), + lines); + } + + @Test + void writeShouldNotCreateMissingFile() { + Path file = tempDir.resolve("students.csv"); + + Student student = new Student("A12", 4.5, 10001); + + StudentCsvWriter writer = new StudentCsvWriter(file.toAbsolutePath().toString()); + + assertThrows(IOException.class, () -> writer.write(student)); + + assertFalse(Files.exists(file)); + } + + @Test + void writeShouldAppendSeveralStudentsInOrder() throws Exception { + Path file = tempDir.resolve("students.csv"); + Files.createFile(file); + + Student student1 = new Student("A12", 4.5, 10001); + Student student2 = new Student("B07", 3.8, 10002); + Student student3 = new Student("C25", 4.9, 10003); + + StudentCsvWriter writer = new StudentCsvWriter(file.toAbsolutePath().toString()); + + writer.write(student1); + writer.write(student2); + writer.write(student3); + + List lines = Files.readAllLines(file); + + assertEquals( + List.of( + "groupNumber,averageGrade,recordBookNumber", + "A12,4.5,10001", + "B07,3.8,10002", + "C25,4.9,10003"), + lines); + } + + @Test + void writeArrayShouldAppendStudentsFromAllListsInOrder() throws Exception { + + Path file = tempDir.resolve("students.csv"); + Files.createFile(file); + + Student student1 = new Student("A12", 4.5, 10001); + Student student2 = new Student("B07", 3.8, 10002); + Student student3 = new Student("C25", 4.9, 10003); + Student student4 = new Student("D15", 4.2, 10004); + Student student5 = new Student("E20", 3.6, 10005); + + MyList firstList = new MyLinkedList<>(); + firstList.add(student1); + firstList.add(student2); + + MyList secondList = new MyLinkedList<>(); + secondList.add(student3); + + MyList thirdList = new MyLinkedList<>(); + thirdList.add(student4); + thirdList.add(student5); + + @SuppressWarnings("unchecked") + MyList[] students = new MyList[] {firstList, secondList, thirdList}; + + StudentCsvWriter writer = new StudentCsvWriter(file.toAbsolutePath().toString()); + + writer.write(students); + + List lines = Files.readAllLines(file); + + assertEquals( + List.of( + "groupNumber,averageGrade,recordBookNumber", + "A12,4.5,10001", + "B07,3.8,10002", + "C25,4.9,10003", + "D15,4.2,10004", + "E20,3.6,10005"), + lines); + } + + @Test + void writeArrayShouldSkipEmptyLists() throws Exception { + Path file = tempDir.resolve("students.csv"); + Files.createFile(file); + + Student student = new Student("A12", 4.5, 10001); + + MyList emptyList = new MyLinkedList<>(); + + MyList nonEmptyList = new MyLinkedList<>(); + nonEmptyList.add(student); + + @SuppressWarnings("unchecked") + MyList[] students = new MyList[] {emptyList, nonEmptyList, new MyLinkedList<>()}; + + StudentCsvWriter writer = new StudentCsvWriter(file.toAbsolutePath().toString()); + + writer.write(students); + + List lines = Files.readAllLines(file); + + assertEquals(List.of("groupNumber,averageGrade,recordBookNumber", "A12,4.5,10001"), lines); + } + + @Test + void writeEmptyArrayShouldNotModifyFile() throws Exception { + Path file = tempDir.resolve("students.csv"); + + List initialContent = + List.of("groupNumber,averageGrade,recordBookNumber", "A12,4.5,10001"); + + Files.write(file, initialContent); + + @SuppressWarnings("unchecked") + MyList[] students = new MyList[0]; + + StudentCsvWriter writer = new StudentCsvWriter(file.toAbsolutePath().toString()); + + writer.write(students); + + assertEquals(initialContent, Files.readAllLines(file)); + } + + @Test + void writeEmptyArrayShouldNotCreateMissingFile() { + Path file = tempDir.resolve("students.csv"); + + @SuppressWarnings("unchecked") + MyList[] students = new MyList[0]; + + StudentCsvWriter writer = new StudentCsvWriter(file.toAbsolutePath().toString()); + + assertThrows(IOException.class, () -> writer.write(students)); + + assertFalse(Files.exists(file)); + } +} From 0144d42d0071bb2d3be0669d0acff5894dac381d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D0=B0=D0=B4=D0=B6=D0=B8=D0=BA=D1=83=D1=80=D0=B1?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=A2=D0=B5=D0=BC=D0=B8=D1=80=D0=BB?= =?UTF-8?q?=D0=B0=D0=BD?= Date: Tue, 1 Sep 2026 17:54:21 +0300 Subject: [PATCH 4/4] fix : Student and collection import --- app/src/main/java/ru/project/search/StudentSearchService.java | 4 ++-- app/src/test/java/ru/project/search/StudentSearchTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/ru/project/search/StudentSearchService.java b/app/src/main/java/ru/project/search/StudentSearchService.java index 4ced3c9..4d92689 100644 --- a/app/src/main/java/ru/project/search/StudentSearchService.java +++ b/app/src/main/java/ru/project/search/StudentSearchService.java @@ -1,7 +1,7 @@ package ru.project.search; -import ru.project.list.MyList; -import ru.project.list.MyLinkedList; +import ru.project.collection.MyList; +import ru.project.collection.MyLinkedList; import ru.project.student.Student; public class StudentSearchService { diff --git a/app/src/test/java/ru/project/search/StudentSearchTest.java b/app/src/test/java/ru/project/search/StudentSearchTest.java index d12bd9c..e76e122 100644 --- a/app/src/test/java/ru/project/search/StudentSearchTest.java +++ b/app/src/test/java/ru/project/search/StudentSearchTest.java @@ -1,8 +1,8 @@ package ru.project.search; import org.junit.jupiter.api.Test; -import ru.project.list.MyList; -import ru.project.list.MyLinkedList; +import ru.project.collection.MyList; +import ru.project.collection.MyLinkedList; import ru.project.student.Student; import static org.junit.jupiter.api.Assertions.*;