2025-02-17 21:01:27 +02:00
|
|
|
|
from sqlalchemy import Column, Integer, String, Text, DateTime, Float, Boolean, BigInteger, ForeignKey, DateTime
|
2025-02-17 18:18:14 +02:00
|
|
|
|
from sqlalchemy.dialects.mysql import JSON
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
|
2025-02-17 21:01:27 +02:00
|
|
|
|
|
|
|
|
|
from sqlalchemy.orm import relationship
|
|
|
|
|
from sqlalchemy.sql import func
|
2025-02-17 18:18:14 +02:00
|
|
|
|
from sqlalchemy.orm import sessionmaker, declarative_base
|
|
|
|
|
from sqlalchemy.sql import func
|
|
|
|
|
from passlib.context import CryptContext
|
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
|
from sqlalchemy.future import select
|
2025-02-17 21:01:27 +02:00
|
|
|
|
from datetime import datetime
|
2025-02-17 18:18:14 +02:00
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
# Загружаем переменные окружения
|
|
|
|
|
load_dotenv()
|
|
|
|
|
|
|
|
|
|
# Правильная строка подключения
|
|
|
|
|
DATABASE_URL = os.getenv('DATABASE_URL') # Должно быть "mysql+aiomysql://..." или "postgresql+asyncpg://..."
|
|
|
|
|
|
|
|
|
|
# Создаём асинхронный движок
|
|
|
|
|
async_engine = create_async_engine(DATABASE_URL, echo=True)
|
|
|
|
|
|
|
|
|
|
# Создаём фабрику сессий
|
|
|
|
|
async_session = sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False)
|
|
|
|
|
|
|
|
|
|
# Базовый класс для моделей
|
|
|
|
|
Base = declarative_base()
|
|
|
|
|
|
|
|
|
|
# Контекст хеширования паролей
|
|
|
|
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
|
|
|
|
|
|
|
|
# Функция для инициализации базы данных
|
|
|
|
|
async def init_db():
|
|
|
|
|
async with async_engine.begin() as conn:
|
|
|
|
|
await conn.run_sync(Base.metadata.create_all)
|
|
|
|
|
async with async_session() as session:
|
|
|
|
|
# Проверяем, есть ли уже данные в таблице пользователей
|
|
|
|
|
result = await session.execute(select(User).limit(1))
|
|
|
|
|
user_exists = result.scalars().first()
|
|
|
|
|
|
|
|
|
|
if not user_exists:
|
|
|
|
|
new_user = User(
|
|
|
|
|
username="admin",
|
|
|
|
|
hashed_password=hash_password("admin123"), # Пароль хешируем
|
|
|
|
|
role="admin",
|
|
|
|
|
is_active=True
|
|
|
|
|
)
|
|
|
|
|
session.add(new_user)
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
|
|
# Зависимость FastAPI для работы с БД
|
|
|
|
|
async def get_async_session() -> AsyncSession:
|
|
|
|
|
async with async_session() as session:
|
|
|
|
|
yield session
|
|
|
|
|
|
|
|
|
|
# Функции для хеширования паролей
|
|
|
|
|
def hash_password(password: str) -> str:
|
|
|
|
|
return pwd_context.hash(password)
|
|
|
|
|
|
|
|
|
|
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|
|
|
|
return pwd_context.verify(plain_password, hashed_password)
|
|
|
|
|
|
|
|
|
|
# async def get_db():
|
|
|
|
|
# async with SessionLocal() as session:
|
|
|
|
|
# yield session
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Модель пользователя
|
|
|
|
|
class User(Base):
|
|
|
|
|
__tablename__ = "users"
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
|
|
|
username = Column(String(55), unique=True, index=True, nullable=False) # ✅ Правильно
|
|
|
|
|
hashed_password = Column(String(255), nullable=False)
|
|
|
|
|
role = Column(String(55), default="user")
|
|
|
|
|
is_active = Column(Boolean, default=True)
|
2025-02-17 21:01:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
applications = relationship("AppliedJob", back_populates="users")
|
2025-02-17 18:18:14 +02:00
|
|
|
|
|
|
|
|
|
# Модель профиля
|
|
|
|
|
class Profile(Base):
|
|
|
|
|
__tablename__ = "profile"
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
|
|
|
link = Column(String(255), unique=True, nullable=True)
|
|
|
|
|
first_name = Column(String(255), nullable=False)
|
|
|
|
|
last_name = Column(String(255), nullable=False)
|
|
|
|
|
pronouns = Column(String(50), nullable=True)
|
|
|
|
|
email = Column(String(255), nullable=True)
|
|
|
|
|
telephone = Column(String(20), nullable=True)
|
|
|
|
|
location = Column(String(255), nullable=True)
|
|
|
|
|
statement = Column(Text, nullable=True)
|
|
|
|
|
skills = Column(JSON, nullable=True) # JSON-поле для списка навыков
|
|
|
|
|
websites = Column(JSON, nullable=True) # JSON-поле для списка сайтов
|
|
|
|
|
languages = Column(JSON, nullable=True) # JSON-поле для языков
|
|
|
|
|
date = Column(DateTime, server_default=func.now())
|
|
|
|
|
|
|
|
|
|
# Модель вакансий
|
|
|
|
|
class Job(Base):
|
|
|
|
|
__tablename__ = "jobs"
|
|
|
|
|
|
|
|
|
|
job_id = Column(BigInteger, primary_key=True, index=True)
|
|
|
|
|
job_title = Column(String(255), nullable=False)
|
|
|
|
|
job_company = Column(String(255), nullable=True)
|
|
|
|
|
minimum_annual_salary = Column(Float, nullable=True)
|
|
|
|
|
salary_currency = Column(String(10), nullable=True)
|
|
|
|
|
location_type = Column(String(50), nullable=True) # Remote, On-site, Hybrid
|
|
|
|
|
location = Column(String(255), nullable=True)
|
|
|
|
|
job_level = Column(String(50), nullable=True) # Entry-level, Junior, Mid
|
|
|
|
|
job_type = Column(String(50), nullable=True) # Full-time, Part-time
|
|
|
|
|
days_posted = Column(Integer, nullable=True)
|
|
|
|
|
hourly_rate = Column(Float, nullable=True)
|
|
|
|
|
link = Column(String(2083), nullable=True) # URL вакансии
|
|
|
|
|
link_company = Column(String(2083), nullable=True) # URL компании
|
|
|
|
|
active = Column(Boolean, default=True) # Вакансия активна?
|
2025-02-17 21:01:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Связь с AppliedJobs
|
|
|
|
|
applications = relationship("AppliedJob", back_populates="job")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Client(Base):
|
|
|
|
|
__tablename__ = "client"
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
|
|
|
username = Column(String(55), unique=True, index=True, nullable=False) # ✅ Правильно
|
|
|
|
|
phone = Column(String(55), unique=True, index=True, nullable=True) # ✅ Правильно
|
|
|
|
|
email = Column(String(55), unique=True, index=True, nullable=True) # ✅ Правильно
|
|
|
|
|
link = Column(String(2083), nullable=True) # URL вакансии
|
|
|
|
|
is_active = Column(Boolean, default=True)
|
|
|
|
|
|
|
|
|
|
# Связь с AppliedJobs (заявки на вакансии)
|
|
|
|
|
applications = relationship("AppliedJob", back_populates="client")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AppliedJob(Base):
|
|
|
|
|
__tablename__ = "applied_jobs"
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
|
|
|
client_id = Column(Integer, ForeignKey("client.id"), nullable=False)
|
|
|
|
|
job_id = Column(BigInteger, ForeignKey("jobs.job_id"), nullable=False)
|
|
|
|
|
applied_on = Column(DateTime, nullable=True) # ✅ Теперь может быть NULL
|
|
|
|
|
status = Column(String(50), nullable=True) # ✅ Может быть NULL
|
|
|
|
|
assignee = Column(Integer, ForeignKey("users.id"), nullable=True) # ✅ Может быть NULL
|
|
|
|
|
priority = Column(String(50), nullable=True) # ✅ Может быть NULL
|
|
|
|
|
|
|
|
|
|
client = relationship("Client", back_populates="applications")
|
|
|
|
|
job = relationship("Job", back_populates="applications")
|
|
|
|
|
users = relationship("User", back_populates="applications")
|
|
|
|
|
|