diff --git a/reports/Melnik/lab4/rep/rep.pdf b/reports/Melnik/lab4/rep/rep.pdf new file mode 100644 index 00000000..2ce3c79a Binary files /dev/null and b/reports/Melnik/lab4/rep/rep.pdf differ diff --git a/reports/Melnik/lab4/src/last_releases.json b/reports/Melnik/lab4/src/last_releases.json new file mode 100644 index 00000000..e69de29b diff --git a/reports/Melnik/lab4/src/task.py b/reports/Melnik/lab4/src/task.py new file mode 100644 index 00000000..af205c3f --- /dev/null +++ b/reports/Melnik/lab4/src/task.py @@ -0,0 +1,87 @@ +# pylint: disable=invalid-name +""" +Скрипт для автоматического отслеживания новых релизов в репозиториях GitHub. +Использует GitHub REST API и сохраняет состояние в JSON файл. +""" + +import json +import os +import requests + +STATE_FILE = "last_releases.json" + + +def get_latest_release(repo: str) -> dict: + """ + Получает информацию о последнем релизе репозитория через GitHub API. + """ + url = f"https://api.github.com/repos/{repo}/releases/latest" + headers = {"Accept": "application/vnd.github.v3+json"} + + try: + response = requests.get(url, headers=headers, timeout=10) + if response.status_code == 200: + return response.json() + return None + except requests.exceptions.RequestException as err: + print(f"Ошибка сети для {repo}: {err}") + return None + + +def load_state() -> dict: + """Загружает данные о прошлых проверках из JSON файла.""" + if os.path.exists(STATE_FILE): + with open(STATE_FILE, "r", encoding="utf-8") as f: + return json.load(f) + return {} + + +def save_state(state: dict) -> None: + """Сохраняет текущие данные о версиях в JSON файл.""" + with open(STATE_FILE, "w", encoding="utf-8") as f: + json.dump(state, f, ensure_ascii=False, indent=4) + + +def main() -> None: + """Основная логика мониторинга обновлений.""" + user_input = input("Введите репозитории для отслеживания (через запятую): ") + repo_list = [r.strip() for r in user_input.split(",") if r.strip()] + + if not repo_list: + print("Список репозиториев пуст.") + return + + last_state = load_state() + new_state = last_state.copy() + + for repo in repo_list: + print(f"\nПроверяем обновления для {repo}...") + release_data = get_latest_release(repo) + + if not release_data: + print(f"Информация о релизах в {repo} не найдена.") + continue + + version = release_data.get("tag_name") + last_seen_version = last_state.get(repo) + + if version != last_seen_version: + date = release_data.get("published_at", "не указана")[:10] + link = release_data.get("html_url") + body = release_data.get("body", "Нет описания") + changelog = "\n".join(body.splitlines()[:5]) + + print(f"✅ НАЙДЕН НОВЫЙ РЕЛИЗ: {version} ({date})") + print(f" Ссылка: {link}") + print(f" Основные изменения:\n{changelog}...") + + new_state[repo] = version + else: + print(f"😴 Новых обновлений для {repo} нет (текущая: {version}).") + + save_state(new_state) + print("\nПроверка завершена. Состояние сохранено в last_releases.json") + + +if __name__ == "__main__": + main() diff --git a/reports/Melnik/lab5/rep/rep.pdf b/reports/Melnik/lab5/rep/rep.pdf new file mode 100644 index 00000000..5ece80aa Binary files /dev/null and b/reports/Melnik/lab5/rep/rep.pdf differ diff --git a/reports/Melnik/lab5/src/accounting.db b/reports/Melnik/lab5/src/accounting.db new file mode 100644 index 00000000..e69de29b diff --git a/reports/Melnik/lab5/src/main.py b/reports/Melnik/lab5/src/main.py new file mode 100644 index 00000000..ae94790f --- /dev/null +++ b/reports/Melnik/lab5/src/main.py @@ -0,0 +1,189 @@ +# pylint: disable=invalid-name, too-few-public-methods, no-name-in-module +""" +Модуль для управления базой данных 'Бухгалтерия' через FastAPI. + +Для запуска сервера используйте команду: +# uvicorn task:app --reload +Для просмотра документации перейдите по ссылке: +# http://127.0.0.1:8000/docs +""" + +import datetime +from typing import List + +from fastapi import FastAPI, Depends, HTTPException +from pydantic import BaseModel +from sqlalchemy import ( + create_engine, + Column, + Integer, + String, + Float, + ForeignKey, + DateTime, +) +from sqlalchemy.orm import sessionmaker, relationship, Session, declarative_base + +SQLALCHEMY_DATABASE_URL = "sqlite:///./accounting.db" +engine = create_engine( + SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() + + +class Department(Base): + """Модель таблицы отделов.""" + + __tablename__ = "departments" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, unique=True) + employees = relationship("Employee", back_populates="department") + + +class Employee(Base): + """Модель таблицы сотрудников.""" + + __tablename__ = "employees" + + id = Column(Integer, primary_key=True, index=True) + full_name = Column(String) + position = Column(String) + salary = Column(Float) + dept_id = Column(Integer, ForeignKey("departments.id")) + + department = relationship("Department", back_populates="employees") + + +class Counterparty(Base): + """Модель таблицы контрагентов.""" + + __tablename__ = "counterparties" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String) + inn = Column(String, unique=True) + + +class Category(Base): + """Модель таблицы категорий операций.""" + + __tablename__ = "categories" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String) + + +class Transaction(Base): + """Модель таблицы финансовых транзакций.""" + + __tablename__ = "transactions" + + id = Column(Integer, primary_key=True, index=True) + amount = Column(Float) + description = Column(String) + created_at = Column(DateTime, default=datetime.datetime.now) + + emp_id = Column(Integer, ForeignKey("employees.id")) + counterparty_id = Column(Integer, ForeignKey("counterparties.id")) + category_id = Column(Integer, ForeignKey("categories.id")) + + +Base.metadata.create_all(bind=engine) + + +class EmployeeCreate(BaseModel): + """Схема для создания нового сотрудника.""" + + full_name: str + position: str + salary: float + dept_id: int + + +class EmployeeOut(EmployeeCreate): + """Схема для вывода данных о сотруднике.""" + + id: int + + class Config: + """Настройки Pydantic для работы с SQLAlchemy.""" + + orm_mode = True + + +app = FastAPI(title="Система Бухгалтерии (Accounting API)") + + +def get_db(): + """Генератор сессий базы данных для каждого запроса.""" + db = SessionLocal() + try: + yield db + finally: + db.close() + + +@app.get( + "/employees/", + response_model=List[EmployeeOut], + summary="Получить список всех сотрудников", +) +def read_employees(db: Session = Depends(get_db)): + """Возвращает список всех сотрудников из базы данных.""" + return db.query(Employee).all() + + +@app.post( + "/employees/", response_model=EmployeeOut, summary="Добавить нового сотрудника" +) +def create_employee(employee: EmployeeCreate, db: Session = Depends(get_db)): + """Создает нового сотрудника и сохраняет его в базу.""" + db_employee = Employee(**employee.dict()) + db.add(db_employee) + db.commit() + db.refresh(db_employee) + return db_employee + + +@app.put("/employees/{emp_id}", summary="Изменить зарплату сотрудника") +def update_employee(emp_id: int, new_salary: float, db: Session = Depends(get_db)): + """Находит сотрудника по ID и обновляет его заработную плату.""" + db_emp = db.query(Employee).filter(Employee.id == emp_id).first() + if not db_emp: + raise HTTPException(status_code=404, detail="Сотрудник не найден") + + db_emp.salary = new_salary + db.commit() + return { + "message": f"Зарплата сотрудника ID={emp_id} успешно обновлена на {new_salary}" + } + + +@app.delete("/employees/{emp_id}", summary="Уволить (удалить) сотрудника") +def delete_employee(emp_id: int, db: Session = Depends(get_db)): + """Удаляет сотрудника из базы данных по его ID.""" + db_emp = db.query(Employee).filter(Employee.id == emp_id).first() + if not db_emp: + raise HTTPException(status_code=404, detail="Сотрудник не найден") + + db.delete(db_emp) + db.commit() + return {"message": "Сотрудник удален из базы"} + + +@app.post("/setup/", summary="Заполнить справочники (отделы, категории)") +def setup_db(db: Session = Depends(get_db)): + """Заполняет пустую базу данных базовыми отделами и контрагентами.""" + if db.query(Department).first(): + return {"message": "База уже инициализирована"} + + dept1 = Department(name="Бухгалтерия") + dept2 = Department(name="IT Отдел") + cat1 = Category(name="Выплата зарплаты") + cp1 = Counterparty(name="Банк ВТБ", inn="123456789") + + db.add_all([dept1, dept2, cat1, cp1]) + db.commit() + return {"message": "Базовые данные добавлены!"}