Skip to content
Open
28 changes: 28 additions & 0 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI with Ruff

on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff

- name: Run Ruff
run: |
ruff check .
60 changes: 37 additions & 23 deletions git/src/main.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import json
import os

def load_books(filename='library.json'):

def load_books(filename="library.json"):
"""
Загрузка списка книг из JSON-файла.
Возвращает список книг (каждая книга - это словарь).
"""
if not os.path.isfile(filename):
return []
with open(filename, 'r', encoding='utf-8') as file:
with open(filename, "r", encoding="utf-8") as file:
try:
return json.load(file)
except json.JSONDecodeError:
return []

def save_books(books, filename='library.json'):

def save_books(books, filename="library.json"):
"""
Сохранение списка книг в JSON-файл.
"""
with open(filename, 'w', encoding='utf-8') as file:
with open(filename, "w", encoding="utf-8") as file:
json.dump(books, file, ensure_ascii=False, indent=4)


def list_books(books):
"""
Возвращает строку со списком всех книг.
Expand All @@ -29,29 +32,31 @@ def list_books(books):
return "Библиотека пуста."
result_lines = []
for idx, book in enumerate(books, start=1):
result_lines.append(f"{idx}. {book['title']} | {book['author']} | {book['year']}")
result_lines.append(
f"{idx}. {book['title']} | {book['author']} | {book['year']}"
)
return "\n".join(result_lines)


def add_book(books, title, author, year):
"""
Принимает текущий список книг и данные о новой книге.
Возвращает новый список, в котором добавлена новая книга.
"""
new_book = {
'title': title,
'author': author,
'year': year
}
new_book = {"title": title, "author": author, "year": year}
# Создаём НОВЫЙ список, добавляя new_book
return books + [new_book]


def remove_book(books, title):
"""
Принимает текущий список книг и название книги для удаления.
Возвращает новый список без книги, у которой совпадает название.
"""
# Фильтруем список: оставляем только те книги, у которых название не совпадает с переданным
return [book for book in books if book['title'].lower() != title.lower()]
# Фильтруем список: оставляем только те книги,
# у которых название не совпадает с переданным
return [book for book in books if book["title"].lower() != title.lower()]


def search_books(books, keyword):
"""
Expand All @@ -60,13 +65,16 @@ def search_books(books, keyword):
"""
keyword_lower = keyword.lower()
return [
book for book in books
if keyword_lower in book['title'].lower() or keyword_lower in book['author'].lower()
book
for book in books
if keyword_lower in book["title"].lower()
or keyword_lower in book["author"].lower()
]


def main():
"""
Точка входа в программу: здесь мы загружаем книги,
Точка входа в программу: здесь мы загружаем книги,
показываем меню и обрабатываем ввод пользователя.
"""
books = load_books() # Загрузили список книг из JSON
Expand All @@ -81,25 +89,28 @@ def main():

choice = input("Выберите действие (1-5): ").strip()

if choice == '1':
if choice == "1":
print("\nСписок книг:")
print(list_books(books))

elif choice == '2':
elif choice == "2":
print("\nДобавление новой книги:")
title = input("Введите название: ").strip()
author = input("Введите автора: ").strip()
year = input("Введите год издания: ").strip()

# Получаем новый список с добавленной книгой
new_books = add_book(books, title, author, year)
books = new_books # Обновляем переменную, чтобы сохранить изменения
# Обновляем переменную, чтобы сохранить изменения
books = new_books
save_books(books) # Сразу сохраняем в файл
print("Книга добавлена!")

elif choice == '3':
elif choice == "3":
print("\nУдаление книги:")
title_to_remove = input("Введите название книги, которую хотите удалить: ").strip()
title_to_remove = input(
"Введите название книги, которую хотите удалить: "
).strip()

new_books = remove_book(books, title_to_remove)
if len(new_books) < len(books):
Expand All @@ -109,22 +120,25 @@ def main():
else:
print("Книга с таким названием не найдена.")

elif choice == '4':
elif choice == "4":
print("\nПоиск книг:")
keyword = input("Введите ключевое слово для поиска (в названии или авторе): ").strip()
keyword = input(
"Введите ключевое слово для поиска (в названии или авторе): "
).strip()
found_books = search_books(books, keyword)
if found_books:
print("\nНайденные книги:")
print(list_books(found_books))
else:
print("Ничего не найдено.")

elif choice == '5':
elif choice == "5":
print("Выход из программы.")
break

else:
print("Некорректный ввод. Попробуйте ещё раз.")


if __name__ == "__main__":
main()
41 changes: 41 additions & 0 deletions simple_backend/src/task_tracker/base_http_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from abc import ABC, abstractmethod
import requests
from typing import Dict, Any, Optional


class BaseHTTPClient(ABC):
"""Базовый класс для HTTP клиентов"""

def __init__(self, base_url: str, headers: Optional[Dict[str, str]] = None):
self.base_url = base_url
self.headers = headers or {}
self.timeout = 30

def _make_request(
self, method: str, endpoint: str = "", **kwargs
) -> Dict[str, Any]:
"""Общий метод для выполнения HTTP запросов"""
url = f"{self.base_url}{endpoint}"

try:
response = requests.request(
method=method,
url=url,
headers=self.headers,
timeout=self.timeout,
**kwargs,
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise Exception(f"Ошибка {method} запроса к {url}: {e}")

@abstractmethod
def load_data(self) -> Any:
"""Абстрактный метод для загрузки данных"""
pass

@abstractmethod
def save_data(self, data: Any) -> bool:
"""Абстрактный метод для сохранения данных"""
pass
52 changes: 52 additions & 0 deletions simple_backend/src/task_tracker/cloud_flare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from typing import Any
from base_http_client import BaseHTTPClient


class CloudflareAIClient(BaseHTTPClient):
"""Клиент для работы с Cloudflare AI API"""

def __init__(self, account_id: str, api_key: str):
base_url = f"https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/run"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
super().__init__(base_url, headers)

def load_data(self) -> Any:
"""Не используется для AI клиента, но требуется абстрактным методом"""
raise NotImplementedError("Метод load_data не реализован для AI клиента")

def save_data(self, data: Any) -> bool:
"""Не используется для AI клиента, но требуется абстрактным методом"""
raise NotImplementedError("Метод save_data не реализован для AI клиента")

def get_task_solutions(self, task_title: str) -> str:
"""Получить способы решения задачи от LLM"""
prompt = f"""
Задача: {task_title}

