Skip to content

feat: enhance student dashboard with points, badges, and activity chart#1022

Open
ayesha1145 wants to merge 1 commit intoalphaonelabs:mainfrom
ayesha1145:feat/student-dashboard-points-badges
Open

feat: enhance student dashboard with points, badges, and activity chart#1022
ayesha1145 wants to merge 1 commit intoalphaonelabs:mainfrom
ayesha1145:feat/student-dashboard-points-badges

Conversation

@ayesha1145
Copy link

@ayesha1145 ayesha1145 commented Mar 12, 2026

Enhances the existing student dashboard with points tracking, badge display, and a 30-day activity chart.

Changes:

  • web/views.py: added Points import, total_points aggregation, 30-day points chart data, recent badges query
  • web/templates/dashboard/student.html: added Total Points and Badges Earned stat cards, Chart.js bar chart for points over last 30 days, Recent Badges grid section

Features:

  • Total Points stat card showing all-time points earned
  • Badges Earned stat card showing total badge count
  • Bar chart (Chart.js) showing points earned per day over the last 30 days
  • Recent Badges section showing latest 6 badges with icon, name, and date

Enhanced Student Dashboard with Points, Badges, and 30‑Day Activity Chart

This PR adds gamification and recent-activity visualization to the student dashboard: total points, badge counts, a recent badges grid, and a 30‑day points bar chart.

Key Changes

Backend (web/views.py)

  • Imports: adds Points model and TruncDate.
  • student_dashboard view:
    • Computes total_points (sum of Points.amount, defaults to 0).
    • Builds 30‑day labels and daily summed points (chart_labels, chart_data) using a datetime-range filter (start_datetime) plus TruncDate for per-day aggregation to preserve index efficiency.
    • Fetches up to 6 recent_badges and computes total_badges.
  • Exposes new context keys: total_points, chart_labels, chart_data, recent_badges, total_badges.

Frontend (web/templates/dashboard/student.html)

  • Adds two stat cards: "Total Points" and "Badges Earned" with icons and dark-text styling.
  • Adds a Chart.js bar chart for points per day over the last 30 days; chart data embedded via Django’s json_script. Canvas includes role and aria-label for accessibility. Chart.js CDN tag includes an SRI integrity hash.
  • Adds a "Recent Badges" grid showing up to 6 badges with image (ImageField) fallback icon, visible badge name and award date; <img> uses alt="" to avoid duplicate announcements (name shown as text).
  • Layout/UI: chart and badges are inside the main container; color adjusted to the project yellow palette.
  • Security/accessibility: uses json_script (avoids |safe), adds SRI, and improves canvas accessibility attributes.

Impact / Notes for Review

  • Functionality: improves visibility of student progress and engagement with points and badges.
  • Performance: date aggregation uses a datetime-range filter + TruncDate to remain index-friendly; verify timezone handling meets app expectations (start_datetime uses timezone-aware construction).
  • Security/Policy: Chart.js loaded from CDN with SRI — confirm CDN usage aligns with project policy.
  • Tests: test coverage for new context keys and template rendering not included in this PR and should be considered in follow-ups.

@github-actions
Copy link
Contributor

👀 Peer Review Required

Hi @ayesha1145! This pull request does not yet have a peer review.

Before this PR can be merged, please request a review from one of your peers:

  • Go to the PR page and click "Reviewers" on the right sidebar.
  • Select a team member or contributor to review your changes.
  • Once they approve, this reminder will be automatically removed.

Thank you for contributing! 🎉

@github-actions github-actions bot added the files-changed: 2 PR changes 2 files label Mar 12, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'tools'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

Backend aggregates user Points and Badges and exposes total_points, chart_labels, chart_data, recent_badges, and total_badges. Frontend adds Total Points and Badges Earned cards, a 30-day Points bar chart (Chart.js), and a Recent Badges grid. The chart+badges block is duplicated in the template.

Changes

