Skip to content

feat: add personalized course recommendation system for students#1039

Open
ayesha1145 wants to merge 1 commit intoalphaonelabs:mainfrom
ayesha1145:feat/course-recommendations
Open

feat: add personalized course recommendation system for students#1039
ayesha1145 wants to merge 1 commit intoalphaonelabs:mainfrom
ayesha1145:feat/course-recommendations

Conversation

@ayesha1145
Copy link

@ayesha1145 ayesha1145 commented Mar 21, 2026

Adds a personalized course recommendation system that surfaces relevant courses to students on their dashboard.

Changes:

  • web/views.py: added recommendation logic to student_dashboard context and new standalone course_recommendations view
  • web/urls.py: added route dashboard/student/recommendations/
  • web/templates/dashboard/recommendations.html: new full-page recommendations template with course cards (image, subject, title, teacher, price, rating)
  • web/templates/dashboard/student.html: added "Recommended for You" section with 3 course cards and "See all" link

Recommendation algorithm (3-tier priority):

  1. Published courses in subjects the student is already enrolled in (sorted by enrollment count)
  2. Highest-rated published courses (avg rating ≥ 4.0) not yet enrolled
  3. Fallback: most popular published courses by enrollment count

Features:

  • Excludes courses the student is already enrolled in
  • Excludes courses taught by the student themselves
  • Shows up to 3 recommendations on dashboard, full list at /dashboard/student/recommendations/
  • Empty state with "Browse Courses" CTA when no recommendations available
  • Dark mode support throughout

Purpose

Adds a personalized course recommendation system for students, surfaced on the student dashboard and via a dedicated recommendations page.

Key Changes

  • Templates

    • Added web/templates/dashboard/recommendations.html — full-page recommendations view rendering a responsive grid of course cards (image or placeholder, subject, truncated title, teacher name, price label, average rating when present) and an empty-state with a "Browse Courses" CTA. Dark-mode styling supported.
    • Updated web/templates/dashboard/student.html — new "Recommended for You" section showing up to 3 recommended course cards and a "See all" link to the full recommendations page. Section was moved inside the main container to fix layout/padding; minor template escaping removed.
  • Route

    • Added URL: /dashboard/student/recommendations/ → views.course_recommendations (login-required), URL name course_recommendations.
  • Views / Logic

    • student_dashboard now computes and injects up to 3 recommendations into the template context.
    • Added @login_required course_recommendations view that applies the same tiered selection logic and renders recommendations.html (page-level results).
    • Recommendation selection (3-tier priority):
      1. Published courses in subjects the student is enrolled in (sorted by enrollment count).
      2. Highest-rated published courses (avg_rating ≥ 4.0) not yet enrolled (ordered by -avg_rating, -enrollment_count).
      3. Fallback: most popular published courses by enrollment count.
    • Excludes courses the student is already enrolled in (only enrollments with status "approved" or "completed" are treated as enrolled) and excludes courses taught by the student.
    • Query fixes: added distinct=True to Count("enrollments") to avoid row multiplication from joins; tier-2 ordering unified between views.
  • Accessibility / UX

    • Dashboard limited to 3 items to avoid clutter; full list available on the recommendations page.
    • Empty state with "Browse Courses" CTA provided.
    • Keyboard accessibility styles added to "View Course" buttons (focus styles).

Impact / Notes

  • Improves course discovery and personalization on the dashboard while keeping the UI compact.
  • Query changes (distinct counts, enrollment-status filtering, unified ordering) fix correctness and consistency; review for query performance and indexes on enrollments/ratings is recommended.
  • Template placement and accessibility tweaks improve layout and keyboard navigation.

@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: 4 PR changes 4 files label Mar 21, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 21, 2026

Warning

Rate limit exceeded

@ayesha1145 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 52 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro

Run ID: 449a14ab-1fbb-4c76-80f5-8cdd229cf141

📥 Commits

Reviewing files that changed from the base of the PR and between ec90020 and 8c510ef.

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

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

Adds student-facing course recommendations: a dashboard inline section and a standalone recommendations page, a tiered recommendation algorithm (subject-match, high-rated, popular), a login-protected view endpoint, and a route; templates render course cards (image fallback, teacher, price, rating) and an empty state.

Changes

