Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions data/data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<student name="Иванов Иван Иванович">
<subject name="математика">67</subject>
<subject name="литература">100</subject>
<subject name="программирование">91</subject>
</student>
<student name="Петров Петр Петрович">
<subject name="математика">78</subject>
<subject name="химия">87</subject>
<subject name="социология">61</subject>
</student>
</root>
48 changes: 48 additions & 0 deletions src/CalcFirstQuartile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
from src.Types import DataType
from src.CalcRating import CalcRating

# Определяем тип для словаря рейтингов: Имя -> Балл
RatingType = dict[str, float]


class CalcFirstQuartile:

def __init__(self, data: DataType) -> None:
self.data: DataType = data
self.rating: RatingType = {}

def calc(self) -> RatingType:
# Сначала считаем средний рейтинг всех студентов
# используем уже готовый класс CalcRating
full_rating = CalcRating(self.data).calc()

# Собираем все баллы в список и сортируем их
values = sorted(full_rating.values())

# Если студентов нет, возвращаем пустоту
if not values:
return {}

# Вычисляем порог первой квартили (25%)
# Это элемент, стоящий на позиции 25% от длины списка
# Например, если 4 студента, то index = 1
# (второй по счету, если округлять вверх)
# Упрощенная формула: q1_index = len(values) // 4
# Если нужно более точное (математическое) вычисление:
import math
q1_index = math.ceil(len(values) * 0.25) - 1

# Защита от отрицательного индекса
if q1_index < 0:
q1_index = 0

q1_value = values[q1_index]

# Фильтруем студентов: берем тех, у кого рейтинг <= q1_value
filtered_students: RatingType = {}
for student, rating in full_rating.items():
if rating <= q1_value:
filtered_students[student] = rating

return filtered_students
7 changes: 4 additions & 3 deletions src/TextDataReader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from src.DataReader import DataReader
from src.Types import DataType


class TextDataReader(DataReader):

def __init__(self) -> None:
Expand All @@ -15,6 +16,6 @@ def read(self, path: str) -> DataType:
self.students[self.key] = []
else:
subj, score = line.split(":", maxsplit=1)
self.students[self.key].append(
(subj.strip(), int(score.strip())))
return self.students # <--- ИСПРАВЛЕНИЕ: return должен быть на одном уровне с with
self.students[self.key].append((subj.strip(),
int(score.strip())))
return self.students
29 changes: 29 additions & 0 deletions src/XMLDataReader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
from src.Types import DataType
from src.DataReader import DataReader


class XMLDataReader(DataReader):
def __init__(self) -> None:
self.key: str = ""
self.students: DataType = {}

def read(self, path: str) -> DataType:
# Открываем и парсим XML файл
tree = ET.parse(path)
root = tree.getroot()

# Проходим по всем элементам <student>
for student_element in root.findall("student"):
self.key = student_element.get("name")
self.students[self.key] = []

# Проходим по всем предметам внутри студента
for subject_element in student_element.findall("subject"):
subj_name = subject_element.get("name")
# Текст внутри тега может содержать пробелы, чистим его
score = int(subject_element.text.strip())
self.students[self.key].append((subj_name, score))

return self.students
35 changes: 35 additions & 0 deletions test/test_CalcFirstQuartile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
from src.Types import DataType
from src.CalcFirstQuartile import CalcFirstQuartile
import pytest

RatingType = dict[str, float]


class TestCalcFirstQuartile:

@pytest.fixture()
def input_data(self) -> tuple[DataType, RatingType]:
data: DataType = {
"Студент 1 (Отличник)": [("m", 100), ("l", 100)], # avg 100
"Студент 2 (Хорошист)": [("m", 80), ("l", 80)], # avg 80
"Студент 3 (Троечник)": [("m", 60), ("l", 60)], # avg 60
"Студент 4 (Двоечник)": [("m", 40), ("l", 40)], # avg 40
}

# Всего 4 студента. Первая квартиль (25%) - это нижние 25%.
# Сортированные баллы: 40, 60, 80, 100.
# Индекс 25%: 4 * 0.25 = 1 элемент (это 40).
# Значит, в первую квартиль попадает только "Двоечник" с баллом 40.
expected_students: RatingType = {
"Студент 4 (Двоечник)": 40.0
}

return data, expected_students

def test_calc(self, input_data: tuple[DataType, RatingType]) -> None:
# Вызываем наш новый класс
quartile_students = CalcFirstQuartile(input_data[0]).calc()

# Проверяем, что вернулся именно тот студент
assert quartile_students == input_data[1]
51 changes: 51 additions & 0 deletions test/test_XMLDataReader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
import pytest
from src.Types import DataType
from src.XMLDataReader import XMLDataReader


class TestXMLDataReader:

@pytest.fixture()
def file_and_data_content(self) -> tuple[str, DataType]:
# Данные для теста
xml_content = """<?xml version="1.0" encoding="UTF-8" ?>
<root>
<student name="Иванов Иван Иванович">
<subject name="математика">67</subject>
<subject name="литература">100</subject>
<subject name="программирование">91</subject>
</student>
<student name="Петров Петр Петрович">
<subject name="математика">78</subject>
<subject name="химия">87</subject>
<subject name="социология">61</subject>
</student>
</root>"""

data = {
"Иванов Иван Иванович": [
("математика", 67), ("литература", 100),
("программирование", 91)
],
"Петров Петр Петрович": [
("математика", 78), ("химия", 87), ("социология", 61)
]
}
return xml_content, data

@pytest.fixture()
def filepath_and_data(self, file_and_data_content: tuple[str, DataType],
tmpdir) -> tuple[str, DataType]:
# Создаем файл
p = tmpdir.mkdir("datadir").join("my_data.xml")

# Пишем в бинарном режиме, чтобы не было проблем с кодировкой в Windows
p.write(file_and_data_content[0].encode('utf-8'), mode='wb')

return str(p), file_and_data_content[1]

def test_read(self, filepath_and_data: tuple[str, DataType]) -> None:
# Проверяем чтение
file_content = XMLDataReader().read(filepath_and_data[0])
assert file_content == filepath_and_data[1]