hh-bot/hh_bot/config/settings.py

235 lines
7.6 KiB
Python
Raw Normal View History

2025-06-27 09:57:34 +02:00
"""
Конфигурация для HH.ru автоматизации
"""
import os
from dataclasses import dataclass
from pathlib import Path
class AppConstants:
"""Константы приложения"""
HH_BASE_URL = "https://api.hh.ru"
HH_SITE_URL = "https://hh.ru"
GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta"
GEMINI_MODEL = "gemini-2.0-flash"
DEFAULT_TIMEOUT = 30
API_PAUSE_SECONDS = 0.5
AI_REQUEST_PAUSE = 1
MAX_VACANCIES_PER_PAGE = 50
MAX_SEARCH_PAGES = 5
DEFAULT_MAX_APPLICATIONS = 40
DEFAULT_EXPERIENCE_FILE = "data/experience.txt"
DEFAULT_ABOUT_FILE = "data/about_me.txt"
DEFAULT_SKILLS_FILE = "data/skills.txt"
DEFAULT_AI_THRESHOLD = 0.7
MIN_AI_SCORE = 0.0
MAX_AI_SCORE = 1.0
SHORT_SEPARATOR_LENGTH = 50
LONG_SEPARATOR_LENGTH = 60
SHORT_TEXT_LIMIT = 50
MEDIUM_TEXT_LIMIT = 60
GEMINI_TEMPERATURE = 0.3
GEMINI_MAX_OUTPUT_TOKENS = 1000
LOG_FILE_MAX_SIZE_MB = 10
PERCENT_MULTIPLIER = 100
@dataclass
class HHSearchConfig:
"""Настройки поиска вакансий"""
keywords: str = "python junior"
area: str = "1"
experience: str = "noExperience"
per_page: int = AppConstants.MAX_VACANCIES_PER_PAGE
max_pages: int = 3
order_by: str = "publication_time"
@dataclass
class BrowserConfig:
"""Настройки браузера"""
headless: bool = False
wait_timeout: int = 15
page_load_timeout: int = 30
implicit_wait: int = 10
@dataclass
class ApplicationConfig:
"""Настройки подачи заявок"""
max_applications: int = AppConstants.DEFAULT_MAX_APPLICATIONS
pause_min: float = 3.0
pause_max: float = 6.0
manual_login: bool = True
@dataclass
class GeminiConfig:
"""Настройки Gemini AI"""
api_key: str = ""
model: str = AppConstants.GEMINI_MODEL
base_url: str = AppConstants.GEMINI_BASE_URL
match_threshold: float = AppConstants.DEFAULT_AI_THRESHOLD
@dataclass
class ResumeConfig:
"""Настройки резюме"""
experience_file: str = AppConstants.DEFAULT_EXPERIENCE_FILE
about_me_file: str = AppConstants.DEFAULT_ABOUT_FILE
skills_file: str = AppConstants.DEFAULT_SKILLS_FILE
class ResumeFileManager:
"""Менеджер для работы с файлами резюме"""
@staticmethod
def create_sample_files() -> None:
"""Создание примеров файлов резюме"""
data_dir = Path("data")
data_dir.mkdir(exist_ok=True)
experience_file = data_dir / "experience.txt"
if not experience_file.exists():
experience_file.write_text(
"""
Опыт работы:
- Изучаю Python уже 6 месяцев
- Прошел курсы по основам программирования
- Делал учебные проекты: калькулятор, игра в крестики-нолики
- Изучаю Django и Flask для веб-разработки
- Базовые знания SQL и работы с базами данных
- Знаком с Git для контроля версий
""".strip(),
encoding="utf-8",
)
print(f"✅ Создан файл: {experience_file}")
about_file = data_dir / "about_me.txt"
if not about_file.exists():
about_file.write_text(
"""
О себе:
Начинающий Python разработчик с большим желанием учиться и развиваться.
Интересуюсь веб-разработкой и анализом данных.
Быстро обучаюсь, ответственно подхожу к работе.
Готов к стажировке или junior позиции для получения практического опыта.
Хочу работать в команде опытных разработчиков и вносить вклад в интересные проекты.
""".strip(),
encoding="utf-8",
)
print(f"✅ Создан файл: {about_file}")
skills_file = data_dir / "skills.txt"
if not skills_file.exists():
skills_file.write_text(
"""
Технические навыки:
- Python (основы, ООП, модули)
- SQL (SELECT, JOIN, базовые запросы)
- Git (commit, push, pull, merge)
- HTML/CSS (базовые знания)
- Django (учебные проекты)
- Flask (микрофреймворк)
- PostgreSQL, SQLite
- Linux (базовые команды)
- VS Code, PyCharm
""".strip(),
encoding="utf-8",
)
print(f"✅ Создан файл: {skills_file}")
class UIFormatter:
"""Утилиты для форматирования пользовательского интерфейса"""
@staticmethod
def create_separator(long: bool = False) -> str:
"""Создание разделительной линии"""
length = AppConstants.LONG_SEPARATOR_LENGTH if long else AppConstants.SHORT_SEPARATOR_LENGTH
return "=" * length
@staticmethod
def truncate_text(text: str, medium: bool = False) -> str:
"""Обрезание текста до заданного лимита"""
limit = AppConstants.MEDIUM_TEXT_LIMIT if medium else AppConstants.SHORT_TEXT_LIMIT
return text[:limit]
@staticmethod
def format_percentage(value: float, total: float) -> str:
"""Форматирование процентного соотношения"""
if total <= 0:
return "0.0%"
percentage = (value / total) * AppConstants.PERCENT_MULTIPLIER
return f"{percentage:.1f}%"
@staticmethod
def print_section_header(title: str, long: bool = False) -> None:
"""Печать заголовка секции с разделителями"""
separator = UIFormatter.create_separator(long)
print(f"\n{separator}")
print(title)
print(separator)
class Settings:
"""Главный класс настроек"""
def __init__(self):
self._load_env()
self.hh_search = HHSearchConfig()
self.browser = BrowserConfig()
self.application = ApplicationConfig()
self.gemini = GeminiConfig(api_key=os.getenv("GEMINI_API_KEY", ""))
self.resume = ResumeConfig()
self._validate_config()
def _load_env(self) -> None:
"""Загрузка переменных окружения"""
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
print("💡 Установите python-dotenv для работы с .env файлами")
def _validate_config(self) -> None:
"""Валидация настроек"""
if not self.gemini.api_key:
print("⚠️ GEMINI_API_KEY не установлен в переменных окружения")
data_dir = Path("data")
data_dir.mkdir(exist_ok=True)
logs_dir = Path("logs")
logs_dir.mkdir(exist_ok=True)
def update_search_keywords(self, keywords: str) -> None:
"""Обновление ключевых слов поиска"""
self.hh_search.keywords = keywords
print(f"🔄 Обновлены ключевые слова: {keywords}")
def enable_ai_matching(self) -> bool:
"""Проверяем можно ли использовать AI сравнение"""
return bool(self.gemini.api_key)
settings = Settings()