Skip to content

Conversation

@Shhazzz
Copy link
Contributor

@Shhazzz Shhazzz commented Dec 22, 2025

Validate educational videos before quiz generation (Fixes #3769)

What this PR does

  • Adds OpenAI-based educational content detection before quiz generation
  • Prevents non-educational videos (music, entertainment, etc.) from generating quizzes
  • Uses transcript only temporarily (not stored)
  • Keeps changes limited to the education view (no new models or migrations)

Why

This aligns with maintainer feedback to:

  • Doesn't save video transcripts
  • Avoid YouTube API usage
  • Use existing OpenAI integration
  • Keep implementation minimal (form + view only)

Testing

  • Tested locally using youtube-transcript-api
  • Quiz generation relies on transcript availability, which is reliably supported for YouTube but not Vimeo without additional APIs/auth.
  • UI validation and backend logic are now aligned to YouTube-only.
  • ✅ Educational videos generate quizzes ( Only YouTube)
  • ❌ Non-educational videos are rejected with an error message

Below is the working example:
For Educational Videos
Screenshot 2025-12-22 130251

Screenshot 2025-12-22 130628 Screenshot 2025-12-22 130641 Screenshot 2025-12-22 130713 Screenshot 2025-12-22 130723

For Non-Educational Videos
Screenshot 2025-12-22 135739

** For Vimeo Links**
Screenshot 2025-12-22 210615

Summary by CodeRabbit

  • New Features

    • Added an educational video submission flow: YouTube-only URL validation, transcript retrieval, automated educational-content detection, and AI-generated multiple-choice quizzes displayed in the submission UI with user-facing messages.
  • Chores

    • Added submission form, template, and integration with transcript retrieval and AI quiz-generation services; dependency added for transcript fetching.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link
Contributor

👋 Hi @Shhazzz!

This pull request needs a peer review before it can be merged. Please request a review from a team member who is not:

  • The PR author
  • DonnieBLT
  • coderabbitai
  • copilot

Once a valid peer review is submitted, this check will pass automatically. Thank you!

@github-actions github-actions bot added the files-changed: 5 PR changes 5 files label Dec 22, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Walkthrough

Adds a YouTube-based educational video submission flow: validates YouTube URLs, fetches transcripts, classifies content with OpenAI, generates JSON MCQ quizzes, exposes a UI page and API endpoint, and persists quizzes as EducationalVideo records.

Changes

Cohort / File(s) Change Summary
URL routing
\blt/urls.py``
New route education/submit/ mapped to submit_educational_video.
API endpoints & helpers
\website/api/views.py``
Adds openai setup, YouTubeTranscriptApi usage, get_transcript(), generate_quiz(), and a POST submit_educational_video() endpoint that classifies videos, generates quizzes, and persists EducationalVideo.
Forms
\website/forms.py``
Adds EducationalVideoForm with required video_url and validation restricting URLs to YouTube domains.
Templates
\website/templates/education/submit_video.html``
New template rendering submission form, Django messages, and generated quiz output (questions/options/answers).
Website views (UI flow)
\website/views/education.py``
Adds utilities and view: extract_youtube_video_id(), get_transcript_text(), is_educational_video(), generate_quiz(), and submit_educational_video() implementing extraction, transcript fetch, OpenAI classification/quiz generation, messaging, and persistence.
Dependencies
\pyproject.toml``
Adds youtube-transcript-api dependency (^0.6.2).
Imports, logging & validation
\website/forms.py`, `website/views/education.py`, `website/api/views.py``
Adds imports for openai, YouTubeTranscriptApi, urllib.parse/urlparse, logging, and settings; includes error handling and logging for transcript/OpenAI failures.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant Browser
    participant App as Django App
    participant YT as YouTubeTranscriptApi
    participant OpenAI as OpenAI API
    participant DB as Database

    User->>Browser: Submit YouTube URL (POST)
    Browser->>App: submit_educational_video(request)
    App->>App: validate URL & extract video_id
    App->>YT: fetch transcript(video_id)
    YT-->>App: transcript text / error
    App->>OpenAI: classify transcript (educational?)
    OpenAI-->>App: classification result
    alt educational
        App->>OpenAI: generate quiz (prompt includes transcript)
        OpenAI-->>App: quiz JSON
        App->>DB: save EducationalVideo (quiz JSON)
        DB-->>App: saved
        App-->>Browser: 200 / render form + quiz
    else not educational
        App-->>Browser: 400 / render form + rejection message
    end
    Browser-->>User: display result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: adding educational content validation before quiz generation, which aligns with the core functionality across all modified files.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 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.

@github-actions github-actions bot added the needs-peer-review PR needs peer review label Dec 22, 2025
@github-actions
Copy link
Contributor

📊 Monthly Leaderboard

Hi @Shhazzz! Here's how you rank for December 2025:

Rank User PRs Reviews Comments Total
#16 @mdkaifansari04 0 5 4 28
#17 @Shhazzz 2 1 0 24
#18 @ananya-09 1 1 5 24

Leaderboard based on contributions in December 2025. Keep up the great work! 🚀

@github-actions github-actions bot added the pre-commit: passed Pre-commit checks passed label Dec 22, 2025
@github-actions github-actions bot added the tests: failed Django tests failed label Dec 22, 2025
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

🧹 Nitpick comments (3)
website/api/views.py (1)

49-49: Import should be at the top of the file.

The youtube_transcript_api import is placed in the middle of the file. Move it to the import section at the top for consistency and PEP 8 compliance.

website/views/education.py (2)

61-70: CodeQL warning: URL substring check may be bypassed.

The static analysis tool flags that checking "youtube.com" in parsed_url.netloc (line 66) could match malicious domains like evil-youtube.com. However, since EducationalVideoForm.clean_video_url() already validates the exact netloc against an allowlist before this function is called, the risk is mitigated in the current flow.

For defense-in-depth, consider using exact domain matching here as well:

🔎 Proposed fix for stricter validation
 def extract_youtube_video_id(url):
     parsed_url = urlparse(url)
+    
+    youtube_domains = {"youtube.com", "www.youtube.com", "youtu.be"}
+    if parsed_url.netloc not in youtube_domains:
+        return None

-    if "youtu.be" in parsed_url.netloc:
+    if parsed_url.netloc == "youtu.be":
         return parsed_url.path.lstrip("/")
-    elif "youtube.com" in parsed_url.netloc:
+    elif parsed_url.netloc in {"youtube.com", "www.youtube.com"}:
         qs = parse_qs(parsed_url.query)
         return qs.get("v", [None])[0]

     return None

73-85: Empty string return on error could be improved.

When transcript fetching fails, the function returns an empty string "". This works but None would be more semantically correct for "no transcript available" and would be consistent with how generate_quiz handles missing input (line 143-144 checks if not transcript_text).

🔎 Proposed fix
 def get_transcript_text(video_id):
     try:
         ytt_api = YouTubeTranscriptApi()
         fetched_transcript = ytt_api.fetch(video_id)

         # fetched_transcript is iterable
         text = " ".join(snippet.text for snippet in fetched_transcript)

         return text.strip()

     except Exception as e:
         logger.error(f"Transcript fetch failed: {e}")
-        return ""
+        return None
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1c3b84d and ffa2076.

📒 Files selected for processing (5)
  • blt/urls.py
  • website/api/views.py
  • website/forms.py
  • website/templates/education/submit_video.html
  • website/views/education.py
🧰 Additional context used
🧬 Code graph analysis (3)
website/api/views.py (1)
website/views/education.py (3)
  • generate_quiz (142-182)
  • submit_educational_video (88-139)
  • extract_youtube_video_id (61-70)
website/views/education.py (1)
website/forms.py (1)
  • EducationalVideoForm (610-632)
blt/urls.py (2)
website/api/views.py (1)
  • submit_educational_video (63-79)
website/views/education.py (1)
  • submit_educational_video (88-139)
🪛 GitHub Check: CodeQL
website/views/education.py

[failure] 66-66: Incomplete URL substring sanitization
The string youtube.com may be at an arbitrary position in the sanitized URL.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (3)
blt/urls.py (1)

747-751: URL routing looks correct; verify if public access is intended.

The endpoint is wired correctly. Note that submit_educational_video has no authentication requirement, meaning anonymous users can submit videos and trigger OpenAI API calls. If this is intentional for public use, consider adding rate limiting to prevent abuse and cost overruns.

website/views/education.py (2)

142-182: Quiz generation looks good with appropriate safeguards.

The implementation includes:

  • Early return for empty transcript
  • Truncation to 3500 chars to stay within token limits
  • Strict JSON-only prompt with clear format specification
  • Low temperature (0.4) for consistent output
  • Proper JSON parsing with error handling

185-221: Educational classification implementation is solid.

Good use of:

  • Temperature 0 for deterministic classification
  • Transcript truncation (2000 chars)
  • Clear binary YES/NO output format
  • Explicit criteria for educational vs non-educational content

@github-project-automation github-project-automation bot moved this from Backlog to Ready in 📌 OWASP BLT Project Board Dec 22, 2025
@github-actions github-actions bot added the changes-requested PR has requested changes from a reviewer label Dec 22, 2025
@github-actions
Copy link
Contributor

❌ Pre-commit checks failed

The pre-commit hooks found issues that need to be fixed. Please run the following commands locally to fix them:

# Install pre-commit if you haven't already
pip install pre-commit

# Run pre-commit on all files
pre-commit run --all-files

# Or run pre-commit on staged files only
pre-commit run

After running these commands, the pre-commit hooks will automatically fix most issues.
Please review the changes, commit them, and push to your branch.

💡 Tip: You can set up pre-commit to run automatically on every commit by running:

pre-commit install
Pre-commit output
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[WARNING] repo `https://github.com/pre-commit/pre-commit-hooks` uses deprecated stage names (commit, push) which will be removed in a future version.  Hint: often `pre-commit autoupdate --repo https://github.com/pre-commit/pre-commit-hooks` will fix this.  if it does not -- consider reporting an issue to that repo.
[INFO] Initializing environment for https://github.com/pycqa/isort.
[WARNING] repo `https://github.com/pycqa/isort` uses deprecated stage names (commit, merge-commit, push) which will be removed in a future version.  Hint: often `pre-commit autoupdate --repo https://github.com/pycqa/isort` will fix this.  if it does not -- consider reporting an issue to that repo.
[INFO] Initializing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Initializing environment for https://github.com/djlint/djLint.
[INFO] Initializing environment for local.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pycqa/isort.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/djlint/djLint.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for local.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
check python ast.........................................................Passed
check builtin type constructor use.......................................Passed
check yaml...............................................................Passed
fix python encoding pragma...............................................Passed
mixed line ending........................................................Passed
isort....................................................................Passed
ruff.....................................................................Passed
ruff-format..............................................................Failed
- hook id: ruff-format
- files were modified by this hook

1 file reformatted, 475 files left unchanged


For more information, see the pre-commit documentation.

@github-actions github-actions bot added pre-commit: failed Pre-commit checks failed and removed pre-commit: passed Pre-commit checks passed labels Dec 22, 2025
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 (2)
website/views/education.py (2)

20-28: This issue was already flagged in a previous review.

The duplicate logger definition and scattered imports were already identified. Please refer to the previous review comment for the recommended fix.


91-142: This issue was already flagged in a previous review.

The missing @login_required decorator and exposure of costly API calls to unauthenticated users was already identified. Please refer to the previous review comment for details.

🧹 Nitpick comments (3)
website/views/education.py (3)

61-73: Clarify URL validation logic to address static analysis warning.

The static analysis warning about substring sanitization is a false positive here because line 67 already validates netloc against an exact allowlist. However, the nested condition at line 70 ("youtube.com" in netloc) is redundant and confusing since netloc was already confirmed to be in allowed_domains.

🔎 Suggested refactor for clarity
 def extract_youtube_video_id(url):
     parsed_url = urlparse(url)
     netloc = parsed_url.netloc.lower() 
 
     allowed_domains = ["youtu.be", "youtube.com", "www.youtube.com"]
 
     if netloc in allowed_domains:
         if netloc == "youtu.be":
             return parsed_url.path.lstrip("/")
-        elif "youtube.com" in netloc:
+        else:  # youtube.com or www.youtube.com
             qs = parse_qs(parsed_url.query)
             return qs.get("v", [None])[0]
     return None

145-186: Consider making quiz length and transcript window configurable.

The function hardcodes 20 questions (line 150) and uses only the first 3500 characters of the transcript (line 167). For short videos, 20 questions may be excessive; for longer educational content, the 3500-character limit captures only a small portion.

Consider:

  • Scaling the number of questions based on transcript length (e.g., 1 question per 200 characters)
  • Making the transcript window configurable or using a smarter sampling strategy for longer content
  • Adding a parameter to control quiz difficulty or focus areas

These changes would improve the user experience across different video lengths.


188-224: Strengthen response parsing for edge cases.

The classification relies on an exact "YES" match after stripping and uppercasing (line 223-224). If OpenAI returns variations like "YES.", "YES\n", or includes additional whitespace, the classification will fail and incorrectly reject educational videos.

🔎 Proposed fix
     answer = response.choices[0].message.content.strip().upper()
-    return answer == "YES"
+    # Handle common variations: "YES", "YES.", "YES\n", etc.
+    return answer.startswith("YES")

Or more robustly:

     answer = response.choices[0].message.content.strip().upper()
-    return answer == "YES"
+    # Extract first word to handle punctuation/whitespace
+    first_word = answer.split()[0] if answer else ""
+    return first_word == "YES"
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between ffa2076 and c58744c.

📒 Files selected for processing (1)
  • website/views/education.py
🧰 Additional context used
🧬 Code graph analysis (1)
website/views/education.py (1)
website/forms.py (1)
  • EducationalVideoForm (610-632)
🪛 GitHub Check: CodeQL
website/views/education.py

[failure] 70-70: Incomplete URL substring sanitization
The string youtube.com may be at an arbitrary position in the sanitized URL.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (1)
website/views/education.py (1)

76-88: No action required—API usage is correct.

The YouTubeTranscriptApi() instantiation with fetch() method is a documented and valid usage pattern in the youtube-transcript-api library.

@github-actions github-actions bot added pre-commit: passed Pre-commit checks passed and removed pre-commit: failed Pre-commit checks failed labels Dec 22, 2025
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 (1)
website/views/education.py (1)

20-28: Import organization and duplicate logger issues persist.

This was already flagged in a previous review: imports are scattered (should be grouped at top), logger is defined twice (lines 18 and 28), and openai.api_key assignment on line 27 is redundant since the client on line 25 already uses the API key.

🧹 Nitpick comments (2)
website/views/education.py (2)

61-73: Simplify conditional to eliminate CodeQL warning.

The static analysis flags "youtube.com" in netloc as incomplete URL sanitization. While the outer netloc in allowed_domains guard makes this safe, the substring check is redundant since allowed_domains only contains "youtube.com" and "www.youtube.com". Simplify to avoid the warning.

🔎 Proposed fix
 def extract_youtube_video_id(url):
     parsed_url = urlparse(url)
     netloc = parsed_url.netloc.lower()
 
     allowed_domains = ["youtu.be", "youtube.com", "www.youtube.com"]
 
     if netloc in allowed_domains:
         if netloc == "youtu.be":
             return parsed_url.path.lstrip("/")
-        elif "youtube.com" in netloc:
+        else:  # youtube.com or www.youtube.com
             qs = parse_qs(parsed_url.query)
             return qs.get("v", [None])[0]
     return None

103-130: Use reverse() instead of hardcoded URL paths.

Multiple redirects use the hardcoded string "/education/submit/". This is fragile if the URL pattern changes. Use Django's reverse() function with the URL name for maintainability.

🔎 Proposed fix
+from django.urls import reverse
+
 # Then replace all occurrences:
-                return redirect("/education/submit/")
+                return redirect(reverse("submit_educational_video"))
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between c58744c and c990d1c.

📒 Files selected for processing (1)
  • website/views/education.py
🧰 Additional context used
🧬 Code graph analysis (1)
website/views/education.py (1)
website/forms.py (1)
  • EducationalVideoForm (610-632)
🪛 GitHub Check: CodeQL
website/views/education.py

[failure] 70-70: Incomplete URL substring sanitization
The string youtube.com may be at an arbitrary position in the sanitized URL.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (1)
website/views/education.py (1)

76-88: LGTM!

The transcript fetching logic handles errors gracefully by logging and returning an empty string, which the caller properly checks.

@github-actions github-actions bot added tests: passed Django tests passed and removed tests: failed Django tests failed labels Dec 22, 2025
@Shhazzz
Copy link
Contributor Author

Shhazzz commented Dec 22, 2025

@Shhazzz can you please address code-rabbit comments.

@mdkaifansari,
I’ve resolved all the requested changes and addressed the review comments.
The PR is updated and ready for maintainer review now. Thanks!

@Shhazzz Shhazzz changed the title Validates educational videos before quiz generation (#3769) Validate educational videos before quiz generation (#3769) Dec 22, 2025
@Shhazzz
Copy link
Contributor Author

Shhazzz commented Dec 22, 2025

Hi @DonnieBLT !

All review feedbacks have been addressed and CI is now passing.
The PR is conflict-free and ready to merge.

Please take a look when you have time!
Thanks a lot!

@Shhazzz
Copy link
Contributor Author

Shhazzz commented Dec 23, 2025

@coderabbitai whom should i ask to review the PR and merge?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Check for CODEOWNERS file
echo "=== Checking for CODEOWNERS file ==="
fd -t f "CODEOWNERS" . 2>/dev/null | head -5

if [ -f "CODEOWNERS" ] || [ -f ".github/CODEOWNERS" ]; then
    echo "Content of CODEOWNERS:"
    cat CODEOWNERS 2>/dev/null || cat .github/CODEOWNERS 2>/dev/null
fi

# Get repository collaborators with write/maintain/admin access
echo -e "\n=== Repository collaborators with write/maintain/admin access ==="
gh api repos/OWASP-BLT/BLT/collaborators --paginate --jq '.[] | select(.permissions.push == true or .permissions.maintain == true or .permissions.admin == true) | "\(.login) - Role: \(.role_name // "collaborator") - Admin: \(.permissions.admin) - Maintain: \(.permissions.maintain)"' 2>/dev/null | head -20

# Get recent PR mergers (last 30 merged PRs)
echo -e "\n=== Recent PR mergers (last 30 PRs) ==="
gh pr list --repo OWASP-BLT/BLT --state merged --limit 30 --json number,mergedBy,mergedAt --jq '.[] | "PR #\(.number): merged by \(.mergedBy.login // "unknown") on \(.mergedAt)"' 2>/dev/null

# Count who merges most frequently
echo -e "\n=== Most frequent PR mergers ==="
gh pr list --repo OWASP-BLT/BLT --state merged --limit 50 --json mergedBy --jq '.[].mergedBy.login' 2>/dev/null | sort | uniq -c | sort -rn | head -10

# Check current PR status
echo -e "\n=== Current PR #5356 status ==="
gh pr view 5356 --repo OWASP-BLT/BLT --json number,title,state,isDraft,reviewDecision,statusCheckRollup --jq '{number, title, state, isDraft, reviewDecision, passing_checks: [.statusCheckRollup[] | select(.conclusion == "SUCCESS" or .conclusion == "NEUTRAL") | .name] | length, total_checks: [.statusCheckRollup[]] | length}'

Length of output: 2269


@Shhazzz Great news! You've already done the right thing by tagging @DonnieBLT in your previous comment.

Based on the repository activity:

Your PR is already APPROVED by reviewers
CI checks are passing (44/49 checks successful)
DonnieBLT is the primary maintainer — they merge 100% of PRs in this repository (all 50 recent PRs were merged by them)

What to do now:

Wait for DonnieBLT to merge — they're the admin and handle all merges for this repository. Since you've already tagged them and the PR is approved with passing CI, it's in the merge queue.

If you don't hear back within a few days, you can:

  • Add a gentle follow-up comment pinging @DonnieBLT again
  • Alternatively, you could mention @AtmegaBuzz or @Sarthak5598 (who have write access) to help get DonnieBLT's attention if needed

But based on recent activity (DonnieBLT merged PR #5357 just yesterday), they're actively reviewing and merging PRs, so your approved PR should be merged soon! 🎉

@Sarthak5598
Copy link
Member

Other than this everything looks good to me , you can further ask @DonnieBLT for the review on it

@github-actions github-actions bot added the last-active: 0d PR last updated 0 days ago label Dec 24, 2025
@github-actions
Copy link
Contributor

💬 Reminder: Unresolved Conversations

Hi @Shhazzz!

This pull request has 1 unresolved conversation that need to be addressed.

Please review and resolve the pending discussions so we can move forward with merging this PR.

Thank you! 🙏

@github-actions github-actions bot added last-active: 0d PR last updated 0 days ago and removed last-active: 0d PR last updated 0 days ago labels Dec 26, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 27, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Check for unresolved review comment threads
echo "=== Checking for unresolved review comment threads ==="
gh api repos/OWASP-BLT/BLT/pulls/5356/comments --paginate --jq '.[] | select(.in_reply_to_id == null) | {id, user: .user.login, created: .created_at, resolved: (.pull_request_review_id != null), body: .body[0:100]}' | head -50

# Check review threads specifically
echo -e "\n=== Checking review threads ==="
gh api repos/OWASP-BLT/BLT/pulls/5356/reviews --jq '.[] | {id, user: .user.login, state, submitted: .submitted_at}' | tail -20

# Get conversation threads status
echo -e "\n=== Getting PR review decision and conversation status ==="
gh pr view 5356 --repo OWASP-BLT/BLT --json reviewDecision,reviews,comments --jq '{reviewDecision, review_count: (.reviews | length), comment_count: (.comments | length), latest_reviews: [.reviews[-5:] | .[] | {author: .author.login, state}]}'

# Check for requested changes
echo -e "\n=== Checking for reviews requesting changes ==="
gh api repos/OWASP-BLT/BLT/pulls/5356/reviews --jq '.[] | select(.state == "CHANGES_REQUESTED" or .state == "COMMENTED") | {user: .user.login, state, submitted: .submitted_at, body: .body[0:200]}'

# Check current PR status
echo -e "\n=== Current PR status ==="
gh pr view 5356 --repo OWASP-BLT/BLT --json state,isDraft,mergeable,reviewDecision,statusCheckRollup --jq '{state, isDraft, mergeable, reviewDecision, passing_checks: [.statusCheckRollup[] | select(.conclusion == "SUCCESS" or .conclusion == "NEUTRAL")] | length, total_checks: [.statusCheckRollup[]] | length}'

# Get recent comments from the PR to see latest status
echo -e "\n=== Recent issue comments on the PR ==="
gh pr view 5356 --repo OWASP-BLT/BLT --json comments --jq '.comments[-10:] | .[] | {author: .author.login, created: .createdAt, body: .body[0:150]}'

Length of output: 14002


🏁 Script executed:

#!/bin/bash

# Check for unresolved conversation threads more specifically
echo "=== Checking review threads resolution status ==="
gh api graphql -f query='
query {
  repository(owner: "OWASP-BLT", name: "BLT") {
    pullRequest(number: 5356) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          isOutdated
          comments(first: 1) {
            nodes {
              author {
                login
              }
              body
              createdAt
            }
          }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {isResolved, isOutdated, firstComment: .comments.nodes[0]}'

# Count unresolved threads
echo -e "\n=== Count of unresolved threads ==="
gh api graphql -f query='
query {
  repository(owner: "OWASP-BLT", name: "BLT") {
    pullRequest(number: 5356) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
        }
      }
    }
  }
}' --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)] | length'

# Get all review threads summary
echo -e "\n=== All review threads summary ==="
gh api graphql -f query='
query {
  repository(owner: "OWASP-BLT", name: "BLT") {
    pullRequest(number: 5356) {
      reviewThreads(first: 100) {
        totalCount
        nodes {
          isResolved
          isOutdated
        }
      }
    }
  }
}' --jq '{totalCount: .data.repository.pullRequest.reviewThreads.totalCount, resolved: [.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == true)] | length, unresolved: [.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)] | length, outdated: [.data.repository.pullRequest.reviewThreads.nodes[] | select(.isOutdated == true)] | length}'

Length of output: 897


@Shhazzz Great question! I checked all conversation threads on your PR. Here's the status:

✅ All Actionable Review Comments Are Resolved

All review comments from human reviewers and my previous feedback have been addressed and marked as resolved.

📋 Status Summary

  • Total review threads: 21
  • Resolved: 19 ✅
  • Unresolved: 2 (but both are outdated)

🔍 The 2 "Unresolved" Threads

The 2 unresolved threads are automated security alerts from GitHub Advanced Security (CodeQL) about "Incomplete URL substring sanitization" in your URL validation code. However:

  1. Both are marked as outdated (the code has changed since they were posted)
  2. These are automated security scans, not human review comments
  3. You've already updated the URL validation logic since these alerts

These outdated security alerts don't block your PR from merging.

✨ Current PR Status

  • Approved by reviewers
  • Mergeable (no conflicts)
  • 59/68 CI checks passing
  • ✅ All human review feedback addressed
  • @Sarthak5598 confirmed everything looks good
  • ✅ You've already tagged @DonnieBLT for final merge

🎯 Next Steps

You're done! 🎉 There's nothing else you need to address. Just wait for @DonnieBLT to merge your PR. Based on recent activity, they actively merge approved PRs, so it should happen soon.

The outdated security alerts will be automatically dismissed once the PR is merged or you can ignore them since the code has been updated.

@github-actions github-actions bot added last-active: 0d PR last updated 0 days ago last-active: 1d PR last updated 1 day ago and removed last-active: 0d PR last updated 0 days ago labels Dec 28, 2025
@Shhazzz
Copy link
Contributor Author

Shhazzz commented Dec 31, 2025

Hi @DonnieBLT
Just a gentle follow-up — all changes are addressed and checks are passing.
Thanks!

@github-actions github-actions bot added last-active: 0d PR last updated 0 days ago last-active: 1d PR last updated 1 day ago last-active: 2d PR last updated 2 days ago and removed last-active: 1d PR last updated 1 day ago last-active: 0d PR last updated 0 days ago last-active: 2d PR last updated 2 days ago labels Dec 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

files-changed: 7 PR changes 7 files last-active: 0d PR last updated 0 days ago needs-peer-review PR needs peer review pre-commit: passed Pre-commit checks passed quality: high quality: medium tests: passed Django tests passed

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

Add a feature to /education that lets people add educational videos from YouTube just from a link

3 participants