Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: generate article summaries with chat gpt #203

Merged
merged 8 commits into from
Dec 17, 2024

Conversation

iankressin
Copy link
Member

@iankressin iankressin commented Dec 11, 2024

Adds a new summary field, called gpt_summary to the article model, which is the field that will store the article summary generated by ChatGPT API.

Also includes new GPT service, which takes a system prompt and a user prompt and return the result from the prompt. This is what's being used to generate summaries.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a "Generate Summary" button in the article administration interface for automatic summary generation.
    • Added rich text editing capabilities for article summaries.
    • Integrated a new GPT service for generating summaries based on article content.
    • Included a new field for storing GPT-generated summaries in the article model.
  • Bug Fixes

    • Enhanced error handling for summary generation requests.
  • Documentation

    • Updated documentation for the services package to clarify its purpose.
  • Style

    • Added new CSS styles for the summary generation button and status display.
  • Chores

    • Updated dependencies to improve project functionality and security.

Copy link

coderabbitai bot commented Dec 11, 2024

Warning

Rate limit exceeded

@iankressin has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 5 minutes and 25 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 4b789e8 and e393972.

📒 Files selected for processing (1)
  • server/apps/research/models/article.py (1 hunks)

Walkthrough

The pull request introduces significant updates to the project, primarily through the addition of new dependencies in requirements.txt to enhance functionality in web development, asynchronous task management, and data processing. Additionally, the ArticleAdmin class is enhanced with new features for generating summaries using a GPT service, and the article model is updated to support new summary fields. CSS and JavaScript files are modified to accommodate the new summary generation functionality in the admin interface.

Changes

File Path Change Summary
requirements.txt Added multiple new dependencies with specific versions, including libraries for Django, Celery, and data handling.
server/apps/research/admin/article_admin.py Enhanced ArticleAdmin with a new gpt_summary field, added methods for generating summaries, and updated media.
server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py Added gpt_summary field and changed summary field type to HTMLField in the Article model.
server/apps/research/models/article.py Updated summary field type to HTMLField and added gpt_summary field to the Article model.
server/apps/research/services/init.py Added a docstring to describe the services package.
server/apps/research/services/gpt_service.py Introduced GPTService class for OpenAI API interactions, including an asynchronous prompt method.
server/core/config/base.py Added configuration for OPENAI_API_KEY to retrieve it from environment variables.
server/static/css/article_admin.css Introduced new CSS styles for .generate-summary-btn, .summary-status, and .generate-summary-container-btn.
server/static/js/article_admin.js Added functionality for a "Generate Summary" button, including content validation and asynchronous summary generation.

Possibly related PRs

  • feat: auto-redirect url when slug changes. #193: The changes in this PR involve modifications to the article_admin.py file, which is also affected by the main PR that updates the requirements.txt file. The enhancements in both PRs relate to the ArticleAdmin class, indicating a connection through the shared context of managing article-related functionalities.
  • chore(feat): add relatedArticles logic #198: This PR also modifies the article_admin.py file, adding inline management for related articles. The main PR's updates to dependencies may support the functionalities introduced in this PR, particularly if any of the new libraries are utilized in the related articles logic.

Suggested reviewers

  • losndu

🐰 In the garden, where ideas bloom,
New tools and features chase away gloom.
With summaries generated, oh what a delight,
Our articles shine, oh so bright!
Let's hop and cheer for the code we create,
Together we build, it's never too late! 🌼✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • 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

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

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

🧹 Outside diff range and nitpick comments (7)
server/apps/research/admin/article_admin.py (3)

6-6: Remove unused import settings

The django.conf.settings module is imported but not used in this file. Consider removing it to clean up the imports.

Apply this diff to fix the issue:

- from django.conf import settings
🧰 Tools
🪛 Ruff (0.8.2)

6-6: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


10-10: Remove unused import asyncio

After replacing asyncio.run with async_to_sync, the asyncio module is no longer used and can be removed.

Apply this diff to fix the issue:

- import asyncio

21-21: Remove unnecessary f prefix in string

The string in the placeholder attribute does not contain any variables, so the f prefix is unnecessary. Removing it avoids confusion.

Apply this diff to fix the issue:

- self.fields['summary'].widget = TinyMCE(attrs={'cols': 80, 'rows': 30, 'id': "summary_richtext_field", 'placeholder': f"Article summary will be generated here"})
+ self.fields['summary'].widget = TinyMCE(attrs={'cols': 80, 'rows': 30, 'id': "summary_richtext_field", 'placeholder': "Article summary will be generated here"})
🧰 Tools
🪛 Ruff (0.8.2)

21-21: f-string without any placeholders

Remove extraneous f prefix

(F541)

server/apps/research/services/gpt_service.py (2)

41-41: Use logging instead of print statements in exception handling

Using print statements is not recommended for production code. Consider using Python's logging module to log exceptions appropriately.

Apply this diff to fix the issue:

  except Exception as e:
-     print(e)
+     import logging
+     logging.exception("Error calling OpenAI API:", exc_info=e)
      raise Exception(f"Error calling OpenAI API: {str(e)}") from e

