Labs and hws

This commit is contained in:
EDeev 2026-02-13 15:01:19 +03:00
parent 5c07dc05dc
commit 3e9da032a3
58 changed files with 3160 additions and 0 deletions

502
hws/hw-1/README.md Normal file
View file

@ -0,0 +1,502 @@
Решите задачи, приведённые ниже. Код каждой задачи разместите в отдельном файле, название которого соответствует указанному в условии задачи. В качестве ответа на это задание загрузите архив (ZIP) с решениями. Архив должен содержать только файлы с решениями, файл test.py и вспомогательные файлы, необходимые для отдельных задач (не должно быть папок).
К заданию прилагается файл test.py (в архиве hhww1.zip) c заготовкой для написания тестов с использованием фреймворка pytest. Дополните его тестами для всех задач. Для каждой задачи достаточно рассмотреть принципиально различные ситуации (входные данные). Всего должно быть написано не менее 60 тестов (в прикреплённом файле приведено 8 тестов).
Для запуска тестов необходимо выполнить следующие шаги.
1. Создайте виртуальное окружение:
```
python -m venv ve
```
2. Активируйте виртуальное окружение**:
- Команда для Windows: `ve\Scripts\activate`
- Команда для Linux: `source ve/bin/activate`
3. Установите pytest:
```
pip install pytest
```
4. Запустите тесты
```
pytest test.py
```
Если вы работаете в PowerShell и столкнулись с ошибкой "...Scripts\Activate.ps1 cannot be loaded because running scripts is disabled on this system." необходимо запустить PowerShell с правами администратора и выполнить команду Set-ExecutionPolicy RemoteSigned. Затем перезагрузить PowerShell и ошибка уйдет.
При необходимости замените значение переменной INTERPRETER на команду запуска интерпретатора, которая используется в вашей системе (например, python или py).
При обнаружении ошибок или неточностей в формулировках задач свяжитесь с преподавателем.
---
## 1) Скажи "Hello, World!" с помощью Python
Выведите "Hello, World!" на стандартный поток вывода.
**Название файла:** hello.py
---
## 2) Python If-Else
Получив целое число (n), выполните следующие условные действия:
- Если n нечетное значение, выведите "Weird";
- Если n четно и находится в диапазоне от 2 до 5 включительно, выведите "Not Weird";
- Если n четно и находится в диапазоне от 6 до 20 включительно, выведите "Weird";
- Если n четно и больше 20, выведите "Not Weird".
**Формат ввода:** Строка, содержащая положительное целое число (n).
**Ограничения:** 1 ⩽ n ⩽ 100
**Название файла:** python_if_else.py
---
## 3) Арифметические операторы
Считайте два целых числа из стандартного потока ввода (a и b), и напишите код для вывода трех строк, где:
1. Первая строка содержит сумму двух чисел.
2. Вторая строка содержит разницу двух чисел (первое - второе).
3. Третья строка содержит произведение двух чисел.
Например, для a = 3 и b = 5 программа должны вывести:
```
8
-2
15
```
**Формат ввода:** Первая строка содержит первое целое число (a), вторая строка -- второе целое число (b).
**Ограничения:** 1 ⩽ a ⩽ 10¹⁰ 1 ⩽ b ⩽ 10¹⁰
**Название файла:** arithmetic_operators.py
---
## 4) Деление
Считайте два целых числа a и b из стандартного потока ввода.
Реализуйте логику для печати двух строк. Первая строка должна содержать результат целочисленного деления, (a // b). Вторая строка должна содержать результат деления с плавающей точкой (a/b).
Округление или форматирование не требуется. Ваша программа должна корректно отбрабатывать деление на ноль.
Например, при а = 3 и b = 5:
- Результат целочисленного деления 3 // 5 = 0.
- Результатом деления с плавающей точкой является 3 / 5 = 0.6
**Формат ввода:** Первая строка содержит первое целое число (a), вторая строка -- второе целое число (b).
**Название файла:** division.py
---
## 5) Циклы
Считайте целое число n. Для всех неотрицательных целых чисел i < n выведите i².
Например, для n = 3 список неотрицательных целых чисел, которые меньше n, является [0,1,2]. Выведите квадрат каждого числа в отдельной строке:
```
0
1
4
```
**Формат ввода:** Строка, содержащая целое число (n).
**Ограничения:** 1 ⩽ n ⩽ 20
**Название файла:** loops.py
---
## 6) Функция вывода
Считайте целое число n из стандартного потока ввода.
Без использования каких-либо строковых методов попробуйте вывести следующее: 123…n, где «…» означает последовательность числел между 3 и n.
Например, при n = 5 нужно выыести строку "12345".
**Формат ввода:** Строка, содержащая целое число (n).
**Ограничения:** 1 ⩽ n ⩽ 20
**Название файла:** print_function.py
---
## 7) Узнай, кто занял второе место!
Учитывая таблицу результатов участников вашей университетской спартакиады, вам необходимо указать результат, занявший второе место. Вам даны быллы n участников. Запишите их в список и найдите оценку занявшего второе место.
**Формат ввода:** Первая строка содержит n. Вторая строка содержит массив A[] из n целых чисел, разделённых пробелом.
**Формат вывода:** Выведите результат, занявший второе место.
**Пример ввода:**
```
5
2 3 6 6 5
```
**Пример вывода:** 5
**Объяснение** Дан список [2,3,6,6,5]. Максимальный балл равен 6, второй максимальный - 5. Следовательно, мы выводим 5 как результат, занявший второе место.
**Название файла:** second_score.py
---
## 8) Вложенные списки
Учитывая имена и оценки каждого учащегося в классе, сохраните их во вложенном списке и выведите имя (имена) любого учащегося, имеющего вторую по величине оценку.
**Примечание:** Если есть несколько учеников со вторыми по величине оценками, расположите их имена в алфавитном порядке и выведите каждое имя с новой строки.
Например:
```python
records = [['chi', 20.0], ['beta', 50.0], ['alpha', 50.0]]
```
Упорядоченный список оценок - [20.0,50.0], поэтому второй по величине балл - 50.0. Есть два студента с таким баллом: ['beta','alpha']. Имена в алфавитном порядке напечатаны как:
```
alpha
beta
```
**Формат ввода:** Первая строка содержит целое число (N) -- количество учащихся. В 2N последующих строках каждый учащийся описывается в 2 строках:
- В первой строке указано имя учащегося.
- Во второй строке указана его оценка.
**Формат вывода:** Выведите имя (имена) любого ученика (ов), имеющего вторую по величине оценку. Если учеников несколько, расположите их имена в алфавитном порядке и выведите каждое с новой строки.
**Пример ввода:**
```
5
Гарри
37.21
Берри
37.21
Тина
37.2
Акрити
41
Харш
39
```
**Пример вывода:**
```
Берри
Гарри
```
**Объяснение** В этом классе есть 5 учеников, чьи имена и оценки собраны для составления следующего списка:
```python
students = [['Harry', 37.21], ['Berry', 37.21], ['Tina', 37.2], ['Akriti', 41], ['Harsh', 39]]
```
Самая низкая оценка 37.2 принадлежит Тине. Вторая по величине оценка (37.21) принадлежит как Гарри, так и Берри, поэтому мы упорядочиваем их имена в алфавитном порядке и выводим каждое имя с новой строки.
**Ограничения:** 2 ⩽ N ⩽ 5
Во вводимых данных всегда должен присутствовать хотя бы один учащийся со второй по величине оценкой.
**Название файла:** nested_list.py
---
## 9) Cписки
Рассмотрим список (arr = []). Вы можете выполнить следующие команды:
1. insert i e: вставить целое число e в позицию i.
2. print: вывести список.
3. remove e: удалить первое вхождение целого числа e.
4. append e: вставить целое число e в конце списка.
5. sort: сортировать список.
6. pop: удалить последний элемент из списка.
7. reverse: перевернуть список.
Инициализируйте свой список и считайте из стандартного потока ввода значение n, за которым следуют n строк команд, где каждая команда будет соответствовать одному из приведённых выше 7 типов. Пройдитесь по командам в порядке их ввода и выполните соответствующую операцию в вашем списке.
Например:
```
n = 4
append 1
append 2
insert 1 3
print
```
- append 1: Добавить 1 в список, arr=[1].
- append 2: Добавить 2 в список, arr=[1,2].
- insert 1 3: Вставить 3 по индексу 1, arr=[1,3,2].
- print: Вывести массив.
**Вывод:**
```
[1, 3, 2]
```
**Формат ввода:** Первая строка содержит целое число n, обозначающее количество команд. Каждая строка i из n последующих строк содержит одну из команд, описанных выше.
**Ограничения:**
- Элементы, добавляемые в список, должны быть целыми числами.
**Формат вывода:** Для каждой команды типа print выведите список с новой строки.
**Пример ввода**
```
12
insert 0 5
insert 1 10
insert 0 6
print
remove 6
append 9
append 1
sort
print
pop
reverse
print
```
**Пример вывода**
```
[6, 5, 10]
[1, 5, 9, 10]
[9, 5, 1]
```
**Название файла:** lists.py
---
## 10) Пример обмена
Вам дается строка, и ваша задача - поменять регистры местами. Другими словами, преобразуйте все строчные буквы в прописные и наоборот.
Например:
```
Www.MosPolytech.ru → wWW.mOSpOLYTECH.ru
Pythonist 2 → pYTHONIST 2
```
**Формат ввода:** Строка (s), содержащая некоторую последовательность символов.
**Ограничения:** 0 < len(s) 1000
**Название файла:** swap_case.py
---
## 11) Разделение и объединение строк
Вам через стандартный поток ввода будет предоставлена строка. Разделите строку по пробелу и соедините получившиеся фрагменты с помощью дефиса.
**Пример ввода:**
```
this is a string
```
**Пример вывода:**
```
this-is-a-string
```
**Название файла:** split_and_join.py
---
## 12) Поиск слова максимальной длины
Напишите программу, которая считывает содержимое файла example.txt (приложен к заданию) и выводит на стандартный поток вывода слово максимальной длины, встречающееся в этом файле. Если таких слов несколько, программа должна вывести все. При подсчёте длины слова не должны учитываться знаки препинания, кавычки и прочие спецсимволы.
**Формат вывода:** Строка, содержащая слово максимальной длины (если таких слов несколько -- каждое на отдельной строке в порядке их встречаемости в тексте).
**Название файла:** max_word.py
---
## 13) Агрегирование данных
В файле products.csv (приложен к заданию) приведены данные ежемесячных трат на продукты для различных категорий потребителей. Напишите программу, которая считывает данные из файла и вычисляет суммарные затраты для каждой категории потребителей (взрослый, пенсионер, ребёнок). Полученные значения выведите на стандартный поток вывода.
**Формат вывода:** Три числа разделённых пробелами -- суммарные затраты для каждой категории потребителей. Каждое число должно быть округлено до двух знаков после десятичного разделителя.
**Название файла:** price_sum.py
---
## 14) Анаграммы
Даны две строки. Определите, можно ли получить одну из другой перестановкой букв.
**Формат ввода:** Программа получает на вход две строки, содержащие только ASCII-символы, не содержащие пробелы.
**Формат вывода:** Программа должна вывести слово YES, если одна строка может быть получена из другой перестановкой букв или NO.
**Название файла:** anagram.py
---
## 15) Пассажиры в метро
Для изучения пассажиропотока в метро было записано время входа и время выхода в метро каждого пассажира. На основании этих данных определите, сколько пассажиров было в метро в некоторый заданный момент времени T.
**Формат ввода:** Программа получает на вход число пассажиров N. Далее в N строках записано время входа (Aᵢ) и время выхода (Bᵢ) каждого пассажира (Aᵢ < Bᵢ). Время задаётся в виде целого числа -- количества минут от начала работы метрополитена. Значения времени входа и времени выхода разделены пробелом. В следующей строке дано время T.
**Формат вывода:** Выведите одно число: количество пассажиров в момент времени T. Если какой-то пассажир в момент T входит или выходит, то его тоже необходимо посчитать.
**Название файла:** metro.py
---
## 16) Игра миньонов
Кевин и Стюарт хотят сыграть в «Игру миньонов».
**Правила игры** Обоим игрокам даётся одна и та же строка. Оба игрока должны составить подстроки, используя буквы строки. Стюарт должен составить слова, начинающиеся с согласных. Кевину нужно составить слова, начинающиеся с гласных. Игра заканчивается, когда оба игрока составили все возможные подстроки.
**Подсчет очков** Игрок получает +1 очко за каждое вхождение подстроки в строку.
Например: Строка = БАНАНА Начальное гласное слово Кевина = АНА. Здесь АНА встречается дважды в БАНАНА. Следовательно, Кевин получит 2 очка.
Для лучшего понимания посмотрите изображение ниже:
Ваша задача — определить победителя игры и его счёт.
**Формат ввода:** Одна строка ввода, содержащая строку S.
**Примечание.** Строка будет содержать только буквы верхнего регистра: [A-Z].
**Формат вывода:** Одна строка, содержащая имя победителя (Кевин или Стюарт) и набранное им количество очков (значения должны быть разделены пробелом).
**Ограничения:** 0 < len(S) 10
**Пример ввода:**
```
BANANA
```
**Пример вывода:**
```
Стюарт 12
```
**Название файла:** minion_game.py
---
## 17) Високосный год
Почти каждые четыре года в календарь добавляется дополнительный день — 29 февраля, и этот день называется високосным. Он вносит в календарь поправку на тот факт, что наша планета совершает оборот вокруг Солнца примерно за 365,25 дней.
В григорианском календаре год является високосным, если он соответствует следующим правилам:
- Годы, делящиеся на 100 без остатка, не являются високосными, за исключением годов, которые делятся на 400 без остатка. Например, 1900 год не является високосным, а 2000 год — является.
- Годы делящиеся на 4 без остатка (например, 2016, 2024), являются високосными.
Это означает, что в григорианском календаре 2000 и 2400 годы являются високосными, а 1800, 1900, 2100, 2200, 2300 и 2500 НЕ являются високосными.(https://www.timeanddate.com/date/leapyear.html)
**Задача** Напишите функцию, позволяющую определить, является ли год високосным. Если это високосный год, верните логическое значение True, в противном случае верните False.
Ваша программа должна считывать год для проверки из стандартного потока ввода.
**Формат ввода:** Строка, содержащая год для проверки (year).
**Ограничения:** 1900 ⩽ year ⩽ 10⁵
**Выходной формат:** Результат вызова функции (True/False).
**Название файла:** is_leap.py
---
## 18) Предпочтения
Вам дан массив из n целых чисел. Существуют также 2 непересекающиеся множества A и B, каждое из которых содержит m целых чисел. Вам нравятся все числа в наборе A и не нравятся все числа в наборе B. Ваше первоначальное настроение равно 0. Для каждого целого числа i в массиве, если i ∈ A, ваше настроение повышается на 1. Если i ∈ B, ваше настроение уменьшается на 1. В противном случае ваше настроение не изменится. В конце выведите своё окончательное настроение.
**Примечание.** Поскольку A и B являются множествами, они не содержат повторяющихся элементов. Однако массив может содержать повторяющиеся элементы.
**Ограничения:** 1 ⩽ n ⩽ 10⁵ 1 ⩽ m ⩽ 10⁵ 1 ⩽ i ⩽ 10⁹
**Формат ввода:** Первая строка содержит целые числа n и m, разделённые пробелом. Вторая строка содержит n целых чисел — элементы массива. Третья и четвёртая строки содержат по m целых чисел, входящих в множества A и B соответственно.
**Формат вывода:** Выведите одно целое число — ваше общее настроение.
**Пример ввода:**
```
3 2
1 5 3
3 1
5 7
```
**Пример вывода:**
```
1
```
**Название файла:** happiness.py
---
## 19) Пиратский корабль
Капитан пиратского корабля награбил больше, чем рассчитывал, а грузоподъёмность его судна ограничена. Помогите ему найти комбинацию наиболее ценных предметов с учётом того, что на корабль можно загружать не только целые предметы, но и их части.
**Формат ввода:** Первая строка содержит целые числа n и m, разделённые пробелом (n -- грузоподъёмность судна, m -- количество наименований грузов). Далее идёт m срок, в каждой из которых содержится по три значения: название груза, вес (целое число), стоимость (целое число).
**Формат вывода:** Выведите в порядке убывания стоимости список грузов, которые нужно погрузить на корабль. Каждый груз выведите в отдельной строке в формате "название вес стоимость" (вес и стоимость должны быть указаны с учётом ограничения грузоподъёмности судна, в случае дробных значений округлите до второго знака после десятичного разделителя).
**Название файла:** pirate_ship.py
---
## 20) Произведение матриц
Напишите функцию, которая вычисляет произведение двух квадратных матриц (A и B). При реализации нельзя использовать сторонние библиотеки (NumPy и т. д.).
**Формат ввода:** Первая строка содержит целое число n -- размерность матрицы. Далее идут 2n строк, содержащие по n целых чисел, разделённых пробелами, -- содержимое матриц (первые n строк относятся к матрице A, остальные -- к матрице B).
**Формат вывода:** Произведение матриц A и B (n строк по n целых чисел, разделённых пробелами).
**Ограничения:** 2 ⩽ n ⩽ 10
**Название файла:** matrix_mult.py

10
hws/hw-1/anagram.py Normal file
View file

@ -0,0 +1,10 @@
s1 = input()
s2 = input()
if s1.isascii() and s2.isascii() and ' ' not in s1 and ' ' not in s2:
if sorted(s1) == sorted(s2):
print("YES")
else:
print("NO")
else:
print("Error")

View file

@ -0,0 +1,8 @@
a = float(input())
b = float(input())
if 1 <= a <= 10 ** 10 and 1 <= b <= 10 ** 10:
print(a + b)
print(a - b)
print(a * b)
else:
print("Error")

7
hws/hw-1/division.py Normal file
View file

@ -0,0 +1,7 @@
a = int(input())
b = int(input())
if b == 0:
print("Error: Division by zero")
else:
print(a // b)
print(a / b)

17
hws/hw-1/example.txt Normal file
View file

@ -0,0 +1,17 @@
В Гороховой улице, в одном из больших домов, народонаселения которого стало бы на
целый уездный город, лежал утром в постели, на своей квартире, Илья Ильич Обломов.
Это был человек лет тридцати двух-трех от роду, среднего роста, приятной наружности,
с темно-серыми глазами, но с отсутствием всякой определенной идеи, всякой сосредоточенности
в чертах лица. Мысль гуляла вольной птицей по лицу, порхала в глазах, садилась на
полуотворенные губы, пряталась в складках лба, потом совсем пропадала, и тогда
во всем лице теплился ровный свет беспечности. С лица беспечность переходила в
позы всего тела, даже в складки шлафрока.
Иногда взгляд его помрачался выражением будто усталости или скуки; но ни
усталость, ни скука не могли ни на минуту согнать с лица мягкость, которая была
господствующим и основным выражением, не лица только, а всей души; а душа так
открыто и ясно светилась в глазах, в улыбке, в каждом движении головы, руки. И
поверхностно наблюдательный, холодный человек, взглянув мимоходом на Обломова,
сказал бы: «Добряк должен быть, простота!» Человек поглубже и посимпатичнее,
долго вглядываясь в лицо его, отошел бы в приятном раздумье, с улыбкой.

19
hws/hw-1/happiness.py Normal file
View file

@ -0,0 +1,19 @@
n, m = map(int, input().split())
if 1 <= n <= 10**5 and 1 <= m <= 10**5:
arr = list(map(int, input().split()))
A = set(map(int, input().split()))
B = set(map(int, input().split()))
if all(1 <= i <= 10**9 for i in arr):
happiness = 0
for i in arr:
if i in A:
happiness += 1
elif i in B:
happiness -= 1
print(happiness)
else:
print("Error")
else:
print("Error")

1
hws/hw-1/hello.py Normal file
View file

@ -0,0 +1 @@
print('Hello, world!')

12
hws/hw-1/is_leap.py Normal file
View file

@ -0,0 +1,12 @@
year = int(input())
if 1900 <= year <= 10**5:
if year % 400 == 0:
print(True)
elif year % 100 == 0:
print(False)
elif year % 4 == 0:
print(True)
else:
print(False)
else:
print("Error")

22
hws/hw-1/lists.py Normal file
View file

@ -0,0 +1,22 @@
n = int(input())
arr = []
try:
for _ in range(n):
command = input().split()
cmd = command[0]
if cmd == "insert":
arr.insert(int(command[1]), int(command[2]))
elif cmd == "print":
print(arr)
elif cmd == "remove":
arr.remove(int(command[1]))
elif cmd == "append":
arr.append(int(command[1]))
elif cmd == "sort":
arr.sort()
elif cmd == "pop":
arr.pop()
elif cmd == "reverse":
arr.reverse()
except (ValueError, IndexError):
print("Error")

6
hws/hw-1/loops.py Normal file
View file

@ -0,0 +1,6 @@
n = int(input())
if 1 <= n <= 20:
for i in range(n):
print(i ** 2)
else:
print("Error")

20
hws/hw-1/matrix_mult.py Normal file
View file

@ -0,0 +1,20 @@
n = int(input())
if 2 <= n <= 10:
A = []
for _ in range(n):
A.append(list(map(int, input().split())))
B = []
for _ in range(n):
B.append(list(map(int, input().split())))
result = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
for k in range(n):
result[i][j] += A[i][k] * B[k][j]
for row in result:
print(' '.join(map(str, row)))
else:
print("Error")

14
hws/hw-1/max_word.py Normal file
View file

@ -0,0 +1,14 @@
import re
with open('example.txt', 'r', encoding='utf-8') as f:
text = f.read()
words = re.findall(r'[а-яА-ЯёЁa-zA-Z]+', text)
max_len = max(len(w) for w in words)
result = []
for w in words:
if len(w) == max_len and w not in result:
result.append(w)
for w in result:
print(w)

23
hws/hw-1/metro.py Normal file
View file

@ -0,0 +1,23 @@
n = int(input())
passengers = []
valid = True
for _ in range(n):
a, b = map(int, input().split())
if a < b:
passengers.append((a, b))
else:
valid = False
break
if valid and passengers:
t = int(input())
count = 0
for a, b in passengers:
if a <= t <= b:
count += 1
print(count)
else:
if not valid:
t = input()
print("Error")

20
hws/hw-1/minion_game.py Normal file
View file

@ -0,0 +1,20 @@
s = input()
if 0 < len(s) <= 10**6:
vowels = "AEIOUY"
kevin_score = 0
stuart_score = 0
for i in range(len(s)):
if s[i] in vowels:
kevin_score += len(s) - i
else:
stuart_score += len(s) - i
if kevin_score > stuart_score:
print(f"Kevin {kevin_score}")
elif stuart_score > kevin_score:
print(f"Stuart {stuart_score}")
else:
print("Draw")
else:
print("Error")

16
hws/hw-1/nested_list.py Normal file
View file

@ -0,0 +1,16 @@
n = int(input())
if 2 <= n <= 5:
students = []
for _ in range(n):
name = input()
score = float(input())
students.append([name, score])
scores = sorted(set([s[1] for s in students]))
second_lowest = scores[1]
names = sorted([s[0] for s in students if s[1] == second_lowest])
for name in names:
print(name)
else:
print("Error")

23
hws/hw-1/pirate_ship.py Normal file
View file

@ -0,0 +1,23 @@
n, m = map(int, input().split())
items = []
for _ in range(m):
parts = input().split()
name = parts[0]
weight = int(parts[1])
value = int(parts[2])
items.append((name, weight, value, value / weight))
items.sort(key=lambda x: x[3], reverse=True)
remaining = n
for name, weight, value, ratio in items:
if remaining == 0:
break
if weight <= remaining:
print(f"{name} {weight} {value}")
remaining -= weight
else:
new_weight = remaining
new_value = round(ratio * remaining, 2)
print(f"{name} {new_weight} {new_value}")
remaining = 0

14
hws/hw-1/price_sum.py Normal file
View file

@ -0,0 +1,14 @@
import csv
adult_sum = 0
senior_sum = 0
child_sum = 0
with open('products.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
adult_sum += float(row['Взрослый'])
senior_sum += float(row['Пенсионер'])
child_sum += float(row['Ребенок'])
print(f"{adult_sum:.2f} {senior_sum:.2f} {child_sum:.2f}")

View file

@ -0,0 +1,8 @@
n = int(input())
if 1 <= n <= 20:
result = ""
for i in range(1, n + 1):
result += str(i)
print(result)
else:
print("Error")

34
hws/hw-1/products.csv Normal file
View file

@ -0,0 +1,34 @@
Продукт,Взрослый,Пенсионер,Ребенок
говядина,878.66,814.37,754.37
молоко,744.90,651.79,852.91
Фрукты свежие (Яблоки),588.45,441.34,1158.27
хлеб пшеничный,510.95,482.03,358.18
мясо птицы,502.28,482.96,428.87
творог,477.17,409.00,477.17
хлеб ржаной,426.09,216.45,155.84
рыба свежая,325.72,279.19,327.58
свинина,300.76,257.79,105.98
сыр,288.41,256.36,288.41
масло сливочное,232.62,211.48,387.70
Картофель,230.67,183.80,202.41
Яйца (штук),187.25,178.33,179.23
сахар,139.05,130.43,115.05
столовые корнеплоды,125.64,109.76,123.04
капуста свежая и квашеная,114.48,94.74,92.63
масло растительное,102.80,86.57,48.69
баранина,83.66,52.29,0.00
макаронные изделия,81.38,72.33,63.29
прочие овощи (морковь),78.14,69.15,76.41
огурцы и помидоры свежие и соленые,71.38,57.11,179.89
чай,51.22,51.22,40.97
сметана,46.54,46.54,69.82
другие крупы (кроме риса),40.77,37.06,44.48
конфеты,39.17,39.17,78.33
рис,31.23,31.23,41.64
маргарин и другие жиры,29.58,39.43,9.86
мука пшеничная,24.83,24.83,20.69
бобовые,21.88,18.23,10.94
специи,21.85,21.85,21.85
печенье,21.24,21.24,74.35
сельдь,18.33,18.33,18.33
соль,5.74,4.66,3.72
1 Продукт Взрослый Пенсионер Ребенок
2 говядина 878.66 814.37 754.37
3 молоко 744.90 651.79 852.91
4 Фрукты свежие (Яблоки) 588.45 441.34 1158.27
5 хлеб пшеничный 510.95 482.03 358.18
6 мясо птицы 502.28 482.96 428.87
7 творог 477.17 409.00 477.17
8 хлеб ржаной 426.09 216.45 155.84
9 рыба свежая 325.72 279.19 327.58
10 свинина 300.76 257.79 105.98
11 сыр 288.41 256.36 288.41
12 масло сливочное 232.62 211.48 387.70
13 Картофель 230.67 183.80 202.41
14 Яйца (штук) 187.25 178.33 179.23
15 сахар 139.05 130.43 115.05
16 столовые корнеплоды 125.64 109.76 123.04
17 капуста свежая и квашеная 114.48 94.74 92.63
18 масло растительное 102.80 86.57 48.69
19 баранина 83.66 52.29 0.00
20 макаронные изделия 81.38 72.33 63.29
21 прочие овощи (морковь) 78.14 69.15 76.41
22 огурцы и помидоры свежие и соленые 71.38 57.11 179.89
23 чай 51.22 51.22 40.97
24 сметана 46.54 46.54 69.82
25 другие крупы (кроме риса) 40.77 37.06 44.48
26 конфеты 39.17 39.17 78.33
27 рис 31.23 31.23 41.64
28 маргарин и другие жиры 29.58 39.43 9.86
29 мука пшеничная 24.83 24.83 20.69
30 бобовые 21.88 18.23 10.94
31 специи 21.85 21.85 21.85
32 печенье 21.24 21.24 74.35
33 сельдь 18.33 18.33 18.33
34 соль 5.74 4.66 3.72

View file

@ -0,0 +1,12 @@
n = int(input())
if 1 <= n <= 100:
if n % 2 == 1:
print("Weird")
elif n % 2 == 0 and n >= 2 and n <= 5:
print("Not Weird")
elif n % 2 == 0 and n >= 6 and n <= 20:
print("Weird")
else:
print("Not Weird")
else:
print("Error")

5
hws/hw-1/second_score.py Normal file
View file

@ -0,0 +1,5 @@
n = int(input())
scores = list(map(int, input().split()))
max_score = max(scores)
second_max = max([s for s in scores if s < max_score])
print(second_max)

View file

@ -0,0 +1,2 @@
s = input()
print('-'.join(s.split()))

13
hws/hw-1/swap_case.py Normal file
View file

@ -0,0 +1,13 @@
s = input()
if 0 < len(s) <= 1000:
result = ""
for c in s:
if c.isupper():
result += c.lower()
elif c.islower():
result += c.upper()
else:
result += c
print(result)
else:
print("Error")

323
hws/hw-1/test.py Normal file
View file

@ -0,0 +1,323 @@
import subprocess
import pytest
INTERPRETER = 'python'
def run_script(filename, input_data=None):
proc = subprocess.run(
[INTERPRETER, filename],
input='\n'.join(input_data if input_data else []),
capture_output=True,
text=True,
check=False
)
return proc.stdout.strip()
test_data = {
'python_if_else': [
('1', 'Weird'), # граница: минимум n=1
('4', 'Not Weird'),
('3', 'Weird'),
('6','Weird'),
('22', 'Not Weird'),
('2', 'Not Weird'),
('5', 'Weird'),
('7', 'Weird'),
('19', 'Weird'),
('20', 'Weird'),
('21', 'Weird'),
('100', 'Not Weird') # граница: максимум n=100
],
'arithmetic_operators': [
(['1', '1'], ['2.0', '0.0', '1.0']), # граница: минимум a=1, b=1
(['1', '2'], ['3.0', '-1.0', '2.0']),
(['10', '5'], ['15.0', '5.0', '50.0']),
(['10000000000', '10000000000'], ['20000000000.0', '0.0', '1e+20']), # граница: максимум a=10^10, b=10^10
(['100', '50'], ['150.0', '50.0', '5000.0']),
(['1', '10000000000'], ['10000000001.0', '-9999999999.0', '10000000000.0']) # граница: проверка диапазона
],
'division': [
(['3', '5'], ['0', '0.6']),
(['10', '2'], ['5', '5.0']),
(['7', '3'], ['2', '2.3333333333333335']),
(['100', '10'], ['10', '10.0'])
],
'loops': [
(['1'], ['0']), # граница: минимум n=1
(['3'], ['0', '1', '4']),
(['5'], ['0', '1', '4', '9', '16']),
(['10'], ['0', '1', '4', '9', '16', '25', '36', '49', '64', '81']),
(['20'], ['0', '1', '4', '9', '16', '25', '36', '49', '64', '81', '100', '121', '144', '169', '196', '225', '256', '289', '324', '361']) # граница: максимум n=20
],
'print_function': [
(['1'], ['1']), # граница: минимум n=1
(['5'], ['12345']),
(['10'], ['12345678910']),
(['15'], ['123456789101112131415']),
(['20'], ['1234567891011121314151617181920']) # граница: максимум n=20
],
'second_score': [
(['5', '2 3 6 6 5'], ['5']),
(['4', '10 20 30 40'], ['30']),
(['3', '1 1 2'], ['1']),
(['6', '5 5 5 10 10 15'], ['10'])
],
'nested_list': [
(['2', 'Harry', '37.21', 'Berry', '37.2'], ['Harry']), # граница: минимум N=2
(['3', 'Alice', '50', 'Bob', '45', 'Charlie', '45'], ['Alice']),
(['5', 'Гарри', '37.21', 'Берри', '37.21', 'Тина', '37.2', 'Акрити', '41', 'Харш', '39'], ['Берри', 'Гарри']), # граница: максимум N=5
(['3', 'Ann', '10', 'Bob', '20', 'Carl', '20'], ['Bob', 'Carl']) # несколько студентов со вторым баллом
],
'swap_case': [
(['a'], ['A']), # граница: минимальная длина len(s)=1
(['Hello'], ['hELLO']),
(['Www.MosPolytech.ru'], ['wWW.mOSpOLYTECH.RU']),
(['Pythonist 2'], ['pYTHONIST 2']),
(['ABC123xyz'], ['abc123XYZ']),
(['a' * 1000], ['A' * 1000]) # граница: максимальная длина len(s)=1000
],
'split_and_join': [
(['this is a string'], ['this-is-a-string']),
(['hello world'], ['hello-world']),
(['one two three four'], ['one-two-three-four'])
],
'anagram': [
(['abc', 'bca'], ['YES']),
(['abc', 'def'], ['NO']),
(['listen', 'silent'], ['YES']),
(['hello', 'world'], ['NO']),
(['test', 'sett'], ['YES'])
],
'is_leap': [
(['1900'], ['False']), # граница: минимум year=1900
(['2000'], ['True']), # делится на 400
(['1900'], ['False']), # делится на 100, но не на 400
(['2016'], ['True']), # делится на 4, но не на 100
(['2017'], ['False']), # не делится на 4
(['2400'], ['True']), # делится на 400
(['2100'], ['False']), # делится на 100, но не на 400
(['2020'], ['True']),
(['2024'], ['True']),
(['100000'], ['True']) # граница: максимум year=10^5, делится на 400
],
'happiness': [
(['1 1', '1', '1', '2'], ['1']), # граница: минимум n=1, m=1
(['3 2', '1 5 3', '3 1', '5 7'], ['1']),
(['5 3', '1 2 3 4 5', '1 2 3', '4 5 6'], ['1']),
(['4 2', '10 20 10 20', '10 20', '30 40'], ['4']),
(['6 2', '1 2 3 4 5 6', '1 3 5', '2 4 6'], ['0']),
(['3 3', '1000000000 1000000000 1000000000', '1000000000 999999999 999999998', '1 2 3'], ['3']) # граница: проверка больших чисел (до 10^9)
],
'minion_game': [
(['A'], ['Kevin 1']), # граница: минимальная длина len(S)=1 (гласная)
(['B'], ['Stuart 1']), # граница: минимальная длина len(S)=1 (согласная)
(['BANANA'], ['Stuart 12']),
(['ABC'], ['Draw']),
(['AEIOUY'], ['Kevin 21']),
(['A' * 1000000], ['Kevin 500000500000']) # граница: максимальная длина len(S)=10^6
],
'matrix_mult': [
(['2', '1 2', '3 4', '5 6', '7 8'], ['19 22', '43 50']), # граница: минимум n=2
(['2', '1 0', '0 1', '2 3', '4 5'], ['2 3', '4 5']),
(['3', '1 2 3', '4 5 6', '7 8 9', '9 8 7', '6 5 4', '3 2 1'], ['30 24 18', '84 69 54', '138 114 90']),
(['10', '1 0 0 0 0 0 0 0 0 0', '0 1 0 0 0 0 0 0 0 0', '0 0 1 0 0 0 0 0 0 0', '0 0 0 1 0 0 0 0 0 0', '0 0 0 0 1 0 0 0 0 0', '0 0 0 0 0 1 0 0 0 0', '0 0 0 0 0 0 1 0 0 0', '0 0 0 0 0 0 0 1 0 0', '0 0 0 0 0 0 0 0 1 0', '0 0 0 0 0 0 0 0 0 1', '1 2 3 4 5 6 7 8 9 10', '10 9 8 7 6 5 4 3 2 1', '1 1 1 1 1 1 1 1 1 1', '2 2 2 2 2 2 2 2 2 2', '3 3 3 3 3 3 3 3 3 3', '4 4 4 4 4 4 4 4 4 4', '5 5 5 5 5 5 5 5 5 5', '6 6 6 6 6 6 6 6 6 6', '7 7 7 7 7 7 7 7 7 7', '8 8 8 8 8 8 8 8 8 8'], ['1 2 3 4 5 6 7 8 9 10', '10 9 8 7 6 5 4 3 2 1', '1 1 1 1 1 1 1 1 1 1', '2 2 2 2 2 2 2 2 2 2', '3 3 3 3 3 3 3 3 3 3', '4 4 4 4 4 4 4 4 4 4', '5 5 5 5 5 5 5 5 5 5', '6 6 6 6 6 6 6 6 6 6', '7 7 7 7 7 7 7 7 7 7', '8 8 8 8 8 8 8 8 8 8']) # граница: максимум n=10
],
'lists': [
(['4', 'append 1', 'append 2', 'insert 1 3', 'print'], ['[1, 3, 2]']),
(['3', 'append 5', 'append 10', 'print'], ['[5, 10]']),
(['5', 'insert 0 1', 'insert 1 2', 'insert 2 3', 'sort', 'print'], ['[1, 2, 3]'])
],
'metro': [
(['2', '10 30', '20 40', '25'], ['2']),
(['3', '5 15', '10 20', '25 35', '12'], ['2']),
(['4', '0 10', '5 15', '20 30', '25 35', '10'], ['2'])
],
'max_word': [
# max_word.py читает из файла example.txt и не принимает входные данные
],
'price_sum': [
# price_sum.py читает из файла products.csv и не принимает входные данные
# Ожидаемый вывод: "6842.84 5891.06 6810.90"
],
'pirate_ship': [
(['10 3', 'золото 5 100', 'серебро 10 80', 'бронза 15 60'], ['золото 5 100', 'серебро 5 40.0']),
(['50 4', 'алмазы 10 500', 'золото 20 400', 'серебро 30 300', 'бронза 40 200'], ['алмазы 10 500', 'золото 20 400', 'серебро 20 200.0']),
(['5 2', 'товар1 10 100', 'товар2 3 30'], ['товар1 5 50.0']),
(['100 1', 'груз 50 1000'], ['груз 50 1000']), # граница: груз меньше грузоподъемности
(['10 1', 'груз 20 1000'], ['груз 10 500.0']) # граница: груз больше грузоподъемности
]
}
# Тесты для проверки обработки ошибок (значения вне диапазона)
error_test_data = {
'python_if_else': [
('0', 'Error'), # n < 1
('101', 'Error'), # n > 100
],
'arithmetic_operators': [
(['0', '5'], ['Error']), # a < 1
(['5', '0'], ['Error']), # b < 1
(['10000000001', '5'], ['Error']), # a > 10^10
(['5', '10000000001'], ['Error']), # b > 10^10
],
'loops': [
(['0'], ['Error']), # n < 1
(['21'], ['Error']), # n > 20
],
'print_function': [
(['0'], ['Error']), # n < 1
(['21'], ['Error']), # n > 20
],
'nested_list': [
(['1', 'Alice', '50'], ['Error']), # N < 2
(['6', 'A', '1', 'B', '2', 'C', '3', 'D', '4', 'E', '5', 'F', '6'], ['Error']), # N > 5
],
'swap_case': [
(['a' * 1001], ['Error']), # len(s) > 1000
],
'minion_game': [
(['a' * (10 ** 6 + 1)], ['Error']), # len(s) > 10 ** 6
],
'is_leap': [
(['1899'], ['Error']), # year < 1900
(['100001'], ['Error']), # year > 10^5
],
'happiness': [
(['0 5', '1', '1', '2'], ['Error']), # n < 1
(['5 0', '1 2 3', '1', '2'], ['Error']), # m < 1
],
'matrix_mult': [
(['1'], ['Error']), # n < 2
(['11'], ['Error']), # n > 10
],
}
def test_hello():
assert run_script('hello.py') == 'Hello, world!'
@pytest.mark.parametrize("input_data, expected", test_data['python_if_else'])
def test_python_if_else(input_data, expected):
assert run_script('python_if_else.py', [input_data]) == expected
@pytest.mark.parametrize("input_data, expected", test_data['arithmetic_operators'])
def test_arithmetic_operators(input_data, expected):
assert run_script('arithmetic_operators.py', input_data).split('\n') == expected
@pytest.mark.parametrize("input_data, expected", test_data['division'])
def test_division(input_data, expected):
assert run_script('division.py', input_data).split('\n') == expected
@pytest.mark.parametrize("input_data, expected", test_data['loops'])
def test_loops(input_data, expected):
assert run_script('loops.py', input_data).split('\n') == expected
@pytest.mark.parametrize("input_data, expected", test_data['print_function'])
def test_print_function(input_data, expected):
assert run_script('print_function.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", test_data['second_score'])
def test_second_score(input_data, expected):
assert run_script('second_score.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", test_data['nested_list'])
def test_nested_list(input_data, expected):
result = run_script('nested_list.py', input_data).split('\n')
assert result == expected
@pytest.mark.parametrize("input_data, expected", test_data['swap_case'])
def test_swap_case(input_data, expected):
assert run_script('swap_case.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", test_data['split_and_join'])
def test_split_and_join(input_data, expected):
assert run_script('split_and_join.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", test_data['anagram'])
def test_anagram(input_data, expected):
assert run_script('anagram.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", test_data['is_leap'])
def test_is_leap(input_data, expected):
assert run_script('is_leap.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", test_data['happiness'])
def test_happiness(input_data, expected):
assert run_script('happiness.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", test_data['minion_game'])
def test_minion_game(input_data, expected):
assert run_script('minion_game.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", test_data['matrix_mult'])
def test_matrix_mult(input_data, expected):
assert run_script('matrix_mult.py', input_data).split('\n') == expected
@pytest.mark.parametrize("input_data, expected", test_data['lists'])
def test_lists(input_data, expected):
assert run_script('lists.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", test_data['metro'])
def test_metro(input_data, expected):
assert run_script('metro.py', input_data) == expected[0]
def test_max_word():
# max_word.py читает файл example.txt, поэтому проверяем только что программа запускается
result = run_script('max_word.py')
assert len(result) > 0
words = result.split('\n')
assert len(words) > 0
def test_price_sum():
result = run_script('price_sum.py')
assert result == '6842.84 5891.06 6810.90'
@pytest.mark.parametrize("input_data, expected", test_data['pirate_ship'])
def test_pirate_ship(input_data, expected):
result = run_script('pirate_ship.py', input_data).split('\n')
assert result == expected
# Тесты для проверки обработки ошибок (значения вне допустимого диапазона)
@pytest.mark.parametrize("input_data, expected", error_test_data['python_if_else'])
def test_python_if_else_errors(input_data, expected):
assert run_script('python_if_else.py', [input_data]) == expected
@pytest.mark.parametrize("input_data, expected", error_test_data['arithmetic_operators'])
def test_arithmetic_operators_errors(input_data, expected):
result = run_script('arithmetic_operators.py', input_data)
assert result == expected[0]
@pytest.mark.parametrize("input_data, expected", error_test_data['loops'])
def test_loops_errors(input_data, expected):
assert run_script('loops.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", error_test_data['print_function'])
def test_print_function_errors(input_data, expected):
assert run_script('print_function.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", error_test_data['nested_list'])
def test_nested_list_errors(input_data, expected):
result = run_script('nested_list.py', input_data)
assert result == expected[0]
@pytest.mark.parametrize("input_data, expected", error_test_data['swap_case'])
def test_swap_case_errors(input_data, expected):
assert run_script('swap_case.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", error_test_data['minion_game'])
def test_minion_game_errors(input_data, expected):
assert run_script('minion_game.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", error_test_data['is_leap'])
def test_is_leap_errors(input_data, expected):
assert run_script('is_leap.py', input_data) == expected[0]
@pytest.mark.parametrize("input_data, expected", error_test_data['happiness'])
def test_happiness_errors(input_data, expected):
result = run_script('happiness.py', input_data)
assert result == expected[0]
@pytest.mark.parametrize("input_data, expected", error_test_data['matrix_mult'])
def test_matrix_mult_errors(input_data, expected):
result = run_script('matrix_mult.py', input_data)
assert result == expected[0]

354
hws/hw-2/README.md Normal file
View file

@ -0,0 +1,354 @@
Решите задачи, приведённые ниже. Код каждой задачи разместите в отдельном файле, название которого соответствует указанному в условии задачи. В качестве ответа на это задание загрузите архив (ZIP) с решениями. Архив должен содержать только файлы с решениями, файл test.py и вспомогательные файлы, необходимые для отдельных задач (не должно быть папок).
Напишите тесты для решённых задач с использованием фреймворка pytest, разместите их в файле test.py. Для каждой задачи достаточно рассмотреть принципиально различные ситуации (входные данные). Всего должно быть написано не менее 60 тестов. В отличие от предыдущего домашнего задания, в данном задании предполагается, что решение каждой задачи будет представлено в виде функции (если не сказано иного), которую вам необходимо импортировать в файле test.py для последующего тестирования (т. е. в тестах должны непосредственно вызываться тестируемые функции). При импорте функций не должно выполняться ничего лишнего. При необходимости выполнить какие-либо действия при запуске файла с программой (например, запросить пользовательский ввод, осуществить замер времени выполнения функции т. д.) не забудьте заключить их в условие `if __name__ == '__main__':`.
При обнаружении ошибок или неточностей в формулировках задач свяжитесь с преподавателем.
## 1) Вычисление факториала
Реализуйте рекурсивную (fact_rec) и итерационную (fact_it) функции вычисления факториала целого положительного числа n. Сравните скорость работы реализованных функций, результат сравнения приведите в комментарии в файле с программой. Для измерения времени выполнения можно использовать модули `time` или `timeit`. Функцией math.factorial пользоваться нельзя.
**Ограничения:**
- 1 ⩽ n ⩽ 10⁵
**Название файла:** fact.py
## 2) Данные сотрудника
Реализуйте функцию show_employee, принимающую в качестве аргументов имя сотрудника (name) и величину его заработной платы (salary) и возвращающую строку с данными в следующем формате: "Иванов Иван Иванович: 30000 ₽". В случае, если значение заработной платы не было передано при вызове функции, установить его равным 100000.
**Название файла:** show_employee.py
## 3) Сумма и разность
Реализуйте функцию sum_and_sub, которая принимает в качестве аргументов два действительных числа (a и b) и возвращает их сумму и разность.
**Название файла:** sum_and_sub.py
## 4) Обработка списка
В файле process_list.py приведён код функции обработки данных списка (arr).
Перепишите приведённую функцию с использованием list comprehension. Реализуйте функцию-генератор с аналогичной функциональностью (process_list_gen). Сравните скорость работы реализованных функций, результат сравнения приведите в комментарии в файле с программой.
**Ограничения:**
- 1 ⩽ len(arr) ⩽ 10³
**Название файла:** process_list.py
## 5) Функция сложения с произвольным количеством аргументов
Реализуйте функцию my_sum, принимающую произвольное количество аргументов (действительных чисел) и возвращающую их сумму.
**Название файла:** my_sum.py
## 6) Аргументы командной строки
Перепишите программу из 5-й задачи таким образом, чтобы числа для сложения передавались при вызове программы как аргументы командной строки, а результат выводился на стандартный поток вывода.
**Название файла:** my_sum_argv.py
**Пример вызова:** python3 my_sum_argv.py 1 2 3 4 5
**Пример вывода:**
```
15
```
## 7) Сортировка файлов
Напишите программу, которая в качестве аргумента командной строки принимает путь до некоторой директории и выводит список всех файлов в этой директории, упорядоченных в лексикографическом порядке и сгруппированных по расширению. Имена вложенных подкаталогов выводить не нужно. Например, если в директории находятся файлы a.py, a.txt, b.py, b.txt, c.py, c.txt, они должны быть выведены в следующем порядке:
```
a.py
b.py
c.py
a.txt
b.txt
c.txt
```
**Подсказка:** для того, чтобы отличить строку с именем файла от строки с именем подкаталога можно использовать функции os.path.isfile или os.path.isdir.
**Название файла:** files_sort.py
## 8) Рекурсивный поиск файла
Напишите программу, которая в качестве аргумента командной строки принимает имя файла и осуществляет рекурсивный поиск указанного файла относительно директории, в которой находится файл с программой. Если файл найден -- вывести первые 5 строк, в противном случае -- вывести сообщение "Файл {названиеайла} не найден".
**Подсказка:** если вы пишете рекурсивную функцию, то после рекурсивного вызова нужно возвращаться в родительский каталог при помощи os.chdir(".."). Другой вариант решения задачи: воспользоваться функцией os.walk.
**Название файла:** file_search.py
## 9) Валидация адресов электронной почты
Вам дается целое число N, за которым следуют N адресов электронной почты. Ваша задача -- вывести список, содержащий только корректные адреса электронной почты в лексикографическом порядке.
Корректные адреса электронной почты должны соответствовать следующим правилам:
- Он должен иметь формат username@websitename.extension.
- Имя пользователя может содержать только цифры, латинские буквы, тире и подчеркивания.
- Название веб-сайта может содержать только цифры и латинские буквы.
- Расширение может содержать только латинские буквы.
- Максимальная длина расширения составляет 3.
Допишите функцию fun, которая прилагается к заданию, чтобы она возвращала True для корректных адресов, а для остальных -- False.
**Формат ввода:**
Первая строка ввода -- целое число, количество адресов электронной почты. Далее следуют N строк, каждая из которых содержит адрес электронной почты.
**Ограничения:**
Не может быть введено пустых значений.
**Пример ввода:**
```
3
lara@mospolytech.ru
brian-23@mospolytech.ru
britts_54@mospolytech.ru
```
**Пример вывода:**
```
['brian-23@mospolytech.ru', 'britts_54@mospolytech.ru', 'lara@mospolytech.ru']
```
**Название файла:** email_validation.py
## 10) Числа Фибоначчи
Дополните код, приведённый в файле fibonacci.py. Вы должны сгенерировать список первых n чисел Фибоначчи, начиная с 0. Затем примените функцию map и лямбда-выражение для возведения в куб каждого из полученных чисел Фибоначчи и выведите список на стандартный поток вывода.
**Формат ввода:**
Одна строка: целое число n.
**Формат вывода:**
Список в одной строке, содержащий кубы первых n чисел Фибоначчи.
**Ограничения:**
- 1 ⩽ n ⩽ 15
**Пример ввода:**
```
5
```
**Пример вывода:**
```
[0, 1, 1, 8, 27]
```
**Название файла:** fibonacci.py
## 11) Средние оценки
Университет проводит экзамен для N студентов по X предметам. Реализуйте функцию compute_average_scores, которая подсчитывает средние баллы каждого студента. Функция принимает один параметр scores -- список из X кортежей, каждый из которых содержит по N чисел в диапазоне от 0 до 100. Каждый кортеж соответствует оценкам по определённому предмету. Элементы с одинаковым индексом в каждом из кортежей соответствуют одному и тому же студенту. Возвращает функция кортеж из X элементов -- средние баллы каждого студента.
**Формат ввода:**
Первая строка содержит значения N и X, разделенные пробелом. Следующие X строк содержат оценки, разделенные пробелом, полученные учащимися по определенному предмету.
**Ограничения:**
- 0 < N 100
- 0 < X 100
**Формат вывода:**
Выведите средние значения по всем учащимся в отдельных строках. Средние значения должны быть корректными с точностью до 1 десятичного знака.
**Пример ввода:**
```
5 3
89 90 78 93 80
90 91 85 88 86
91 92 83 89 90.5
```
**Пример вывода:**
```
90.0
91.0
82.0
90.0
85.5
```
**Название файла:** average_scores.py
## 12) Угол между плоскостями
Вам даны четыре точки (A, B, C, D) в трехмерной декартовой системе координат. Вам нужно вычислить угол между плоскостями, образованными точками A, B, C и B, C, D в градусах (не в радианах). Обозначим этот угол как ϕ, тогда cos(ϕ) = (X, Y)/|X||Y|, где X = AB × BC и Y = BC × CD. (X, Y) -- скалярное произведение X и Y, AB × BC -- векторное произведение AB и BC, где AB = B A и BC = B C. Оформите решение в виде функции plane_angle, которая принимает четыре аргумента: точки A, B, C, D (экземпляры класса Point). В файле plane_angle.py приведена заготовка класса Point -- допишите недостающие методы, которые вам потребуются для решения задачи. Функция plane_angle должна возвращать одно число -- искомый угол в градусах.
**Название файла:** plane_angle.py
## 13) Форматирование номера телефона
Вам даны номера мобильных телефонов. Отсортируйте их в порядке возрастания, затем выведите в стандартном формате: +7 (xxx) xxx-xx-xx. Указанные мобильные номера могут начинаться с 8, +7 или 0, после чего следует десятизначный номер. Также префикс может отсутствовать вовсе. Вам дана заготовка кода в файле phone_number.py -- заполните недостающие фрагменты.
**Формат ввода:**
Первая строка ввода содержит целое число N, количество номеров мобильных телефонов. Далее следуют N строк, содержащие номера мобильных телефонов.
**Формат вывода:**
Выведите N номеров мобильных телефонов отдельными строками в требуемом формате.
**Пример ввода:**
```
3
07895462130
89875641230
9195969878
```
**Пример вывода:**
```
+7 (789) 546-21-30
+7 (919) 596-98-78
+7 (987) 564-12-30
```
**Название файла:** phone_number.py
## 14) Сортировка списка
Вам предоставляется некоторая информация о людях. Для каждого человека в вашем распоряжении есть имя, фамилия, возраст и пол. Выведите их имена в определенном формате (см. ниже), отсортируйте список по возрасту в порядке возрастания, т.е. имя самого младшего человека должно быть напечатано первым. Для двух человек одного возраста выведите их в порядке ввода. Вам дана заготовка кода в файле people_sort.py -- заполните недостающие фрагменты.
**Формат ввода:**
Первая строка содержит целое число N, количество человек. Следующие N строк содержат разделенные пробелом значения имени, фамилии, возраста и пола соответственно.
**Формат вывода:**
Выведите имена в отдельных строках в приведённом ниже формате в порядке возрастания возраста.
Пример формата вывода: для "Henry Davids" вывод должен быть "Mr. Henry Davids", для "Mary George" вывод должен быть "Ms. Mary George".
**Ограничения:**
- 1 ⩽ N ⩽ 10
**Пример ввода:**
```
3
Mike Thomson 20 M
Robert Bustle 32 M
Andria Bustle 30 F
```
**Пример вывода:**
```
Mr. Mike Thomson
Ms. Andria Bustle
Mr. Robert Bustle
```
**Название файла:** people_sort.py
## 15) Комплексные числа
Для этого задания вам даны два комплексных числа, и вы должны вывести результат их сложения, вычитания, умножения, деления и операции вычисления модуля. Действительная и мнимая части должны быть представлены с точностью до двух знаков после запятой. Вам дана заготовка кода в файле complex_numbers.py -- заполните недостающие фрагменты.
**Формат ввода:**
Одна строка ввода: действительная и мнимая части числа, разделенные пробелом.
**Формат вывода:**
Для двух комплексных чисел C и D выходные данные должны быть в следующей последовательности в отдельных строках:
- C + D,
- C - D,
- C * D,
- C/D,
- mod(C),
- mod(D).
Для комплексных чисел с ненулевой действительной (A) и комплексной (B) частью выходные данные должны быть в следующем формате: A + Bi. Знак "+" должен быть заменён на "-", если B < 0.
Для комплексных чисел с нулевой комплексной частью (B), т.е. действительных чисел, выходные данные должны быть в формате: A + 0.00i.
Для комплексных чисел, где действительная часть (A) равна нулю, а комплексная часть (B) ненулевая, выходные данные должны быть: 0.00 + Bi.
**Пример ввода:**
```
2 1
5 6
```
**Пример вывода:**
```
7.00+7.00i
-3.00-5.00i
4.00+17.00i
0.26-0.11i
2.24+0.00i
7.81+0.00i
```
**Название файла:** complex_numbers.py
## 16) Монте-Карло
Реализуйте вычисление площади окружности методом Монте-Карло. Оформите решение в виде функции circle_square_mk, которая принимает два параметра: r -- радиус окружности и n -- количество экспериментов в методе Монте-Карло, и возвращает действительное число -- полученную площадь. При тестировании функции сравните полученные результаты с площадью, вычисленной по формуле. Оцените получаемую погрешность расчёта в зависимости от количества экспериментов (n), результат укажите в комментарии.
**Название файла:** circle_square_mk.py
## 17) Декоратор для логирования
Напишите декоратор function_logger, который принимает в качестве аргумента путь к файлу. Если данный декоратор добавить к функции, то в указанный файл будет логироваться информация вида:
- Название функции,
- Время вызова функции (в формате yyyy-mm-dd HH:MM:SS),
- Входящие аргументы (при наличии: в виде кортежа -- позиционные, в виде словаря -- ключевые),
- Возвращаемое значение (если есть, если нет то логировать '-')
- Время завершения работы функции (в формате yyyy-mm-dd HH:MM:SS)
- Время работы функции (в секундах).
При отсутствии указанного файла он должен быть создан, в противном случае логи должны добавляться в конец файла.
**Пример использования:**
```python
@function_logger('test.log')
def greeting_format(name):
return f'Hello, {name}!'
greeting_format('John')
```
**Пример содержимого файла test.log:**
```
greeting_format
2024-02-19 16:00:12.670738
('John',)
Hello, John!
2024-02-19 16:00:12.670741
0:00:00.000003
```
**Название файла:** log_decorator.py
**Замечание:** При написании тестов вам может быть полезна библиотека FreezeGun.

View file

@ -0,0 +1,22 @@
def compute_average_scores(scores):
if not scores or len(scores) <= 0 or len(scores) > 100:
return "Error"
num_students = len(scores[0])
if num_students <= 0 or num_students > 100:
return "Error"
averages = []
for i in range(num_students):
total = sum(score[i] for score in scores)
avg = total / len(scores)
averages.append(avg)
return tuple(averages)
if __name__ == '__main__':
n, x = map(int, input().split())
scores = []
for _ in range(x):
scores.append(tuple(map(float, input().split())))
result = compute_average_scores(scores)
for avg in result:
print(f"{avg:.1f}")

View file

@ -0,0 +1,13 @@
import random
import math
def circle_square_mk(r, n):
inside = 0
for _ in range(n):
x = random.uniform(-r, r)
y = random.uniform(-r, r)
if x**2 + y**2 <= r**2:
inside += 1
square_area = (2 * r) ** 2
circle_area = square_area * inside / n
return circle_area

View file

@ -0,0 +1,47 @@
import math
class Complex(object):
def __init__(self, real, imaginary):
self.real = real
self.imaginary = imaginary
def __add__(self, no):
return Complex(self.real + no.real, self.imaginary + no.imaginary)
def __sub__(self, no):
return Complex(self.real - no.real, self.imaginary - no.imaginary)
def __mul__(self, no):
real = self.real * no.real - self.imaginary * no.imaginary
imaginary = self.real * no.imaginary + self.imaginary * no.real
return Complex(real, imaginary)
def __truediv__(self, no):
denominator = no.real ** 2 + no.imaginary ** 2
real = (self.real * no.real + self.imaginary * no.imaginary) / denominator
imaginary = (self.imaginary * no.real - self.real * no.imaginary) / denominator
return Complex(real, imaginary)
def mod(self):
return Complex(math.sqrt(self.real ** 2 + self.imaginary ** 2), 0)
def __str__(self):
if self.imaginary == 0:
result = "%.2f+0.00i" % (self.real)
elif self.real == 0:
if self.imaginary >= 0:
result = "0.00+%.2fi" % (self.imaginary)
else:
result = "0.00-%.2fi" % (abs(self.imaginary))
elif self.imaginary > 0:
result = "%.2f+%.2fi" % (self.real, self.imaginary)
else:
result = "%.2f-%.2fi" % (self.real, abs(self.imaginary))
return result
if __name__ == '__main__':
c = map(float, input().split())
d = map(float, input().split())
x = Complex(*c)
y = Complex(*d)
print(*map(str, [x+y, x-y, x*y, x/y, x.mod(), y.mod()]), sep='\n')

View file

@ -0,0 +1,17 @@
def fun(s):
import re
pattern = r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9]+\.[a-zA-Z]{1,3}$'
return bool(re.match(pattern, s))
def filter_mail(emails):
return list(filter(fun, emails))
if __name__ == '__main__':
n = int(input())
emails = []
for _ in range(n):
emails.append(input())
filtered_emails = filter_mail(emails)
filtered_emails.sort()
print(filtered_emails)

36
hws/hw-2/fact.py Normal file
View file

@ -0,0 +1,36 @@
import time
def fact_rec(n):
if n < 1 or n >= 1000:
return "Error"
if n <= 1:
return 1
return n * fact_rec(n - 1)
def fact_it(n):
if n < 1 or n >= 1000:
return "Error"
result = 1
for i in range(2, n + 1):
result *= i
return result
if __name__ == '__main__':
n = 999
max_it = 0
for _ in range(1000):
start_1 = time.time()
fact_it(n)
it_time = time.time() - start_1
if max_it < it_time: max_it = it_time
max_rec = 0
for _ in range(1000):
start_2 = time.time()
fact_rec(n)
rec_time = time.time() - start_2
if max_rec < rec_time: max_rec = rec_time
print(f"Итеративная: {max_it}с") # 0.001142740249633789
print(f"Рекурсивная: {max_rec}с") # 0.0011646747589111328

15
hws/hw-2/fibonacci.py Normal file
View file

@ -0,0 +1,15 @@
cube = lambda x: x**3
def fibonacci(n):
if n < 1 or n > 15:
return "Error"
if n == 1:
return [0]
result = [0, 1]
for i in range(2, n):
result.append(result[-1] + result[-2])
return result
if __name__ == '__main__':
n = int(input())
print(list(map(cube, fibonacci(n))))

30
hws/hw-2/file_search.py Normal file
View file

@ -0,0 +1,30 @@
import sys
import os
if __name__ == '__main__':
filename = sys.argv[1]
found = False
for root, dirs, files in os.walk('.'):
if filename in files:
file_path = os.path.join(root, filename)
try:
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
for i, line in enumerate(lines[:5]):
print(line, end='')
found = True
break
except:
try:
with open(file_path, 'r') as f:
lines = f.readlines()
for i, line in enumerate(lines[:5]):
print(line, end='')
found = True
break
except:
pass
if not found:
print(f"Файл {filename} не найден")

25
hws/hw-2/files_sort.py Normal file
View file

@ -0,0 +1,25 @@
import sys
import os
if __name__ == '__main__':
directory = sys.argv[1]
files = []
for item in os.listdir(directory):
full_path = os.path.join(directory, item)
if os.path.isfile(full_path):
files.append(item)
files_by_ext = {}
for file in files:
if '.' in file:
ext = file.split('.')[-1]
else:
ext = ''
if ext not in files_by_ext:
files_by_ext[ext] = []
files_by_ext[ext].append(file)
for ext in sorted(files_by_ext.keys()):
for file in sorted(files_by_ext[ext]):
print(file)

31
hws/hw-2/log_decorator.py Normal file
View file

@ -0,0 +1,31 @@
from datetime import datetime
import time
def function_logger(log_file):
def decorator(func):
def wrapper(*args, **kwargs):
start_time = datetime.now()
start_timestamp = time.time()
result = func(*args, **kwargs)
end_time = datetime.now()
duration = time.time() - start_timestamp
with open(log_file, 'a', encoding='utf-8') as f:
f.write(func.__name__ + '\n')
f.write(str(start_time) + '\n')
if args:
f.write(str(args) + '\n')
if kwargs:
f.write(str(kwargs) + '\n')
if result is not None:
f.write(str(result) + '\n')
else:
f.write('-\n')
f.write(str(end_time) + '\n')
f.write(f"{duration:.6f}\n")
return result
return wrapper
return decorator

2
hws/hw-2/my_sum.py Normal file
View file

@ -0,0 +1,2 @@
def my_sum(*args):
return sum(args)

8
hws/hw-2/my_sum_argv.py Normal file
View file

@ -0,0 +1,8 @@
import sys
def my_sum(*args):
return sum(args)
if __name__ == '__main__':
numbers = [float(x) for x in sys.argv[1:]]
print(int(my_sum(*numbers)))

15
hws/hw-2/people_sort.py Normal file
View file

@ -0,0 +1,15 @@
import operator
def person_lister(f):
def inner(people):
people.sort(key=lambda x: int(x[2]))
return [f(person) for person in people]
return inner
@person_lister
def name_format(person):
return ("Mr. " if person[3] == "M" else "Ms. ") + person[0] + " " + person[1]
if __name__ == '__main__':
people = [input().split() for i in range(int(input()))]
print(*name_format(people), sep='\n')

21
hws/hw-2/phone_number.py Normal file
View file

@ -0,0 +1,21 @@
def wrapper(f):
def fun(l):
normalized = []
for phone in l:
digits = ''.join(filter(str.isdigit, phone))
if len(digits) == 11:
digits = digits[1:]
elif len(digits) > 10:
digits = digits[-10:]
normalized.append(digits)
formatted = [f"+7 ({num[:3]}) {num[3:6]}-{num[6:8]}-{num[8:]}" for num in normalized]
return f(formatted)
return fun
@wrapper
def sort_phone(l):
return sorted(l)
if __name__ == '__main__':
l = [input() for _ in range(int(input()))]
print(*sort_phone(l), sep='\n')

38
hws/hw-2/plane_angle.py Normal file
View file

@ -0,0 +1,38 @@
import math
class Point:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __sub__(self, no):
return Point(self.x - no.x, self.y - no.y, self.z - no.z)
def dot(self, no):
return self.x * no.x + self.y * no.y + self.z * no.z
def cross(self, no):
return Point(
self.y * no.z - self.z * no.y,
self.z * no.x - self.x * no.z,
self.x * no.y - self.y * no.x
)
def absolute(self):
return math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
def plane_angle(a, b, c, d):
ab = b - a
bc = c - b
cd = d - c
x = ab.cross(bc)
y = bc.cross(cd)
cos_phi = x.dot(y) / (x.absolute() * y.absolute())
phi = math.acos(cos_phi)
return math.degrees(phi)
if __name__ == '__main__':
pass

46
hws/hw-2/process_list.py Normal file
View file

@ -0,0 +1,46 @@
def process_list(arr):
if len(arr) < 1 or len(arr) > 1000:
return "Error"
result = []
for i in arr:
if i % 2 == 0:
result.append(i**2)
else:
result.append(i**3)
return result
def process_list_lc(arr):
if len(arr) < 1 or len(arr) > 1000:
return "Error"
return [i**2 if i % 2 == 0 else i**3 for i in arr]
def process_list_gen(arr):
if len(arr) < 1 or len(arr) > 1000:
yield "Error"
return
for i in arr:
yield i**2 if i % 2 == 0 else i**3
if __name__ == '__main__':
import time
test_arr = list(range(1, 1000))
start = time.time()
for _ in range(10):
process_list(test_arr)
orig_time = time.time() - start
start = time.time()
for _ in range(10):
process_list_lc(test_arr)
lc_time = time.time() - start
start = time.time()
for _ in range(10):
list(process_list_gen(test_arr))
gen_time = time.time() - start
print(f"Исходная: {orig_time}с") # 0.0019960403442382812
print(f"List comprehension: {lc_time}с") # 0.001004934310913086
print(f"Генератор: {gen_time}с") # 0.0009999275207519531

View file

@ -0,0 +1,3 @@
def show_employee(name, salary=100000):
return f"{name}: {salary}"

2
hws/hw-2/sum_and_sub.py Normal file
View file

@ -0,0 +1,2 @@
def sum_and_sub(a, b):
return a + b, a - b

523
hws/hw-2/test.py Normal file
View file

@ -0,0 +1,523 @@
import pytest
import math
import os
import subprocess
import tempfile
from fact import fact_it, fact_rec
from show_employee import show_employee
from sum_and_sub import sum_and_sub
from process_list import process_list, process_list_lc, process_list_gen
from my_sum import my_sum
from email_validation import fun
from fibonacci import fibonacci
from average_scores import compute_average_scores
from plane_angle import Point, plane_angle
from complex_numbers import Complex
from circle_square_mk import circle_square_mk
from log_decorator import function_logger
from people_sort import name_format
from phone_number import sort_phone
# ==================== Тестовые данные ====================
test_data = {
'fact_it': [
(1, 1), # граница: минимум n=1
(2, 2),
(5, 120),
(7, 5040),
(10, 3628800),
],
'fact_rec': [
(1, 1), # граница: минимум n=1
(2, 2),
(5, 120),
(7, 5040),
(10, 3628800),
],
'show_employee': [
(("Иванов Иван Иванович", 30000), "Иванов Иван Иванович: 30000 ₽"),
(("Петров Петр Петрович",), "Петров Петр Петрович: 100000 ₽"),
(("Сидоров Сидор Сидорович", 0), "Сидоров Сидор Сидорович: 0 ₽"),
(("Test User", 1000000), "Test User: 1000000 ₽"),
(("", 50000), ": 50000 ₽"),
(("Test", -1000), "Test: -1000 ₽"),
],
'sum_and_sub': [
((5, 3), (8, 2)),
((-5, -3), (-8, -2)),
((5, -3), (2, 8)),
((5.5, 2.5), (8.0, 3.0)),
((0, 0), (0, 0)),
((1000.5, 500.5), (1501.0, 500.0)),
((-10, -5), (-15, -5)),
],
'process_list': [
([1, 2, 3, 4], [1, 4, 27, 16]),
([2, 4, 6], [4, 16, 36]),
([1, 3, 5], [1, 27, 125]),
([5], [125]), # граница: минимум len(arr)=1
],
'my_sum': [
((1, 2), 3),
((1, 2, 3, 4, 5), 15),
((), 0),
((1.5, 2.5, 3.0), 7.0),
((-1, -2, -3), -6),
((5,), 5),
((1000, 2000, 3000), 6000),
((10, -5, 3, -2), 6),
],
'email_validation': [
("test@example.com", True),
("test_user@example.com", True),
("test-user@example.com", True),
("testexample.com", False),
("test@exam!ple.com", False),
("test@example.comm", False), # extension > 3
("user123@test45.com", True),
("user123@test.com", True),
("test@site123.com", True),
("test@example", False),
("@example.com", False),
("test@.com", False),
("test@example.abc", True), # граница: максимум extension=3
],
'fibonacci': [
(1, [0]), # граница: минимум n=1
(2, [0, 1]),
(5, [0, 1, 1, 2, 3]),
(8, [0, 1, 1, 2, 3, 5, 8, 13]),
(10, [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]),
],
'complex_add': [
((2, 1, 5, 6), (7, 7)),
((100, 200, 50, 75), (150, 275)),
],
'complex_sub': [
((2, 1, 5, 6), (-3, -5)),
],
'complex_mul': [
((2, 1, 5, 6), (4, 17)),
],
'complex_str': [
((2, 1), "2.00+1.00i"),
((2, -1), "2.00-1.00i"),
((2, 0), "2.00+0.00i"),
((0, 5), "0.00+5.00i"),
((0, 0), "0.00+0.00i"),
],
'point_sub': [
((5, 3, 2, 1, 1, 1), (4, 2, 1)),
((-1, -2, -3, -4, -5, -6), (3, 3, 3)),
],
'people_sort': [
([["Mike", "Thomson", "20", "M"],
["Robert", "Downey", "30", "M"],
["Alice", "Wonder", "25", "F"]],
["Mr. Mike Thomson", "Ms. Alice Wonder", "Mr. Robert Downey"]),
([["Anna", "Smith", "18", "F"],
["John", "Doe", "18", "M"]],
["Ms. Anna Smith", "Mr. John Doe"]),
([["Bob", "Brown", "50", "M"]],
["Mr. Bob Brown"]),
],
'phone_number': [
(["89991234567"], ["+7 (999) 123-45-67"]),
(["79991234567"], ["+7 (999) 123-45-67"]),
(["9991234567"], ["+7 (999) 123-45-67"]),
(["+7(999)123-45-67"], ["+7 (999) 123-45-67"]),
(["89991234567", "84951112233"],
["+7 (495) 111-22-33", "+7 (999) 123-45-67"]), # проверка сортировки
],
'my_sum_argv': [
(['1', '2'], '3'),
(['1', '2', '3', '4', '5'], '15'),
(['10', '20', '30'], '60'),
],
}
# Тестовые данные для проверки обработки ошибок
error_test_data = {
'fact_it': [
(0, "Error"), # n < 1
(-1, "Error"), # n < 1
(1000, "Error"), # n >= 1000
],
'fact_rec': [
(0, "Error"), # n < 1
(-5, "Error"), # n < 1
(1000, "Error"), # n >= 1000
],
'process_list': [
([], "Error"), # len(arr) < 1
(list(range(1, 1002)), "Error"), # len(arr) > 1000
],
'process_list_lc': [
([], "Error"), # len(arr) < 1
(list(range(1, 1002)), "Error"), # len(arr) > 1000
],
'process_list_gen': [
([], ["Error"]), # len(arr) < 1
(list(range(1, 1002)), ["Error"]), # len(arr) > 1000
],
'fibonacci': [
(0, "Error"), # n < 1
(-1, "Error"), # n < 1
(16, "Error"), # n > 15
(100, "Error"), # n > 15
],
'average_scores': [
([], "Error"), # X <= 0
([tuple([50.0] * 5) for _ in range(101)], "Error"), # X > 100
([tuple([50.0] * 101)], "Error"), # N > 100
],
}
# ==================== Основные тесты ====================
@pytest.mark.parametrize("input_data, expected", test_data['fact_it'])
def test_fact_it(input_data, expected):
assert fact_it(input_data) == expected
@pytest.mark.parametrize("input_data, expected", test_data['fact_rec'])
def test_fact_rec(input_data, expected):
assert fact_rec(input_data) == expected
@pytest.mark.parametrize("input_data, expected", test_data['show_employee'])
def test_show_employee(input_data, expected):
assert show_employee(*input_data) == expected
@pytest.mark.parametrize("input_data, expected", test_data['sum_and_sub'])
def test_sum_and_sub(input_data, expected):
assert sum_and_sub(*input_data) == expected
@pytest.mark.parametrize("input_data, expected", test_data['process_list'])
def test_process_list(input_data, expected):
assert process_list(input_data) == expected
@pytest.mark.parametrize("input_data, expected", test_data['process_list'])
def test_process_list_lc(input_data, expected):
assert process_list_lc(input_data) == expected
@pytest.mark.parametrize("input_data, expected", test_data['process_list'])
def test_process_list_gen(input_data, expected):
assert list(process_list_gen(input_data)) == expected
@pytest.mark.parametrize("input_data, expected", test_data['my_sum'])
def test_my_sum(input_data, expected):
assert my_sum(*input_data) == expected
@pytest.mark.parametrize("input_data, expected", test_data['email_validation'])
def test_email_validation(input_data, expected):
assert fun(input_data) == expected
@pytest.mark.parametrize("input_data, expected", test_data['fibonacci'])
def test_fibonacci(input_data, expected):
assert fibonacci(input_data) == expected
# Тесты для average_scores.py
def test_average_scores():
scores = [(89, 90, 78, 93, 80), (90, 91, 85, 88, 86), (91, 92, 83, 89, 90.5)]
result = compute_average_scores(scores)
assert len(result) == 5
assert abs(result[0] - 90.0) < 0.1
def test_average_scores_boundary_max():
scores = [tuple([50.0] * 100) for _ in range(100)] # граница: максимум X=100, N=100
result = compute_average_scores(scores)
assert result != "Error" and len(result) == 100
# Тесты для plane_angle.py
@pytest.mark.parametrize("input_data, expected", test_data['point_sub'])
def test_point_sub(input_data, expected):
p1 = Point(input_data[0], input_data[1], input_data[2])
p2 = Point(input_data[3], input_data[4], input_data[5])
p3 = p1 - p2
assert (p3.x, p3.y, p3.z) == expected
def test_point_dot():
p1 = Point(1, 2, 3)
p2 = Point(4, 5, 6)
assert p1.dot(p2) == 32
def test_point_cross():
p1 = Point(1, 0, 0)
p2 = Point(0, 1, 0)
p3 = p1.cross(p2)
assert (p3.x, p3.y, p3.z) == (0, 0, 1)
def test_point_absolute():
p = Point(3, 4, 0)
assert p.absolute() == 5.0
def test_point_zero_vector():
p = Point(0, 0, 0)
assert p.absolute() == 0.0
# Тесты для complex_numbers.py
@pytest.mark.parametrize("input_data, expected", test_data['complex_add'])
def test_complex_add(input_data, expected):
c1 = Complex(input_data[0], input_data[1])
c2 = Complex(input_data[2], input_data[3])
c3 = c1 + c2
assert (c3.real, c3.imaginary) == expected
@pytest.mark.parametrize("input_data, expected", test_data['complex_sub'])
def test_complex_sub(input_data, expected):
c1 = Complex(input_data[0], input_data[1])
c2 = Complex(input_data[2], input_data[3])
c3 = c1 - c2
assert (c3.real, c3.imaginary) == expected
@pytest.mark.parametrize("input_data, expected", test_data['complex_mul'])
def test_complex_mul(input_data, expected):
c1 = Complex(input_data[0], input_data[1])
c2 = Complex(input_data[2], input_data[3])
c3 = c1 * c2
assert (c3.real, c3.imaginary) == expected
def test_complex_div():
c1 = Complex(2, 1)
c2 = Complex(5, 6)
c3 = c1 / c2
assert abs(c3.real - 0.26) < 0.01 and abs(c3.imaginary - (-0.11)) < 0.01
def test_complex_mod():
c = Complex(2, 1)
m = c.mod()
assert abs(m.real - 2.236) < 0.01
@pytest.mark.parametrize("input_data, expected", test_data['complex_str'])
def test_complex_str(input_data, expected):
c = Complex(input_data[0], input_data[1])
assert str(c) == expected
# Тесты для circle_square_mk.py
def test_circle_square_mk_small():
result = circle_square_mk(1, 1000)
expected = math.pi
assert abs(result - expected) < 1.0
def test_circle_square_mk_large():
result = circle_square_mk(5, 10000)
expected = math.pi * 25
assert abs(result - expected) < 5.0
def test_circle_square_mk_zero_radius():
result = circle_square_mk(0, 100)
assert result == 0
def test_circle_square_mk_large_experiments():
result = circle_square_mk(10, 100000)
expected = math.pi * 100
assert abs(result - expected) < 10.0
# Тесты для process_list.py: граничные значения
def test_process_list_boundary_max():
arr = list(range(1, 1001)) # граница: максимум len(arr)=1000
result = process_list(arr)
assert result != "Error" and len(result) == 1000
def test_process_list_lc_boundary_max():
arr = list(range(1, 1001)) # граница: максимум len(arr)=1000
result = process_list_lc(arr)
assert result != "Error" and len(result) == 1000
def test_fibonacci_boundary_max():
result = fibonacci(15) # граница: максимум n=15
assert result != "Error" and len(result) == 15 and result[14] == 377
def test_fact_it_boundary_max():
result = fact_it(999) # граница: максимум n=999
assert result != "Error"
# ==================== Тесты обработки ошибок ====================
@pytest.mark.parametrize("input_data, expected", error_test_data['fact_it'])
def test_fact_it_errors(input_data, expected):
assert fact_it(input_data) == expected
@pytest.mark.parametrize("input_data, expected", error_test_data['fact_rec'])
def test_fact_rec_errors(input_data, expected):
assert fact_rec(input_data) == expected
@pytest.mark.parametrize("input_data, expected", error_test_data['process_list'])
def test_process_list_errors(input_data, expected):
assert process_list(input_data) == expected
@pytest.mark.parametrize("input_data, expected", error_test_data['process_list_lc'])
def test_process_list_lc_errors(input_data, expected):
assert process_list_lc(input_data) == expected
@pytest.mark.parametrize("input_data, expected", error_test_data['process_list_gen'])
def test_process_list_gen_errors(input_data, expected):
assert list(process_list_gen(input_data)) == expected
@pytest.mark.parametrize("input_data, expected", error_test_data['fibonacci'])
def test_fibonacci_errors(input_data, expected):
assert fibonacci(input_data) == expected
@pytest.mark.parametrize("input_data, expected", error_test_data['average_scores'])
def test_average_scores_errors(input_data, expected):
assert compute_average_scores(input_data) == expected
# ==================== Тесты для people_sort.py ====================
@pytest.mark.parametrize("input_data, expected", test_data['people_sort'])
def test_people_sort(input_data, expected):
assert name_format(input_data) == expected
# ==================== Тесты для phone_number.py ====================
@pytest.mark.parametrize("input_data, expected", test_data['phone_number'])
def test_phone_number(input_data, expected):
assert sort_phone(input_data) == expected
# ==================== Тесты для log_decorator.py ====================
def test_log_decorator_writes_file(tmp_path):
log_file = str(tmp_path / "test.log")
@function_logger(log_file)
def add(a, b):
return a + b
result = add(2, 3)
assert result == 5
assert os.path.exists(log_file)
with open(log_file, 'r', encoding='utf-8') as f:
content = f.read()
assert 'add' in content
assert '(2, 3)' in content
assert '5' in content
def test_log_decorator_no_return(tmp_path):
log_file = str(tmp_path / "test.log")
@function_logger(log_file)
def noop():
pass
result = noop()
assert result is None
with open(log_file, 'r', encoding='utf-8') as f:
content = f.read()
assert 'noop' in content
assert '-' in content # None записывается как '-'
def test_log_decorator_kwargs(tmp_path):
log_file = str(tmp_path / "test.log")
@function_logger(log_file)
def greet(name="World"):
return f"Hello, {name}!"
result = greet(name="Test")
assert result == "Hello, Test!"
with open(log_file, 'r', encoding='utf-8') as f:
content = f.read()
assert 'greet' in content
assert "name" in content
def test_log_decorator_multiple_calls(tmp_path):
log_file = str(tmp_path / "test.log")
@function_logger(log_file)
def square(x):
return x * x
square(3)
square(5)
with open(log_file, 'r', encoding='utf-8') as f:
content = f.read()
assert content.count('square') == 2
# ==================== Тесты для my_sum_argv.py (subprocess) ====================
INTERPRETER = 'python'
def run_script(filename, args=None):
cmd = [INTERPRETER, filename] + (args if args else [])
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
return proc.stdout.strip()
@pytest.mark.parametrize("input_data, expected", test_data['my_sum_argv'])
def test_my_sum_argv(input_data, expected):
assert run_script('my_sum_argv.py', input_data) == expected
# ==================== Тесты для file_search.py (subprocess) ====================
def test_file_search_found(tmp_path):
test_file = tmp_path / "hello.txt"
test_file.write_text("line1\nline2\nline3\n", encoding='utf-8')
result = subprocess.run(
[INTERPRETER, 'file_search.py', 'hello.txt'],
capture_output=True, text=True, check=False, cwd=str(tmp_path)
)
import shutil
shutil.copy('file_search.py', str(tmp_path / 'file_search.py'))
result = subprocess.run(
[INTERPRETER, 'file_search.py', 'hello.txt'],
capture_output=True, text=True, check=False, cwd=str(tmp_path)
)
assert 'line1' in result.stdout
def test_file_search_not_found(tmp_path):
import shutil
shutil.copy('file_search.py', str(tmp_path / 'file_search.py'))
result = subprocess.run(
[INTERPRETER, 'file_search.py', 'nonexistent.txt'],
capture_output=True, text=True, check=False, cwd=str(tmp_path)
)
assert 'не найден' in result.stdout
def test_file_search_max_5_lines(tmp_path):
import shutil
shutil.copy('file_search.py', str(tmp_path / 'file_search.py'))
test_file = tmp_path / "many_lines.txt"
test_file.write_text('\n'.join([f"line{i}" for i in range(10)]), encoding='utf-8')
result = subprocess.run(
[INTERPRETER, 'file_search.py', 'many_lines.txt'],
capture_output=True, text=True, check=False, cwd=str(tmp_path)
)
lines = result.stdout.strip().split('\n')
assert len(lines) == 5 # выводит только первые 5 строк
# ==================== Тесты для files_sort.py (subprocess) ====================
def test_files_sort(tmp_path):
import shutil
test_dir = tmp_path / "data"
test_dir.mkdir()
(test_dir / "b.txt").write_text("", encoding='utf-8')
(test_dir / "a.txt").write_text("", encoding='utf-8')
(test_dir / "c.py").write_text("", encoding='utf-8')
shutil.copy('files_sort.py', str(tmp_path / 'files_sort.py'))
result = subprocess.run(
[INTERPRETER, 'files_sort.py', str(test_dir)],
capture_output=True, text=True, check=False, cwd=str(tmp_path)
)
lines = result.stdout.strip().split('\n')
# Сортировка: сначала по расширению, потом по имени
assert lines == ['c.py', 'a.txt', 'b.txt']
def test_files_sort_no_extension(tmp_path):
import shutil
test_dir = tmp_path / "data"
test_dir.mkdir()
(test_dir / "Makefile").write_text("", encoding='utf-8')
(test_dir / "a.txt").write_text("", encoding='utf-8')
shutil.copy('files_sort.py', str(tmp_path / 'files_sort.py'))
result = subprocess.run(
[INTERPRETER, 'files_sort.py', str(test_dir)],
capture_output=True, text=True, check=False, cwd=str(tmp_path)
)
lines = result.stdout.strip().split('\n')
# Файлы без расширения (ext='') идут первыми по sorted
assert lines[0] == 'Makefile'

51
labs/lab-1/app/app.py Normal file
View file

@ -0,0 +1,51 @@
import random
from flask import Flask, render_template
from faker import Faker
fake = Faker('ru_RU')
app = Flask(__name__)
application = app
images_ids = ['Pic1', 'Pic2', 'Pic3', 'Pic4', 'Pic5']
def generate_comments(replies=True):
comments = []
for i in range(random.randint(1, 3)):
comment = { 'author': fake.name(), 'text': fake.text() }
if replies:
comment['replies'] = generate_comments(replies=False)
comments.append(comment)
return comments
def generate_post(i):
return {
'title': fake.company(),
'text': fake.paragraph(nb_sentences=100),
'author': fake.name(),
'date': fake.date_time_between(start_date='-2y', end_date='now'),
'image_id': f'{images_ids[i]}.jpg',
'comments': generate_comments()
}
posts_list = sorted([generate_post(i) for i in range(5)], key=lambda p: p['date'], reverse=True)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/posts')
def posts():
return render_template('posts.html', title='Посты', posts=posts_list)
@app.route('/posts/<int:index>')
def post(index):
p = posts_list[index]
return render_template('post.html', title=p['title'], post=p)
@app.route('/about')
def about():
return render_template('about.html', title='Об авторе')
if __name__ == '__main__':
app.run(debug=True)

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View file

@ -0,0 +1,504 @@
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400;600;700&family=Inter:wght@400;500;600&display=swap');
:root {
--primary: #e63946;
--primary-light: #ff6b6b;
--primary-dark: #c1121f;
--primary-bg: rgba(230, 57, 70, 0.15);
--primary-glow: rgba(230, 57, 70, 0.3);
--bg-main: #0d0d0d;
--bg-darker: #080808;
--bg-card: #141414;
--bg-card-hover: #1a1a1a;
--bg-elevated: #1e1e1e;
--bg-input: #1a1a1a;
--text-primary: #ffffff;
--text-secondary: #b0b0b0;
--text-muted: #666666;
--border: #2a2a2a;
--border-light: #3a3a3a;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 24px;
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
font-size: 16px;
line-height: 1.6;
color: var(--text-secondary);
background-color: var(--bg-main);
-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Raleway', sans-serif;
font-weight: 700;
line-height: 1.2;
letter-spacing: -0.02em;
color: var(--text-primary);
}
h1 { font-size: clamp(2rem, 4vw, 3.5rem); }
h2 { font-size: clamp(1.5rem, 3vw, 2.5rem); }
h3 { font-size: clamp(1.25rem, 2.5vw, 1.75rem); }
a {
color: var(--primary);
text-decoration: none;
transition: var(--transition);
}
a:hover {
color: var(--primary-light);
}
::selection {
background: var(--primary);
color: white;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-main);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 9999px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--primary);
}
/* ===== NAVBAR ===== */
.navbar {
background: rgba(13, 13, 13, 0.8) !important;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
padding: 1rem 0;
transition: var(--transition);
}
.navbar-brand {
font-family: 'Raleway', sans-serif;
font-weight: 700;
color: var(--text-primary) !important;
font-size: 1.1rem;
}
.navbar-brand:hover {
color: var(--primary) !important;
}
.navbar .nav-link {
color: var(--text-secondary) !important;
font-weight: 500;
font-size: 0.9rem;
padding: 0.5rem 1rem !important;
border-radius: var(--radius-sm);
transition: var(--transition);
}
.navbar .nav-link:hover {
color: var(--text-primary) !important;
background: var(--primary-bg);
}
.navbar .nav-link.active {
color: var(--primary) !important;
background: var(--primary-bg);
}
.navbar-toggler {
border-color: var(--border) !important;
}
.navbar-toggler-icon {
filter: invert(1);
}
/* ===== MAIN CONTAINER ===== */
.container {
max-width: 1200px;
}
main.my-3 {
padding-top: 2rem;
padding-bottom: 2rem;
}
/* ===== CARDS (posts list) ===== */
.card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
transition: var(--transition);
width: 100%;
}
.card:hover {
border-color: var(--border-light);
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}
.card-img-top {
aspect-ratio: 16/9;
object-fit: cover;
border-radius: 0;
}
.card-body {
padding: 1.5rem;
}
.card-title {
margin-bottom: 0.75rem;
font-size: 1.25rem;
}
.card-text {
color: var(--text-secondary);
font-size: 0.95rem;
margin-bottom: 1rem;
}
.card-footer {
background: transparent;
border-top: 1px solid var(--border);
color: var(--text-muted) !important;
padding: 1rem 1.5rem;
font-size: 0.85rem;
}
/* ===== BUTTONS ===== */
.btn-primary {
display: inline-flex;
align-items: center;
padding: 0.75rem 1.5rem;
font-weight: 600;
font-size: 0.9rem;
border-radius: var(--radius-md);
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
border: none;
color: white;
box-shadow: 0 4px 15px var(--primary-glow);
transition: var(--transition);
cursor: pointer;
}
.btn-primary:hover,
.btn-primary:focus {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(230, 57, 70, 0.4);
background: linear-gradient(135deg, var(--primary-light) 0%, var(--primary) 100%);
border: none;
}
.btn-outline {
display: inline-flex;
align-items: center;
padding: 0.75rem 1.5rem;
font-weight: 600;
font-size: 0.9rem;
border-radius: var(--radius-md);
background: transparent;
border: 2px solid var(--border-light);
color: var(--text-primary);
transition: var(--transition);
cursor: pointer;
}
.btn-outline:hover {
border-color: var(--primary);
background: var(--primary-bg);
color: var(--primary);
}
/* ===== POST PAGE ===== */
.post-header {
margin-bottom: 2rem;
}
.post-title {
margin-bottom: 0.75rem;
}
.post-meta {
display: flex;
align-items: center;
gap: 1.5rem;
color: var(--text-muted);
font-size: 0.9rem;
margin-bottom: 1.5rem;
}
.post-meta span {
display: flex;
align-items: center;
gap: 0.4rem;
}
.post-image {
width: 100%;
border-radius: var(--radius-lg);
aspect-ratio: 16/9;
object-fit: cover;
margin-bottom: 2rem;
border: 1px solid var(--border);
}
.post-content {
color: var(--text-secondary);
line-height: 1.8;
font-size: 1rem;
margin-bottom: 3rem;
}
/* ===== COMMENT FORM ===== */
.comment-form-section {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 2rem;
margin-bottom: 2.5rem;
}
.comment-form-section h3 {
margin-bottom: 1.25rem;
font-size: 1.25rem;
}
.comment-form-section textarea {
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 0.75rem 1rem;
color: var(--text-primary);
font-size: 0.9rem;
font-family: 'Inter', sans-serif;
transition: var(--transition);
width: 100%;
min-height: 120px;
resize: vertical;
}
.comment-form-section textarea:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(230, 57, 70, 0.15);
outline: none;
}
.comment-form-section textarea::placeholder {
color: var(--text-muted);
}
/* ===== COMMENTS ===== */
.comments-section h3 {
margin-bottom: 1.5rem;
font-size: 1.25rem;
}
.comment {
display: flex;
gap: 1rem;
padding: 1.25rem 0;
border-bottom: 1px solid var(--border);
}
.comment:last-child {
border-bottom: none;
}
.comment-avatar {
width: 48px;
height: 48px;
min-width: 48px;
border-radius: 9999px;
background: var(--primary-bg);
color: var(--primary);
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 1.1rem;
font-family: 'Raleway', sans-serif;
}
.comment-body {
flex: 1;
}
.comment-author {
font-weight: 600;
color: var(--text-primary);
font-size: 0.95rem;
margin-bottom: 0.25rem;
}
.comment-text {
color: var(--text-secondary);
font-size: 0.9rem;
line-height: 1.6;
}
/* Replies */
.replies {
margin-top: 1rem;
padding-left: 1.5rem;
border-left: 2px solid var(--border);
}
.reply {
display: flex;
gap: 0.75rem;
padding: 0.75rem 0;
}
.reply-avatar {
width: 36px;
height: 36px;
min-width: 36px;
border-radius: 9999px;
background: rgba(255, 255, 255, 0.05);
color: var(--text-muted);
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 0.85rem;
font-family: 'Raleway', sans-serif;
}
.reply .comment-author {
font-size: 0.9rem;
}
.reply .comment-text {
font-size: 0.85rem;
}
/* ===== ABOUT PAGE ===== */
.avatar {
max-width: 100%;
border-radius: 9999px;
border: 3px solid var(--border);
transition: var(--transition);
}
.avatar:hover {
border-color: var(--primary);
box-shadow: 0 0 40px rgba(230, 57, 70, 0.2);
}
/* ===== FOOTER ===== */
footer {
background: var(--bg-darker);
border-top: 1px solid var(--border);
padding: 2rem 0;
margin-top: 3rem;
}
.footer-content {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
.footer-name {
color: var(--text-primary);
font-family: 'Raleway', sans-serif;
font-weight: 600;
font-size: 0.95rem;
}
.footer-group {
color: var(--text-muted);
font-size: 0.85rem;
}
.footer-links {
display: flex;
gap: 1.5rem;
}
.footer-links a {
color: var(--text-secondary);
font-size: 0.85rem;
transition: var(--transition);
}
.footer-links a:hover {
color: var(--primary);
transform: translateY(-2px);
}
/* ===== INDEX PAGE ===== */
.task-list li {
color: var(--text-secondary);
margin-bottom: 0.5rem;
}
.task-description {
color: var(--text-secondary);
}
.task-description .fw-bold {
color: var(--text-primary);
}
.img-fluid.border {
border-color: var(--border) !important;
border-radius: var(--radius-md);
}
/* ===== RESPONSIVE ===== */
@media (max-width: 768px) {
.post-meta {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.footer-content {
flex-direction: column;
text-align: center;
}
.comment {
gap: 0.75rem;
}
.comment-avatar {
width: 40px;
height: 40px;
min-width: 40px;
font-size: 0.95rem;
}
.replies {
padding-left: 1rem;
}
}

View file

@ -0,0 +1,19 @@
{% extends 'base.html' %}
{% block content %}
<h1 class="mt-5 text-center">Об авторе</h1>
<div class="row">
<div class="col-md-4 mb-3">
<img class="avatar" src="{{ url_for('static', filename='images/avatar.jpg') }}" alt="Author">
</div>
<div class="col-md-8 text-justify d-flex align-items-center">
<p>
Деев Егор Викторович — backend-разработчик и Python-специалист из Москвы, специализирующийся на создании backend-решений, парсинге данных, автоматизации процессов и интеграции AI-компонентов.
<br><br>
Обучается в Московском Политехническом университете (МосПолитех), владеет широким технологическим стеком, включающим Python (Django, FastAPI, Flask), работу с базами данных (PostgreSQL, SQLite), DevOps-инструменты (Docker, Nginx), а также frontend-технологии (HTML5, CSS3, JavaScript).
<br><br>
Контакты для связи: email egor@deev.space, Telegram @Egor_Deev, GitHub @EDeev, личный сайт deev.space с блогом и портфолио проектов.
</p>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,71 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="https://deev.space/media/favicon.ico" type="image/x-icon">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
<title>
{% if title %}
{{ title }}
{% else %}
Лабораторная работа №1
{% endif %}
</title>
</head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container">
<a class="navbar-brand" href="{{ url_for('index') }}">Лабораторная работа № 1</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" aria-current="page" href="{{ url_for('index') }}">Задание</a>
</li>
<li class="nav-item">
<a class="nav-link" aria-current="page" href="{{ url_for('posts') }}">Посты</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('about') }}">Об авторе</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<main class="my-1">
<div class="container">
{% block content %}
<h1>Содержимое по умолчанию</h1>
{% endblock %}
</div>
</main>
<footer>
<div class="container">
<div class="footer-content">
<div>
<div class="footer-name">Деев Егор Викторович, ст. гр. 241-327</div>
<div class="footer-group">Московский Политехнический университет</div>
</div>
<div class="footer-links">
<a href="mailto:egor@deev.space">egor@deev.space</a>
<a href="https://deev.space" target="_blank">My Site</a>
<a href="https://t.me/Egor_Deev" target="_blank">Telegram</a>
<a href="https://github.com/EDeev" target="_blank">GitHub</a>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>

View file

@ -0,0 +1,46 @@
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col-lg-12">
<h1 class="my-5">Задание к лабораторной работе</h1>
<p class="fw-bold">
1. Доработайте прилагаемое к заданию веб-приложение, добавив в него
шаблон post.html, отображающий содержимое поста.
</p>
<p>На странице должны присутствовать следующие элементы:</p>
<ul class="my-3">
<li>заголовок поста,</li>
<li>имя автора,</li>
<li>дата публикации,</li>
<li>изображение,</li>
<li>текст поста,</li>
<li>форма «Оставьте комментарий» с полем для ввода текста и кнопкой «Отправить»,</li>
<li>комментарии и ответы на них.</li>
</ul>
<p>Все данные должны подставляться в шаблон из приложения.</p>
<p>
<a href="https://www.tutorialrepublic.com/twitter-bootstrap-tutorial/bootstrap-media-objects.php" target="_blank">Пример</a> того, как можно сделать отображение комментариев.
</p>
<p>Пример возможного оформления страницы:</p>
<img class="img-fluid my-3 border" src="{{ url_for('static', filename='images/example.png') }}" alt="Post page example">
<p class="fw-bold">
2. Добавьте в базовый шаблон подвал (англ. footer) сайта. Укажите там ФИО и номер группы.
</p>
<p>
Выложите ваше веб-приложение на хостинг. В качестве ответа на соответствующее этой лабораторной работе задание в LMS
вам нужно указать ссылку на репозиторий с вашим решением и ссылку на ваше приложение на хостинге. В
случае, если репозиторий закрытый, -- не забудьте предоставить преподавателю доступ.
</p>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,56 @@
{% extends 'base.html' %}
{% block content %}
<div class="row justify-content-center my-5">
<div class="col-lg-10">
<div class="post-header">
<h1 class="post-title">{{ post.title }}</h1>
<div class="post-meta">
<span>{{ post.author }}</span>
<span>{{ post.date.strftime('%d.%m.%Y') }}</span>
</div>
</div>
<img class="post-image" src="{{ url_for('static', filename='images/' + post.image_id) }}" alt="{{ post.title }}">
<div class="post-content">
{{ post.text }}
</div>
<div class="comment-form-section">
<h3>Оставьте комментарий</h3>
<form method="POST">
<textarea name="comment" placeholder="Напишите ваш комментарий..." rows="4"></textarea>
<button type="submit" class="btn btn-primary mt-3">Отправить</button>
</form>
</div>
<div class="comments-section">
<h3>Комментарии ({{ post.comments | length }})</h3>
{% for comment in post.comments %}
<div class="comment">
<div class="comment-avatar">{{ comment.author[0] }}</div>
<div class="comment-body">
<div class="comment-author">{{ comment.author }}</div>
<div class="comment-text">{{ comment.text }}</div>
{% if comment.replies %}
<div class="replies">
{% for reply in comment.replies %}
<div class="reply">
<div class="reply-avatar">{{ reply.author[0] }}</div>
<div class="comment-body">
<div class="comment-author">{{ reply.author }}</div>
<div class="comment-text">{{ reply.text }}</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,25 @@
{% extends 'base.html' %}
{% block content %}
<h1 class="my-5">Последние посты</h1>
<div class="row mb-3">
{% for post in posts %}
<div class="col-md-6 d-flex">
<div class="card mb-4">
<img class="card-img-top" src="{{ url_for('static', filename='images/' + post.image_id) }}" alt="Card image cap">
<div class="card-body">
<h2 class="card-title">{{ post.title }}</h2>
<p class="card-text">
{{ post.text | truncate(100) }}
</p>
<a href="{{ url_for('post', index=loop.index0) }}" class="btn btn-primary">Читать дальше &rarr;</a>
</div>
<div class="card-footer text-muted">
Опубликовано {{ post.date.strftime('%d.%m.%Y') }}.
Автор: {{ post.author }}
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}

View file

@ -0,0 +1,9 @@
click==8.0.4
Faker==13.3.2
Flask==2.0.3
itsdangerous==2.1.1
Jinja2==3.0.3
MarkupSafe==2.1.1
python-dateutil==2.8.2
six==1.16.0
Werkzeug==2.0.3