Cohort / File(s) Summary
Templates
web/templates/dashboard/recommendations.html, web/templates/dashboard/student.html
New standalone recommendations template and injected dashboard recommendations block. Both render responsive course cards with image fallback, subject, truncated title, teacher display name, price/Free, optional average rating, "View Course" links, and an empty-state with a "Browse Courses" link.
Views / Logic
web/views.py
Added tiered recommendation logic: Tier 1 same-subject published courses, Tier 2 high-rated published courses (avg_rating ≥ 4.0), Tier 3 popular published courses; dashboard view populates recommendations (capped at 3) and a new @login_required course_recommendations view applies the same logic with a larger result limit and renders the recommendations template.
Routing
web/urls.py
Added i18n-prefixed URL pattern dashboard/student/recommendations/ mapped to views.course_recommendations (name course_recommendations).

Sequence Diagram

sequenceDiagram
    participant Student as Student (Client)
    participant View as RecommendationView
    participant DB as Database
    participant Tpl as TemplateRenderer

    Student->>View: GET /dashboard/student/recommendations/ (authenticated)
    activate View
    View->>DB: Fetch student's enrollments (to exclude)
    View->>DB: Query Tier 1 (published, same subject, exclude enrolled/teacher)
    alt Tier 1 meets target
        View->>View: select Tier 1 results
    else
        View->>DB: Query Tier 2 (published, avg_rating ≥ 4.0, exclude prior)
        alt Tier1+Tier2 meet target
            View->>View: combine Tier1 + Tier2
        else
            View->>DB: Query Tier 3 (published, popular by enrollment, exclude prior)
            View->>View: combine tiers to reach target
        end
    end
    View->>Tpl: Render `dashboard/recommendations.html` with recommendations
    deactivate View
    Tpl-->>Student: HTML response (cards or empty state)
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 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 'feat: add personalized course recommendation system for students' directly and clearly summarizes the main change: implementing a new recommendation system feature for student users.
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

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: 6

