feat: Smart Weekly Digest with AI Narrative (#121)#463
Open
qiridigital wants to merge 1 commit intorohitdash08:mainfrom
Open
feat: Smart Weekly Digest with AI Narrative (#121)#463qiridigital wants to merge 1 commit intorohitdash08:mainfrom
qiridigital wants to merge 1 commit intorohitdash08:mainfrom
Conversation
- Backend: services/digest.py computes weekly income, expenses, net flow, category breakdown, top expense, upcoming bills, and week-over-week change - Backend: outes/digest.py exposes GET /digest/weekly and GET /digest/weekly/history with JWT auth and Redis caching - Backend: Gemini-powered narrative generation with heuristic fallback - Frontend: �pi/digest.ts typed API client - Frontend: pages/Digest.tsx full dashboard with summary cards, AI insights, category breakdown, upcoming bills, and weekly history - Tests: est_digest.py covering empty state, seeded data, auth, validation, week-over-week, history, and caching - Nav: added Digest link in Navbar Closes rohitdash08#121
There was a problem hiding this comment.
Pull request overview
Adds a new “Smart Weekly Digest” feature spanning backend and frontend, providing weekly financial summaries (with optional Gemini-generated narrative) and a new UI page to view current + recent weekly digests.
Changes:
- Backend: introduce digest computation service +
/digest/weeklyand/digest/weekly/historyendpoints with Redis caching and AI narrative generation. - Frontend: add
/digestprotected route, digest API client, and a full digest dashboard page + navbar link. - Tests: add backend endpoint tests for empty state, seeded data, auth, history, and caching.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/backend/tests/test_digest.py | Adds test coverage for weekly digest endpoints and caching. |
| packages/backend/app/services/digest.py | Implements weekly digest aggregation + Gemini narrative generation. |
| packages/backend/app/routes/digest.py | Adds weekly digest + history endpoints (JWT-protected) and caching. |
| packages/backend/app/routes/init.py | Registers the digest blueprint under /digest. |
| app/src/pages/Digest.tsx | New digest dashboard page (summary cards, narrative, breakdowns, history, week picker). |
| app/src/components/layout/Navbar.tsx | Adds “Digest” nav link. |
| app/src/api/digest.ts | Adds typed client for weekly digest + history endpoints. |
| app/src/App.tsx | Adds protected /digest route. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+1
to
+8
| """Weekly digest endpoints.""" | ||
|
|
||
| from datetime import date | ||
| from flask import Blueprint, jsonify, request | ||
| from flask_jwt_extended import jwt_required, get_jwt_identity | ||
|
|
||
| from ..services.digest import compute_digest, generate_narrative | ||
| from ..services.cache import cache_get, cache_set |
| ) : ( | ||
| <div className="space-y-2"> | ||
| {digest.upcoming_bills.map((b) => ( | ||
| <div key={b.name} className="flex justify-between text-sm"> |
| @@ -0,0 +1,147 @@ | |||
| """Tests for the weekly digest endpoints.""" | |||
|
|
|||
| from datetime import date, timedelta | |||
Comment on lines
+22
to
+23
| return 2000 <= year <= 2100 and 1 <= week <= 53 | ||
| except Exception: |
| def digest_history(): | ||
| """Return digests for the last *n* weeks (default 4).""" | ||
| uid = int(get_jwt_identity()) | ||
| count = min(int(request.args.get("count", 4)), 12) |
Comment on lines
+68
to
+83
| Category.name, | ||
| func.coalesce(func.sum(Expense.amount), 0), | ||
| ) | ||
| .outerjoin(Category, Expense.category_id == Category.id) | ||
| .filter( | ||
| Expense.user_id == user_id, | ||
| Expense.spent_at >= monday, | ||
| Expense.spent_at <= sunday, | ||
| Expense.expense_type != "INCOME", | ||
| ) | ||
| .group_by(Category.name) | ||
| .all() | ||
| ) | ||
| category_breakdown = [ | ||
| {"category": name or "Uncategorized", "amount": round(float(amt), 2)} | ||
| for name, amt in sorted(cat_rows, key=lambda r: float(r[1]), reverse=True) |
Comment on lines
+198
to
+208
| with url_request.urlopen(req, timeout=15) as resp: | ||
| data = json.loads(resp.read()) | ||
| return ( | ||
| data.get("candidates", [{}])[0] | ||
| .get("content", {}) | ||
| .get("parts", [{}])[0] | ||
| .get("text", _fallback_narrative(digest)) | ||
| ) | ||
| except Exception: | ||
| logger.warning("Gemini digest narrative failed, using fallback") | ||
| return _fallback_narrative(digest) |
Comment on lines
+18
to
+21
| const jan4 = new Date(now.getFullYear(), 0, 4); | ||
| const daysSinceJan4 = Math.floor((now.getTime() - jan4.getTime()) / 86400000); | ||
| const weekNum = Math.ceil((daysSinceJan4 + jan4.getDay() + 1) / 7); | ||
| return `${now.getFullYear()}-W${String(weekNum).padStart(2, '0')}`; |
Comment on lines
+24
to
+46
| export function Digest() { | ||
| const { toast } = useToast(); | ||
| const [week, setWeek] = useState(currentISOWeek); | ||
| const [digest, setDigest] = useState<WeeklyDigest | null>(null); | ||
| const [history, setHistory] = useState<WeeklyDigest[]>([]); | ||
| const [loading, setLoading] = useState(true); | ||
|
|
||
| async function load() { | ||
| setLoading(true); | ||
| try { | ||
| const [d, h] = await Promise.all([ | ||
| getWeeklyDigest(week), | ||
| getDigestHistory(4), | ||
| ]); | ||
| setDigest(d); | ||
| setHistory(h); | ||
| } catch (err: unknown) { | ||
| const msg = err instanceof Error ? err.message : 'Failed to load digest'; | ||
| toast({ title: 'Error', description: msg }); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| } |
Comment on lines
+63
to
+70
| year_s, week_s = week.split("-W") | ||
| y, w = int(year_s), int(week_s) | ||
| if w == 1: | ||
| y -= 1 | ||
| w = 52 | ||
| else: | ||
| w -= 1 | ||
| week = f"{y}-W{w:02d}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the Smart Weekly Digest feature as described in #121.
Backend
outes/digest.py** – Two endpoints:
Frontend
Tests
Tech Stack Alignment
/claim #121