Skip to content

Conversation

@rasulkireev
Copy link
Owner

@rasulkireev rasulkireev commented Nov 23, 2025

Summary by CodeRabbit

  • New Features

    • Blog post generation is now asynchronous with progress tracking via task polling.
    • Generation tasks persist across page reloads for seamless recovery.
    • Email notifications sent when blog posts are ready.
    • New task status API endpoint to monitor generation progress.
  • Documentation

    • Updated CHANGELOG with unreleased changes and feature updates.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 23, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

The changes implement an asynchronous workflow for blog post content generation, replacing synchronous processing with a task queue-based system. Updates span backend task definitions, status polling endpoints, frontend task persistence and polling, email notifications, and supporting schema/choice definitions.

Changes

Cohort / File(s) Summary
Schema & API Definitions
core/api/schemas.py, core/choices.py
Modified GeneratedContentOut to remove id field, make status required, default message to empty string, and add task_id field. Added new TaskStatusOut schema with status, message, task_id, and blog_post_id fields. Added BLOG_POST_READY email type to EmailType enum.
Backend Task Processing
core/api/views.py, core/tasks.py
Updated blog content generation flow to queue async tasks instead of processing synchronously. Added new get_task_status() endpoint to poll task progress. Introduced two task functions: generate_blog_post_content() and send_blog_post_ready_email() that orchestrate content generation and completion notifications.
Database Migration
core/migrations/0048_alter_emailsent_email_type.py
New migration altering emailsent.email_type field to include explicit choices including the new BLOG_POST_READY type.
Frontend Task Management
frontend/src/controllers/generate-content-controller.js
Added task persistence and recovery mechanism via _checkForOngoingTask(). Implemented polling loop _pollTaskStatus() to monitor task completion. Added state management methods _updateToCompletedState() and _resetToInitialState() to handle task lifecycle and UI updates.
Email Communication
frontend/templates/emails/blog_post_ready.html
New email template with MJML rendering support and plain-text fallback, notifying users when their blog post is ready with a call-to-action button.
Documentation
CHANGELOG.md
Restructured changelog with new Unreleased section consolidating recent feature additions and changes with improved heading organization (Added/Changed/Fixed/Removed sections).

Sequence Diagrams

sequenceDiagram
    participant User
    participant Frontend as Frontend<br/>(generate-content)
    participant API as Backend API
    participant TaskQueue as Task Queue<br/>(django-q)
    participant Email as Email Service
    
    User->>Frontend: Click "Generate Post"
    Frontend->>API: POST /api/generate-blog-content
    API->>TaskQueue: Queue async_task:<br/>generate_blog_post_content
    API-->>Frontend: {"status": "processing", "task_id": "xyz"}
    Frontend->>Frontend: Store task_id in localStorage
    Frontend->>Frontend: Start polling loop
    
    loop Polling (every N seconds)
        Frontend->>API: GET /api/task-status/xyz
        API->>TaskQueue: Fetch Task status
        alt Task Processing
            API-->>Frontend: {"status": "processing"}
            Frontend->>Frontend: Update elapsed time
        else Task Complete
            TaskQueue->>TaskQueue: generate_blog_post_content executes
            TaskQueue->>Email: Queue send_blog_post_ready_email
            Email->>Email: Render & send notification
            API-->>Frontend: {"status": "completed",<br/>"blog_post_id": 123}
            Frontend->>Frontend: _updateToCompletedState
            Frontend->>Frontend: Clear localStorage
            Frontend->>Frontend: Stop polling
        else Task Failed
            API-->>Frontend: {"status": "failed"}
            Frontend->>Frontend: _resetToInitialState
        end
    end
    
    Frontend->>User: Show completion status & link
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Async task queue integration: Verify task queuing logic, error handling, and status tracking consistency across views.py and tasks.py.
  • Frontend polling mechanism: Review the polling loop timing, retry limits, timeout logic, and state transitions in generate-content-controller.js.
  • Data consistency: Ensure task_id flows correctly from API response through localStorage persistence, polling requests, and database task lookups.
  • Email template rendering: Validate MJML fallback logic and variable interpolation in blog_post_ready.html.
  • Database migration: Confirm enum choices are exhaustive and consistent with updated EmailType class.

Possibly related PRs

  • Check blog post length before sending #72: Addresses async generation tasks, task status APIs, and send_blog_post_ready_email task implementations that directly overlap with this PR's core async refactoring and task lifecycle management.

Poem

🐰 Async tasks now queue with grace,
Polling gently checks the race,
Blog posts bloom when work is done,
Emails celebrate the fun! ✨
No more waiting, tasks will soar—
The long-run dream is here, hooray! 🎉

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch generate-post-via-task

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b16697f and 29acda4.

📒 Files selected for processing (8)
  • CHANGELOG.md (3 hunks)
  • core/api/schemas.py (1 hunks)
  • core/api/views.py (3 hunks)
  • core/choices.py (1 hunks)
  • core/migrations/0048_alter_emailsent_email_type.py (1 hunks)
  • core/tasks.py (1 hunks)
  • frontend/src/controllers/generate-content-controller.js (3 hunks)
  • frontend/templates/emails/blog_post_ready.html (1 hunks)

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.

@rasulkireev rasulkireev merged commit 154e7e5 into main Nov 23, 2025
2 of 3 checks passed
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 23, 2025

Greptile Overview

Greptile Summary

