From c8c9c05a655f33202764a466be45fd9be6534834 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: Fri, 28 Aug 2026 21:10:13 +0400 Subject: [PATCH] docs: replace data source task with csv student io task --- docs/tasks/CsvStudentIO.md | 324 ++++++++++++++++++++++++++ docs/tasks/DataSource.md | 399 --------------------------------- docs/tasks/DataSourceUpdate.md | 81 ------- 3 files changed, 324 insertions(+), 480 deletions(-) create mode 100644 docs/tasks/CsvStudentIO.md delete mode 100644 docs/tasks/DataSource.md delete mode 100644 docs/tasks/DataSourceUpdate.md diff --git a/docs/tasks/CsvStudentIO.md b/docs/tasks/CsvStudentIO.md new file mode 100644 index 0000000..89e2ad5 --- /dev/null +++ b/docs/tasks/CsvStudentIO.md @@ -0,0 +1,324 @@ +# Техническое задание: чтение и дозапись `Student` в CSV + +## Задача + +Реализовать два независимых класса для работы с объектами `Student` в формате CSV: + +- `StudentCsvWriter` — дозапись студентов в файл; +- `StudentCsvReader` — последовательное чтение студентов из файла. + +Для работы с форматом CSV использовать готовую библиотеку для работы с CSV. Самостоятельно реализовывать парсинг CSV запрещено. + +Оба класса должны принимать **абсолютный путь к файлу**. + +Исключения `Student` и `StudentBuilder` не обрабатывать. + +--- + +# Формат CSV-файла + +Первая строка файла должна содержать заголовок: + +```text +groupNumber,averageGrade,recordBookNumber +``` + +Каждая следующая строка содержит данные одного студента: + +```text +groupNumber,averageGrade,recordBookNumber +``` + +Пример: + +```text +groupNumber,averageGrade,recordBookNumber +A12,4.5,10001 +B07,3.8,10002 +C25,4.9,10003 +``` + +Для формирования и чтения CSV использовать стандартную библиотеку для работы с форматом CSV. + +--- + +# `StudentCsvWriter` + +## Назначение + +Дозаписывать одного студента в указанный CSV-файл. + +## Контракт + +Конструктор: + +```java +public StudentCsvWriter(String absoluteFilePath) +``` + +Метод: + +```java +public void write(Student student) throws IOException +``` + +### Требования + +1. Метод принимает одного `Student`. +2. Запись выполняется в режиме добавления. +3. Существующие записи не изменяются. +4. Если файл существует, заголовок повторно не записывается. +5. Если файл не существует, необходимо создать файл. +6. При создании нового файла имя должно соответствовать формату: + +```text +students-yyyy-MM-dd-HH-mm.csv +``` + +Например: + +```text +students-2026-08-28-20-45.csv +``` + +1. Новый файл создаётся в директории, указанной абсолютным путём. +2. При создании нового файла сначала записывается заголовок. +3. При успешной записи метод завершается без исключения. +4. Метод не возвращает значение. +5. Исключения, связанные с записью файла, не обрабатывать внутри метода — они должны передаваться вызывающему коду. +6. Исключения `Student` и `StudentBuilder` также не обрабатывать. + +## Невозможность записи + +Если студент не может быть записан, например: + +- отсутствуют права на запись; +- невозможно создать файл; +- файл недоступен; +- произошла ошибка файловой системы; + +метод должен выбросить соответствующее исключение, например `IOException`. + +Запись не должна возвращать специальное значение вроде `0` для обозначения ошибки. + +--- + +# `StudentCsvReader` + +## Назначение + +Последовательно читать студентов из CSV-файла. + +## Контракт + +Класс должен реализовывать: + +```java +Iterator +``` + +Конструктор: + +```java +public StudentCsvReader(String absoluteFilePath) +``` + +Методы: + +```java +@Override +public boolean hasNext() +``` + +```java +@Override +public Student next() +``` + +### Требования к `hasNext()` + +1. Проверяет наличие следующей записи `Student`. +2. Заголовок CSV не считается записью. +3. Повторный вызов `hasNext()` не должен пропускать следующую запись. +4. После окончания файла метод возвращает `false`. + +### Требования к `next()` + +1. Возвращает следующего `Student`. +2. `Student` создаётся через существующий `StudentBuilder`. +3. При отсутствии следующего элемента выбрасывается: + +```text +NoSuchElementException +``` + +1. Весь файл не должен предварительно загружаться в коллекцию. + +## Невозможность прочитать студента + +Если следующую запись невозможно прочитать или преобразовать в `Student`, например: + +- некорректный формат CSV; +- отсутствует обязательное поле; +- значение невозможно преобразовать в нужный тип; +- данные не проходят валидацию `Student`; + +исключение должно передаваться вызывающему коду. + +Исключения `Student` и `StudentBuilder` **не перехватывать и не обрабатывать**. + +Некорректные записи не должны молча пропускаться. + +--- + +# Общие требования + +1. Оба класса принимают абсолютный путь к файлу. +2. Использовать готовую библиотеку для работы с CSV. +3. Не реализовывать собственный CSV-парсер. +4. Не использовать стандартные коллекции Java как основное хранилище студентов. +5. Не дублировать в этих классах валидацию `Student`. +6. Не дублировать логику создания `Student` вне `StudentBuilder`. +7. `StudentCsvWriter` отвечает только за запись. +8. `StudentCsvReader` отвечает только за чтение. + +--- + +# Примеры тестов + +## `StudentCsvWriter` + +### Создание нового файла + +Передать путь к несуществующему файлу и вызвать: + +```java +writer.write(student); +``` + +Проверить: + +- файл создан; +- имя соответствует `students-yyyy-MM-dd-HH-mm.csv`; +- присутствует заголовок; +- студент записан; +- метод завершился без исключения. + +### Дозапись + +Создать файл с одним студентом и выполнить: + +```java +writer.write(student2); +``` + +Проверить, что: + +- первая запись сохранилась; +- `student2` добавлен в конец; +- заголовок присутствует только один раз. + +### Несколько последовательных записей + +```java +writer.write(student1); +writer.write(student2); +writer.write(student3); +``` + +Проверить наличие трёх записей в правильном порядке. + +### Невозможность записи + +Передать путь, по которому невозможно создать или открыть файл для записи. + +Проверить, что `write()` выбрасывает `IOException`. + +Не проверять специальное возвращаемое значение — метод ничего не возвращает. + +--- + +## `StudentCsvReader` + +### Чтение корректного файла + +Для файла: + +```text +groupNumber,averageGrade,recordBookNumber +A12,4.5,10001 +B07,3.8,10002 +C25,4.9,10003 +``` + +Проверить: + +- `hasNext()` возвращает `true` до последнего студента; +- `next()` возвращает студентов в правильном порядке; +- после последнего студента `hasNext()` возвращает `false`. + +### Повторный вызов `hasNext()` + +```java +reader.hasNext(); +reader.hasNext(); +Student student = reader.next(); +``` + +Проверить, что первый студент не был пропущен. + +### `next()` после окончания файла + +После чтения всех студентов вызвать: + +```java +reader.next(); +``` + +Проверить выброс: + +```text +NoSuchElementException +``` + +### Некорректная запись + +Передать файл: + +```text +groupNumber,averageGrade,recordBookNumber +A12,4.5,10001 +INVALID +C25,4.9,10003 +``` + +Проверить, что ошибка при чтении некорректной записи передаётся вызывающему коду. + +### Некорректные данные `Student` + +Передать данные, нарушающие ограничения `Student`. + +Проверить, что исключение от `Student` или `StudentBuilder` выходит наружу и не перехватывается `StudentCsvReader`. + +--- + +# Названия файлов + +```text +StudentCsvWriter.java +StudentCsvReader.java +``` + +# Git + +Название ветки: + +```text +feature/csv-student-io +``` + +Conventional Commit: + +```text +feat: add csv student reader and writer +``` diff --git a/docs/tasks/DataSource.md b/docs/tasks/DataSource.md deleted file mode 100644 index 79a6c14..0000000 --- a/docs/tasks/DataSource.md +++ /dev/null @@ -1,399 +0,0 @@ -# Техническое задание: источники данных - -## Задача - -Реализовать три независимых источника данных для формирования `MyList`: - -- `ManualDataSource`; -- `RandomDataSource`; -- `FileDataSource`. - -Все источники должны формировать только валидные объекты `Student`. - -Класс `Student` содержит: - -```java -private final String groupNumber; -private final double averageGrade; -private final String recordBookNumber; -``` - -Создание объектов `Student` выполнять через `StudentBuilder`. - ---- - -# Общий контракт - -Создать интерфейс `DataSource`: - -```java -public interface DataSource { - - MyList create(); -} -``` - -Источник данных не должен самостоятельно получать данные от пользователя. - -Все необходимые входные значения должны передаваться извне. - -Результатом работы каждого источника является `MyList`. - -Стандартные `List`, `ArrayList` и `LinkedList` для хранения результата не использовать. - ---- - -# `ManualDataSource` - -## Назначение - -Постепенно формировать коллекцию `Student` на основе значений, передаваемых извне для каждого отдельного объекта. - -## Входные данные одного объекта - -Для создания одного `Student` передаются: - -```text -groupNumber -averageGrade -recordBookNumber -``` - -Также передаётся `index` — позиция в коллекции, на которой должен находиться создаваемый объект. - -Размер результирующей коллекции задаётся заранее. - -## Контракт - -`ManualDataSource` должен: - -1. Принимать требуемый размер коллекции. -2. Принимать значения одного `Student`: - - - `groupNumber`; - - `averageGrade`; - - `recordBookNumber`. - -3. Принимать индекс, на котором должен находиться этот объект. -4. Создавать новый `Student` через `StudentBuilder`. -5. Добавлять созданный объект в нужную позицию коллекции. -6. Позволять повторно передавать данные для существующего индекса, заменяя объект на новый. -7. Проверять допустимость индекса. -8. Не изменять уже созданный объект `Student`. -9. Позволять получить текущее состояние коллекции для просмотра. -10. После заполнения вернуть сформированную `MyList`. - -Рекомендуемый метод для передачи одного объекта: - -```java -setStudent( - int index, - String groupNumber, - double averageGrade, - String recordBookNumber -) -``` - -Таким образом, пользовательский код может последовательно передать данные для всех элементов: - -```java -dataSource.setStudent(0, "101", 4.5, "10001"); -dataSource.setStudent(1, "102", 3.8, "10002"); -dataSource.setStudent(2, "101", 4.9, "10003"); -``` - -После заполнения: - -```java -MyList students = dataSource.create(); -``` - -### Изменение объекта - -Для изменения объекта повторно вызывается `setStudent(...)` с нужным индексом: - -```java -dataSource.setStudent(1, "105", 4.2, "10025"); -``` - -Объект на позиции `1` заменяется новым валидным `Student`. - -### Размер коллекции - -Если задан: - -```text -size = 5 -``` - -то для успешного создания коллекции должны быть заданы данные для всех пяти позиций: - -```text -0 -1 -2 -3 -4 -``` - -Порядок заполнения не обязан совпадать с порядком индексов: - -```java -dataSource.setStudent(3, "103", 4.1, "10004"); -dataSource.setStudent(0, "101", 4.5, "10001"); -dataSource.setStudent(4, "102", 3.9, "10005"); -dataSource.setStudent(1, "101", 4.8, "10002"); -dataSource.setStudent(2, "104", 3.7, "10003"); -``` - -После заполнения коллекция должна содержать ровно пять объектов в соответствии с указанными индексами. - -Если хотя бы одна позиция не заполнена, `create()` должен сообщить об ошибке и не возвращать неполную коллекцию. - ---- - -# `RandomDataSource` - -## Назначение - -Создавать заданное количество `Student` со случайными значениями в диапазонах, заданных извне. - -## Входные данные - -Передаются: - -```text -size -``` - -и диапазоны для каждого поля: - -```text -groupNumber: - minimum - maximum - -averageGrade: - minimum - maximum - -recordBookNumber: - minimum - maximum -``` - -## Контракт - -`RandomDataSource` должен: - -1. Принять размер создаваемой коллекции. -2. Принять минимальное и максимальное значение каждого поля. -3. Проверить корректность каждого диапазона. -4. Сгенерировать значения в заданных пределах. -5. Создать каждый объект через `StudentBuilder`. -6. Поместить объекты в `MyList`. -7. Вернуть коллекцию заданного размера. - -Минимальное и максимальное значения включаются в диапазон генерации. - -`RandomDataSource` не должен самостоятельно получать или отображать параметры генерации. - ---- - -# `FileDataSource` - -## Назначение - -Создавать `MyList` на основании содержимого файла. - -## Входные данные - -Передаётся **абсолютный путь к файлу**. - -Относительный путь не является входным параметром `FileDataSource`. - -Формат одной записи: - -```text -groupNumber;averageGrade;recordBookNumber -``` - -Пример: - -```text -101;4.75;123456 -102;3.80;123457 -103;4.20;123458 -``` - -## Контракт - -`FileDataSource` должен: - -1. Получить абсолютный путь к файлу. -2. Открыть файл. -3. Последовательно обработать его записи. -4. Разделить каждую запись на три значения. -5. Создать `Student` через `StudentBuilder`. -6. Добавить только валидные объекты в `MyList`. -7. Пропустить некорректную запись и продолжить обработку остальных. -8. При невозможности открыть файл сообщить об ошибке. -9. Вернуть сформированную `MyList`. - ---- - -# Общие требования - -1. Все источники должны создавать `Student` только через `StudentBuilder`. -2. Источник данных не должен самостоятельно вводить данные. -3. Источник данных не должен зависеть от интерфейса пользователя. -4. Источник данных не должен содержать логику сортировки. -5. Результат хранить только в `MyList`. -6. Невалидный `Student` не должен попасть в коллекцию. -7. Проверка корректности самого объекта выполняется средствами `Student`. -8. Для ручного заполнения изменение существующего `Student` выполнять заменой объекта, а не изменением его полей. - ---- - -# Примеры тестов - -## `ManualDataSource` - -### Заполнение всей коллекции - -Создать источник размера `3`: - -```java -ManualDataSource dataSource = new ManualDataSource(3); - -dataSource.setStudent(0, "101", 4.5, "10001"); -dataSource.setStudent(1, "102", 3.8, "10002"); -dataSource.setStudent(2, "103", 4.2, "10003"); - -MyList result = dataSource.create(); -``` - -Проверить: - -```text -result.size() == 3 -``` - -и соответствие каждого объекта своему индексу. - -### Заполнение в произвольном порядке - -```java -dataSource.setStudent(2, "103", 4.2, "10003"); -dataSource.setStudent(0, "101", 4.5, "10001"); -dataSource.setStudent(1, "102", 3.8, "10002"); -``` - -Проверить правильное расположение объектов. - -### Изменение объекта - -После заполнения: - -```java -dataSource.setStudent(1, "105", 4.9, "10025"); -``` - -Проверить, что изменился только объект с индексом `1`. - -### Незаданный индекс - -Для коллекции размера `3` вызвать: - -```java -dataSource.setStudent(3, ...); -``` - -Проверить выброс исключения. - -### Неполное заполнение - -Заполнить только две позиции из трёх и вызвать `create()`. - -Проверить, что неполная коллекция не возвращается. - ---- - -## `RandomDataSource` - -### Корректная генерация - -Задать размер `100` и допустимые диапазоны для всех трёх полей. - -Проверить: - -```text -result.size() == 100 -``` - -и нахождение каждого значения внутри заданного диапазона. - -### Граничный диапазон - -Задать одинаковые минимальное и максимальное значения. - -Проверить, что все сгенерированные значения соответствуют этому значению. - -### Некорректный диапазон - -Задать: - -```text -minimum > maximum -``` - -Проверить выброс исключения. - ---- - -## `FileDataSource` - -### Корректный файл - -Для файла: - -```text -101;4.75;123456 -102;3.80;123457 -103;4.20;123458 -``` - -проверить получение трёх корректных объектов. - -### Файл с некорректной записью - -```text -101;4.75;123456 -invalid;abc;123457 -103;4.20;123458 -``` - -Проверить, что в коллекцию попадают только две корректные записи. - -### Пустой файл - -Проверить получение пустой коллекции. - -### Несуществующий файл - -Передать путь к несуществующему файлу и проверить корректную обработку ошибки. - ---- - -# Результат - -Реализовать: - -```text -DataSource.java -ManualDataSource.java -RandomDataSource.java -FileDataSource.java -``` - -Допускается создание вспомогательных классов для чтения файла и генерации случайных значений. diff --git a/docs/tasks/DataSourceUpdate.md b/docs/tasks/DataSourceUpdate.md deleted file mode 100644 index c175d9c..0000000 --- a/docs/tasks/DataSourceUpdate.md +++ /dev/null @@ -1,81 +0,0 @@ -# Дополнение к техническому заданию: изменение типа `recordBookNumber` - -Данное дополнение имеет **приоритет над основным ТЗ `DataSource.md`**. - -## Изменение модели `Student` - -Поле: - -```java -private final String recordBookNumber; -``` - -заменено на: - -```java -private final int recordBookNumber; -``` - -Метод: - -```java -getRecordBookNumber() -``` - -возвращает `int`. - -## Требования к `DataSource` - -Во всех реализациях источников данных необходимо учитывать новый тип `recordBookNumber`. - -### `ManualDataSource` - -`recordBookNumber` должен передаваться как `int`: - -```java -setStudent( - int index, - String groupNumber, - double averageGrade, - int recordBookNumber -); -``` - -Строковое значение номера зачётной книжки больше не используется. - -### `RandomDataSource` - -Для `recordBookNumber` необходимо задавать и генерировать **числовой диапазон**: - -```text -минимальное значение — int -максимальное значение — int -``` - -Генерируемое значение должно быть целым числом и соответствовать ограничениям `Student`. - -### `FileDataSource` - -В файле `recordBookNumber` должен записываться как целое число. - -Формат записи: - -```text -groupNumber;averageGrade;recordBookNumber -``` - -Пример: - -```text -A12;4.75;12345 -B32;3.80;12346 -C17;4.20;12347 -``` - -При чтении значение необходимо преобразовать в `int` до создания `Student`. - -## Приоритет - -При противоречии между данным дополнением и основным `DataSource.md` использовать требования этого дополнения. - -Все остальные требования основного технического задания остаются без изменений.