Cohort / File(s) Summary
Student Dashboard Backend
web/views.py
Added Points and TruncDate imports. student_dashboard computes total_points, aggregates Points by day for the last 30 days into chart_labels/chart_data, and fetches recent_badges (limit 6) and total_badges, exposing these context keys.
Student Dashboard Frontend
web/templates/dashboard/student.html
Added UI cards for Total Points and Badges Earned, injected chart_labels/chart_data via json_script, included Chart.js and a bar chart init script for a 30-day chart, and added a Recent Badges grid. Note: the chart+badges block appears twice (duplicate canvases/scripts).

Sequence Diagram

sequenceDiagram
    participant Client
    participant DjangoView as Django View
    participant DB as Database
    participant Template
    participant ChartJS as Chart.js

    Client->>DjangoView: GET /student_dashboard
    DjangoView->>DB: Query Points (user), aggregate by date (last 30 days) using TruncDate
    DB-->>DjangoView: Points per day
    DjangoView->>DB: Query UserBadge (user, latest 6) and total badges
    DB-->>DjangoView: Recent badges + totals
    DjangoView->>Template: Render with context (total_points, chart_labels, chart_data, recent_badges, total_badges)
    Template->>ChartJS: Provide labels/data and initialize bar chart in browser
    ChartJS-->>Client: Render chart
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding points tracking, badge display, and a 30-day activity chart to the student dashboard.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/templates/dashboard/student.html`:
- Around line 55-75: The new stat cards use out-of-palette purple utilities and
lack dark-mode text classes: update the badge card and icon classes to the
project palette (e.g., replace bg-purple-100/dark:bg-purple-900 and
text-purple-* with bg-teal-100 dark:bg-teal-900 and text-teal-500
dark:text-teal-300 or other approved teal variants) and add explicit light/dark
text color classes to the stat values (the <h3> elements rendering {{
total_points }} and {{ total_badges }}) such as text-gray-900 dark:text-gray-200
(or dark:text-white) so counts remain high-contrast in dark mode; apply the same
palette/dark-mode fixes to the recent-badge section referenced in the comment.
- Around line 237-265: The new analytics blocks (the Points Chart canvas with id
"pointsChart" and the {% if recent_badges %} badges section) are currently
outside the main dashboard container, causing them to span full width; move
these blocks inside the existing container wrapper (the div using container
mx-auto px-4 py-8) by relocating these sections before the container's closing
tag or by moving the container closing tag to after the chart and badges block
so they inherit the container mx-auto px-4 (and px/y padding) classes; ensure
HTML remains well-formed (matching opening/closing divs) and that the
pointsChart and recent_badges blocks keep their markup and accessibility
attributes unchanged.
- Line 242: Add accessible attributes to the chart canvas: update the <canvas
id="pointsChart"> element to include role="img" and a descriptive aria-label
(e.g., aria-label="Student points over time chart") so screen readers can
announce it. Replace direct inlined JS data embedding (the template variables
used around lines 273 and 276) with Django's json_script filter: output each
chart data variable via json_script with unique script IDs (e.g., "points-data",
"labels-data") and then parse those script tag contents in the chart
initialization code to build the Chart.js dataset; keep referencing the canvas
by its id pointsChart when creating the chart.

In `@web/views.py`:
- Around line 2650-2661: Add a unit test for the dashboard view that verifies
the new context keys chart_labels, chart_data, recent_badges and total_badges
handle edge cases: create scenarios with (1) empty learning/activity history,
(2) multiple badge awards on the same day, and (3) a badge awarded exactly at
the 30-day cutoff; call the view (or use the test client) to retrieve the
response context and assert chart_labels/chart_data reflect empty or aggregated
data as expected, recent_badges includes the correct badges within 30 days, and
total_badges counts all awards including the cutoff one; implement tests
referencing the dashboard view function in web.views and the context keys
chart_labels, chart_data, recent_badges, total_badges so future changes to these
paths will be caught.
- Around line 2631-2644: The query uses awarded_at__date__gte which prevents
using the (user, awarded_at) index; change the filter to a datetime range using
awarded_at__gte=thirty_days_ago and awarded_at__lt=thirty_days_tomorrow (or
thirty_days_ago + timedelta(days=30)) on the Points queryset (points_qs) and
replace the values/annotate grouping that relied on awarded_at__date with
TruncDate(awarded_at) (import TruncDate from django.db.models.functions) to
produce a date key for aggregation and then build points_by_date and the
chart_labels/chart_data the same way from those truncated dates.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 85196e61-41c5-41a9-9459-c6102312b18a

📥 Commits

Reviewing files that changed from the base of the PR and between c94caf8 and 342a4ed.

📒 Files selected for processing (2)
  • web/templates/dashboard/student.html
  • web/views.py

@ayesha1145 ayesha1145 force-pushed the feat/student-dashboard-points-badges branch from 342a4ed to d6e43a9 Compare March 12, 2026 03:03
@github-actions
Copy link
Contributor

github-actions bot commented Mar 12, 2026

💬 Unresolved Review Conversations

Hi @ayesha1145! 👋

This pull request currently has 1 unresolved review conversation.

Please address all review feedback and push a new commit to resolve them before this PR can be merged.

Steps to resolve:

  1. Review each comment thread in the "Files changed" tab.
  2. Make the necessary changes to your code.
  3. Reply to each conversation to explain your changes or ask for clarification.
  4. Click "Resolve conversation" once the feedback has been addressed.
  5. Push a new commit with your changes.

Once all conversations are resolved, this notice will be removed automatically. Thank you! 🙏

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR has 4 unresolved review conversations. Please resolve them before this PR can be merged.

@ayesha1145
Copy link
Author

Addressed CodeRabbit feedback:

  • Fixed container structure — chart and badges sections are now inside the container div
  • Replaced awarded_at__date__gte with a datetime range filter + TruncDate annotation to preserve index efficiency
  • Used json_script filter instead of |safe for chart data embedding
  • Added role="img" and aria-label to the canvas element
  • Replaced purple with yellow to match project palette
  • Added dark:text-white to Total Points and Badges Earned stat values
  • Nitpick about test coverage acknowledged — out of scope for this PR

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
web/views.py (1)

2629-2646: ⚠️ Potential issue | 🟠 Major

Constrain the chart query to the actual 30-day window.

Line 2633 currently applies only a lower bound, so this query returns all points from the last 30 days and any future-dated rows. That means the dashboard is not strictly showing the requested 30-day window, and because points_by_date is keyed by "%b %d" strings, an out-of-window row with the same month/day can overwrite the in-window total. Add an upper bound (< tomorrow at 00:00) and keep the aggregation map keyed by the truncated date object until you build the labels.

♻️ Proposed fix
     # Points over last 30 days for chart
     today = timezone.now().date()
     thirty_days_ago = today - timedelta(days=29)
-    start_datetime = timezone.make_aware(timezone.datetime.combine(thirty_days_ago, timezone.datetime.min.time()))
+    start_datetime = timezone.make_aware(datetime.combine(thirty_days_ago, datetime.min.time()))
+    end_datetime = timezone.make_aware(datetime.combine(today + timedelta(days=1), datetime.min.time()))
     points_qs = (
-        Points.objects.filter(user=request.user, awarded_at__gte=start_datetime)
+        Points.objects.filter(
+            user=request.user,
+            awarded_at__gte=start_datetime,
+            awarded_at__lt=end_datetime,
+        )
         .annotate(day=TruncDate("awarded_at"))
         .values("day")
         .annotate(total=Sum("amount"))
         .order_by("day")
     )
-    points_by_date = {entry["day"].strftime("%b %d"): entry["total"] for entry in points_qs}
+    points_by_date = {entry["day"]: entry["total"] for entry in points_qs}
     chart_labels = []
     chart_data = []
     for i in range(30):
         day = thirty_days_ago + timedelta(days=i)
-        label = day.strftime("%b %d")
-        chart_labels.append(label)
-        chart_data.append(points_by_date.get(label, 0))
+        chart_labels.append(day.strftime("%b %d"))
+        chart_data.append(points_by_date.get(day, 0))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` around lines 2629 - 2646, Query currently only uses
awarded_at__gte so it includes future-dated rows and string-keying by "%b %d"
lets those overwrite in-window totals; add an upper bound to the ORM filter
(awarded_at__lt) using tomorrow at 00:00 (compute tomorrow =
timezone.now().date() + timedelta(days=1) and make_aware combine as you did for
start_datetime) and keep the aggregation map keyed by the truncated date object
(use entry["day"] as the dict key rather than entry["day"].strftime) then when
building chart_labels/chart_data format the day to "%b %d" for labels and lookup
totals from points_by_date using the date object; reference Points,
request.user, awarded_at__gte, TruncDate, points_by_date, chart_labels and
chart_data to locate changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/templates/dashboard/student.html`:
- Around line 251-261: The template currently checks ub.badge.icon which doesn't
exist on the Badge model so the fallback fa-award is always shown; update the
recent_badges block to render the Badge.image when present (use
ub.badge.image.url via an <img> tag) and only fall back to the Font Awesome icon
if no image exists, referencing the same loop and ub.badge symbols;
alternatively, if you prefer icon classes add an icon field to the Badge model
and migrate, then use ub.badge.icon in the template; also add the dark mode
class (dark:text-yellow-300) to the fallback icon to match styling.
- Line 269: Add Subresource Integrity and crossorigin attributes to the Chart.js
CDN script tag in the template (the <script
src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js"></script>
element) by copying the SRI hash for Chart.js 4.4.0 from the CDN provider and
adding integrity="sha384-..." plus crossorigin="anonymous" so the browser
verifies the file and matches the pattern used for other CDN scripts (jQuery,
Tailwind, Sentry) in your templates.

---

Duplicate comments:
In `@web/views.py`:
- Around line 2629-2646: Query currently only uses awarded_at__gte so it
includes future-dated rows and string-keying by "%b %d" lets those overwrite
in-window totals; add an upper bound to the ORM filter (awarded_at__lt) using
tomorrow at 00:00 (compute tomorrow = timezone.now().date() + timedelta(days=1)
and make_aware combine as you did for start_datetime) and keep the aggregation
map keyed by the truncated date object (use entry["day"] as the dict key rather
than entry["day"].strftime) then when building chart_labels/chart_data format
the day to "%b %d" for labels and lookup totals from points_by_date using the
date object; reference Points, request.user, awarded_at__gte, TruncDate,
points_by_date, chart_labels and chart_data to locate changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f2c9fd16-651f-4038-9c6b-4f6c0f3b72fa

📥 Commits

Reviewing files that changed from the base of the PR and between 342a4ed and d6e43a9.

📒 Files selected for processing (2)
  • web/templates/dashboard/student.html
  • web/views.py

@ayesha1145 ayesha1145 force-pushed the feat/student-dashboard-points-badges branch from d6e43a9 to 783dc9b Compare March 12, 2026 03:13
@ayesha1145
Copy link
Author

Addressed feedback:

  • Fixed badge display: template now uses ub.badge.image (ImageField) with an tag instead of a non-existent icon field — fallback fa-award icon now includes dark:text-yellow-300
  • Added SRI integrity hash to the Chart.js CDN script tag for security consistency with the rest of the project

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
web/views.py (1)

2631-2646: ⚠️ Potential issue | 🟡 Minor

Close the chart window on the upper bound and keep date keys unformatted.

Right now the queryset only applies awarded_at__gte=start_datetime, then points_by_date collapses grouped dates to "%b %d" strings. If future-dated Points rows exist from fixtures, imports, or manual corrections, a later row like 2027-02-12 can overwrite the 2026-02-12 bucket and show the wrong value in this “last 30 days” chart.

Suggested fix
     today = timezone.now().date()
     thirty_days_ago = today - timedelta(days=29)
-    start_datetime = timezone.make_aware(timezone.datetime.combine(thirty_days_ago, timezone.datetime.min.time()))
+    start_datetime = timezone.make_aware(datetime.combine(thirty_days_ago, datetime.min.time()))
+    end_datetime = timezone.make_aware(datetime.combine(today + timedelta(days=1), datetime.min.time()))
     points_qs = (
-        Points.objects.filter(user=request.user, awarded_at__gte=start_datetime)
+        Points.objects.filter(user=request.user, awarded_at__gte=start_datetime, awarded_at__lt=end_datetime)
         .annotate(day=TruncDate("awarded_at"))
         .values("day")
         .annotate(total=Sum("amount"))
         .order_by("day")
     )
-    points_by_date = {entry["day"].strftime("%b %d"): entry["total"] for entry in points_qs}
+    points_by_date = {entry["day"]: entry["total"] for entry in points_qs}
     chart_labels = []
     chart_data = []
     for i in range(30):
         day = thirty_days_ago + timedelta(days=i)
         label = day.strftime("%b %d")
         chart_labels.append(label)
-        chart_data.append(points_by_date.get(label, 0))
+        chart_data.append(points_by_date.get(day, 0))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` around lines 2631 - 2646, The query and grouping currently only
filter with awarded_at__gte and collapse dates to "%b %d" strings, which lets
future-dated rows collide; change the time filter to a closed-open range by
computing an end_datetime (e.g., end_datetime = start_datetime +
timedelta(days=30)) and use awarded_at__gte=start_datetime,
awarded_at__lt=end_datetime (or awarded_at__range with the proper end) on
Points.objects.filter(...), keep the TruncDate("awarded_at") values as date
objects in points_by_date (map entry["day"] to entry["total"] instead of
entry["day"].strftime), and only format the label strings when appending to
chart_labels/chart_data by deriving the date for each day in the 30-day window
and looking up points_by_date[day.date()] (or points_by_date[day]) to avoid
cross-year collisions; update references: start_datetime,
Points.objects.filter(...), TruncDate, points_by_date, chart_labels, chart_data.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/templates/dashboard/student.html`:
- Around line 253-258: The img alt currently duplicates the visible label ({{
ub.badge.name }}) causing screen readers to announce the badge name twice;
update the image tag for decorative images (when ub.badge.image exists) to use
an empty alt attribute (alt="") so the visible <p>{{ ub.badge.name }}</p>
remains the accessible label; locate the conditional block that references
ub.badge.image and ub.badge.name and change only the img alt value to an empty
string.

---

Duplicate comments:
In `@web/views.py`:
- Around line 2631-2646: The query and grouping currently only filter with
awarded_at__gte and collapse dates to "%b %d" strings, which lets future-dated
rows collide; change the time filter to a closed-open range by computing an
end_datetime (e.g., end_datetime = start_datetime + timedelta(days=30)) and use
awarded_at__gte=start_datetime, awarded_at__lt=end_datetime (or
awarded_at__range with the proper end) on Points.objects.filter(...), keep the
TruncDate("awarded_at") values as date objects in points_by_date (map
entry["day"] to entry["total"] instead of entry["day"].strftime), and only
format the label strings when appending to chart_labels/chart_data by deriving
the date for each day in the 30-day window and looking up
points_by_date[day.date()] (or points_by_date[day]) to avoid cross-year
collisions; update references: start_datetime, Points.objects.filter(...),
TruncDate, points_by_date, chart_labels, chart_data.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3c62090d-01cb-46eb-9874-7f1fd96d3519

📥 Commits

Reviewing files that changed from the base of the PR and between d6e43a9 and 783dc9b.

📒 Files selected for processing (2)
  • web/templates/dashboard/student.html
  • web/views.py

@ayesha1145 ayesha1145 force-pushed the feat/student-dashboard-points-badges branch from 783dc9b to d90d299 Compare March 12, 2026 03:23
@ayesha1145
Copy link
Author

Fixed: badge image now uses alt="" to avoid announcing the badge name twice for screen readers — the visible text label below carries the accessible name.

@ayesha1145
Copy link
Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
web/views.py (1)

2631-2646: ⚠️ Potential issue | 🟡 Minor

Bound the chart query to the 30-day window and keep date keys until render time.

This query only has a lower bound, while points_by_date collapses rows onto %b %d strings. If any future-dated Points row slips in, it can overwrite the in-window bucket with the same month/day label and skew the chart. Filter through the start of tomorrow and key the lookup by the truncated date object instead.

Suggested fix
     today = timezone.now().date()
     thirty_days_ago = today - timedelta(days=29)
-    start_datetime = timezone.make_aware(timezone.datetime.combine(thirty_days_ago, timezone.datetime.min.time()))
+    start_datetime = timezone.make_aware(datetime.combine(thirty_days_ago, datetime.min.time()))
+    end_datetime = timezone.make_aware(datetime.combine(today + timedelta(days=1), datetime.min.time()))
     points_qs = (
-        Points.objects.filter(user=request.user, awarded_at__gte=start_datetime)
+        Points.objects.filter(
+            user=request.user,
+            awarded_at__gte=start_datetime,
+            awarded_at__lt=end_datetime,
+        )
         .annotate(day=TruncDate("awarded_at"))
         .values("day")
         .annotate(total=Sum("amount"))
         .order_by("day")
     )
-    points_by_date = {entry["day"].strftime("%b %d"): entry["total"] for entry in points_qs}
+    points_by_date = {entry["day"]: entry["total"] for entry in points_qs}
     chart_labels = []
     chart_data = []
     for i in range(30):
         day = thirty_days_ago + timedelta(days=i)
-        label = day.strftime("%b %d")
-        chart_labels.append(label)
-        chart_data.append(points_by_date.get(label, 0))
+        chart_labels.append(day.strftime("%b %d"))
+        chart_data.append(points_by_date.get(day, 0))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` around lines 2631 - 2646, The query currently only filters by a
lower bound and keys points_by_date by formatted strings, so future-dated Points
can collide; change the filter to bound the window by adding an upper bound
(e.g., end_datetime = start of tomorrow and use awarded_at__lt=end_datetime on
the Points queryset), key points_by_date by the truncated date object returned
by TruncDate (entry["day"]) instead of entry["day"].strftime(...), and in the
loop that builds chart_labels/chart_data (referencing points_qs, points_by_date,
thirty_days_ago, TruncDate) use the date objects to look up totals and defer any
strftime formatting of labels until render time.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/templates/dashboard/student.html`:
- Around line 238-240: The chart heading icon uses the class "text-teal-500"
without a dark-mode variant; update the icon element (the <i> with classes "fas
fa-chart-bar text-teal-500 mr-2") to include an appropriate dark: prefixed color
(for example dark:text-teal-300 or another accessible teal shade) so the icon
remains visible and consistent when dark mode (dark:bg-gray-800) is active.

---

Duplicate comments:
In `@web/views.py`:
- Around line 2631-2646: The query currently only filters by a lower bound and
keys points_by_date by formatted strings, so future-dated Points can collide;
change the filter to bound the window by adding an upper bound (e.g.,
end_datetime = start of tomorrow and use awarded_at__lt=end_datetime on the
Points queryset), key points_by_date by the truncated date object returned by
TruncDate (entry["day"]) instead of entry["day"].strftime(...), and in the loop
that builds chart_labels/chart_data (referencing points_qs, points_by_date,
thirty_days_ago, TruncDate) use the date objects to look up totals and defer any
strftime formatting of labels until render time.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 79a888b1-2c85-4f27-b30e-05c2f7144e92

📥 Commits

Reviewing files that changed from the base of the PR and between 783dc9b and d90d299.

📒 Files selected for processing (2)
  • web/templates/dashboard/student.html
  • web/views.py

@ayesha1145 ayesha1145 force-pushed the feat/student-dashboard-points-badges branch from d90d299 to b258efa Compare March 12, 2026 11:41
@ayesha1145
Copy link
Author

Fixed: added dark:text-teal-300 to the chart heading icon for dark mode consistency.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/templates/dashboard/student.html`:
- Around line 267-296: The template embeds Chart.js and the chart initialization
inline instead of using the project's established template blocks; move the
Chart.js <script> include into the {% block extra_head %} and relocate the JSON
script tags (chart_labels|json_script:"points-chart-labels",
chart_data|json_script:"points-chart-data") plus the inline chart initialization
into {% block extra_js %} so Chart.js loads in the head and the initialization
runs in the page JS block, keeping consistency with base.html and other
dashboard templates (references: chart_labels, chart_data, pointsChart,
extra_head, extra_js).
- Line 248: The Recent Badges heading icon (<i class="fas fa-award ...">) lacks
a dark-mode variant; update the icon's class list by adding a dark: prefixed
color (e.g., dark:text-yellow-400) alongside text-yellow-500 so the award icon
matches dark theme styling (modify the <i> element with class "fas fa-award
text-yellow-500 mr-2" to include the dark: variant).
- Line 269: Replace the incorrect SRI hash on the Chart.js script tag in the
student dashboard template by updating the integrity attribute value on the
Chart.js <script> element (the line that loads "chart.umd.min.js") to the
official cdnjs SHA-512 value; specifically change the existing integrity string
to
"sha512-SIMGYRUjwY8+gKg7nn9EItdD8LCADSDfJNutF9TPrvEo86sQmFMh6MyralfIyhADlajSxqc7G0gs7+MwWF/ogQ=="
so the browser integrity check succeeds.

In `@web/views.py`:
- Line 2649: The query building recent_badges is non-deterministic when multiple
UserBadge.awarded_at values tie; update the UserBadge queryset (recent_badges)
to include a stable tie-breaker in order_by (e.g., "-id") so the slice is
deterministic: keep select_related("badge") and order by "-awarded_at" then
"-id" before slicing to 6.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 38256435-2112-49e6-b5d3-e8fbf3a338d2

📥 Commits

Reviewing files that changed from the base of the PR and between d90d299 and b258efa.

📒 Files selected for processing (2)
  • web/templates/dashboard/student.html
  • web/views.py

web/views.py Outdated
chart_data.append(points_by_date.get(label, 0))

# Badges
recent_badges = UserBadge.objects.filter(user=request.user).select_related("badge").order_by("-awarded_at")[:6]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Make the recent-badges slice deterministic.

If multiple UserBadge rows share the same awarded_at, ordering by that field alone makes the top 6 unstable. Add a tie-breaker such as -id so the dashboard doesn’t reshuffle badges between renders.

Suggested fix
-    recent_badges = UserBadge.objects.filter(user=request.user).select_related("badge").order_by("-awarded_at")[:6]
+    recent_badges = (
+        UserBadge.objects.filter(user=request.user)
+        .select_related("badge")
+        .order_by("-awarded_at", "-id")[:6]
+    )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` at line 2649, The query building recent_badges is
non-deterministic when multiple UserBadge.awarded_at values tie; update the
UserBadge queryset (recent_badges) to include a stable tie-breaker in order_by
(e.g., "-id") so the slice is deterministic: keep select_related("badge") and
order by "-awarded_at" then "-id" before slicing to 6.

@ayesha1145 ayesha1145 force-pushed the feat/student-dashboard-points-badges branch from b258efa to da1e2f1 Compare March 12, 2026 13:07
@ayesha1145
Copy link
Author

Addressed feedback:

  • Added dark:text-yellow-300 to the Recent Badges heading icon for dark mode consistency
  • Fixed Chart.js SRI integrity hash to the correct official cdnjs value (previous hash was incorrect and would have broken chart loading)
  • Added -id as a tie-breaker to recent_badges ordering for deterministic results
  • Nitpick about extra_head/extra_js blocks acknowledged — out of scope for this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

files-changed: 2 PR changes 2 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant