94 lines
3.0 KiB
Python
94 lines
3.0 KiB
Python
from __future__ import annotations
|
|
|
|
from .shared import *
|
|
from .helpers import _as_dict, _get_project_or_404
|
|
from .schemas import LinkSignalRequest
|
|
|
|
@router.post("/{project_id}/signals/{signal_id}")
|
|
async def link_signal_to_project(
|
|
project_id: int,
|
|
signal_id: int,
|
|
body: LinkSignalRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(require_roles("BOSS", "ADMIN")),
|
|
):
|
|
tenant_id = int(current_user.tenant_id)
|
|
await _get_project_or_404(db, tenant_id=tenant_id, project_id=project_id)
|
|
|
|
signal = (
|
|
await db.execute(
|
|
select(Signal).where(
|
|
and_(
|
|
Signal.tenant_id == tenant_id,
|
|
Signal.id == signal_id,
|
|
)
|
|
)
|
|
)
|
|
).scalar_one_or_none()
|
|
if not signal:
|
|
raise HTTPException(status_code=404, detail="Signal not found")
|
|
|
|
existing = (
|
|
await db.execute(
|
|
select(ProjectSignalLink).where(
|
|
and_(
|
|
ProjectSignalLink.tenant_id == tenant_id,
|
|
ProjectSignalLink.project_id == project_id,
|
|
ProjectSignalLink.signal_id == signal_id,
|
|
)
|
|
)
|
|
)
|
|
).scalar_one_or_none()
|
|
if existing:
|
|
return {
|
|
"id": str(existing.id),
|
|
"project_id": str(existing.project_id),
|
|
"signal_id": str(existing.signal_id),
|
|
"link_type": existing.link_type,
|
|
"relevance_score": float(existing.relevance_score or 0.0),
|
|
"created_at": existing.created_at.isoformat() if existing.created_at else None,
|
|
}
|
|
|
|
link = ProjectSignalLink(
|
|
tenant_id=tenant_id,
|
|
project_id=project_id,
|
|
signal_id=signal_id,
|
|
link_type=body.link_type or "related",
|
|
relevance_score=float(body.relevance_score or 1.0),
|
|
)
|
|
db.add(link)
|
|
await db.commit()
|
|
await db.refresh(link)
|
|
await context_cache_service.invalidate_system_context(tenant_id=tenant_id)
|
|
return {
|
|
"id": str(link.id),
|
|
"project_id": str(link.project_id),
|
|
"signal_id": str(link.signal_id),
|
|
"link_type": link.link_type,
|
|
"relevance_score": float(link.relevance_score or 0.0),
|
|
"created_at": link.created_at.isoformat() if link.created_at else None,
|
|
}
|
|
|
|
@router.delete("/{project_id}/signals/{signal_id}")
|
|
async def unlink_signal_from_project(
|
|
project_id: int,
|
|
signal_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(require_roles("BOSS", "ADMIN")),
|
|
):
|
|
tenant_id = int(current_user.tenant_id)
|
|
_ = current_user
|
|
await db.execute(
|
|
delete(ProjectSignalLink).where(
|
|
and_(
|
|
ProjectSignalLink.tenant_id == tenant_id,
|
|
ProjectSignalLink.project_id == project_id,
|
|
ProjectSignalLink.signal_id == signal_id,
|
|
)
|
|
)
|
|
)
|
|
await db.commit()
|
|
await context_cache_service.invalidate_system_context(tenant_id=tenant_id)
|
|
return {"message": "Signal unlinked"}
|
|
|