feat: Add job blacklist (hide companies/roles you don't want to see)
Browse files
backend/app/api/routes/blacklist.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Job blacklist - hide specific companies or role keywords from search results.
|
| 3 |
+
"""
|
| 4 |
+
import uuid
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
|
| 7 |
+
from fastapi import APIRouter, HTTPException, status
|
| 8 |
+
from pydantic import BaseModel
|
| 9 |
+
from sqlalchemy import DateTime, String, func, select
|
| 10 |
+
from sqlalchemy.dialects.postgresql import UUID as PGUUID
|
| 11 |
+
from sqlalchemy.orm import Mapped, mapped_column
|
| 12 |
+
|
| 13 |
+
from app.api.deps import CurrentUser, DBSession
|
| 14 |
+
from app.core.database import Base
|
| 15 |
+
|
| 16 |
+
router = APIRouter(prefix="/blacklist", tags=["blacklist"])
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class BlacklistEntry(Base):
|
| 20 |
+
"""Companies or keywords user wants to hide from results."""
|
| 21 |
+
__tablename__ = "blacklist_entries"
|
| 22 |
+
|
| 23 |
+
id: Mapped[uuid.UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
| 24 |
+
user_id: Mapped[uuid.UUID] = mapped_column(PGUUID(as_uuid=True), nullable=False, index=True)
|
| 25 |
+
entry_type: Mapped[str] = mapped_column(String(20), nullable=False) # company, keyword, domain
|
| 26 |
+
value: Mapped[str] = mapped_column(String(256), nullable=False)
|
| 27 |
+
reason: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
| 28 |
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class BlacklistCreate(BaseModel):
|
| 32 |
+
entry_type: str # company, keyword, domain
|
| 33 |
+
value: str
|
| 34 |
+
reason: str | None = None
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class BlacklistResponse(BaseModel):
|
| 38 |
+
id: str
|
| 39 |
+
entry_type: str
|
| 40 |
+
value: str
|
| 41 |
+
reason: str | None
|
| 42 |
+
created_at: datetime
|
| 43 |
+
|
| 44 |
+
class Config:
|
| 45 |
+
from_attributes = True
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@router.get("", response_model=list[BlacklistResponse])
|
| 49 |
+
async def list_blacklist(db: DBSession, user: CurrentUser):
|
| 50 |
+
"""Get user's blacklist entries."""
|
| 51 |
+
result = await db.execute(
|
| 52 |
+
select(BlacklistEntry).where(BlacklistEntry.user_id == user.id).order_by(BlacklistEntry.created_at.desc())
|
| 53 |
+
)
|
| 54 |
+
return [
|
| 55 |
+
BlacklistResponse(id=str(e.id), entry_type=e.entry_type, value=e.value, reason=e.reason, created_at=e.created_at)
|
| 56 |
+
for e in result.scalars().all()
|
| 57 |
+
]
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
@router.post("", response_model=BlacklistResponse, status_code=status.HTTP_201_CREATED)
|
| 61 |
+
async def add_to_blacklist(data: BlacklistCreate, db: DBSession, user: CurrentUser):
|
| 62 |
+
"""Add a company, keyword, or domain to blacklist."""
|
| 63 |
+
if data.entry_type not in ("company", "keyword", "domain"):
|
| 64 |
+
raise HTTPException(status_code=400, detail="entry_type must be: company, keyword, or domain")
|
| 65 |
+
|
| 66 |
+
entry = BlacklistEntry(
|
| 67 |
+
user_id=user.id, entry_type=data.entry_type, value=data.value.strip(), reason=data.reason
|
| 68 |
+
)
|
| 69 |
+
db.add(entry)
|
| 70 |
+
await db.flush()
|
| 71 |
+
await db.refresh(entry)
|
| 72 |
+
return BlacklistResponse(id=str(entry.id), entry_type=entry.entry_type, value=entry.value, reason=entry.reason, created_at=entry.created_at)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
@router.delete("/{entry_id}", status_code=status.HTTP_204_NO_CONTENT)
|
| 76 |
+
async def remove_from_blacklist(entry_id: uuid.UUID, db: DBSession, user: CurrentUser):
|
| 77 |
+
"""Remove an entry from blacklist."""
|
| 78 |
+
result = await db.execute(
|
| 79 |
+
select(BlacklistEntry).where(BlacklistEntry.id == entry_id, BlacklistEntry.user_id == user.id)
|
| 80 |
+
)
|
| 81 |
+
entry = result.scalar_one_or_none()
|
| 82 |
+
if not entry:
|
| 83 |
+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Entry not found")
|
| 84 |
+
await db.delete(entry)
|