File size: 2,293 Bytes
34b0226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from datetime import UTC, datetime, timedelta

from fastapi import APIRouter, Depends
from sqlalchemy import select

from app.auth import Ctx, get_ctx
from app.models import Document, Source
from app.routers.sources import serialize_document
from app.services.jobs import create_job, serialize_job

router = APIRouter(prefix="/api/resurface", tags=["resurface"])

COOLDOWN = timedelta(days=90)


def _aware(dt: datetime | None) -> datetime | None:
    return dt if dt is None or dt.tzinfo else dt.replace(tzinfo=UTC)


@router.get("")
def suggestions(ctx: Ctx = Depends(get_ctx), limit: int = 20):
    now = datetime.now(UTC)
    rows = ctx.db.execute(
        select(Document, Source).outerjoin(Source, Source.id == Document.source_id)
        .where(Document.workspace_id == ctx.workspace.id, Document.path.is_not(None))
    ).all()
    evergreen, on_this_day = [], []
    unscored = 0
    for d, s in rows:
        published = _aware(d.published_at)
        resurfaced = _aware(d.last_resurfaced_at)
        meta = d.metadata_json or {}
        item = {**serialize_document(d, s), "reason": meta.get("evergreen_reason"), "angle": meta.get("reshare_angle"),
                "last_resurfaced_at": resurfaced.isoformat() if resurfaced else None}
        if published and published.month == now.month and published.day == now.day and published.year < now.year:
            on_this_day.append({**item, "years_ago": now.year - published.year})
        if d.evergreen_score is None:
            unscored += 1
            continue
        if resurfaced and now - resurfaced < COOLDOWN:
            continue
        age_days = (now - published).days if published else 365
        # Old, timeless and not recently reshared ranks highest; brand new posts do not need resurfacing.
        age_factor = min(age_days / 365, 2.0) if age_days > 30 else 0.1
        evergreen.append({**item, "rank": round(d.evergreen_score * (0.5 + age_factor / 2), 3)})
    evergreen.sort(key=lambda x: -x["rank"])
    return {"evergreen": evergreen[: min(limit, 100)], "on_this_day": on_this_day, "unscored": unscored,
            "total_posts": len(rows)}


@router.post("/scan")
def scan(ctx: Ctx = Depends(get_ctx)):
    return serialize_job(create_job(ctx.db, ctx.workspace.id, "resurface_scan", {}, max_attempts=2))