anky2002 commited on
Commit
51747d4
·
verified ·
1 Parent(s): 5ca8a69

feat: Add daily job digest with personalized AI summary

Browse files
Files changed (1) hide show
  1. backend/app/api/routes/digest.py +107 -0
backend/app/api/routes/digest.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Daily job digest - personalized AI-summarized digest of new relevant jobs.
3
+ """
4
+ import json
5
+ import logging
6
+ from datetime import datetime, timedelta, timezone
7
+
8
+ import litellm
9
+ from fastapi import APIRouter
10
+ from sqlalchemy import and_, or_, select
11
+
12
+ from app.api.deps import CurrentUser, DBSession
13
+ from app.core.config import settings
14
+ from app.models.job import Job, JobStatus
15
+ from app.models.user import Profile
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ router = APIRouter(prefix="/digest", tags=["digest"])
20
+
21
+
22
+ @router.get("/today")
23
+ async def get_daily_digest(db: DBSession, user: CurrentUser):
24
+ """
25
+ Generate a personalized daily digest of new relevant jobs.
26
+ Uses profile skills and preferences to filter and rank.
27
+ """
28
+ # Get profile
29
+ profile_result = await db.execute(select(Profile).where(Profile.user_id == user.id))
30
+ profile = profile_result.scalar_one_or_none()
31
+
32
+ # Get jobs from last 24 hours
33
+ since = datetime.now(timezone.utc) - timedelta(hours=24)
34
+
35
+ query = select(Job).where(
36
+ and_(Job.date_seen_first >= since, Job.status == JobStatus.ACTIVE)
37
+ ).order_by(Job.date_posted.desc().nullslast()).limit(50)
38
+
39
+ # If user has skills, prioritize matching jobs
40
+ if profile and profile.skills:
41
+ skill_names = []
42
+ for s in profile.skills:
43
+ if isinstance(s, dict):
44
+ skill_names.append(s.get("name", "").lower())
45
+ elif isinstance(s, str):
46
+ skill_names.append(s.lower())
47
+
48
+ if skill_names:
49
+ # Try to find jobs matching skills
50
+ skill_conditions = [Job.title.ilike(f"%{s}%") for s in skill_names[:5]]
51
+ query = select(Job).where(
52
+ and_(
53
+ Job.date_seen_first >= since,
54
+ Job.status == JobStatus.ACTIVE,
55
+ or_(*skill_conditions) if skill_conditions else True,
56
+ )
57
+ ).order_by(Job.date_posted.desc().nullslast()).limit(30)
58
+
59
+ result = await db.execute(query)
60
+ jobs = result.scalars().all()
61
+
62
+ # Format jobs
63
+ digest_jobs = []
64
+ for job in jobs[:20]:
65
+ digest_jobs.append({
66
+ "id": str(job.id),
67
+ "title": job.title,
68
+ "company": job.company_name,
69
+ "location": job.locations[0]["raw"] if job.locations else None,
70
+ "remote": job.remote_type,
71
+ "salary_min": job.salary_min,
72
+ "salary_max": job.salary_max,
73
+ "posted": job.date_posted.isoformat() if job.date_posted else None,
74
+ "url": f"/jobs/{job.id}",
75
+ })
76
+
77
+ # Generate AI summary if we have enough jobs
78
+ ai_summary = None
79
+ if len(digest_jobs) >= 3 and (settings.OPENAI_API_KEY or settings.DEEPSEEK_API_KEY or settings.OLLAMA_BASE_URL):
80
+ try:
81
+ model = "ollama/llama3.2"
82
+ if settings.OPENAI_API_KEY:
83
+ model = "openai/gpt-4o-mini"
84
+ elif settings.DEEPSEEK_API_KEY:
85
+ model = "deepseek/deepseek-chat"
86
+
87
+ jobs_text = "\n".join([f"- {j['title']} at {j['company']}" for j in digest_jobs[:10]])
88
+ response = await litellm.acompletion(
89
+ model=model,
90
+ messages=[
91
+ {"role": "system", "content": "You're a career advisor. Write a 2-3 sentence summary of today's job market activity based on new postings. Be encouraging and highlight interesting opportunities. Keep it under 60 words."},
92
+ {"role": "user", "content": f"Today's new jobs ({len(digest_jobs)} total):\n{jobs_text}"},
93
+ ],
94
+ temperature=0.7,
95
+ max_tokens=100,
96
+ )
97
+ ai_summary = response.choices[0].message.content.strip()
98
+ except Exception as e:
99
+ logger.warning(f"Digest AI summary failed: {e}")
100
+
101
+ return {
102
+ "date": datetime.now(timezone.utc).strftime("%B %d, %Y"),
103
+ "total_new_jobs": len(digest_jobs),
104
+ "ai_summary": ai_summary,
105
+ "jobs": digest_jobs,
106
+ "personalized": bool(profile and profile.skills),
107
+ }