Проанализируй эту задачу и предложи 3-5 конкретных способов её решения.
Ответ должен быть кратким, практичным и на русском языке.
Формат: маркированный список.
"""

payload = {
"messages": [
{
"role": "system",
"content": "Ты помощник по решению задач. Давай практичные советы.",
},
{"role": "user", "content": prompt},
],
"model": "@cf/meta/llama-3-8b-instruct",
"max_tokens": 500,
}

try:
result = self._make_request(
"POST", "/@cf/meta/llama-3-8b-instruct", json=payload
)
return result["result"]["response"]
except Exception:
return "Не удалось получить рекомендации"
29 changes: 29 additions & 0 deletions simple_backend/src/task_tracker/cloud_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from base_http_client import BaseHTTPClient


class CloudTaskStorage(BaseHTTPClient):
"""Класс для хранения задач в jsonbin.io"""

def __init__(self, bin_id: str, api_key: str):
base_url = f"https://api.jsonbin.io/v3/b/{bin_id}"
headers = {"X-Master-Key": api_key, "Content-Type": "application/json"}
super().__init__(base_url, headers)

def load_data(self) -> list:
"""Загрузка задач из облака"""
response_data = self._make_request("GET")
data = response_data["record"]
return data.get("tasks", data) if isinstance(data, dict) else data

def save_data(self, tasks: list) -> bool:
"""Сохранение задач в облаке"""
data = {"tasks": tasks}
self._make_request("PUT", json=data)
return True

# Для обратной совместимости
def load_tasks(self):
return self.load_data()

def save_tasks(self, tasks):
return self.save_data(tasks)
Loading