Ensure that your logging configuration is set up to handle logged exceptions.


42-42: Use exception chaining when re-raising exceptions

When re-raising an exception, use raise ... from e to preserve the original stack trace and exception context. This aids in debugging and provides more informative error messages.

Apply this diff to fix the issue:

  except Exception as e:
      logging.exception("Error calling OpenAI API:", exc_info=e)
-     raise Exception(f"Error calling OpenAI API: {str(e)}") 
+     raise Exception(f"Error calling OpenAI API: {str(e)}") from e
🧰 Tools
🪛 Ruff (0.8.2)

42-42: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

server/apps/research/models/article.py (1)

28-29: Add validation methods for summary fields

Consider adding methods to validate summary length and content to ensure they meet your requirements.

+    def clean(self):
+        super().clean()
+        if self.summary and len(self.summary) > 5000:  # adjust limit as needed
+            raise ValidationError({
+                'summary': 'Summary is too long. Maximum length is 5000 characters.'
+            })
+        if self.gpt_summary and len(self.gpt_summary) > 5000:
+            raise ValidationError({
+                'gpt_summary': 'GPT summary is too long. Maximum length is 5000 characters.'
+            })
server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py (1)

19-23: Consider adding a data migration strategy

The migration alters the summary field type and adds gpt_summary, but doesn't handle existing data. Consider:

  1. Adding a data migration to populate gpt_summary for existing articles
  2. Ensuring consistency between summary and gpt_summary fields

Would you like me to help create a data migration strategy?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5f42eb2 and dad1e79.