Converted blog post generation from synchronous to asynchronous task-based execution with client-side polling. Users can now navigate away during generation and receive email notifications when complete.

Key changes:

  • API endpoint queues task via django-q2 instead of blocking for 2-5 minutes
  • New /api/task-status/{task_id} endpoint for polling task completion
  • Frontend polls every 3 seconds with localStorage persistence across page reloads
  • Email notification sent when blog post is ready (manual generations only)
  • Task state survives page refreshes for up to 10 minutes

Issues found:

  • generate_and_post_blog_post automated task still uses synchronous generation (lines 477-479, 490-492 in core/tasks.py), which will block workers for 2-5 minutes
  • Authorization check correctly validates task ownership via project__profile=profile filter

Confidence Score: 3/5

  • This PR has a critical architectural inconsistency that should be resolved before merging
  • Manual blog generation properly uses async tasks, but generate_and_post_blog_post (automated posting) still uses synchronous generation. This creates inconsistent behavior and will block workers. The async pattern should be applied consistently across all generation paths.
  • Pay close attention to core/tasks.py - the automated posting task needs to use the new async pattern

Important Files Changed

File Analysis

Filename Score Overview
core/api/views.py 4/5 converted synchronous blog generation to async task with polling endpoint, improved error handling and logging
core/tasks.py 3/5 added blog post content generation task and email notification, but generate_and_post_blog_post still uses synchronous generation
frontend/src/controllers/generate-content-controller.js 4/5 implemented client-side task polling with localStorage persistence and resume on page reload

Sequence Diagram

sequenceDiagram
    participant User
    participant Browser
    participant API as Django API
    participant Queue as django-q2
    participant Task as generate_blog_post_content
    participant Email as Email Service
    
    User->>Browser: Click "Generate" button
    Browser->>Browser: Store taskId in localStorage
    Browser->>API: POST /api/generate-blog-content/{id}
    API->>Queue: Queue task with suggestion_id
    Queue-->>API: Return task_id
    API-->>Browser: {status: "processing", task_id}
    Browser->>Browser: Show "Generating..." state
    
    loop Poll every 3 seconds (max 5 min)
        Browser->>API: GET /api/task-status/{task_id}
        API->>Queue: Check task result
        
        alt Task still processing
            Queue-->>API: None (not complete)
            API-->>Browser: {status: "processing"}
            Browser->>Browser: Update elapsed time display
        else Task completed
            Queue-->>API: Task result
            API->>API: Fetch blog post from DB
            API-->>Browser: {status: "completed", blog_post_id, content}
            Browser->>Browser: Clear localStorage
            Browser->>Browser: Show "View Post" button
            Browser->>User: Show success message
        end
    end
    
    Note over Queue,Task: Background execution (2-5 minutes)
    Queue->>Task: Execute async task
    Task->>Task: suggestion.generate_content()
    Task->>Task: Blog post created
    
    alt send_email=True (manual generation)
        Task->>Queue: Queue email notification
        Queue->>Email: Send blog_post_ready email
        Email->>User: Email notification
    end
    
    Task-->>Queue: Return success message
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (2)

  1. core/tasks.py, line 477-479 (link)

    logic: generate_and_post_blog_post still uses synchronous generation, not the new task-based approach

    This will block the worker for 2-5 minutes. Use the new generate_blog_post_content task with send_email=False:

  2. core/tasks.py, line 490-492 (link)

    logic: generate_and_post_blog_post still uses synchronous generation

    Use the new task with send_email=False:

8 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +476 to +477
task = Task.objects.get(id=task_id)
suggestion_id = task.args[0] if task.args else None
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: missing authorization check - user could access another user's task

Verify task belongs to requesting user:

Suggested change
task = Task.objects.get(id=task_id)
suggestion_id = task.args[0] if task.args else None
try:
task = Task.objects.get(id=task_id)
suggestion_id = task.args[0] if task.args else None
Prompt To Fix With AI
This is a comment left during a code review.
Path: core/api/views.py
Line: 476:477

Comment:
**logic:** missing authorization check - user could access another user's task

Verify task belongs to requesting user:

```suggestion
        try:
            task = Task.objects.get(id=task_id)
            suggestion_id = task.args[0] if task.args else None
```

How can I resolve this? If you propose a fix, please make it concise.

async _pollTaskStatus(taskId) {
const pollInterval = 3000; // Poll every 3 seconds
const initialDelay = 2000; // Wait 2 seconds before first poll
const maxAttempts = 100; // 5 minutes maximum (100 * 3 seconds)
Copy link
Contributor

Choose a reason for hiding this comment

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

style: 5 minutes (100 attempts × 3 seconds) may be insufficient given the stated 2-5 minute generation time plus DB polling delays

Consider increasing to 120 attempts (6 minutes) to provide buffer:

Suggested change
const maxAttempts = 100; // 5 minutes maximum (100 * 3 seconds)
const maxAttempts = 120; // 6 minutes maximum (120 * 3 seconds)
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/controllers/generate-content-controller.js
Line: 174:174

Comment:
**style:** 5 minutes (100 attempts × 3 seconds) may be insufficient given the stated 2-5 minute generation time plus DB polling delays

Consider increasing to 120 attempts (6 minutes) to provide buffer:

```suggestion
    const maxAttempts = 120; // 6 minutes maximum (120 * 3 seconds)
```

How can I resolve this? If you propose a fix, please make it concise.

@coderabbitai coderabbitai bot mentioned this pull request Nov 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants