Skip to content

Trending Courses/Professors Feature | UI & Analytics#1247

Closed
ArislanT wants to merge 27 commits into
devfrom
trending-analytics/ui-mockup
Closed

Trending Courses/Professors Feature | UI & Analytics#1247
ArislanT wants to merge 27 commits into
devfrom
trending-analytics/ui-mockup

Conversation

@ArislanT
Copy link
Copy Markdown
Collaborator

@ArislanT ArislanT commented Apr 16, 2026

GitHub Issues addressed

Closed as frontend and backend PR just got consolidated into one PR (#1248)

(diff has backend as well as must be merged for true functionality)
Frontend changes are in landing.html and landing.css
Only difference between backend code here and other PR is

  • .select_related("subdepartment")
    which was added to optimize the frontend rendering and prevent N+1 query issues.

What I did

Some notes:
Placed top 5 trending courses/professors on front page. Location is just mock up as the priority was to ensure functionality of the backend engine (advised this and that there was no need for extensive design).
Cards are clickable for all trending items and has a hovering effect
Course Symbol, Mnemonic, and Full Name displayed
Professor only has their name displayed as dealing with departments gets tricky for professors with relationships with several departments

CSS key ideas:

  • Implemented a two-column grid using CSS Flexbox and Grid. Used a fixed height of 5rem on cards to ensure pixel-perfect alignment between Course cards (multi-line) and Instructor cards (single-line).
  • Added media queries to ensure the grid stacks vertically on mobile devices and expands to a dual-column layout on tablets and desktops.
  • Integrated site-wide CSS variables for colors, spacing, and transitions.
    Performance:
  • Template Logic: Implemented {% empty %} handlers and used select_related in the view to ensure the frontend renders efficiently without N+1 query overhead.

Screenshots

Screenshot 2026-04-15 at 11 07 17 PM

Testing

  • Verified across various screen widths in local Docker environment
  • Passed status 200 rendering tests using Django Test Client, proving that every url tag found a valid route, subdepartment logic was fixed, and get_trending_ids (query) logic successfully returned data that the template could use

Summary by CodeRabbit

  • New Features

    • Added a Trending section on the landing page showing top courses and instructors.
    • Enabled background view tracking for course and instructor pages to power trending analytics.
    • Added styles and interactive cards for the Trending display.
  • Chores

    • Updated environment template with optional analytics variables for DynamoDB-backed trending.

@ArislanT ArislanT added the Feature New feature label Apr 16, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

Adds DynamoDB-backed trending analytics: asynchronous recording of course and instructor page views, DynamoDB updates with TTL and view counts, retrieval of top trending IDs with caching, UI and CSS for a Trending section on the landing page, and environment variables to enable/configure analytics.

Changes

Cohort / File(s) Summary
Environment
\.env.example
Added optional AWS/DynamoDB env vars: AWS_ANALYTICS_ACCESS_KEY_ID, AWS_ANALYTICS_SECRET_ACCESS_KEY, AWS_REGION=us-east-1, DYNAMODB_TABLE_NAME=trending_analytics; ensured trailing newline.
Analytics module
tcf_website/analytics_utils.py
New module: initializes optional boto3 session, thread-pool background dispatch, backlog cap, get_table(), record_course_view() and record_instructor_view() which enqueue fire-and-forget DynamoDB update_item increments with TTL handling and logging.
Trending retrieval & caching
tcf_website/views/home/pages.py
New functions: get_top_trending_ids(entity_type, count), get_trending_courses(), get_trending_instructors() that query DynamoDB GSI, parse/deduplicate IDs, fetch ORM objects, preserve ranking, and cache results (24h). index() now includes trending context.
View hooks & browse context
tcf_website/views/courses/course.py, tcf_website/views/courses/course_instructor.py, tcf_website/views/catalog/browse.py
Added GET-only hooks to call analytics recorders on course/instructor pages; browse view injects trending_courses and trending_instructors for default (non-search) flows.
Landing UI & styles
tcf_website/templates/site/home/landing.html, tcf_website/static/css/site/pages/landing.css
Added Trending section markup and responsive card/grid styles, rank/badge layout, hover/focus/empty states, and compact text truncation.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ViewHandler as Course View Handler
    participant Analytics as analytics_utils
    participant Executor as ThreadPoolExecutor
    participant DynamoDB

    User->>ViewHandler: GET /course/{id}
    ViewHandler->>Analytics: record_course_view(course_id)
    Analytics->>Analytics: Validate config/session
    alt analytics enabled
        Analytics->>Analytics: Increment pending counter
        alt pending <= MAX_BACKLOG
            Analytics->>Executor: Submit task
            Executor->>Executor: Queue task
            Executor->>DynamoDB: update_item(pk, sk, increment view_count, set TTL if_not_exists)
            DynamoDB-->>Executor: Success / Error
        else pending > MAX_BACKLOG
            Analytics->>Analytics: Drop event (log)
        end
        Executor->>Analytics: Decrement pending counter (finally)
    else analytics disabled
        Analytics-->>ViewHandler: No-op
    end
    ViewHandler-->>User: Render course page
Loading
sequenceDiagram
    participant User
    participant IndexView as Landing View
    participant Pages as home/pages.py
    participant Cache as Django Cache
    participant DynamoDB
    participant ORM as Django ORM
    participant Template

    User->>IndexView: GET /
    IndexView->>Pages: get_trending_courses()
    Pages->>Cache: check 'trending_courses'
    alt cache hit
        Cache-->>Pages: return cached list
    else cache miss
        Pages->>DynamoDB: Query EntityIndex (expires_at filter, GSI)
        DynamoDB-->>Pages: return items (pk, sk)
        Pages->>Pages: parse IDs, dedupe, preserve order
        Pages->>ORM: fetch Course objects by IDs (validity filters)
        ORM-->>Pages: return queryset
        Pages->>Cache: store result (24h)
    end
    IndexView->>Template: include trending_courses & trending_instructors
    Template-->>User: Render landing with trending section
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰
I hopped to count each curious view,
DynamoDB keeping tally true,
Cards that sparkle, ranks that hum,
A little cache to keep things calm,
Hop on—trending paths have begun!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the main changes (landing.html/CSS for UI, backend analytics), includes implementation details, provides a screenshot, and explains testing performed. However, the GitHub Issues section lacks the specific issue number closure statement.
Title check ✅ Passed The title accurately summarizes the main changes: adding a trending courses/professors feature with both UI (landing page, CSS styling) and analytics backend (DynamoDB integration).

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch trending-analytics/ui-mockup

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
Copy Markdown
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

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

Inline comments:
In @.env.example:
- Around line 52-58: The file's analytics env block (ending with the commented
variable DYNAMODB_TABLE_NAME) is missing a trailing newline which fails
dotenv-linter; fix by adding a single blank line (newline) at the end of the
file so the file ends with a newline character after the DYNAMODB_TABLE_NAME
comment.