📒 Files selected for processing (9)
  • requirements.txt (1 hunks)
  • server/apps/research/admin/article_admin.py (3 hunks)
  • server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py (1 hunks)
  • server/apps/research/models/article.py (1 hunks)
  • server/apps/research/services/__init__.py (1 hunks)
  • server/apps/research/services/gpt_service.py (1 hunks)
  • server/core/config/base.py (1 hunks)
  • server/static/css/article_admin.css (1 hunks)
  • server/static/js/article_admin.js (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • server/static/css/article_admin.css
  • server/apps/research/services/init.py
🧰 Additional context used
🪛 Ruff (0.8.2)
server/apps/research/services/gpt_service.py

42-42: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

server/apps/research/admin/article_admin.py

6-6: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


21-21: f-string without any placeholders

Remove extraneous f prefix

(F541)

🪛 GitHub Check: CodeQL
server/apps/research/admin/article_admin.py

[warning] 57-57: Information exposure through an exception
Stack trace information flows to this location and may be exposed to an external user.

🔇 Additional comments (4)
server/apps/research/services/gpt_service.py (1)

1-2: ⚠️ Potential issue

Correct the usage of OpenAI's asynchronous API

The AsyncOpenAI class does not exist in the OpenAI Python library. To use the asynchronous API, import the openai module and use await openai.ChatCompletion.acreate(...).

Apply this diff to fix the issue:

- from django.conf import settings
- from openai import AsyncOpenAI
+ import openai
+ from django.conf import settings

  class GPTService:
      """Service for handling OpenAI GPT API interactions."""
      
      def __init__(self):
-         self.client = AsyncOpenAI(api_key=settings.OPENAI_API_KEY)
+         openai.api_key = settings.OPENAI_API_KEY
          self.model = "gpt-3.5-turbo"
          self.max_tokens = 500

      async def prompt(self, system: str, user: str) -> str:
          """
          Send a prompt to GPT and get the response.
          """
          if not settings.OPENAI_API_KEY:
              raise Exception("OpenAI API key is not configured")

          try:
-             completion = await self.client.chat.completions.create(
+             completion = await openai.ChatCompletion.acreate(
                  model=self.model,
                  messages=[
                      {"role": "system", "content": system},
                      {"role": "user", "content": user}
                  ],
                  max_tokens=self.max_tokens
              )
              return completion.choices[0].message.content
          except Exception as e:
              print(e)
              raise Exception(f"Error calling OpenAI API: {str(e)}") 

Likely invalid or redundant comment.

server/core/config/base.py (1)

201-202: ⚠️ Potential issue

Add validation for required OpenAI API key

The current implementation allows None as a default value, which could lead to runtime errors. Since this is a required feature, we should fail fast if the API key is missing.

-OPENAI_API_KEY = config('OPENAI_API_KEY', default=None)
+OPENAI_API_KEY = config('OPENAI_API_KEY')

Also, verify that the API key is properly set in your environment:

server/apps/research/models/article.py (1)

28-29: 🛠️ Refactor suggestion

Verify migration strategy for existing summaries

The change from TextField to HTMLField for summary requires careful consideration of existing data. Additionally, consider adding help text for both fields to clarify their purpose.

-    summary = HTMLField(blank=True, null=True)
-    gpt_summary = models.TextField(blank=True, null=True)
+    summary = HTMLField(
+        blank=True,
+        null=True,
+        help_text="Manual summary with rich text formatting support"
+    )
+    gpt_summary = models.TextField(
+        blank=True,
+        null=True,
+        help_text="Auto-generated summary by GPT"
+    )
✅ Verification successful

Migration strategy for summary field is properly handled

The migration strategy is appropriate and safe:

  • Initial migration (0001) shows summary was a TextField with blank=True
  • Current migration (0017) properly handles the field change from TextField to HTMLField while preserving nullability
  • No data migration is needed as HTMLField is compatible with TextField data

Your suggestion to add help text is valuable for code clarity but not critical for the migration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for existing migrations related to summary field
fd -t f ".*_article.*\.py$" "server/apps/research/migrations" -x grep -l "summary"

Length of output: 162


Script:

#!/bin/bash
# Check the content of the migration file to understand the migration strategy
cat server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py

Length of output: 680


Script:

#!/bin/bash
# Check for any previous migrations that might have defined the initial summary field
fd -t f "^[0-9]{4}_initial\.py$" "server/apps/research/migrations" -x grep -l "summary"

Length of output: 133


Script:

#!/bin/bash
# Look for any data migrations related to summary field
rg "migrations\.RunPython" "server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py"

Length of output: 110


Script:

#!/bin/bash
# Check the initial migration to understand the original field type
cat server/apps/research/migrations/0001_initial.py

Length of output: 3374


Script:

#!/bin/bash
# Check if there are any existing data in the summary field that might need special handling
rg "summary\s*=\s*models\." server/apps/research/models/article.py -B2 -A2

Length of output: 365

server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py (1)

14-18: Verify the impact of adding the new gpt_summary field

The migration adds a new nullable TextField for GPT summaries. Let's verify there are no existing constraints or triggers that might be affected.

✅ Verification successful

Let me check the complete Article model definition to verify any constraints or dependencies related to the summary fields.


Adding gpt_summary field is safe and won't affect existing functionality

After reviewing the Article model, I can confirm that:

  • The new gpt_summary field is properly added as a nullable TextField
  • There are no constraints, triggers, or model methods that depend on or reference the summary fields
  • The save method and other model methods don't have any logic tied to summary or gpt_summary fields
  • Both summary and gpt_summary being nullable maintains backward compatibility
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any database constraints or triggers related to the Article model
rg -A 5 "class Article" server/apps/research/models/

Length of output: 1016


Script:

#!/bin/bash
# Get the complete Article model definition
cat server/apps/research/models/article.py

Length of output: 5726

server/apps/research/admin/article_admin.py Outdated Show resolved Hide resolved
server/apps/research/admin/article_admin.py Outdated Show resolved Hide resolved
server/static/js/article_admin.js Outdated Show resolved Hide resolved
server/static/js/article_admin.js Outdated Show resolved Hide resolved
server/static/js/article_admin.js Outdated Show resolved Hide resolved
requirements.txt Show resolved Hide resolved
requirements.txt Show resolved Hide resolved
requirements.txt Show resolved Hide resolved
Copy link

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

🧹 Outside diff range and nitpick comments (5)
server/static/css/article_admin.css (1)

1-11: LGTM! Consider adding hover state for better UX

The button styling is well-defined with good visual feedback for disabled state.

Consider adding hover state for better user experience:

 .generate-summary-btn {
     background-color: #0C4B33;
     color: white;
     padding: 10px 15px;
     border: none;
     border-radius: 4px;
     cursor: pointer;
     font-weight: bold;
     margin-left: 10px;
     align-self: baseline;
 }
+.generate-summary-btn:hover:not(:disabled) {
+    background-color: #0d5c3f;
+}
server/apps/research/admin/article_admin.py (4)

6-6: Remove unused import

The settings import is not used in the code.

-from django.conf import settings
🧰 Tools
🪛 Ruff (0.8.2)

6-6: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


21-21: Remove unnecessary f-string

The string doesn't contain any placeholders, making the f-string unnecessary.

-        self.fields['gpt_summary'].widget = TinyMCE(attrs={'cols': 80, 'rows': 15, 'id': "gpt_summary_richtext_field", 'placeholder': f"GPT-generated summary will appear here"})
+        self.fields['gpt_summary'].widget = TinyMCE(attrs={'cols': 80, 'rows': 15, 'id': "gpt_summary_richtext_field", 'placeholder': "GPT-generated summary will appear here"})
🧰 Tools
🪛 Ruff (0.8.2)

21-21: f-string without any placeholders

Remove extraneous f prefix

(F541)


38-47: Consider making system prompt configurable

The system prompt is hardcoded in the code. Consider moving it to settings or a configuration file for easier maintenance.

+    SYSTEM_PROMPT = (
+        "You are a professional summarizer at 2077 Research. Below is an article on Ethereum technical aspects. "
+        "Your goal is to produce a summary that is shorter than the original content, yet detailed enough for readers "
+        "to fully understand the piece without needing to read the original. Your summary should:\n"
+        "- Provide enough depth and detail so the user gets a complete understanding of the core ideas.\n"
+        "- Be in HTML format, use <h3> tags for headings if needed. Avoid other heading levels.\n"
+        "- Minimize the use of bullet points. If you need to list items, you can, but prefer concise paragraph formatting.\n\n"
+    )
+
     async def _generate_summary(self, content: str) -> str:
-        system_prompt = (
-            "You are a professional summarizer at 2077 Research. Below is an article on Ethereum technical aspects. "
-            "Your goal is to produce a summary that is shorter than the original content, yet detailed enough for readers "
-            "to fully understand the piece without needing to read the original. Your summary should:\n"
-            "- Provide enough depth and detail so the user gets a complete understanding of the core ideas.\n"
-            "- Be in HTML format, use <h3> tags for headings if needed. Avoid other heading levels.\n"
-            "- Minimize the use of bullet points. If you need to list items, you can, but prefer concise paragraph formatting.\n\n"
-        )
-        return await self.gpt_service.prompt(system_prompt, content)
+        return await self.gpt_service.prompt(self.SYSTEM_PROMPT, content)

65-65: Consider grouping related fields

The gpt_summary field is placed between summary and status. Consider grouping it with summary for better organization.

-        ('Article Details', {'fields': ['title', 'slug', 'authors', 'acknowledgement', 'categories', 'thumb', 'content', 'summary', 'gpt_summary', 'status', 'scheduled_publish_time']}),
+        ('Article Details', {'fields': ['title', 'slug', 'authors', 'acknowledgement', 'categories', 'thumb', 'content', 'summary', 'status', 'scheduled_publish_time']}),
+        ('AI-Generated Content', {'fields': ['gpt_summary']}),
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between dad1e79 and 13bda40.

📒 Files selected for processing (5)
  • server/apps/research/admin/article_admin.py (3 hunks)
  • server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py (1 hunks)
  • server/apps/research/models/article.py (1 hunks)
  • server/static/css/article_admin.css (1 hunks)
  • server/static/js/article_admin.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py
  • server/apps/research/models/article.py
🧰 Additional context used
🪛 Ruff (0.8.2)
server/apps/research/admin/article_admin.py

6-6: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


21-21: f-string without any placeholders

Remove extraneous f prefix

(F541)

🪛 GitHub Check: CodeQL
server/apps/research/admin/article_admin.py

[warning] 57-57: Information exposure through an exception
Stack trace information flows to this location and may be exposed to an external user.

🔇 Additional comments (4)
server/static/js/article_admin.js (3)

25-52: ⚠️ Potential issue

Previous race condition and error handling concerns still apply

The click handler needs improvements for preventing race conditions and handling timeouts.

Refer to the previous review comment about improving error handling and preventing race conditions.


57-85: ⚠️ Potential issue

Enhance error handling in generateSummary function

The error handling could be more robust with better error message extraction.

Refer to the previous review comment about enhancing error handling and adding content validation.


47-48: ⚠️ Potential issue

Fix undefined variable reference

The variable gptSummaryEditor is undefined. This should be summaryEditor.

-                            contentEditor.setMode('design');
-                            gptSummaryEditor.setMode('design');
+                            contentEditor.setMode('design');
+                            const summaryEditor = tinymce.get('gpt_summary_richtext_field');
+                            if (summaryEditor) {
+                                summaryEditor.setMode('design');
+                            }

Likely invalid or redundant comment.

server/apps/research/admin/article_admin.py (1)

49-58: ⚠️ Potential issue

Previous security and asyncio concerns still apply

The view has two issues that were highlighted in previous reviews:

  1. Exception details exposure
  2. Use of asyncio.run in a synchronous view

Refer to the previous review comments about:

  1. Avoiding exposing internal exception details
  2. Using async_to_sync instead of asyncio.run
🧰 Tools
🪛 GitHub Check: CodeQL

[warning] 57-57: Information exposure through an exception
Stack trace information flows to this location and may be exposed to an external user.

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Copy link

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

🧹 Outside diff range and nitpick comments (4)
server/apps/research/admin/article_admin.py (4)

6-6: Remove unused import

The settings import is not used in this file.

-from django.conf import settings
🧰 Tools
🪛 Ruff (0.8.2)

6-6: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


21-21: Remove unnecessary f-string

The string doesn't contain any placeholders, so the f prefix is not needed.

-        self.fields['gpt_summary'].widget = TinyMCE(attrs={'cols': 80, 'rows': 15, 'id': "gpt_summary_richtext_field", 'placeholder': f"GPT-generated summary will appear here"})
+        self.fields['gpt_summary'].widget = TinyMCE(attrs={'cols': 80, 'rows': 15, 'id': "gpt_summary_richtext_field", 'placeholder': "GPT-generated summary will appear here"})
🧰 Tools
🪛 Ruff (0.8.2)

21-21: f-string without any placeholders

Remove extraneous f prefix

(F541)


27-29: Consider making GPTService a singleton or class-level instance

Creating a new GPTService instance for each ArticleAdmin instance could be inefficient. Consider making it a class-level attribute or implementing it as a singleton.

class ArticleAdmin(admin.ModelAdmin):
    """Admin interface for the Article model."""
    form = ArticleForm
+    gpt_service = GPTService()

    def __init__(self, model, admin_site):
        super().__init__(model, admin_site)
-        self.gpt_service = GPTService()

38-47: Move system prompt to settings or constants

The system prompt should be moved to a settings file or constants module for better maintainability and reusability.

+# In constants.py or settings.py
+GPT_SYSTEM_PROMPT = """
+You are a professional summarizer at 2077 Research. Below is an article on Ethereum technical aspects. 
+Your goal is to produce a summary that is shorter than the original content, yet detailed enough for readers 
+to fully understand the piece without needing to read the original. Your summary should:
+- Provide enough depth and detail so the user gets a complete understanding of the core ideas.
+- Be in HTML format, use <h3> tags for headings if needed. Avoid other heading levels.
+- Minimize the use of bullet points. If you need to list items, you can, but prefer concise paragraph formatting.
+"""

# In article_admin.py
async def _generate_summary(self, content: str) -> str:
-    system_prompt = (
-        "You are a professional summarizer at 2077 Research. Below is an article on Ethereum technical aspects. "
-        "Your goal is to produce a summary that is shorter than the original content, yet detailed enough for readers "
-        "to fully understand the piece without needing to read the original. Your summary should:\n"
-        "- Provide enough depth and detail so the user gets a complete understanding of the core ideas.\n"
-        "- Be in HTML format, use <h3> tags for headings if needed. Avoid other heading levels.\n"
-        "- Minimize the use of bullet points. If you need to list items, you can, but prefer concise paragraph formatting.\n\n"
-    )
-    return await self.gpt_service.prompt(system_prompt, content)
+    return await self.gpt_service.prompt(settings.GPT_SYSTEM_PROMPT, content)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 13bda40 and 90e6547.

📒 Files selected for processing (1)
  • server/apps/research/admin/article_admin.py (3 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
server/apps/research/admin/article_admin.py

6-6: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


21-21: f-string without any placeholders

Remove extraneous f prefix

(F541)


56-56: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

🔇 Additional comments (2)
server/apps/research/admin/article_admin.py (2)

49-60: ⚠️ Potential issue

Replace asyncio.run with async_to_sync

Using asyncio.run in a Django view can lead to performance issues. Use async_to_sync from asgiref.sync instead.

+from asgiref.sync import async_to_sync

def generate_summary_view(self, request):
    if request.method == 'POST':
        content = request.POST.get('content')
        try:
-            gpt_summary = asyncio.run(self._generate_summary(content))
+            gpt_summary = async_to_sync(self._generate_summary)(content)
            return JsonResponse({'summary': gpt_summary})
        except Exception as e:
            import logging
            logging.error("An error occurred while generating the summary", exc_info=True)
            return JsonResponse({'error': 'An internal error has occurred!'}, status=500)
    return JsonResponse({'error': 'Invalid request method'}, status=400)
🧰 Tools
🪛 Ruff (0.8.2)

56-56: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


82-86: Verify static files existence and content

Ensure that the referenced static files exist and contain the necessary code for summary generation functionality.

✅ Verification successful

Static files are properly implemented with summary generation functionality

The static files exist and contain the necessary code:

  • CSS file includes styles for the summary generation button and status indicators
  • JS file implements the complete summary generation functionality:
    • Integrates with TinyMCE editor
    • Handles API calls to generate summaries
    • Manages UI state and error handling
    • Includes CSRF protection
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the static files exist and contain required code
echo "Checking for static files..."
fd -t f "article_admin\.(css|js)$"

echo "Checking CSS content for summary-related styles..."
rg -l "summary|gpt" "**/*article_admin.css"

echo "Checking JS content for summary generation functionality..."
rg -l "generateSummary|summary" "**/*article_admin.js"

Length of output: 639


Script:

#!/bin/bash
# Check content of the static files using the correct paths
echo "Checking CSS content for summary-related styles..."
cat "server/static/css/article_admin.css"

echo -e "\nChecking JS content for summary generation functionality..."
cat "server/static/js/article_admin.js"

Length of output: 4782

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
server/apps/research/serializers/article_serializer.py (2)

29-29: Consider adding field-level configuration for gpt_summary.

The gpt_summary field might benefit from additional configuration:

  1. Consider making it read-only since it's generated by GPT service
  2. Consider grouping it with the summary field for better organization
 class ArticleSerializer(serializers.ModelSerializer):
     authors = AuthorSerializer(many=True, read_only=True)
     categories = CategorySerializer(many=True)       
     views = serializers.ReadOnlyField()
     min_read = serializers.ReadOnlyField()
+    gpt_summary = serializers.CharField(read_only=True)
     
     class Meta:
         model = Article
         fields = [
-            'id', 'slug', 'title', 'authors', 'thumb', 'categories', 'summary',
-            'acknowledgement', 'content', 'min_read', 'status', 'views', 'gpt_summary',
+            'id', 'slug', 'title', 'authors', 'thumb', 'categories',
+            'summary', 'gpt_summary',  # Group summary fields together
+            'acknowledgement', 'content', 'min_read', 'status', 'views',
             'created_at', 'updated_at', 'scheduled_publish_time', 'table_of_contents',
             'is_sponsored', 'sponsor_color', 'sponsor_text_color'
         ]

Line range hint 10-10: Fix typo in ArticleListSerializer's include field.

There's a missing comma in the include list which would cause a syntax error.

     class Meta:
         model = Article
-        include = ['categories' 'authors']
+        include = ['categories', 'authors']
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 90e6547 and 182edbb.

📒 Files selected for processing (1)
  • server/apps/research/serializers/article_serializer.py (2 hunks)

@@ -26,7 +26,7 @@ class Meta:
model = Article
fields = [
'id', 'slug', 'title', 'authors', 'thumb', 'categories', 'summary',
'acknowledgement', 'content', 'min_read', 'status', 'views',
'acknowledgement', 'content', 'min_read', 'status', 'views', 'gpt_summary',
Copy link

@coderabbitai coderabbitai bot Dec 12, 2024

Choose a reason for hiding this comment

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

💡 Codebase verification

The gpt_summary field exists in the Article model but with a different field type

The gpt_summary field exists in both the Article model and the migration file, but there's a discrepancy in the field type:

  • In the model: models.TextField(blank=True, null=True)
  • In the migration: tinymce.models.HTMLField(blank=True, null=True)

This inconsistency needs to be addressed to match the field types.

🔗 Analysis chain

Verify the Article model schema.

Ensure that the gpt_summary field exists in the Article model schema as referenced in both serializers.

Also applies to: 41-41


Let me gather more information about the gpt_summary field from the migration file and the complete Article model.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if gpt_summary field exists in Article model and its migration

# Check Article model
rg -A 5 "class Article" server/apps/research/models/

# Check migrations for gpt_summary field
rg "gpt_summary" server/apps/research/migrations/

Length of output: 1180


Script:

#!/bin/bash
# Check the complete Article model definition
cat server/apps/research/models/article.py

# Check the migration file that added gpt_summary
cat server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py

Length of output: 6230

Copy link
Collaborator

Choose a reason for hiding this comment

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

There still remains an issue here to be resolved

Copy link

Choose a reason for hiding this comment

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

@losndu, to resolve the remaining issue, please ensure that the gpt_summary field in the Article model and the migration have the same field type. Currently, there is a mismatch:

  • In server/apps/research/models/article.py, gpt_summary is defined as:

    gpt_summary = models.TextField(blank=True, null=True)
  • In server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py, it is defined as:

    field=models.HTMLField(blank=True, null=True),

To fix the inconsistency, you can update the model to use HTMLField. Here's how:

  1. Import HTMLField from tinymce.models in your model file:

    from tinymce.models import HTMLField
  2. Update the gpt_summary field in the Article model:

    gpt_summary = HTMLField(blank=True, null=True)

Alternatively, if you prefer to keep gpt_summary as a TextField, you should update the migration file to reflect TextField instead of HTMLField. However, it's generally recommended to have rich text fields like summaries use HTMLField for better content management.

After making the changes, don't forget to run:

python manage.py makemigrations
python manage.py migrate

This will create a new migration to reflect the changes and apply it to your database.

Let me know if you have any questions or need further assistance!

@@ -38,7 +38,7 @@

class Meta:
model = Article
fields = ['title', 'slug', 'categories', 'thumb', 'content', 'summary', 'acknowledgement', 'status', 'authors', 'scheduled_publish_time', 'is_sponsored', 'sponsor_color', 'sponsor_text_color']
fields = ['title', 'slug', 'categories', 'thumb', 'content', 'summary', 'gpt_summary', 'acknowledgement', 'status', 'authors', 'scheduled_publish_time', 'is_sponsored', 'sponsor_color', 'sponsor_text_color']
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve field list readability and handling.

The current implementation has the following issues:

  1. The line is too long, making it hard to read
  2. Field ordering could be improved by grouping related fields
  3. The gpt_summary field might need special handling in create/update methods
     class Meta:
         model = Article
-        fields = ['title', 'slug', 'categories', 'thumb', 'content', 'summary', 'gpt_summary', 'acknowledgement', 'status', 'authors', 'scheduled_publish_time', 'is_sponsored', 'sponsor_color', 'sponsor_text_color']
+        fields = [
+            # Basic info
+            'title', 'slug', 'authors', 'categories',
+            # Content
+            'thumb', 'content',
+            # Summaries
+            'summary', 'gpt_summary',
+            # Additional info
+            'acknowledgement', 'status',
+            'scheduled_publish_time',
+            # Sponsorship
+            'is_sponsored', 'sponsor_color', 'sponsor_text_color'
+        ]

Also, consider adding validation in create/update methods to ensure gpt_summary isn't directly set through the API:

     def create(self, validated_data: dict) -> Article:
         """Create a new article instance."""
+        # Ensure gpt_summary can't be set directly
+        validated_data.pop('gpt_summary', None)
         request = self.context.get('request')
         authors = validated_data.pop('authors', [])
         categories = validated_data.pop('categories', [])

     def update(self, instance: Article, validated_data: dict) -> Article:
         """Update an existing article instance."""
+        # Ensure gpt_summary can't be set directly
+        validated_data.pop('gpt_summary', None)
         authors = validated_data.pop('authors', [])
         categories = validated_data.pop('categories', [])
📝 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
fields = ['title', 'slug', 'categories', 'thumb', 'content', 'summary', 'gpt_summary', 'acknowledgement', 'status', 'authors', 'scheduled_publish_time', 'is_sponsored', 'sponsor_color', 'sponsor_text_color']
fields = [
# Basic info
'title', 'slug', 'authors', 'categories',
# Content
'thumb', 'content',
# Summaries
'summary', 'gpt_summary',
# Additional info
'acknowledgement', 'status',
'scheduled_publish_time',
# Sponsorship
'is_sponsored', 'sponsor_color', 'sponsor_text_color'
]

Copy link

@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

🧹 Outside diff range and nitpick comments (2)
server/apps/research/admin/article_admin.py (2)

7-7: Remove unused import

The settings import is not used in this file.

-from django.conf import settings
🧰 Tools
🪛 Ruff (0.8.2)

7-7: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


29-47: Consider refactoring TinyMCE widget configuration

The TinyMCE widget configuration is duplicated across multiple fields. Consider extracting the common configuration into a helper function.

+    def _get_tinymce_widget(self, rows=30, field_name=""):
+        return TinyMCE(attrs={
+            'cols': 80,
+            'rows': rows,
+            'id': f"{field_name}_richtext_field",
+            'placeholder': f"Enter {field_name.title()} here"
+        })

     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-        self.fields['acknowledgement'].widget = TinyMCE(attrs={
-            'cols': 80, 
-            'rows': 30, 
-            'id': "acknowledgement_richtext_field", 
-            'placeholder': "Enter Acknowledgement here"
-        })
-        self.fields['content'].widget = TinyMCE(attrs={
-            'cols': 80, 
-            'rows': 30, 
-            'id': "content_richtext_field", 
-            'placeholder': "Enter Article Content here"
-        })
-        self.fields['gpt_summary'].widget = TinyMCE(attrs={
-            'cols': 80, 
-            'rows': 15, 
-            'id': "gpt_summary_richtext_field", 
-            'placeholder': "GPT-generated summary will appear here"
-        })
+        self.fields['acknowledgement'].widget = self._get_tinymce_widget(field_name="acknowledgement")
+        self.fields['content'].widget = self._get_tinymce_widget(field_name="content")
+        self.fields['gpt_summary'].widget = self._get_tinymce_widget(rows=15, field_name="gpt_summary")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 182edbb and 96e3449.

📒 Files selected for processing (4)
  • server/apps/research/admin/article_admin.py (4 hunks)
  • server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py (1 hunks)
  • server/apps/research/models/article.py (1 hunks)
  • server/apps/research/serializers/article_serializer.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py
  • server/apps/research/serializers/article_serializer.py
  • server/apps/research/models/article.py
🧰 Additional context used
🪛 Ruff (0.8.2)
server/apps/research/admin/article_admin.py

7-7: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


81-81: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

🔇 Additional comments (4)
server/apps/research/admin/article_admin.py (4)

53-62: LGTM! Clean implementation of service initialization and URL configuration

The implementation follows Django best practices:

  • Service initialization in __init__ ensures single instance per admin
  • URL configuration properly extends Django's admin URLs

119-124: LGTM! Proper setup of admin assets

The Media class is correctly configured to include custom CSS and JavaScript files for the summary generation feature.


75-85: 🛠️ Refactor suggestion

Improve async handling and error management

  1. Replace asyncio.run with async_to_sync for better Django integration
  2. Enhance error handling with structured logging
+from asgiref.sync import async_to_sync
+import logging

     def generate_summary_view(self, request):
         if request.method == 'POST':
             content = request.POST.get('content')
+            if not content:
+                return JsonResponse({'error': 'Content is required'}, status=400)
             try:
-                gpt_summary = asyncio.run(self._generate_summary(content))
+                gpt_summary = async_to_sync(self._generate_summary)(content)
                 return JsonResponse({'summary': gpt_summary})
             except Exception as e:
-                import logging
-                logging.error("An error occurred while generating the summary", exc_info=True)
+                logging.error(
+                    "Failed to generate summary",
+                    extra={
+                        'content_length': len(content),
+                        'error_type': type(e).__name__
+                    },
+                    exc_info=True
+                )
                 return JsonResponse({'error': 'An internal error has occurred!'}, status=500)
🧰 Tools
🪛 Ruff (0.8.2)

81-81: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


10-10: Verify GPT service integration

Let's verify the GPT service implementation and its dependencies.

✅ Verification successful

GPT service integration is properly implemented

The verification shows a complete and well-structured integration:

  • GPTService is correctly implemented with async support and proper error handling
  • The service is properly imported and instantiated in article_admin.py
  • The generate_summary_view implementation includes proper error handling and async execution
  • OpenAI API key configuration is properly handled through settings
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check GPT service implementation and related files

# Check if GPT service exists and has required methods
echo "Checking GPT service implementation..."
ast-grep --pattern 'class GPTService {
  $$$
  prompt($_, $_) {
    $$$
  }
  $$$
}'

# Check for related static files
echo "Checking static files..."
fd -e css -e js "article_admin" server/static

# Check for potential circular imports
echo "Checking for circular imports..."
rg -l "from.*article_admin.*import" server/apps/research/

Length of output: 552


Script:

#!/bin/bash
# Let's check the actual GPTService implementation and its usage

# Check GPTService implementation
echo "Checking GPTService implementation in services directory..."
rg -A 10 "class GPTService" server/apps/research/services/

# Check how GPTService is used in article_admin
echo "Checking GPTService usage in article_admin..."
rg "GPTService" server/apps/research/admin/article_admin.py -A 5

# Check if there are any other files using GPTService
echo "Checking other GPTService usages..."
rg "GPTService" server/apps/research/ --type py

Length of output: 1940


Script:

#!/bin/bash
# Let's verify the complete integration by checking the generate_summary_view implementation

# Check the generate_summary_view implementation
echo "Checking generate_summary_view implementation..."
rg -A 15 "def generate_summary_view" server/apps/research/admin/article_admin.py

# Check if Article model has the necessary fields
echo "Checking Article model fields..."
rg "class Article" -A 10 server/apps/research/models.py

# Verify AsyncOpenAI import and settings
echo "Checking OpenAI settings..."
rg "OPENAI_API_KEY" server/apps/research/ --type py

Length of output: 1410

Comment on lines +64 to +73
async def _generate_summary(self, content: str) -> str:
system_prompt = (
"You are a professional summarizer at 2077 Research. Below is an article on Ethereum technical aspects. "
"Your goal is to produce a summary that is shorter than the original content, yet detailed enough for readers "
"to fully understand the piece without needing to read the original. Your summary should:\n"
"- Provide enough depth and detail so the user gets a complete understanding of the core ideas.\n"
"- Be in HTML format, use <h3> tags for headings if needed. Avoid other heading levels.\n"
"- Minimize the use of bullet points. If you need to list items, you can, but prefer concise paragraph formatting.\n\n"
)
return await self.gpt_service.prompt(system_prompt, content)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Move system prompt to settings or constants

The system prompt should be moved to a settings file or constants module for better maintainability and reusability.

+# In apps/research/constants.py
+GPT_SYSTEM_PROMPT = """
+You are a professional summarizer at 2077 Research. Below is an article on Ethereum technical aspects. 
+Your goal is to produce a summary that is shorter than the original content, yet detailed enough for readers 
+to fully understand the piece without needing to read the original. Your summary should:
+- Provide enough depth and detail so the user gets a complete understanding of the core ideas.
+- Be in HTML format, use <h3> tags for headings if needed. Avoid other heading levels.
+- Minimize the use of bullet points. If you need to list items, you can, but prefer concise paragraph formatting.
+"""

# In article_admin.py
+from ..constants import GPT_SYSTEM_PROMPT

     async def _generate_summary(self, content: str) -> str:
-        system_prompt = (
-            "You are a professional summarizer at 2077 Research. Below is an article on Ethereum technical aspects. "
-            "Your goal is to produce a summary that is shorter than the original content, yet detailed enough for readers "
-            "to fully understand the piece without needing to read the original. Your summary should:\n"
-            "- Provide enough depth and detail so the user gets a complete understanding of the core ideas.\n"
-            "- Be in HTML format, use <h3> tags for headings if needed. Avoid other heading levels.\n"
-            "- Minimize the use of bullet points. If you need to list items, you can, but prefer concise paragraph formatting.\n\n"
-        )
-        return await self.gpt_service.prompt(system_prompt, content)
+        return await self.gpt_service.prompt(GPT_SYSTEM_PROMPT, content)
📝 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
async def _generate_summary(self, content: str) -> str:
system_prompt = (
"You are a professional summarizer at 2077 Research. Below is an article on Ethereum technical aspects. "
"Your goal is to produce a summary that is shorter than the original content, yet detailed enough for readers "
"to fully understand the piece without needing to read the original. Your summary should:\n"
"- Provide enough depth and detail so the user gets a complete understanding of the core ideas.\n"
"- Be in HTML format, use <h3> tags for headings if needed. Avoid other heading levels.\n"
"- Minimize the use of bullet points. If you need to list items, you can, but prefer concise paragraph formatting.\n\n"
)
return await self.gpt_service.prompt(system_prompt, content)
async def _generate_summary(self, content: str) -> str:
return await self.gpt_service.prompt(GPT_SYSTEM_PROMPT, content)

@losndu losndu merged commit 587fd94 into main Dec 17, 2024
3 checks passed
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