🤖 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/recommendations.html`:
- Around line 25-27: The template uses an unnecessary escape in the price
display: change the string that renders the price from the escaped form (\"\\${{
course.price }}\") to an unescaped dollar sign (\"${{ course.price }}\") inside
the span that contains the if/else for course.price so the dollar sign is
rendered normally; update the span that includes the conditional around
course.price accordingly.

In `@web/templates/dashboard/student.html`:
- Around line 239-242: The "View Course" anchor (the link rendered by {% url
'course_detail' course.slug %} with text "View Course") lacks an explicit focus
style for keyboard users; update its class list to include accessible focus
utility classes (for example: focus:outline-none focus:ring-2
focus:ring-offset-2 focus:ring-teal-500 and a dark-mode counterpart like
dark:focus:ring-teal-400) so a visible focus ring appears when tabbing to the
link, preserving existing hover styles and transition classes.
- Around line 212-216: The "Course Recommendations" block is currently placed
after the main container's closing </div>, causing it to render outside the
layout; move the entire recommendations section (the {% if recommendations %}
... matching {% endif %} block labeled "Course Recommendations") so it sits
before the main container's closing </div> that opened near the top of the
template, preserving the container's padding and layout.

In `@web/views.py`:
- Around line 2637-2645: The dashboard and “See all” pages use different
ordering and per-tier limits for the same tier (top_rated_recs) — unify them by
moving the query building into a shared function
(web/recommendations.py:get_course_recommendations) that accepts a
limit/page_size and optional tier identifier; update both call sites (the
dashboard code that builds top_rated_recs and the “See all” view around lines
2698–2710) to call get_course_recommendations with only the desired limit, and
ensure the shared builder uses the same annotate/filter/order_by logic (e.g.,
.annotate(avg_rating=Avg("reviews__rating"),
enrollment_count=Count("enrollments")).filter(avg_rating__gte=4.0).order_by("-avg_rating",
"-enrollment_count")) so ordering and tier limits are consistent across
surfaces.
- Line 2625: The current enrolled_course_ids is built from all Enrollment rows
(enrollments.values_list("course_id", flat=True)) which hides courses with
non-blocking statuses like "rejected"; change the query that builds
enrolled_course_ids to filter enrollments by only the statuses that should
suppress recommendations (e.g., status__in=[...blocking_statuses...]) so it
returns course_ids for truly enrolled/blocked states; update the same logic used
later (the enrollments usage at the block around lines referenced 2679-2681) to
apply the same filtered status set and ensure both occurrences use the shared
filtered queryset or helper to avoid regressions.
- Around line 2642-2644: The Course queryset annotating avg_rating and
enrollment_count is over-counting enrollments due to the join with reviews;
update the Count("enrollments") to Count("enrollments", distinct=True) in both
places where .annotate(avg_rating=Avg("reviews__rating"),
enrollment_count=Count("enrollments")) appears so the enrollment_count is
de-duplicated (this will correct the tie-breaker ordering that uses
-enrollment_count).

ℹ️ Review info
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro

Run ID: 948e882c-ed0a-4c1a-89e8-cd711158651f

📥 Commits

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

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

@ayesha1145 ayesha1145 force-pushed the feat/course-recommendations branch from ab303ca to 7f38ade Compare March 21, 2026 17:48
@github-actions
Copy link
Contributor

github-actions bot commented Mar 21, 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 enrollment filter: only exclude courses with status "approved" or "completed" — rejected enrollments no longer suppress recommendations
  • Added distinct=True to Count("enrollments") in both views to prevent row multiplication from the reviews join
  • Unified tier-2 ordering to -avg_rating, -enrollment_count in both dashboard and standalone view for consistency
  • Removed unnecessary $ escape — using $ directly in Django template
  • Moved recommendations section inside the main container div to fix layout/padding
  • Added focus:outline-none focus:ring-2 focus:ring-teal-300 to "View Course" button for keyboard accessibility

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: 7

♻️ Duplicate comments (2)
web/templates/dashboard/student.html (1)

212-249: ⚠️ Potential issue | 🔴 Critical

Critical: Recommendations section is outside the container, and there's an unmatched closing tag.

The HTMLHint static analysis confirms an unpaired </div> at line 249. Here's what's happening:

  1. Line 212 closes the main .container div that opened on line 7
  2. Lines 214-248 place the recommendations section outside that container
  3. Line 249 adds an extra </div> with no matching opening tag

This breaks the page layout (recommendations won't have proper container padding) and produces invalid HTML.

🐛 Proposed fix to restore proper nesting

Move the recommendations section inside the container by restructuring lines 212-249:

-  </div>
-
     <!-- Course Recommendations -->
     {% if recommendations %}
     <div class="mt-8 bg-white dark:bg-gray-800 rounded-lg shadow p-6">
       <div class="flex items-center justify-between mb-4">
         <!-- ... rest of recommendations content ... -->
       </div>
     </div>
     {% endif %}
-  </div>
+  </div>
 {% endblock content %}

The final structure should have only one </div> closing the container, placed after the recommendations section.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/dashboard/student.html` around lines 212 - 249, The
recommendations block is placed outside the main container and there's an
unmatched closing </div>; move the entire "Course Recommendations" section (the
{% if recommendations %} ... {% endif %} block) so it sits inside the main <div
class="container"> (the container opened near the top) before the container's
closing </div>, then remove the stray extra </div> so there is exactly one
closing tag for the container; ensure the {% if recommendations %} block and its
inner markup remain intact and properly nested.
web/views.py (1)

2625-2625: ⚠️ Potential issue | 🟠 Major

Include pending enrollments in the exclusion set.

A course with a pending enrollment can still be recommended here, but enroll_course later rejects that same course as already enrolled. That creates a broken recommendation path for students who have started checkout but not finished it yet. Please treat pending as a blocking status in both queries.

Possible adjustment
-    enrolled_course_ids = enrollments.filter(status__in=["approved", "completed"]).values_list("course_id", flat=True)
+    enrolled_course_ids = enrollments.filter(
+        status__in=["pending", "approved", "completed"]
+    ).values_list("course_id", flat=True)
     enrolled_course_ids = Enrollment.objects.filter(
-        student=request.user, status__in=["approved", "completed"]
+        student=request.user, status__in=["pending", "approved", "completed"]
     ).values_list("course_id", flat=True)