In `@tcf_website/analytics_utils.py`:
- Around line 31-40: The current logic disables analytics unless both
AWS_ANALYTICS_ACCESS_KEY_ID and AWS_ANALYTICS_SECRET_ACCESS_KEY are set; instead
instantiate boto3.Session with explicit aws_access_key_id/aws_secret_access_key
only when provided, otherwise call boto3.Session(region_name=env("AWS_REGION",
default="us-east-1")) to allow the default credential chain (EC2/ECS role,
shared config, etc.), then determine _ANALYTICS_ENABLED by checking the
resulting session's credentials (e.g., session.get_credentials() is not None) or
by a lightweight credentials check; update the code around _SESSION, access_key,
secret_key, boto3.Session and _ANALYTICS_ENABLED accordingly so analytics is
enabled whenever boto3 can resolve credentials, not only when static keys are
present.

In `@tcf_website/static/css/site/pages/landing.css`:
- Around line 208-223: Remove the stray blank line inside the .trending-card
rule: open the CSS rule for .trending-card and delete the empty line immediately
before background-color so the declarations start without an intervening blank
line (update the .trending-card block that contains height: 5rem; and
background-color: var(--bg-elevated);). Ensure there are no other unexpected
blank lines inside that rule to satisfy Stylelint.

In `@tcf_website/templates/site/home/landing.html`:
- Around line 103-108: Remove the inline styles from the div and span elements
in landing.html (the div containing the name/title and the two spans currently
using inline styles) and instead add the classes trending-card__text to the
wrapper div, trending-card__name--tight to the course name span, and
trending-card__subtitle to the subtitle span; then add the corresponding CSS
rules into tcf_website/static/css/site/pages/landing.css (rules should implement
display:flex; flex-direction:column; justify-content:center; min-width:0 for
.trending-card__text, line-height:1.2 for .trending-card__name--tight, and
font-size:0.85em; opacity:0.7; margin-top:2px; plus overflow/ellipsis rules for
.trending-card__subtitle).
- Around line 96-100: The section element with class "trending" has
aria-labelledby="trending-heading" but no element with that id exists; fix by
updating the aria-labelledby to point to the existing heading id
"trending-courses-heading" (or alternatively add an element with id
"trending-heading"); locate the <section class="trending"
aria-labelledby="trending-heading"> and change the attribute to
aria-labelledby="trending-courses-heading" so it references the <h2
id="trending-courses-heading" class="trending__title"> element.

In `@tcf_website/views/home/pages.py`:
- Line 115: Remove the whitespace-only blank line in pages.py that is triggering
Ruff W293: open tcf_website/views/home/pages.py and delete the blank line at the
location reported (the standalone line with only whitespace), ensuring there are
no trailing spaces so the file no longer contains a whitespace-only line.
- Around line 134-138: The current query that builds the instructors list uses
excludes on first_name/last_name and thus drops valid instructors and doesn't
respect visibility; update the Instructor query that populates the local
variable instructors (the Instructor.objects.filter(id__in=instructor_ids) call)
to instead filter out non-public records by adding a visibility condition (e.g.,
filter(hidden=False) or filter(is_public=True) depending on the model field) and
remove the .exclude(first_name="")/.exclude(last_name="") clauses so optional
names are preserved; keep filtering by id__in=instructor_ids and any existing
ordering/limit logic intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6aadcb0e-8f7b-4789-a723-ec3576cb51ab

📥 Commits

Reviewing files that changed from the base of the PR and between 6d73dd6 and bd67b01.

📒 Files selected for processing (8)
  • .env.example
  • tcf_website/analytics_utils.py
  • tcf_website/static/css/site/pages/landing.css
  • tcf_website/templates/site/home/landing.html
  • tcf_website/views/catalog/browse.py
  • tcf_website/views/courses/course.py
  • tcf_website/views/courses/course_instructor.py
  • tcf_website/views/home/pages.py

Comment thread .env.example
Comment thread tcf_website/analytics_utils.py Outdated
Comment thread tcf_website/static/css/site/pages/landing.css
Comment on lines +96 to +100
<section class="trending" aria-labelledby="trending-heading">
<div class="trending__grid">

<article class="trending__column">
<h2 id="trending-courses-heading" class="trending__title">🔥 Trending Courses</h2>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the broken aria-labelledby target.

Line 96 points to trending-heading, but that id does not exist in this template. The section ends up with an invalid accessible label reference.

One simple fix
-    <section class="trending" aria-labelledby="trending-heading">
+    <section class="trending" aria-label="Trending">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<section class="trending" aria-labelledby="trending-heading">
<div class="trending__grid">
<article class="trending__column">
<h2 id="trending-courses-heading" class="trending__title">🔥 Trending Courses</h2>
<section class="trending" aria-label="Trending">
<div class="trending__grid">
<article class="trending__column">
<h2 id="trending-courses-heading" class="trending__title">🔥 Trending Courses</h2>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tcf_website/templates/site/home/landing.html` around lines 96 - 100, The
section element with class "trending" has aria-labelledby="trending-heading" but
no element with that id exists; fix by updating the aria-labelledby to point to
the existing heading id "trending-courses-heading" (or alternatively add an
element with id "trending-heading"); locate the <section class="trending"
aria-labelledby="trending-heading"> and change the attribute to
aria-labelledby="trending-courses-heading" so it references the <h2
id="trending-courses-heading" class="trending__title"> element.

Comment thread tcf_website/views/home/pages.py Outdated
Comment thread tcf_website/views/home/pages.py
@thecourseforum thecourseforum deleted a comment from coderabbitai Bot Apr 16, 2026
Copy link
Copy Markdown
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.

♻️ Duplicate comments (1)
tcf_website/static/css/site/pages/landing.css (1)

315-317: ⚠️ Potential issue | 🟡 Minor

Remove the empty line before white-space to satisfy Stylelint.

Line 316 has an empty line before a declaration, which triggers declaration-empty-line-before.

Lint-only cleanup
 .trending-card__title {
   display: block; /* Ensures it sits on its own line */
   font-size: var(--text-xs);
   opacity: 0.7;
   margin-top: var(--space-1);
-
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tcf_website/static/css/site/pages/landing.css` around lines 315 - 317, Remove
the stray empty line immediately before the "white-space" declaration to satisfy
Stylelint's declaration-empty-line-before rule; edit the CSS rule block
containing the "white-space" and "overflow" properties so "white-space: nowrap;"
directly follows the previous line (no blank line) and re-run linting to confirm
the warning is gone.
🧹 Nitpick comments (1)
tcf_website/static/css/site/pages/landing.css (1)

274-280: Use the existing design token instead of undefined --color-error-600.

Line 278 references --color-error-600, but the shared tokens define --color-danger-600 (in tcf_website/static/css/site/tokens.css). The current fallback works, but it bypasses the design system contract.

Suggested token-aligned fix
 .trending-card__badge {
   font-size: var(--text-xs);
   font-weight: var(--font-bold);
-  /* Use design system variable with manual red as fallback */
-  color: var(--color-error-600, `#b91c1c`); 
+  color: var(--color-danger-600);
   background-color: rgba(239, 68, 68, 0.1);
   padding: var(--space-1) var(--space-3);
   border-radius: var(--radius-full);
   white-space: nowrap;
   flex-shrink: 0;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tcf_website/static/css/site/pages/landing.css` around lines 274 - 280, The
CSS rule .trending-card__badge is using a non-existent token --color-error-600;
change it to the defined design token --color-danger-600 (i.e., color:
var(--color-danger-600, `#b91c1c`)) so the component uses the shared design token
while retaining the same fallback; update only the variable name in the color
declaration to align with the tokens.css contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tcf_website/static/css/site/pages/landing.css`:
- Around line 315-317: Remove the stray empty line immediately before the
"white-space" declaration to satisfy Stylelint's declaration-empty-line-before
rule; edit the CSS rule block containing the "white-space" and "overflow"
properties so "white-space: nowrap;" directly follows the previous line (no
blank line) and re-run linting to confirm the warning is gone.

---

Nitpick comments:
In `@tcf_website/static/css/site/pages/landing.css`:
- Around line 274-280: The CSS rule .trending-card__badge is using a
non-existent token --color-error-600; change it to the defined design token
--color-danger-600 (i.e., color: var(--color-danger-600, `#b91c1c`)) so the
component uses the shared design token while retaining the same fallback; update
only the variable name in the color declaration to align with the tokens.css
contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 88e947b7-4735-47b9-942f-56c1a68b6c56

📥 Commits

Reviewing files that changed from the base of the PR and between bd67b01 and 433825d.

📒 Files selected for processing (4)
  • tcf_website/analytics_utils.py
  • tcf_website/static/css/site/pages/landing.css
  • tcf_website/templates/site/home/landing.html
  • tcf_website/views/home/pages.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tcf_website/templates/site/home/landing.html
  • tcf_website/views/home/pages.py
  • tcf_website/analytics_utils.py

# Configuration
_MAX_BACKLOG = 100
_MAX_WORKERS = 5
_TTL_DAYS = max(1, env.int("ANALYTICS_TTL_DAYS", default=7)) # Minimum 1 day TTL
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is your reasoning for this duration for the TTL?

@ArislanT ArislanT changed the title Trending Analytics Feature Frontend PR Trending Courses/Professors Feature | UI & Analytics Apr 19, 2026
@ArislanT ArislanT closed this Apr 19, 2026
@ArislanT ArislanT deleted the trending-analytics/ui-mockup branch April 19, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants