235 lines
7.6 KiB
Python
235 lines
7.6 KiB
Python
"""
|
||
⚙️ Конфигурация для 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()
|