11 Commits

Author SHA1 Message Date
Igor 8cd4a8b928 Merge branch 'main' of https://gitea.dimong.ru/Dimong4/Igor-kr
CI Check / lint-and-check (push) Failing after 7s
2026-07-18 20:11:09 +03:00
Igor 2e823d96af add KR 2026-07-18 20:05:03 +03:00
Dimong4 3290ab91fc update task
CI Check / lint-and-check (push) Failing after 8s
2026-07-18 20:00:33 +03:00
Dmitry Belan ee235ece10 feat: implement legacy shop management system and add task documentation files
CI Check / lint-and-check (push) Failing after 9s
2026-07-18 12:25:32 +03:00
Igor 7e8afd2a7e add KR
CI Check / lint-and-check (push) Failing after 8s
2026-07-14 23:23:46 +03:00
Igor 8623d967f0 add KR
CI Check / lint-and-check (push) Failing after 8s
2026-07-14 23:11:34 +03:00
Dmitry Belan da02f5305a fix: fix PEP8
CI Check / lint-and-check (push) Successful in 9s
2026-07-14 17:12:16 +03:00
Dmitry Belan b14f14147f feat: add CI gitea
CI Check / lint-and-check (push) Has been cancelled
2026-07-14 17:07:21 +03:00
Dmitry Belan 5591222f7f Merge branch 'main' of ssh://gitea.dimong.ru:2222/Dimong4/Igor-kr 2026-07-14 12:22:06 +03:00
Dmitry Belan 7bac20b5bd Added tasks for the control work 2026-07-14 12:20:34 +03:00
igor1263 8af895cd75 Merge pull request 'add otvet na kontrolnue' (#1) from temperature into main
Reviewed-on: #1
2026-07-04 21:51:21 +03:00
35 changed files with 1605 additions and 91 deletions
+18
View File
@@ -0,0 +1,18 @@
[flake8]
max-line-length = 80
import-order-style = google
application-import-names = shop
exclude =
.git,
__pycache__,
build,
dist,
.venv,
venv,
.mypy_cache,
.pytest_cache
ignore =
# N802: function name should be lowercase (we want the student to fix this, but standard pep8 has this. Let's not ignore it so they have to fix it!)
# T201, T001: print found (they will need prints for CLI, so we can ignore print check T201 if they use print check plugins, but default flake8 doesn't check prints unless we use flake8-print)
# We will keep it minimal so flake8 checks standard styling, naming, and import order.
E121, E123, E126, E226, E24, E704, W503, W504
+34
View File
@@ -0,0 +1,34 @@
name: CI Check
on: [push, pull_request]
jobs:
lint-and-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
# Важно для commitizen, чтобы подтянуть историю всех коммитов
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install ruff commitizen
- name: Run Ruff Check
# Ruff автоматически подхватит настройки из pyproject.toml
run: ruff check .
- name: Run Ruff Format Check
# Проверка соответствия стилю
run: ruff format --check .
- name: Check Commit Convention
# Проверка всех новых коммитов в PR или последней пуш-сессии
run: cz check --rev-range HEAD~1..HEAD
+34
View File
@@ -0,0 +1,34 @@
def filter_numbers(data):
new_data = []
for dat in data:
if type(dat) is int or float and not bool:
new_data.append(float(dat))
return new_data
def calculate_average(numbers):
result = 0
if len(numbers) == 0:
result = 0
else:
for number in numbers:
result += float(number)
return result
def find_extremes(numbers):
if not numbers:
return (None, None)
else:
return min(numbers), max(numbers)
def analyze_data(data):
new_data = filter_numbers(data)
print(f"Среднее: {calculate_average(new_data)}")
print(f"Минимум: {find_extremes(new_data)[0]}")
print(f"Максимум: {find_extremes(new_data)[1]}")
my_list = []
analyze_data(my_list)
+22
View File
@@ -0,0 +1,22 @@
### Задача 1. Калькулятор статистики (Функции, Списки, Циклы)
Напишите программу из 4 функций для анализа числовых данных:
1. `filter_numbers(data)` — принимает список `data`, который может содержать числа, строки, `None`. Возвращает новый список только с числами (`int` или `float`).
2. `calculate_average(numbers)` — принимает список чисел и возвращает их среднее арифметическое. Если список пуст, возвращает `0`.
3. `find_extremes(numbers)` — принимает список чисел и возвращает кортеж `(минимум, максимум)`. Если список пуст, возвращает `(None, None)`.
4. `analyze_data(data)` — принимает список `data`, фильтрует числа через `filter_numbers`, затем вызывает `calculate_average` и `find_extremes`. Выводит результаты в формате:
```
Среднее: 42.5
Минимум: 10
Максимум: 100
```
**Тесты:**
1. Создайте список с числами, строками и `None`: `[10, "test", 50, None, 100, "data", 25]`
2. Вызовите `analyze_data` и проверьте результат
3. Проверьте работу с пустым списком
+25
View File
@@ -0,0 +1,25 @@
### Задача 10. Многопоточность и очереди (Threading, Queue, Синхронизация)
Напишите программу для обработки задач в нескольких потоках:
1. **Класс `Worker`:**
- В `__init__(self, name, queue)` сохраните имя рабочего и очередь задач
- Метод `run(self)` — бесконечный цикл: берет задачу из очереди (`queue.get()`), обрабатывает её (просто выводит `"Worker {name} обрабатывает: {task}"`), делает `time.sleep(1)` для имитации работы, затем вызывает `queue.task_done()`
2. `create_workers(count, queue)` — создает `count` объектов `Worker`, запускает каждого в отдельном потоке (`threading.Thread`) с `daemon=True`. Возвращает список потоков.
3. `process_tasks(tasks, worker_count=3)` — принимает список задач:
- Создает очередь `queue.Queue()`
- Добавляет все задачи в очередь
- Создает рабочих через `create_workers`
- Ждет завершения всех задач через `queue.join()`
- Выводит *"Все задачи обработаны"*
**Тесты:**
1. Создайте список из 10 задач: `["Задача 1", "Задача 2", ..., "Задача 10"]`
2. Запустите обработку с 3 рабочими потоками
3. Убедитесь, что задачи распределяются между потоками
4. Замерьте время выполнения (должно быть ~4 секунды для 10 задач с 3 потоками)
**Подсказка:** Импортируйте `import threading`, `import queue`, `import time`.
+78
View File
@@ -0,0 +1,78 @@
### Задача 11. Рефакторинг системы управления интернет-магазином
**Контекст**
Вам достался legacy-код системы управления интернет-магазином, расположенный в файле `legacy_shop.py`.
Код является абсолютно **монолитным**: вся логика (регистрация пользователей, авторизация, корзина, оформление заказов, расчёт доставки для разных городов, применение промокодов и админ-отчёты) написана сплошным линейным потоком внутри интерактивного CLI-интерфейса. В программе **нет ни одной функции и ни одного класса**. При этом код содержит огромное количество дублирования логики (copy-paste), нарушает стандарты PEP 8, не имеет обработки ошибок и сложен в поддержке.
---
**Ваша задача:**
Провести полный рефакторинг этого кода, разделив его на модули, применив объектно-ориентированное программирование (ООП) и исправив стиль кодирования.
#### Шаг 1. Анализ и исправление стиля (PEP 8)
- Приведите весь код в соответствие со стандартами **PEP 8**:
- Устраните отсутствие пробелов вокруг операторов (например, `x=y+z`).
- Исправьте именование переменных на `snake_case`.
- Уберите «волшебные числа» и жестко захардкоженные пароли (например, пароль админа), вынеся их в константы или конфигурацию.
#### Шаг 2. Декомпозиция логики в функции
- Найдите все повторяющиеся и логически обособленные куски кода и вынесите их в отдельные переиспользуемые функции с понятными названиями.
- **Особое внимание уделите:**
- Валидации полей при регистрации пользователя (сейчас проверки скопированы для каждого поля отдельно).
- Расчету стоимости доставки (уберите дублирование формул для 10+ городов, обобщите логику).
- Применению промокодов (уберите длинный `if-elif` блок, заменив его на структуру данных/словарь соответствий или единую формулу).
- Валидации чисел при вводе количества товаров и цен.
#### Шаг 3. Проектирование ООП-архитектуры
#### Шаг 3. Проектирование ООП-архитектуры
- Вместо хранения сырых словарей (`dict`) и глобальных переменных спроектируйте систему классов. **Создавайте классы там, где они действительно выгодны** (например, там, где необходимо инкапсулировать состояние, защитить данные от некорректного изменения или связать данные с поведением). Подумайте, для каких сущностей достаточно простых структур данных/функций, а для каких необходимы полноценные классы:
- `Product` — модель товара (id, название, категория, цена, остаток на складе, sku, производитель).
- `User` — модель пользователя. Подумайте над наследованием: например, выделите базовый класс `User` и наследников `Customer` (клиент) и `Admin` (администратор) с разным уровнем прав, если это упростит разграничение доступа.
- `Cart` — корзина покупателя, управляющая добавлением товаров, изменением количества и подсчетом промежуточного итога (здесь класс выгоден для управления состоянием).
- `Order` — заказ пользователя (детали доставки, примененная скидка, стоимость доставки, итоговая сумма, статус).
- `Database` — сервис для работы с файлами базы данных (`users.json`, `products.json`, `orders.json`). Должен инкапсулировать логику чтения и записи.
- `Shop` — центральный сервис-фасад, координирующий бизнес-логику магазина.
#### Шаг 4. Создание модульной файловой структуры
Разделите проект на логические части и файлы. Итоговая структура должна выглядеть следующим образом:
```
shop/
├── __init__.py
├── models/
│ ├── __init__.py
│ ├── product.py
│ ├── user.py
│ ├── order.py
│ └── cart.py
├── services/
│ ├── __init__.py
│ ├── shop.py
│ └── database.py
├── utils/
│ ├── __init__.py
│ └── validators.py
└── main.py
```
- В `main.py` должен остаться только запуск приложения и минималистичный интерактивный CLI-интерфейс, вызывающий методы класса `Shop`.
#### Шаг 5. Качество кода и надежность
- Добавьте осмысленные docstring'и ко всем классам, методам и модулям.
- Используйте аннотации типов (type hints) для аргументов функций и возвращаемых значений.
- Добавьте обработку исключений (`try-except`) в места взаимодействия с файловой системой (загрузка/сохранение БД) и при вводе некорректных данных пользователем.
- **Проверка стиля кода**: Для проверки соответствия стандартам стиля запустите линтер `flake8` в корне проекта:
```bash
flake8 shop/
```
---
**Критерии оценки:**
1. **Работоспособность**: после рефакторинга весь функционал приложения (от регистрации до админских отчетов) должен работать точно так же, как в исходном файле.
2. **Чистота кода**: отсутствие дублирования логики (DRY), понятные имена переменных и методов. **Команда `flake8 shop/` не должна выводить никаких предупреждений или ошибок.**
3. **Качество ООП**: грамотное распределение ответственности между классами, использование инкапсуляции (скрытие внутренних данных), осмысленное применение ООП.
4. **Модульность**: логичное разделение кода по файлам в папке `shop/`.
+583
View File
@@ -0,0 +1,583 @@
# -*- coding: utf-8 -*-
import os
import sys
import json
import datetime
USERS_DB_FILE = 'users.json'
PRODUCTS_DB_FILE = 'products.json'
ORDERS_DB_FILE = 'orders.json'
users = []
products = {}
orders = []
current_user = None
cart = []
# LOAD DATABASE DIRECTLY ON STARTUP
if os.path.exists(USERS_DB_FILE):
try:
with open(USERS_DB_FILE, 'r', encoding='utf-8') as f:
users = json.loads(f.read())
except Exception as e:
print('Error loading users:', e)
if os.path.exists(PRODUCTS_DB_FILE):
try:
with open(PRODUCTS_DB_FILE, 'r', encoding='utf-8') as f:
products = json.loads(f.read())
except Exception as e:
print('Error loading products:', e)
if os.path.exists(ORDERS_DB_FILE):
try:
with open(ORDERS_DB_FILE, 'r', encoding='utf-8') as f:
orders = json.loads(f.read())
except Exception as e:
print('Error loading orders:', e)
if not products:
products['1'] = {'id': '1', 'name': 'Laptop Pro', 'category': 'Electronics', 'price': 75000.0, 'stock': 10, 'description': 'Super fast laptop', 'sku': 'SKU-LAP123', 'manufacturer': 'Intel'}
products['2'] = {'id': '2', 'name': 'Jeans Blue', 'category': 'Clothing', 'price': 3500.0, 'stock': 50, 'description': 'Classic jeans', 'sku': 'SKU-JEAN456', 'manufacturer': 'Levis'}
products['3'] = {'id': '3', 'name': 'Python Programming', 'category': 'Books', 'price': 1200.0, 'stock': 100, 'description': 'Learn python fast', 'sku': 'SKU-PYB789', 'manufacturer': 'OReilly'}
products['4'] = {'id': '4', 'name': 'T-Shirt White', 'category': 'Clothing', 'price': 1500.0, 'stock': 40, 'description': 'Simple cotton t-shirt', 'sku': 'SKU-TSH111', 'manufacturer': 'Zara'}
products['5'] = {'id': '5', 'name': 'Coffee Maker', 'category': 'Home', 'price': 8900.0, 'stock': 15, 'description': 'Espresso coffee maker', 'sku': 'SKU-COF333', 'manufacturer': 'DeLonghi'}
while True:
print('========================================')
print(' LEGACY ONLINE SHOP MANAGER ')
print('========================================')
if current_user:
print('Logged in as:', current_user)
else:
print('Not logged in')
print('1. Register User')
print('2. Login User')
print('3. View Products Catalog')
print('4. Add Product to Shopping Cart')
print('5. View Current Cart')
print('6. Checkout (Calculate Shipping, Coupons, Place Order)')
print('7. View My Order History')
print('8. Logout')
print('9. Exit')
print('10. ADMIN: Add New Product to Catalog')
print('11. ADMIN: View All System Orders')
print('12. ADMIN: System Stats & Reports')
print('========================================')
choice = input('Enter your choice (1-12): ').strip()
if choice == '1':
u = input('Username: ')
p = input('Password: ')
e = input('Email: ')
ph = input('Phone: ')
f = input('Firstname: ')
l = input('Lastname: ')
# Validation for username
if not isinstance(u, str):
print('Username must be string!')
continue
if len(u) < 3 or len(u) > 20:
print('Username length invalid!')
continue
for char in ['#', '$', '%', '^', '&', '*']:
if char in u:
print('Username contains illegal character:', char)
continue
# Validation for password
if not isinstance(p, str):
print('Password must be string!')
continue
if len(p) < 6 or len(p) > 30:
print('Password length invalid!')
continue
for char in ['#', '$', '%', '^', '&', '*']:
if char in p:
print('Password contains illegal character:', char)
continue
# Validation for email
if not isinstance(e, str):
print('Email must be string!')
continue
if len(e) < 5 or len(e) > 50:
print('Email length invalid!')
continue
if '@' not in e or '.' not in e:
print('Email format is invalid!')
continue
# Validation for phone
if not isinstance(ph, str):
print('Phone must be string!')
continue
if len(ph) < 7 or len(ph) > 15:
print('Phone length invalid!')
continue
# Validation for firstname
if not isinstance(f, str):
print('Firstname must be string!')
continue
if len(f) < 2 or len(f) > 30:
print('Firstname length invalid!')
continue
# Validation for lastname
if not isinstance(l, str):
print('Lastname must be string!')
continue
if len(l) < 2 or len(l) > 30:
print('Lastname length invalid!')
continue
user_exists = False
for user_dict in users:
if user_dict['username'] == u:
user_exists = True
if user_exists:
print('Error: User already exists!')
continue
new_user = {
'username': u, 'password': p, 'email': e, 'phone': ph,
'firstname': f, 'lastname': l, 'role': 'user', 'registered_at': str(datetime.datetime.now())
}
users.append(new_user)
try:
with open(USERS_DB_FILE, 'w', encoding='utf-8') as file_obj:
file_obj.write(json.dumps(users, indent=4))
print('Registration successful!')
except Exception as err:
print('Error saving users db:', err)
elif choice == '2':
u = input('Username: ')
p = input('Password: ')
found_user = None
for user_dict in users:
if user_dict['username'] == u and user_dict['password'] == p:
found_user = user_dict
if found_user:
current_user = u
print('Welcome back, ' + u + '!')
else:
print('Login failed! Invalid credentials.')
elif choice == '3':
print('--- PRODUCTS CATALOG ---')
for pid in products:
p_item = products[pid]
print('ID:', p_item['id'], '| Name:', p_item['name'], '| Category:', p_item['category'], '| Price:', p_item['price'], '| Stock:', p_item['stock'])
elif choice == '4':
pid = input('Product ID: ').strip()
qty_str = input('Quantity: ').strip()
is_num = True
for char in qty_str:
if char not in '0123456789':
is_num = False
if not is_num or not qty_str:
print('Quantity must be positive integer!')
continue
qty = int(qty_str)
if pid not in products:
print('Product not found!')
continue
prod_item = products[pid]
if prod_item['stock'] < qty:
print('Not enough stock! Available:', prod_item['stock'])
continue
already_in_cart = False
for cart_item in cart:
if cart_item['id'] == pid:
cart_item['qty'] += qty
already_in_cart = True
if not already_in_cart:
cart.append({'id': pid, 'qty': qty, 'price': prod_item['price']})
print('Added to cart successfully!')
elif choice == '5':
print('--- YOUR CART ---')
if not cart:
print('Your cart is empty!')
else:
cart_total = 0.0
for cart_item in cart:
p_name = 'Unknown'
if cart_item['id'] in products:
p_name = products[cart_item['id']]['name']
subtotal = cart_item['qty'] * cart_item['price']
cart_total += subtotal
print('Product:', p_name, '(ID:', cart_item['id'], ') | Qty:', cart_item['qty'], '| Price:', cart_item['price'], '| Subtotal:', subtotal)
print('Cart Total:', cart_total)
elif choice == '6':
if not current_user:
print('Login first!')
continue
if not cart:
print('Cart is empty!')
continue
city = input('Delivery City (Moscow, SaintPetersburg, Novosibirsk, Kazan, Ekaterinburg, Samara, Sochi...): ').strip()
coupon = input('Promo/Coupon code (optional): ').strip()
total_price = 0.0
total_weight = 0.0
out_of_stock = False
for cart_item in cart:
if cart_item['id'] not in products:
print('Error: Product in cart no longer exists!')
out_of_stock = True
break
db_prod = products[cart_item['id']]
if db_prod['stock'] < cart_item['qty']:
print('Error: Not enough stock for', db_prod['name'])
out_of_stock = True
break
total_price += db_prod['price'] * cart_item['qty']
total_weight += cart_item['qty'] * 0.75
if out_of_stock:
continue
shipping_cost = 250.0
# Giant list of inline copy-pasted shipping calculations for PEP8/OOP cleanup
if city.lower() == 'moscow':
shipping_cost = 200.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 10
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'saintpetersburg':
shipping_cost = 300.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 12
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'novosibirsk':
shipping_cost = 500.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 20
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'ekaterinburg':
shipping_cost = 450.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 18
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'kazan':
shipping_cost = 380.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 15
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'samara':
shipping_cost = 390.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 15
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'sochi':
shipping_cost = 400.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 15
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'vladivostok':
shipping_cost = 680.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 28
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'krasnodar':
shipping_cost = 350.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 12
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'ufa':
shipping_cost = 410.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 16
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'perm':
shipping_cost = 430.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 16
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'tula':
shipping_cost = 300.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 11
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
elif city.lower() == 'kaliningrad':
shipping_cost = 480.0
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 19
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.5
if total_price > 10000.0:
shipping_cost = 0.0
else:
# default calculation for other cities
if total_weight > 5.0:
shipping_cost += (total_weight - 5.0) * 25
if total_price > 5000.0:
shipping_cost = shipping_cost * 0.8
if total_price > 12000.0:
shipping_cost = 0.0
discount = 0.0
# Inline coupon calculations
if coupon == 'SALE10':
discount = total_price * 0.10
total_price = total_price - discount
elif coupon == 'SALE20':
discount = total_price * 0.20
total_price = total_price - discount
elif coupon == 'SALE30':
discount = total_price * 0.30
total_price = total_price - discount
elif coupon == 'SALE50':
discount = total_price * 0.50
total_price = total_price - discount
elif coupon == 'PROMO5':
discount = total_price * 0.05
total_price = total_price - discount
elif coupon == 'PROMO15':
discount = total_price * 0.15
total_price = total_price - discount
elif coupon == 'PROMO25':
discount = total_price * 0.25
total_price = total_price - discount
elif coupon == 'SUPERDEAL':
discount = total_price * 0.40
total_price = total_price - discount
if total_price < 0:
total_price = 0.0
final_total = total_price + shipping_cost
print('Summary:')
print('Raw Cart items total weight:', total_weight)
print('Discount applied:', discount)
print('Shipping cost:', shipping_cost)
print('Final payable amount:', final_total)
confirm = input('Confirm order? (yes/no): ').strip().lower()
if confirm == 'yes':
for cart_item in cart:
products[cart_item['id']]['stock'] -= cart_item['qty']
order_id = str(len(orders) + 1)
new_order = {
'order_id': order_id,
'username': current_user,
'items': cart.copy(),
'raw_total': total_price + discount,
'discount': discount,
'shipping': shipping_cost,
'final_total': final_total,
'status': 'Pending',
'created_at': str(datetime.datetime.now())
}
orders.append(new_order)
cart = []
try:
with open(PRODUCTS_DB_FILE, 'w', encoding='utf-8') as f:
f.write(json.dumps(products, indent=4))
with open(ORDERS_DB_FILE, 'w', encoding='utf-8') as f:
f.write(json.dumps(orders, indent=4))
print('Order placed successfully! Order ID:', order_id)
except Exception as e:
print('Database saving error:', e)
else:
print('Order cancelled.')
elif choice == '7':
if not current_user:
print('Login first!')
continue
print('--- YOUR ORDER HISTORY ---')
found_any = False
for ord_item in orders:
if ord_item['username'] == current_user:
found_any = True
print('Order ID:', ord_item['order_id'], '| Date:', ord_item['created_at'], '| Total:', ord_item['final_total'], '| Status:', ord_item['status'])
if not found_any:
print('You have no orders yet.')
elif choice == '8':
current_user = None
cart = []
print('Logged out successfully!')
elif choice == '9':
sys.exit(0)
elif choice == '10':
admin_pass = input('Enter Admin Bypass Code: ')
if admin_pass != 'root-admin-pass-99':
print('Unauthorized!')
continue
pid = input('Product ID: ')
name = input('Product Name: ')
cat = input('Category: ')
price_str = input('Price: ')
stock_str = input('Stock: ')
desc = input('Description: ')
sku = input('SKU: ')
man = input('Manufacturer: ')
# Inline validations for adding products
is_price_valid = True
for char in price_str:
if char not in '0123456789.':
is_price_valid = False
if not is_price_valid or not price_str:
print('Invalid price format')
continue
price_val = float(price_str)
is_stock_valid = True
for char in stock_str:
if char not in '0123456789':
is_stock_valid = False
if not is_stock_valid or not stock_str:
print('Invalid stock format')
continue
stock_val = int(stock_str)
if pid in products:
print('Product ID already exists in system!')
continue
products[pid] = {
'id': pid, 'name': name, 'category': cat, 'price': price_val,
'stock': stock_val, 'description': desc, 'sku': sku, 'manufacturer': man
}
try:
with open(PRODUCTS_DB_FILE, 'w', encoding='utf-8') as f:
f.write(json.dumps(products, indent=4))
print('Product cataloged!')
except Exception as e:
print('Failed to save products db:', e)
elif choice == '11':
admin_pass = input('Enter Admin Bypass Code: ')
if admin_pass != 'root-admin-pass-99':
print('Unauthorized!')
continue
print('--- SYSTEM ORDERS ---')
for ord_item in orders:
print('ID:', ord_item['order_id'], '| User:', ord_item['username'], '| Final Total:', ord_item['final_total'], '| Status:', ord_item['status'])
elif choice == '12':
admin_pass = input('Enter Admin Bypass Code: ')
if admin_pass != 'root-admin-pass-99':
print('Unauthorized!')
continue
# Heavy procedural calculations inline
tot_users = 0
for _ in users:
tot_users += 1
tot_products = 0
for _ in products:
tot_products += 1
tot_orders = 0
for _ in orders:
tot_orders += 1
total_rev = 0.0
for o_itm in orders:
total_rev += o_itm['final_total']
total_discount_given = 0.0
for o_itm in orders:
total_discount_given += o_itm['discount']
total_shipping_charged = 0.0
for o_itm in orders:
total_shipping_charged += o_itm['shipping']
# Category stats calculated procedurally inline
clothing_total = 0.0
electronics_total = 0.0
books_total = 0.0
other_total = 0.0
for o_itm in orders:
for item in o_itm['items']:
p_id = item['id']
qty = item['qty']
price = item['price']
cat_name = 'Unknown'
if p_id in products:
cat_name = products[p_id]['category']
if cat_name.lower() == 'clothing':
clothing_total += qty * price
elif cat_name.lower() == 'electronics':
electronics_total += qty * price
elif cat_name.lower() == 'books':
books_total += qty * price
else:
other_total += qty * price
print('--- REPORTS SUMMARY ---')
print('Users count:', tot_users)
print('Products catalog count:', tot_products)
print('Orders count:', tot_orders)
print('Total Revenue generated:', total_rev)
print('Total Discount Given:', total_discount_given)
print('Total Shipping Charged:', total_shipping_charged)
print('--- Category Sales Revenue ---')
print('Clothing:', clothing_total)
print('Electronics:', electronics_total)
print('Books:', books_total)
print('Other:', other_total)
else:
print('Invalid Option! Please try again.')
+38
View File
@@ -0,0 +1,38 @@
import random
import string
def validate_length(length):
if length < 8:
print("Пароль должен быть не менее 8 символов")
return False
else:
return True
def generate_password(length, use_digits, use_special):
password_book = string.ascii_letters
if use_digits:
password_book += string.digits
if use_special:
password_book += string.punctuation
result = "".join(random.choices(password_book, k=length))
return result
def create_passwords(count, length):
pasword_list = []
i = 0
for i in range(count):
if validate_length(length):
pasword = generate_password(
length,
use_digits=False,
use_special=False
)
pasword_list.append(pasword)
i += 1
return pasword_list
print(create_passwords(5, 12))
+20
View File
@@ -0,0 +1,20 @@
### Задача 2. Генератор паролей (Строки, Циклы, Условия)
Напишите программу из 3 функций для генерации безопасных паролей:
1. `validate_length(length)` — принимает число `length`. Если оно меньше 8, возвращает `False` и выводит *"Пароль должен быть не менее 8 символов"*. Иначе возвращает `True`.
2. `generate_password(length, use_digits=True, use_special=True)` — генерирует случайный пароль длиной `length`:
- Всегда использует буквы (a-z, A-Z)
- Если `use_digits=True`, добавляет цифры (0-9)
- Если `use_special=True`, добавляет спецсимволы (!@#$%^&*)
- Используйте модуль `random` и `string`
- Возвращает строку-пароль
3. `create_passwords(count, length)` — создает список из `count` паролей, каждый длиной `length`. Перед генерацией проверяет длину через `validate_length`. Возвращает список паролей.
**Тесты:**
1. Создайте 5 паролей длиной 12 символов
2. Попробуйте создать пароль длиной 4 символа (должна сработать валидация)
3. Создайте пароль без цифр и спецсимволов
+45
View File
@@ -0,0 +1,45 @@
### Последняя Надежда Эпсилон-9: Протокол «К-Резонанс»
**Ограничение по времени:** 3.0 с
**Ограничение по памяти:** 512 МБ
Год 2142. Межзвездная колония на планете Эпсилон-9 столкнулась с невидимой смертью. Внешне безобидная флора планеты скрывает фатальную угрозу: питательный, спасающий от голода «Звездный Корень» и парализующая нервную систему «Теневая Лоза» визуально абсолютно неразличимы. Их выдают только биоэлектрические поля, которые сканируют наши харвестеры.
До вчерашнего дня наш Центральный ИИ «Тензор-Оракул» успешно классифицировал растения. Но аномальная вспышка на местной звезде сожгла серверные стойки. Библиотеки машинного обучения расплавились в цифровом небытии. Остался только аварийный терминал жизнеобеспечения. Он принимает исключительно базовый, первобытный диалект языка Python. Использовать оператор `import` категорически запрещено — модулей просто не существует. Голод неминуем. Склады забиты смертельно опасным неклассифицированным урожаем.
Командор колонии Лира врывается в ваш инженерный отсек. В её руках дымящийся кристалл памяти.
— Инженер! — кричит она, смахивая пепел с терминала. — Я вытащила из мертвых архивов чертежи древнего алгоритма K-Ближайших Соседей. Мы должны воссоздать его с нуля, иначе завтра колония вымрет. Слушай меня внимательно, я зачитаю спецификацию протокола прямо с поврежденных секторов, а ты пиши код!
Лира подключает кристалл к вашему монитору, и аварийное освещение окрашивает отсек в красный цвет.
— Для начала, харвестеры присылают нам сплошной текстовый мусор, — диктует Командор, глядя на бегущие строки. — Тебе нужно написать первую функцию **`parse_sensor_data(record)`**. Она должна принимать сырую строку, где числа и класс разделены запятыми, и возвращать кортеж. Первым элементом в нем пойдет список чисел с плавающей точкой — это наши сырые признаки. Вторым — строка класса растения.
Она нервно стучит по экрану:
— Данные с сенсоров скачут! Чтобы алгоритм не сошел с ума от разницы в масштабах измерений, нам нужна калибровка. Создай функцию **`calculate_statistics(dataset)`**. Прогони через нее размеченные данные и вычисли для каждого признака среднее арифметическое и стандартное отклонение.s Верни это как список словарей с ключами `"mean"` и `"std"`. И помни, корень придется извлекать возведением в степень 0.5, у нас нет математических модулей!
— Как только получим статистическую базу, — продолжает Лира, — пиши **`normalize_features(features, stats)`**. Каждый сырой признак растения нужно очистить от шума, вычтя среднее и разделив на стандартное отклонение. Эта функция должна брать список признаков и словарь статистики, а возвращать новый список идеальных, нормализованных чисел.
Свет в отсеке мигает. Система охлаждения воет на пределе возможностей.
— Теперь самое главное — геометрия выживания! Пиши ядро: **`euclidean_distance(vector_a, vector_b)`**. Функция должна брать два нормализованных списка чисел и вычислять дистанцию между ними в многомерном пространстве. Просто верни одно число с плавающей точкой.
Лира выводит на экран голограмму базы данных:
— Когда мы умеем считать дистанцию, нам нужно найти сородичей. Функция **`find_k_nearest(train_set, test_point, k)`** должна прочесать историческую базу `train_set` и найти заданное число `k` самых близких соседей для неизвестного растения `test_point`. От тебя требуется вернуть только список их классов-строк.
— Но соседи могут расходиться во мнениях, — Лира хмурится. — Нам нужна логика большинства. Функция **`majority_vote(neighbors)`** примет этот список классов и вернет тот класс, который встречается чаще всего. Если будет ничья — система должна выжить, просто возвращай любой из лидирующих классов. Это и будет наш приговор.
Она тяжело вздыхает, опираясь на стол:
— Оберни этот поиск и голосование в единый боевой интерфейс **`predict_class(train_set, test_point, k)`**. Ей мы будем скармливать базу и одно неизвестное растение, чтобы сразу получить итоговый класс-строку.
— Но прежде чем отправить харвестеры в поля и кормить людей, — голос Лиры дрожит, — мы обязаны проверить алгоритм на архивах. Напиши **`calculate_accuracy(predictions, true_labels)`**. Сравни списки наших предсказаний с истинными классами на тестовом полигоне и верни долю правильных ответов от 0.0 до 1.0. Цена каждой ошибки — чья-то жизнь.
Лира кладет руку вам на плечо. В её глазах отражается код аварийного терминала:
— И наконец, Инженер... Великий Синтез. Финальная функция **`run_protocol(train_raw_lines, test_raw_lines, k)`**. Она должна объединить весь пайплайн воедино. Сначала распарсить обе выборки. Затем вычислить статистику **строго и только** по обучающей базе, чтобы не было утечки данных из будущего. Потом нормализовать и обучающую, и тестовую выборки. Пройтись циклом и сделать предсказания для каждого растения в тесте. В самом конце функция должна вернуть словарь с двумя ключами: `"accuracy"` с итоговой точностью и `"predictions"` со списком всех предсказанных классов.
Терминал призывно мигает пустым курсором. Система охлаждения реактора издает последний предупреждающий гудок. Жизнь Эпсилон-9 зависит от чистоты вашего кода. Приступайте.
View File
+32
View File
@@ -0,0 +1,32 @@
### Задача 4. Работа с JSON и файлами (ООП, JSON, Исключения)
**Постановка**
* **Конфигурация:** Умеет читать и писать настройки в JSON-файл.
* **Приложение:** Использует конфигурацию для хранения состояния.
**Задание**
**Класс `Config`:**
1. Создайте класс `Config`.
2. В `__init__(self, filename)` сохраните имя файла.
3. Метод `load(self)` — читает JSON-файл и возвращает словарь. Если файла нет, перехватывает `FileNotFoundError` и возвращает пустой словарь `{}`.
4. Метод `save(self, data)` — записывает словарь `data` в JSON-файл с отступами (`indent=4`) и кодировкой `utf-8`.
5. Метод `get(self, key, default=None)` — загружает данные и возвращает значение по ключу или `default`.
6. Метод `set(self, key, value)` — загружает данные, устанавливает новое значение, сохраняет обратно.
**Класс `Application`:**
1. Создайте класс `Application`.
2. В `__init__(self, config)` сохраните объект `Config`.
3. Метод `run(self)` — выводит приветствие с именем пользователя из конфига (ключ `"username"`), по умолчанию `"Гость"`.
4. Метод `set_username(self, name)` — сохраняет имя в конфиг через `config.set`.
**Тесты:**
1. Создайте `Config("settings.json")` и `Application(config)`
2. Запустите приложение (должно вывести "Гость")
3. Установите имя через `set_username("Иван")`
4. Запустите снова (должно вывести "Иван")
5. Проверьте, что файл `settings.json` создался и содержит правильные данные
+42
View File
@@ -0,0 +1,42 @@
### Задача 5. Система логирования (ООП, Наследование, Работа с файлами)
**Постановка**
* **Logger (базовый класс):** Умеет логировать сообщения.
* **FileLogger:** Наследник, который пишет в файл.
* **ConsoleLogger:** Наследник, который пишет в консоль.
* **MultiLogger:** Комбинированный логгер, который пишет везде.
**Задание**
**Класс `Logger`:**
1. Создайте класс `Logger`.
2. Метод `__init__(self, name)` — сохраняет имя логгера.
3. Метод `format_message(self, level, message)` — возвращает строку формата: `"[2026-07-18 10:30:45] [INFO] [LoggerName] Message"`. Используйте `datetime.now()`.
4. Метод `log(self, level, message)` — абстрактный метод, просто пишет `pass`. Переопределяется в наследниках.
**Класс `FileLogger(Logger)`:**
1. Создайте класс `FileLogger`, наследующий `Logger`.
2. В `__init__(self, name, filename)` вызовите `super().__init__(name)` и сохраните имя файла.
3. Переопределите `log(self, level, message)` — форматирует через `format_message` и записывает в файл (режим `'a'`, append).
**Класс `ConsoleLogger(Logger)`:**
1. Создайте класс `ConsoleLogger`, наследующий `Logger`.
2. Переопределите `log(self, level, message)` — форматирует и выводит в консоль через `print`.
**Класс `MultiLogger`:**
1. Создайте класс `MultiLogger`.
2. В `__init__(self, *loggers)` сохраните список логгеров.
3. Метод `log(self, level, message)` — вызывает `log` у каждого логгера.
**Тесты:**
1. Создайте `FileLogger("file_log", "app.log")`
2. Создайте `ConsoleLogger("console_log")`
3. Создайте `MultiLogger(file_logger, console_logger)`
4. Залогируйте несколько сообщений разных уровней: `"INFO"`, `"WARNING"`, `"ERROR"`
5. Проверьте, что сообщения появились и в файле, и в консоли
+17
View File
@@ -0,0 +1,17 @@
### Задача 6. Декораторы и время выполнения (Декораторы, Функции высшего порядка)
Напишите программу с декораторами для измерения производительности:
1. `timer(func)` — декоратор, который измеряет время выполнения функции. Выводит: *"Функция {название} выполнилась за {время} секунд"*. Используйте `time.time()`.
2. `repeat(n)` — декоратор с параметром, который запускает функцию `n` раз подряд и возвращает список результатов.
3. `cache_result(func)` — декоратор, который кэширует результат функции. Если функция вызывается с теми же аргументами, возвращает закэшированный результат, не выполняя функцию повторно. Используйте словарь для хранения кэша.
**Тесты:**
1. Создайте функцию `slow_calculation(x)`, которая делает `time.sleep(1)` и возвращает `x * 2`
2. Примените к ней декоратор `@timer`
3. Создайте функцию `fibonacci(n)` (рекурсивная) и примените `@cache_result`
4. Вызовите `fibonacci(35)` дважды и убедитесь, что второй раз выполняется мгновенно
5. Создайте функцию с декоратором `@repeat(3)` и проверьте, что она выполнилась 3 раза
+39
View File
@@ -0,0 +1,39 @@
### Задача 7. Система управления задачами (ООП, Композиция, Enum)
**Постановка**
* **Приоритет:** Enum с уровнями LOW, MEDIUM, HIGH.
* **Задача:** Имеет название, описание, приоритет и статус (выполнена/не выполнена).
* **Менеджер задач:** Управляет списком задач, может фильтровать по приоритету.
**Задание**
**Enum `Priority`:**
1. Импортируйте `Enum` из модуля `enum`.
2. Создайте класс `Priority(Enum)` с тремя значениями: `LOW = 1`, `MEDIUM = 2`, `HIGH = 3`.
**Класс `Task`:**
1. Создайте класс `Task`.
2. В `__init__(self, title, description, priority)` сохраните параметры и установите `completed = False`.
3. Метод `mark_completed(self)` — устанавливает `completed = True`.
4. Метод `__str__(self)` — возвращает строку: `"[HIGH] Задача: Исправить баг (Не выполнена)"`.
**Класс `TaskManager`:**
1. Создайте класс `TaskManager`.
2. В `__init__(self)` создайте пустой список `tasks`.
3. Метод `add_task(self, task)` — добавляет задачу в список.
4. Метод `get_by_priority(self, priority)` — возвращает список задач с указанным приоритетом.
5. Метод `get_incomplete(self)` — возвращает список невыполненных задач.
6. Метод `show_all(self)` — выводит все задачи циклом через `print`.
**Тесты:**
1. Создайте менеджер задач
2. Добавьте 5 задач с разными приоритетами
3. Отметьте 2 задачи как выполненные
4. Получите все задачи с приоритетом HIGH
5. Получите все невыполненные задачи
6. Выведите все задачи
+27
View File
@@ -0,0 +1,27 @@
### Задача 8. Работа с CSV (Работа с файлами, CSV, Функции)
Напишите программу из 4 функций для работы с CSV-файлами:
1. `create_csv(filename, headers, data)` — создает CSV-файл с заголовками `headers` (список строк) и данными `data` (список списков). Используйте модуль `csv` и метод `csv.writer`.
2. `read_csv(filename)` — читает CSV-файл и возвращает список словарей, где ключи — это заголовки. Используйте `csv.DictReader`.
3. `filter_csv(data, column, value)` — принимает список словарей из `read_csv`, название колонки и значение. Возвращает отфильтрованный список, где значение в колонке совпадает.
4. `calculate_column_sum(data, column)` — принимает список словарей и название колонки с числами. Возвращает сумму всех значений в этой колонке (преобразуя строки в числа).
**Тесты:**
1. Создайте CSV-файл `"students.csv"` с заголовками `["Имя", "Возраст", "Оценка"]` и данными:
```python
[
["Иван", "20", "85"],
["Мария", "22", "92"],
["Петр", "20", "78"],
["Анна", "21", "88"]
]
```
2. Прочитайте файл
3. Отфильтруйте студентов с возрастом "20"
4. Посчитайте сумму всех оценок
5. Выведите результаты
+23
View File
@@ -0,0 +1,23 @@
### Задача 9. Генераторы и итераторы (Генераторы, yield, Итераторы)
Напишите программу с генераторами и кастомными итераторами:
1. `number_generator(start, end, step=1)` — генератор, который yield'ит числа от `start` до `end` с шагом `step`.
2. `fibonacci_generator(limit)` — генератор чисел Фибоначчи до `limit`. Используйте `yield` и цикл `while`.
3. **Класс `RangeIterator`:**
- В `__init__(self, start, end)` сохраните границы
- Метод `__iter__(self)` возвращает `self`
- Метод `__next__(self)` возвращает следующее число или поднимает `StopIteration`
4. `filter_even(generator)` — принимает генератор и возвращает новый генератор, который yield'ит только четные числа. Используйте `yield from` или цикл с `yield`.
**Тесты:**
1. Создайте генератор чисел от 1 до 20 с шагом 2
2. Выведите все значения через цикл `for`
3. Создайте генератор Фибоначчи до 100
4. Отфильтруйте только четные числа
5. Создайте `RangeIterator(5, 10)` и выведите все значения
6. Проверьте, что можно использовать в `list()`: `list(number_generator(1, 10))`
+24
View File
@@ -0,0 +1,24 @@
def check_number(item):
return isinstance(item, (int, float))
def calculate_total_sum(nested_list):
new_summ = 0
for i in nested_list:
if isinstance(i, (list, tuple)):
new_summ += calculate_total_sum(i)
elif check_number(i):
new_summ += i
return new_summ
def process_data(data, limit):
if calculate_total_sum(data) <= limit:
return True
else:
print("Превышен лимит")
return False
print(check_number("u"))
print(process_data(data=(3, 4, 6, 6, 7, (1000, 300)), limit=100))
+7
View File
@@ -0,0 +1,7 @@
### Задача 1. Анализ числовой структуры (Функции и Рекурсия)
Напишите код из 3 взаимосвязанных функций, который вычисляет сумму всех чисел во вложенных списках.
1. `check_number(item)` — принимает одно значение `item`; возвращает `True`, если это число (`int` или `float`), и `False` в противном случае.
2. `calculate_total_sum(nested_list)` — должна пройтись по элементам списка циклом. Если элемент является списком, функция рекурсивно вызывает саму себя. Если число — прибавляет к общей сумме. Возвращает итоговую сумму.
3. `process_data(data, limit)` — принимает список `data` и числовой лимит. Вызывает `calculate_total_sum`. Если итоговая сумма меньше или равна `limit`, возвращает `True`. Если больше — выводит сообщение *"Превышен лимит"* и возвращает `False`.
+8
View File
@@ -0,0 +1,8 @@
### Задача 2. Анализатор текста (Словари, while и исключения)
Напишите программу из 3 функций:
1. `safe_input()` — через цикл `while` запрашивает у пользователя строку. Если строка пустая, выбрасывает `ValueError`, перехватывает его через `try-except`, выводит *"Строка не может быть пустой"* и просит ввести снова. Возвращает введенную строку.
2. `count_chars(text)` — принимает строку и возвращает словарь с частотностью каждого символа (пробелы нужно проигнорировать).
3. `get_top_tuples(freq_dict)` — принимает словарь частотности, переводит его в список кортежей `(символ, количество)`, сортирует по убыванию количества и возвращает этот список.
+23
View File
@@ -0,0 +1,23 @@
def safe_input():
while True:
try:
text = input("Введите строку")
if not text.strip():
raise ValueError
return text
except ValueError:
print("Строка не может быть пустой")
def count_chars(text):
my_list = {}
for char in text.replace(" ", ""):
my_list[char] = my_list.get(char, 0) + 1
return my_list
def get_top_tuples(my_list):
return sorted(my_list.items(), key=lambda x: x[1], reverse=True)
print(get_top_tuples(count_chars(safe_input())))
+10
View File
@@ -0,0 +1,10 @@
### Задача 3. Поиск палиндромов (Циклы и Условия)
Напишите 3 взаимосвязанные функции:
1. `clean_word(word)` — переводит строку в нижний регистр, убирает пробелы и возвращает её.
2. `is_palindrome(word)` — использует `clean_word` и возвращает `True`, если слово читается одинаково слева направо и справа налево.
3. `filter_palindromes(words_list)` — принимает список строк, циклом проверяет каждое слово через `is_palindrome` и возвращает список только из палиндромов.
---
+23
View File
@@ -0,0 +1,23 @@
def clean_word(word):
words = word.lower()
wordss = words.replace(" ", "")
return wordss
def is_palindrome(word):
wolds = clean_word(word)
if wolds == wolds[::-1]:
return True
def filter_palindromes(words_list):
my_list = []
for words in words_list:
if is_palindrome(words) is True:
my_list.append(words)
else:
continue
return my_list
print(filter_palindromes(["gfdfhfhdh", "fsadfdagfadg", "asa"]))
+45
View File
@@ -0,0 +1,45 @@
### Задача 4. Поэма Края (ООП и работа с JSON)
> **Контекст лора:**
> Игрок проделал долгий путь, чтобы достичь конца игры. Теперь он видит слова на экране и слышит голоса из пустоты. Вселенная обращается к нему напрямую. В финале Поэма Края должна связать мысли игрока и послание вселенной воедино, прочитав сакральный текст из внешнего источника.
**Постановка**
* **Читатель поэмы:** Имеет имя файла и может читать текст поэмы из JSON-файла.
* **Игрок:** Имеет имя и может видеть слова на экране.
* **Вселенная:** Имеет имя и может произносить финальное послание.
* **Поэма Края:** Содержит читателя, игрока и вселенную; может воспроизводить титры, используя текст из JSON.
**Задание**
**Класс `PoemReader`:**
1. Импортируйте модуль `json`.
2. Создайте класс `PoemReader`.
3. Создайте метод `__init__(self, filename)`.
4. Создайте метод `read_poem(self)`, который открывает JSON-файл на чтение (с кодировкой `utf-8`), преобразует его содержимое в словарь и возвращает значение по ключу `"poem_text"`.
**Класс `Player`:**
1. Создайте класс `Player`.
2. Создайте метод `__init__(self, name)`.
3. Создайте метод `see_words(self)`, который выводит сообщение: *"[Имя] читает наши мысли так, словно это слова на экране."*
**Класс `Universe`:**
1. Создайте класс `Universe`.
2. Создайте метод `__init__(self)`, определяющий свойство `name` равным `"Вселенная"`.
3. Создайте метод `speak(self)`, выводящий фразу: *"И Вселенная говорит тебе: Я люблю тебя."*
**Класс `EndPoem`:**
1. Создайте класс `EndPoem`.
2. Создайте метод `__init__(self, reader, player, universe)`.
3. Создайте метод `play_credits(self)`, который: вызывает чтение текста из JSON, выводит его на экран, а затем вызывает методы игрока и вселенной.
**Тесты:**
1. Создайте файл `poem.json` рядом с вашим скриптом, поместив туда словарь с ключом `"poem_text"` и текстом поэмы.
2. Создайте объект читателя, передав ему имя файла.
3. Создайте объекты игрока и вселенной.
4. Создайте объект поэмы и запустите титры.
File diff suppressed because one or more lines are too long
+43
View File
@@ -0,0 +1,43 @@
import json
class PoemReader():
def __init__(self, filename):
self.filename = filename
def read_poem(self):
with open(self.filename, "r", encoding="utf-8") as file:
return json.load(file)["poem_text"]
class Player:
def __init__(self, name):
self.name = name
def see_words(self):
print(f"{self.name} читает наши мысли так, словно это слова на экране.")
class Universe:
def __init__(self):
self.name = "Вселенная"
def speak(self):
print("И Вселенная говорит тебе: Я люблю тебя.")
class EndPoem:
def __init__(self, reader, player, universe):
self.reader, self.player, self.universe = reader, player, universe
def play_credits(self):
print(self.reader.read_poem())
self.player.see_words()
self.universe.speak()
reader = PoemReader(r"data\main.json")
player = Player("Дима")
universete = Universe()
poem = EndPoem(reader, player, universete)
poem.play_credits()
+72
View File
@@ -0,0 +1,72 @@
### Задача 5. Библиотека (ООП - Инкапсуляция и Взаимодействие)
**Постановка**
* **Книга:** Имеет название, автора и статус (*"В библиотеке"*, *"Выдана"*). Может менять свой статус на противоположный.
* **Читатель:** Имеет имя и список взятых книг. Может брать и возвращать книги.
* **Библиотека:** Содержит список всех доступных книг. Может выдавать книгу читателю.
**Задание**
**Класс `Book`:**
1. Создайте класс `Book`.
2. В `__init__(self, title, author)` определите свойства `title`, `author` и `status` (по умолчанию *"В библиотеке"*).
3. Создайте метод `change_status(self)`, меняющий статус книги на *"Выдана"*, если она в библиотеке, и обратно.
**Класс `Reader`:**
1. Создайте класс `Reader`.
2. В `__init__(self, name)` сохраните имя и создайте пустой список `borrowed_books`.
3. Создайте метод `take_book(self, book)`, добавляющий переданную книгу в список.
4. Создайте метод `return_book(self, book)`, удаляющий книгу из списка.
**Класс `Library`:**
1. Создайте класс `Library`.
2. В `__init__(self)` создайте список `books` и добавьте туда несколько книг по умолчанию.
3. Создайте метод `lend_book(self, book, reader)`, который проверяет статус книги, меняет его через метод книги на *"Выдана"* и передает читателю в его список.
**Тесты:**
1. Создайте библиотеку.
2. Создайте читателя.
3. Используя библиотеку, выдайте читателю книгу.
---
### Задача 6. Магазин (ООП - Агрегация)
**Постановка**
* **Товар:** Имеет название и цену.
* **Корзина:** Содержит список товаров. Может добавлять товар и считать общую стоимость.
* **Покупатель:** Имеет имя, баланс и корзину. Может оплачивать корзину.
**Задание**
**Класс `Product`:**
1. Создайте класс `Product`.
2. Создайте метод `__init__(self, name, price)`.
**Класс `Cart`:**
1. Создайте класс `Cart`.
2. В `__init__(self)` создайте пустой список `products`.
3. Создайте метод `add_product(self, product)`, добавляющий товар в список.
4. Создайте метод `get_total(self)`, возвращающий сумму цен всех товаров.
**Класс `Customer`:**
1. Создайте класс `Customer`.
2. В `__init__(self, name, balance, cart)` сохраните переданные параметры.
3. Создайте метод `pay(self)`, который проверяет, хватает ли баланса для оплаты корзины. Если хватает — вычитает сумму из баланса и очищает корзину. Если нет — выводит предупреждение.
**Тесты:**
1. Создайте несколько товаров.
2. Создайте корзину и добавьте в нее товары.
3. Создайте покупателя, передав ему корзину и баланс.
4. Заставьте покупателя оплатить товары в корзине.
+35
View File
@@ -0,0 +1,35 @@
### Задача 6. Магазин (ООП - Агрегация)
**Постановка**
* **Товар:** Имеет название и цену.
* **Корзина:** Содержит список товаров. Может добавлять товар и считать общую стоимость.
* **Покупатель:** Имеет имя, баланс и корзину. Может оплачивать корзину.
**Задание**
**Класс `Product`:**
1. Создайте класс `Product`.
2. Создайте метод `__init__(self, name, price)`.
**Класс `Cart`:**
1. Создайте класс `Cart`.
2. В `__init__(self)` создайте пустой список `products`.
3. Создайте метод `add_product(self, product)`, добавляющий товар в список.
4. Создайте метод `get_total(self)`, возвращающий сумму цен всех товаров.
**Класс `Customer`:**
1. Создайте класс `Customer`.
2. В `__init__(self, name, balance, cart)` сохраните переданные параметры.
3. Создайте метод `pay(self)`, который проверяет, хватает ли баланса для оплаты корзины. Если хватает — вычитает сумму из баланса и очищает корзину. Если нет — выводит предупреждение.
**Тесты:**
1. Создайте несколько товаров.
2. Создайте корзину и добавьте в нее товары.
3. Создайте покупателя, передав ему корзину и баланс.
4. Заставьте покупателя оплатить товары в корзине.
+46
View File
@@ -0,0 +1,46 @@
**Постановка**
Мы создаем упрощенную версию нейронной сети, которая умеет классифицировать данные на несколько категорий сразу.
* **Нейрон (`Neuron`):** Теперь это отдельный объект, хранящий свои уникальные веса и смещение (`bias`). Умеет вычислять активацию и корректировать веса методом градиентного спуска.
* **Слой (`Layer`):** Содержит список объектов `Neuron`. Управляет передачей сигналов всем нейронам сразу.
* **Тренер (`Trainer`):** Обучает слой, минимизируя среднюю ошибку по всем нейронам.
**Задание**
**Класс `Neuron`:**
1. Создайте класс `Neuron`.
2. В `__init__(self, inputs_count, learning_rate)` инициализируйте `weights` (список случайных значений, например 0.1) и `bias` (смещение, начните с 0.0).
3. Метод `predict(self, inputs)`: верните сумму произведений (входы * веса) + `bias`.
4. Метод `train(self, inputs, target)`:
* Вычислите `prediction = self.predict(inputs)`.
* Вычислите `error = target - prediction`.
* Обновите веса: `w = w + (error * input * lr)`.
* Обновите смещение: `bias = bias + (error * lr)`.
**Класс `Layer`:**
1. Создайте класс `Layer`.
2. В `__init__(self, neurons_count, inputs_count, learning_rate)` создайте список `neurons`, заполнив его `neurons_count` объектами класса `Neuron`.
3. Метод `forward(self, inputs)`: возвращает список предсказаний каждого нейрона.
4. Метод `train_layer(self, inputs, targets)`: принимает список правильных ответов (для каждого нейрона свой). Проходит циклом по нейронам и вызывает у каждого метод `train` с соответствующим `target`.
**Класс `Trainer`:**
1. Создайте класс `Trainer`.
2. Метод `fit(self, layer, dataset, epochs)`:
* Внешний цикл `for epoch in range(epochs)`.
* Внутренний цикл по датасету: берет `inputs` и список `targets`.
* Вызывает `layer.train_layer(inputs, targets)`.
* (Опционально) Каждую 10-ю эпоху выводить среднюю ошибку по слою.
**Тесты:**
1. Создайте слой из 2 нейронов (каждый принимает 3 входа).
2. Создайте датасет: 3 примера, где каждый пример имеет 3 входа и 2 целевых значения.
3. Обучите слой в течение 200 эпох.
4. После обучения выведите результат предсказания для нового входа `[0.5, 0.2, 0.1]`.
+41 -81
View File
@@ -1,87 +1,47 @@
# 🌱 Farm Automation System # Контрольные работы: Игорь
Учебный проект для практики работы с Git: слияние конфликтов и интерактивный ребейз. Добро пожаловать в рабочее пространство для выполнения контрольных работ. В этом репозитории мы учимся писать чистый, качественный и работающий код, используя современные инструменты разработки.
## Описание проекта ## 🛠 Технологический стек
* **Язык**: Python
* **Автоматизация (CI)**: Gitea Actions
* **Контроль качества**: Ruff (линтер)
* **Стандарты коммитов**: Commitizen
Простая система автоматизации гидропонной фермы на Python с использованием ООП: ## 📋 Статус проверок
[![Gitea Actions Status](https://gitea.dimong.ru/Dimong4/Igor-kr/actions/workflows/ci.yaml/badge.svg)](https://gitea.dimong.ru/Dimong4/Igor-kr/actions)
- **BioCulture** — класс биокультуры со стадиями роста ("Зеленый" → "Красный")
- **HydroponicModule** — модуль, управляющий несколькими культурами
- **FarmAgent** — агент-оператор для управления модулем и сбора урожая
## Структура проекта
```
Igor-kr/
├── main.py # Точка входа
├── farm_core/
│ ├── __init__.py
│ ├── module.py # Классы BioCulture и HydroponicModule
│ └── agent.py # Класс FarmAgent
└── README.md
```
## Ветки
- **main** — базовая заготовка со "сломанным" методом `grow()` (вызывает `NotImplementedError`)
- **feature-api** — рабочая реализация метода `grow()` и новая функция `harvest()`
## 🎯 Практические задания
### Задание 1: Разрешение конфликтов при слиянии
**Цель:** Научиться разрешать merge conflicts.
**Шаги:**
1. Убедитесь, что вы на ветке `main`
2. Попытайтесь слить ветку `feature-api`
3. **Ожидаемый результат:** Конфликт в файле `farm_core/module.py` в методе `grow()`:
- В `main`: `raise NotImplementedError("Рост не реализован в main!")`
- В `feature-api`: `self._stage = "Красный"`
4. Откройте файл `farm_core/module.py` и разрешите конфликт
5. Завершите слияние
### Задание 2: Интерактивный ребейз (очистка истории)
**Цель:** Научиться объединять "мусорные" коммиты в один чистый коммит.
**Шаги:**
1. Переключитесь на ветку `feature-api`
2. Посмотрите на историю коммитов (вы увидите три мусорных коммита: done, fix2, fix)
3. Запустите интерактивный ребейз для последних 3 коммитов
4. Объедините три мусорных коммита в один
5. Проверьте результат — вместо трех мусорных коммитов должен быть один чистый коммит
### Задание 3 (дополнительно): Проверка работы кода
1. Переключитесь на `main` после успешного слияния
2. Допишите в `main.py` цикл для проверки работы:
```python
for culture in module.cultures:
culture.grow()
print(f"Готово к сбору: {agent.harvest()} культур(ы)")
```
3. Запустите код
**Ожидаемый вывод:**
```
Инструкция: pH 6.0
Готово к сбору: 3 культур(ы)
```
## ✅ Критерии успешного выполнения
- [ ] Конфликт в `module.py` успешно разрешен
- [ ] Метод `grow()` работает корректно (изменяет стадию на "Красный")
- [ ] Три мусорных коммита объединены в один
- [ ] История коммитов чистая и понятная
- [ ] Код запускается без ошибок
--- ---
**Автор:** Dmitry Belan ## 🚀 Как работать с этим репозиторием
**Дата:** 2026-07-01
### 1. Подготовка окружения
Перед началом работы убедись, что у тебя установлены:
* **Git** — для управления версиями.
* **Python 3.x** — для написания кода.
* **pip** — для установки зависимостей.
### 2. Установка зависимостей
Если твое решение требует дополнительных библиотек:
1. Создай файл `requirements.txt` в корне проекта.
2. Установи зависимости командой: `pip install -r requirements.txt`.
3. Обязательно добавь все используемые библиотеки в `requirements.txt`, чтобы они корректно работали в среде автоматической проверки.
### 3. Рабочий процесс (Workflow)
1. **Создай новую ветку** для своей работы: `git checkout -b task-name`.
2. **Пиши код** и проверяй его локально.
3. **Сделай коммит**, соблюдая правила (Commitizen): `git commit -m "feat: добавил решение задачи №1"`.
4. **Отправь изменения** в репозиторий: `git push origin task-name`.
### 4. Проверка и сдача
* После `push` перейди на страницу репозитория во вкладку **Actions**.
* Если горит **зеленая галочка ✅** — работа прошла автоматический контроль качества.
* Если **красный крестик ❌** — открой логи задачи, исправь замечания линтера и запушь исправления.
* После успеха создай **Pull Request** из своей ветки в `main` для финальной проверки.
---
## 🎓 Правила
* **Чистота кода**: Игнорирование предупреждений линтера `Ruff` недопустимо.
* **Ветка main**: Прямые пуши в `main` запрещены. Ветка защищена правилами CI/CD.
* **Ответственность**: Ты несешь ответственность за свой код и его работоспособность в CI-среде.
-9
View File
@@ -1,9 +0,0 @@
temperatures = [37, 48, 30, 29, 41, -30, -29, -90]
number_ice = 0
for i in (temperatures):
if i >= 0:
print(i)
else:
number_ice += 1
print(f"Number ice Day: {number_ice}")
+87
View File
@@ -0,0 +1,87 @@
# 🌱 Farm Automation System
Учебный проект для практики работы с Git: слияние конфликтов и интерактивный ребейз.
## Описание проекта
Простая система автоматизации гидропонной фермы на Python с использованием ООП:
- **BioCulture** — класс биокультуры со стадиями роста ("Зеленый" → "Красный")
- **HydroponicModule** — модуль, управляющий несколькими культурами
- **FarmAgent** — агент-оператор для управления модулем и сбора урожая
## Структура проекта
```
Igor-kr/
├── main.py # Точка входа
├── farm_core/
│ ├── __init__.py
│ ├── module.py # Классы BioCulture и HydroponicModule
│ └── agent.py # Класс FarmAgent
└── README.md
```
## Ветки
- **main** — базовая заготовка со "сломанным" методом `grow()` (вызывает `NotImplementedError`)
- **feature-api** — рабочая реализация метода `grow()` и новая функция `harvest()`
## 🎯 Практические задания
### Задание 1: Разрешение конфликтов при слиянии
**Цель:** Научиться разрешать merge conflicts.
**Шаги:**
1. Убедитесь, что вы на ветке `main`
2. Попытайтесь слить ветку `feature-api`
3. **Ожидаемый результат:** Конфликт в файле `farm_core/module.py` в методе `grow()`:
- В `main`: `raise NotImplementedError("Рост не реализован в main!")`
- В `feature-api`: `self._stage = "Красный"`
4. Откройте файл `farm_core/module.py` и разрешите конфликт
5. Завершите слияние
### Задание 2: Интерактивный ребейз (очистка истории)
**Цель:** Научиться объединять "мусорные" коммиты в один чистый коммит.
**Шаги:**
1. Переключитесь на ветку `feature-api`
2. Посмотрите на историю коммитов (вы увидите три мусорных коммита: done, fix2, fix)
3. Запустите интерактивный ребейз для последних 3 коммитов
4. Объедините три мусорных коммита в один
5. Проверьте результат — вместо трех мусорных коммитов должен быть один чистый коммит
### Задание 3 (дополнительно): Проверка работы кода
1. Переключитесь на `main` после успешного слияния
2. Допишите в `main.py` цикл для проверки работы:
```python
for culture in module.cultures:
culture.grow()
print(f"Готово к сбору: {agent.harvest()} культур(ы)")
```
3. Запустите код
**Ожидаемый вывод:**
```
Инструкция: pH 6.0
Готово к сбору: 3 культур(ы)
```
## ✅ Критерии успешного выполнения
- [ ] Конфликт в `module.py` успешно разрешен
- [ ] Метод `grow()` работает корректно (изменяет стадию на "Красный")
- [ ] Три мусорных коммита объединены в один
- [ ] История коммитов чистая и понятная
- [ ] Код запускается без ошибок
---
**Автор:** Dmitry Belan
**Дата:** 2026-07-01
+1 -1
View File
@@ -1,5 +1,5 @@
from farm_core.module import HydroponicModule
from farm_core.agent import FarmAgent from farm_core.agent import FarmAgent
from farm_core.module import HydroponicModule
if __name__ == "__main__": if __name__ == "__main__":
print(FarmAgent.get_manual()) print(FarmAgent.get_manual())
+54
View File
@@ -0,0 +1,54 @@
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "0"
version_scheme = "semver"
version = "0.0.1"
update_changelog_on_bump = true
major_version_zero = true
[tool.ruff]
line-length = 80
exclude = [
".git",
"__pycache__",
"build",
"dist",
".venv",
"venv",
".mypy_cache",
".pytest_cache",
]
[tool.ruff.lint]
select = ["E", "F", "W", "N", "I", "Q", "T20"]
ignore = ["N802", "T201", "T001"]
[tool.ruff.lint.flake8-quotes]
inline-quotes = "double"
multiline-quotes = "double"
docstring-quotes = "double"
# Аналог import-order-style = "google"
[tool.ruff.lint.isort]
known-first-party = []
[tool.flake8]
max-line-length = 80
import-order-style = "google"
inline-quotes = "double"
multiline-quotes = "double"
docstring-quotes = "double"
# Записываем ignore как строку, если их несколько, или список
ignore = "N802,T201,T001"
exclude = [
".git",
"__pycache__",
"build",
"dist",
".venv",
"venv",
".mypy_cache",
".pytest_cache"
]
+6
View File
@@ -0,0 +1,6 @@
ruff
commitizen
parameterized
pre_commit
flake8
flake8-import-order