117 lines
4.0 KiB
Python
117 lines
4.0 KiB
Python
import uuid
|
|
from datetime import datetime, timezone
|
|
from typing import TYPE_CHECKING, Optional
|
|
|
|
from sqlalchemy import DateTime, JSON
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlmodel import Field, Relationship, SQLModel, select
|
|
|
|
if TYPE_CHECKING: # runtime import would be circular: users.models imports this module
|
|
from users.models import Users
|
|
|
|
|
|
def _now() -> datetime:
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
class JobPosts(SQLModel, table=True):
|
|
__tablename__ = "job_posts"
|
|
|
|
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
title: str = Field(index=True)
|
|
|
|
user: Optional["Users"] = Relationship(
|
|
back_populates="job_posts",
|
|
sa_relationship_kwargs={"lazy": "joined"},
|
|
)
|
|
|
|
platform: str = Field(default="linkedin")
|
|
is_active: bool = Field(default=True)
|
|
is_deleted: bool = Field(default=False)
|
|
employment_type: str | None = Field(default=None)
|
|
location: str | None = Field(default=None)
|
|
experience_min: int | None = Field(default=None)
|
|
experience_max: int | None = Field(default=None)
|
|
requirements: list[str] = Field(default_factory=list, sa_type=JSON)
|
|
optional_skills: list[str] = Field(default_factory=list, sa_type=JSON)
|
|
salary: str = Field(default="Anonymous")
|
|
description: str | None = Field(default=None)
|
|
post_text: str
|
|
channel_id: str
|
|
buffer_post_id: str | None = Field(default=None)
|
|
buffer_external_link: str | None = Field(default=None)
|
|
buffer_sent_at: datetime | None = Field(default=None, sa_type=DateTime(timezone=True))
|
|
status: str = Field(default="draft")
|
|
buffer_error: str | None = Field(default=None)
|
|
created_by: uuid.UUID = Field(foreign_key="users.id")
|
|
created_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
|
updated_at: datetime = Field(default_factory=_now, sa_type=DateTime(timezone=True))
|
|
|
|
@staticmethod
|
|
def _as_uuid(record_id: str) -> uuid.UUID | None:
|
|
try:
|
|
return uuid.UUID(str(record_id))
|
|
except ValueError:
|
|
return None
|
|
|
|
@classmethod
|
|
async def get_job_post_by_id(cls, session: AsyncSession, record_id: str):
|
|
uid = cls._as_uuid(record_id)
|
|
if uid is None:
|
|
return None
|
|
result = await session.execute(select(cls).where(cls.id == uid))
|
|
return result.scalars().first()
|
|
|
|
@classmethod
|
|
async def insert_job_post(cls, session: AsyncSession, fields: dict):
|
|
row = cls(**fields)
|
|
session.add(row)
|
|
await session.commit()
|
|
return await cls.get_job_post_by_id(session, row.id)
|
|
|
|
@classmethod
|
|
async def mark_buffer_result(
|
|
cls,
|
|
session: AsyncSession,
|
|
record_id: str,
|
|
*,
|
|
buffer_post_id: str,
|
|
status: str,
|
|
external_link: str | None = None,
|
|
sent_at: datetime | None = None,
|
|
platform: str | None = None,
|
|
):
|
|
"""Record what Buffer reported.
|
|
`status` is the mapped Buffer PostStatus, not an assumption: a queued post lands
|
|
here as "scheduled" and only becomes "published" once Buffer says `sent`.
|
|
"""
|
|
row = await cls.get_job_post_by_id(session, record_id)
|
|
if not row:
|
|
return None
|
|
row.status = status
|
|
row.buffer_post_id = buffer_post_id
|
|
row.buffer_external_link = external_link
|
|
row.buffer_sent_at = sent_at
|
|
if platform:
|
|
row.platform = platform
|
|
row.buffer_error = None
|
|
row.updated_at = _now()
|
|
session.add(row)
|
|
await session.commit()
|
|
await session.refresh(row)
|
|
return row
|
|
|
|
@classmethod
|
|
async def mark_failed(cls, session: AsyncSession, record_id: str, error: str):
|
|
row = await cls.get_job_post_by_id(session, record_id)
|
|
if not row:
|
|
return None
|
|
row.status = "failed"
|
|
row.buffer_error = error
|
|
row.updated_at = _now()
|
|
session.add(row)
|
|
await session.commit()
|
|
await session.refresh(row)
|
|
return row
|
|
|
|
import users.models as _users_models |