Also applies to: 2679-2681

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` at line 2625, The enrolled_course_ids query currently filters
enrollments by status__in=["approved", "completed"] but should also treat
"pending" as a blocking status; update the filter in the enrolled_course_ids
assignment (and the similar query around lines 2679-2681) to include "pending"
in the status__in list so pending enrollments are excluded from recommendations
and match the enroll_course check.
🤖 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/recommendations.html`:
- Line 30: The decorative star icon element in the recommendations template (<i
class="fas fa-star mr-1"></i>) should be hidden from assistive technology; add
aria-hidden="true" to that <i> element so screen readers only announce the
rating text ({{ course.average_rating|floatformat:1 }}).
- Line 44: The decorative search icon element <i class="fas fa-search text-4xl
text-gray-300 dark:text-gray-600 mb-4"></i> should be hidden from assistive
tech; update that element in recommendations.html to include aria-hidden="true"
so screen readers ignore the icon while leaving the visible text unchanged.
- Line 17: The <i> element with classes "fas fa-book-open text-teal-500
dark:text-teal-300 text-3xl" is decorative and should be hidden from assistive
tech; update the <i> element (the icon with class "fas fa-book-open") to include
aria-hidden="true" so screen readers skip it while preserving visual display.
- Line 12: The long <div> element with class="bg-white dark:bg-gray-800
rounded-lg shadow hover:shadow-md transition duration-200 overflow-hidden flex
flex-col" exceeds the 120-char limit; break the class attribute across multiple
lines (or run djlint with 120-char width) so the <div> tag lines are <=120
characters—split class tokens onto separate lines for readability while keeping
the same classes and preserving the element start tag (the <div ...> element
shown in the template).
- Around line 34-37: The anchor element for the "View Course" button has a
single very long class attribute that exceeds the 120-character line limit;
reformat the <a href="{% url 'course_detail' course.slug %}" ...> element by
breaking the long class attribute into multiple lines (one or more class groups
per line) so each line is ≤120 chars, ensuring the {% url 'course_detail'
course.slug %} tag and the "View Course" text remain unchanged and the element
still renders the same; run djlint (120-char) to verify compliance.

In `@web/views.py`:
- Around line 2629-2656: The current tiered recommendation builders
(same_subject_recs, top_rated_recs, popular_recs) each slice independently and
then concatenate, allowing the dashboard recommendations list (recommendations)
to exceed the intended cap; update the logic so each subsequent tier is limited
by remaining_slots = MAX_DASHBOARD_SLOTS - len(recommendations) (e.g., 3 -
len(recommendations)) before querying/slicing top_rated_recs and popular_recs
and only add up to that remaining_slots, and likewise remove or replace fixed
[:4] truncations on the dedicated page with proper pagination or dynamic limits
so tier 2/3 queries use the remaining slot count instead of a fixed independent
slice. Ensure you update the variables referenced (same_subject_recs,
top_rated_recs, popular_recs, recommendations, enrolled_course_ids,
enrolled_subject_ids) so each tier respects remaining_slots.
- Around line 2672-2678: The docstring for the recommendation routine is out of
sync with the implementation: update the docstring in web/views.py for the
function get_personalized_recommendations (or the surrounding recommendation
function) so the tier order matches the code — 1) courses in subjects the
student is already enrolled in (but not yet enrolled in), 2) highest-rated
published courses the student hasn't enrolled in yet (rating fallback), and 3)
popular courses by enrollment count (popularity fallback); keep the brief
description and priority order format in the existing docstring.

---

Duplicate comments:
In `@web/templates/dashboard/student.html`:
- Around line 212-249: The recommendations block is placed outside the main
container and there's an unmatched closing </div>; move the entire "Course
Recommendations" section (the {% if recommendations %} ... {% endif %} block) so
it sits inside the main <div class="container"> (the container opened near the
top) before the container's closing </div>, then remove the stray extra </div>
so there is exactly one closing tag for the container; ensure the {% if
recommendations %} block and its inner markup remain intact and properly nested.

In `@web/views.py`:
- Line 2625: The enrolled_course_ids query currently filters enrollments by
status__in=["approved", "completed"] but should also treat "pending" as a
blocking status; update the filter in the enrolled_course_ids assignment (and
the similar query around lines 2679-2681) to include "pending" in the status__in
list so pending enrollments are excluded from recommendations and match the
enroll_course check.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro

Run ID: 56c0910b-0929-4b6a-8101-f5d5916c7853

📥 Commits

Reviewing files that changed from the base of the PR and between ab303ca and 7f38ade.

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

@ayesha1145 ayesha1145 force-pushed the feat/course-recommendations branch from 7f38ade to ec90020 Compare March 21, 2026 18:01
@ayesha1145
Copy link
Author

Addressed CodeRabbit feedback:

  • Fixed tier slicing: dashboard now builds recommendations incrementally (capped at 3 total), standalone page uses page_limit=8 with same incremental approach — no over-fetching or under-serving
  • Added aria-hidden="true" to all decorative icons (book, star, search) in recommendations.html
  • Updated docstring in course_recommendations view to accurately describe the 3-tier logic
  • Line length and formatting nitpicks acknowledged — djlint not in pre-commit pipeline for this 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: 3

♻️ Duplicate comments (3)
web/templates/dashboard/student.html (1)

214-249: ⚠️ Potential issue | 🟠 Major

Fix the container closing order (mismatched </div> + section outside layout).

The recommendations block is still outside the main container because the container closes at Line 212, then closes again at Line 249. This reintroduces the earlier layout bug and invalid HTML.

🐛 Proposed fix
-  </div>
-
-    <!-- Course Recommendations -->
-    {% if recommendations %}
+    <!-- Course Recommendations -->
+    {% if recommendations %}
     <div class="mt-8 bg-white dark:bg-gray-800 rounded-lg shadow p-6">
       <div class="flex items-center justify-between mb-4">
         <h2 class="text-xl font-semibold text-gray-800 dark:text-white">
@@
     </div>
     {% endif %}
   </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/dashboard/student.html` around lines 214 - 249, The
recommendations block is rendered outside the main container due to a mismatched
closing </div>; fix by removing the premature container close so the Course
Recommendations div (the "{% if recommendations %}" block and its nested divs)
sits inside the main layout wrapper, and ensure there is a single matching
closing </div> for that main container at the end of the template so HTML
nesting is balanced.
web/views.py (1)

2624-2660: 🧹 Nitpick | 🔵 Trivial

Extract a shared recommendation builder to prevent logic drift.

Both views duplicate the same multi-tier algorithm with near-identical query logic. Pull this into one helper and pass only limit per surface to keep future fixes consistent.

♻️ Refactor sketch
+def _build_course_recommendations(user: User, limit: int) -> list[Course]:
+    # shared 3-tier logic here
+    return recommendations
@@
-    # Course recommendations
-    ...
-    recommendations += list(popular_recs)
+    recommendations = _build_course_recommendations(request.user, limit=3)
@@
 def course_recommendations(request: HttpRequest) -> HttpResponse:
@@
-    ...
-    recommendations += list(popular)
+    recommendations = _build_course_recommendations(request.user, limit=8)

Also applies to: 2673-2734

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/views.py` around lines 2624 - 2660, The course recommendation multi-tier
query logic is duplicated; extract it into a single helper (e.g.,
get_recommended_courses or build_course_recommendations) that takes the user
(request.user), enrolled_course_ids/enrollments and a limit argument and returns
a list/queryset of recommended Course objects; move the existing logic that
computes enrolled_subject_ids, same_subject_recs, top_rated_recs and
popular_recs into that helper and replace both duplicated blocks in web.views
(the block shown and the one at lines ~2673-2734) with calls to this helper
passing only limit, ensuring you exclude the current user's courses and
already-enrolled ids and preserve the same annotate/filter/order_by behavior.
web/templates/dashboard/recommendations.html (1)

12-12: ⚠️ Potential issue | 🟡 Minor

Re-run djlint: class lines still exceed the 120-char limit.

Both class attributes are still too long for the configured template formatting rule. Please split these attributes across lines (or run djlint with width 120) to keep the template compliant and readable.

As per coding guidelines: "Format HTML/Django templates with djlint using 120-character line length".

