From e969c499033065d92d8f71e58425b64ed34f7a68 Mon Sep 17 00:00:00 2001 From: Artem Darius Weber Date: Fri, 14 Feb 2025 23:06:51 +0300 Subject: [PATCH] feat: implement Students_list_JSON class for JSON data handling and student management --- lab2/students_list_json.rb | 150 +++++++++++++++++++++++ lab2/tests/test_students_list_json.rb | 166 ++++++++++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 lab2/students_list_json.rb create mode 100644 lab2/tests/test_students_list_json.rb diff --git a/lab2/students_list_json.rb b/lab2/students_list_json.rb new file mode 100644 index 0000000..f355863 --- /dev/null +++ b/lab2/students_list_json.rb @@ -0,0 +1,150 @@ +require 'json' +require 'date' +require_relative 'student' +require_relative 'student_short' +require_relative 'data_list_student_short' +require_relative 'data_table' + +class Students_list_JSON + attr_reader :students + + def initialize(filename) + @filename = filename + @students = [] + end + + # Чтение всех значений из JSON-файла. + # Файл должен содержать массив хэшей, где каждый хэш представляет студента. + def load_from_file + if File.exist?(@filename) + file_content = File.read(@filename) + begin + data = JSON.parse(file_content) + @students = data.map { |student_hash| hash_to_student(student_hash) } + rescue JSON::ParserError => e + warn "Ошибка парсинга JSON: #{e.message}" + @students = [] + end + else + @students = [] + end + self + end + + # Запись всех значений в JSON-файл. + # Преобразуем каждый объект Student в хэш. + def save_to_file + data = @students.map { |student| student_to_hash(student) } + File.open(@filename, 'w') do |file| + file.write(JSON.pretty_generate(data)) + end + self + rescue IOError => e + warn "Ошибка при записи в файл: #{e.message}" + self + end + + # Получить объект Student по ID. + def get_student_by_id(id) + @students.find { |s| s.id.to_s == id.to_s } + end + + # Получить список из k студентов (на "странице" n) в виде DataList объекта. + # При этом сначала список студентов сортируется по ФамилияИнициалы, + # а затем из него выбирается срез нужного размера. + # Если передан существующий объект data_list, то его список обновляется. + def get_k_n_student_short_list(k, n, data_list = nil) + start_index = (n - 1) * k + sorted_students = @students.sort_by { |s| s.surname_initials } + selected_students = sorted_students[start_index, k] || [] + short_objects = selected_students.map { |s| StudentShort.from_student(s) } + + if data_list && data_list.is_a?(DataList) + if data_list.respond_to?(:items=) + data_list.items = short_objects + else + data_list = DataListStudentShort.new(short_objects) + end + data_list + else + DataListStudentShort.new(short_objects) + end + end + + # Сортировать список студентов по ФамилияИнициалы. + def sort_students! + @students.sort_by! { |s| s.surname_initials } + end + + # Добавить объект Student в список (при добавлении формируется новый ID). + # Новый ID вычисляется как (максимальный существующий id + 1) или 1, если список пуст. + def add_student(student) + new_id = if @students.empty? + 1 + else + max_id = @students.map { |s| s.id.to_i }.max + max_id + 1 + end + student.id = new_id.to_s + @students << student + student + end + + # Заменить элемент списка по ID. + # Если элемент найден, новый объект получает тот же ID. + def update_student_by_id(id, new_student) + index = @students.find_index { |s| s.id.to_s == id.to_s } + if index + new_student.id = @students[index].id + @students[index] = new_student + true + else + false + end + end + + # Удалить элемент списка по ID. + # Возвращает true, если удаление прошло успешно. + def delete_student_by_id(id) + initial_count = @students.size + @students.reject! { |s| s.id.to_s == id.to_s } + initial_count != @students.size + end + + # Получить количество студентов в списке. + def get_student_short_count + @students.size + end + + private + + # Преобразование объекта Student в хэш для записи в JSON. + def student_to_hash(student) + { + 'id' => student.id, + 'git' => student.git, + 'surname' => student.surname, + 'name' => student.name, + 'patronymic' => student.patronymic, + 'birth_date' => student.birth_date.to_s, + 'phone' => student.phone, + 'telegram' => student.telegram, + 'email' => student.email + } + end + + # Преобразование хэша в объект Student. + def hash_to_student(hash) + Student.new( + id: hash['id'], + git: hash['git'], + surname: hash['surname'], + name: hash['name'], + patronymic: hash['patronymic'], + birth_date: Date.parse(hash['birth_date']), + phone: hash['phone'], + telegram: hash['telegram'], + email: hash['email'] + ) + end +end diff --git a/lab2/tests/test_students_list_json.rb b/lab2/tests/test_students_list_json.rb new file mode 100644 index 0000000..23ab75f --- /dev/null +++ b/lab2/tests/test_students_list_json.rb @@ -0,0 +1,166 @@ +# test_students_list_json.rb +require 'minitest/autorun' +require 'json' +require 'date' +require_relative '../students_list_json' +require_relative '../student' +require_relative '../student_short' + +class TestStudentsListJSON < Minitest::Test + TEMP_FILE = 'temp_students.json' + + def setup + # Удаляем временный файл перед каждым тестом, если он существует + File.delete(TEMP_FILE) if File.exist?(TEMP_FILE) + @students_list = Students_list_JSON.new(TEMP_FILE) + + # Создаём несколько объектов Student для тестирования + @student1 = Student.new( + id: "1", + git: "https://github.com/test1", + surname: "Иванов", + name: "Иван", + patronymic: "Иванович", + birth_date: Date.new(2000, 1, 1), + phone: "+123456789", + telegram: "@test1", + email: "test1@example.com" + ) + @student2 = Student.new( + id: "2", + git: "https://github.com/test2", + surname: "Петров", + name: "Пётр", + patronymic: "Петрович", + birth_date: Date.new(1999, 2, 2), + phone: "+987654321", + telegram: "@test2", + email: "test2@example.com" + ) + @student3 = Student.new( + id: "3", + git: "https://github.com/test3", + surname: "Сидоров", + name: "Сидор", + patronymic: "Сидорович", + birth_date: Date.new(1998, 3, 3), + phone: "+192837465", + telegram: "@test3", + email: "test3@example.com" + ) + + # Добавляем студентов в список (метод add_student формирует новый ID) + @students_list.add_student(@student1) + @students_list.add_student(@student2) + @students_list.add_student(@student3) + end + + def teardown + # Удаляем временный файл после тестов + File.delete(TEMP_FILE) if File.exist?(TEMP_FILE) + end + + def test_save_and_load_from_file + # Сохраняем текущий список в файл + @students_list.save_to_file + + # Создаём новый объект, который загрузит данные из файла + new_list = Students_list_JSON.new(TEMP_FILE) + new_list.load_from_file + + # Проверяем, что количество студентов совпадает + assert_equal @students_list.get_student_short_count, new_list.get_student_short_count + + # Проверяем, что данные студентов (например, фамилии и инициалы) совпадают + original_names = @students_list.students.map(&:surname_initials) + loaded_names = new_list.students.map(&:surname_initials) + assert_equal original_names.sort, loaded_names.sort + end + + def test_get_student_by_id + # Предполагаем, что add_student сбросил id, поэтому ищем по id "1" (первый добавленный) + student = @students_list.get_student_by_id("1") + refute_nil student + assert_equal "Иванов И.И.", student.surname_initials + end + + def test_get_k_n_student_short_list + # Тестируем получение "страницы" студентов: k = 2, n = 1 (две записи) + data_list = @students_list.get_k_n_student_short_list(2, 1) + # Ожидаем, что DataList содержит 2 элемента + assert_equal 2, data_list.get_data.rows_count + + # Проверяем, что строки таблицы содержат корректное значение ФИО + first_row = data_list.get_data.item(0, 1) + refute_nil first_row + assert_match(/[А-ЯЁ][а-яё]+ [А-Я]\./, first_row) + end + + def test_sort_students! + # Перемешиваем порядок студентов + @students_list.students.shuffle! + @students_list.sort_students! + sorted_names = @students_list.students.map(&:surname_initials) + # Проверяем, что список отсортирован в лексикографическом порядке + assert_equal sorted_names.sort, sorted_names + end + + def test_add_student + new_student = Student.new( + id: "0", # id будет перезаписан + git: "https://github.com/test4", + surname: "Алексеев", + name: "Алексей", + patronymic: "Алексеевич", + birth_date: Date.new(2001, 4, 4), + phone: "+111222333", + telegram: "@test4", + email: "test4@example.com" + ) + added_student = @students_list.add_student(new_student) + # Проверяем, что новый id сформирован корректно (будет равен max(id)+1) + expected_id = (@students_list.students.map { |s| s.id.to_i }.max).to_s + assert_equal expected_id, added_student.id + assert_equal 4, @students_list.get_student_short_count + end + + def test_update_student_by_id + # Создаём нового студента для замены + updated_student = Student.new( + id: "0", # id будет перезаписан + git: "https://github.com/updated", + surname: "Обновлённый", + name: "Студент", + patronymic: "Тестович", + birth_date: Date.new(2002, 5, 5), + phone: "+444555666", + telegram: "@updated", + email: "updated@example.com" + ) + # Обновляем студента с id "2" + result = @students_list.update_student_by_id("2", updated_student) + assert result + + student = @students_list.get_student_by_id("2") + refute_nil student + assert_equal "Обновлённый С.Т.", student.surname_initials + assert_equal "https://github.com/updated", student.git + end + + def test_delete_student_by_id + # Удаляем студента с id "1" + result = @students_list.delete_student_by_id("1") + assert result + + # Проверяем, что студент с id "1" отсутствует + student = @students_list.get_student_by_id("1") + assert_nil student + + # Количество студентов должно уменьшиться + assert_equal 2, @students_list.get_student_short_count + end + + def test_get_student_short_count + assert_equal 3, @students_list.get_student_short_count + end +end