Also applies to: 35-35

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/templates/dashboard/recommendations.html` at line 12, The long class
attribute on the top-level div (the element with class="bg-white
dark:bg-gray-800 rounded-lg shadow hover:shadow-md transition duration-200
overflow-hidden flex flex-col") exceeds the 120-char djlint limit; split the
class attribute across multiple lines (e.g., one class per line or logical
groups) so each line stays under 120 chars and repeat the same change for the
other offending div around the 35-35 occurrence; alternatively, reformat the
template with djlint --width 120, but prefer explicit multi-line class
attributes for readability.
🤖 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/recommendations.html`:
- Around line 46-47: The "Browse Courses" CTA anchor is missing dark-mode and
visible keyboard focus styles; update the anchor (the element containing the
"Browse Courses" text) to include dark: prefixed background/hover classes and
add focus/focus-visible ring classes (e.g., focus:outline-none, focus:ring-2,
focus:ring-offset-2, focus:ring-teal-500 and matching dark:focus:ring color) so
it matches other interactive buttons and is keyboard accessible.
- Line 12: The recommendation card's container div currently uses the Tailwind
class "shadow" instead of the project-standard card class "shadow-lg"; update
the div that has class="bg-white dark:bg-gray-800 rounded-lg shadow
hover:shadow-md transition duration-200 overflow-hidden flex flex-col" to use
"shadow-lg" (and remove or adjust the hover:shadow-md if you want consistent
card behavior) so it matches the project card convention used for project
templates.

In `@web/views.py`:
- Around line 2630-2636: The querysets (e.g., same_subject_recs built from
Course.objects.filter) only annotate avg_rating in one tier while templates call
the model property course.average_rating, causing per-card DB re-queries and
inconsistent display; update every recommendation queryset (including the blocks
around same_subject_recs and the other tiers at the noted ranges) to annotate a
single field like avg_rating (using Avg on ratings) and order by that annotated
name where needed, then change templates to read course.avg_rating with a
fallback to avoid calling the model property.

---

Duplicate comments:
In `@web/templates/dashboard/recommendations.html`:
- Line 12: The long class attribute on the top-level div (the element with
class="bg-white dark:bg-gray-800 rounded-lg shadow hover:shadow-md transition
duration-200 overflow-hidden flex flex-col") exceeds the 120-char djlint limit;
split the class attribute across multiple lines (e.g., one class per line or
logical groups) so each line stays under 120 chars and repeat the same change
for the other offending div around the 35-35 occurrence; alternatively, reformat
the template with djlint --width 120, but prefer explicit multi-line class
attributes for readability.

In `@web/templates/dashboard/student.html`:
- Around line 214-249: The recommendations block is rendered outside the main
container due to a mismatched closing </div>; fix by removing the premature
container close so the Course Recommendations div (the "{% if recommendations
%}" block and its nested divs) sits inside the main layout wrapper, and ensure
there is a single matching closing </div> for that main container at the end of
the template so HTML nesting is balanced.

In `@web/views.py`:
- Around line 2624-2660: The course recommendation multi-tier query logic is
duplicated; extract it into a single helper (e.g., get_recommended_courses or
build_course_recommendations) that takes the user (request.user),
enrolled_course_ids/enrollments and a limit argument and returns a list/queryset
of recommended Course objects; move the existing logic that computes
enrolled_subject_ids, same_subject_recs, top_rated_recs and popular_recs into
that helper and replace both duplicated blocks in web.views (the block shown and
the one at lines ~2673-2734) with calls to this helper passing only limit,
ensuring you exclude the current user's courses and already-enrolled ids and
preserve the same annotate/filter/order_by behavior.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro

Run ID: 247aa9ab-d7c6-4c59-829b-503e932d85c1

📥 Commits

Reviewing files that changed from the base of the PR and between 7f38ade and ec90020.

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

@ayesha1145 ayesha1145 force-pushed the feat/course-recommendations branch from ec90020 to 4c7d30b Compare March 21, 2026 18:15
@ayesha1145 ayesha1145 force-pushed the feat/course-recommendations branch from 4c7d30b to 8c510ef Compare March 21, 2026 18:15
@ayesha1145
Copy link
Author

Addressed CodeRabbit feedback:

  • Changed card shadow from shadow to shadow-lg to match project card guidelines
  • Added dark mode variants and focus ring to "Browse Courses" empty-state CTA
  • Annotated avg_rating on all tiers (tier 1 and tier 3) so templates use the annotated value consistently instead of triggering per-card model property queries

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

Labels

files-changed: 4 PR changes 4 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant