diff --git a/server/apps/research/admin/article_admin.py b/server/apps/research/admin/article_admin.py
index 91b556b..ef090fb 100644
--- a/server/apps/research/admin/article_admin.py
+++ b/server/apps/research/admin/article_admin.py
@@ -18,7 +18,7 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['acknowledgement'].widget = TinyMCE(attrs={'cols': 80, 'rows': 30, 'id': "acknowledgement_richtext_field", 'placeholder': f"Enter Acknowledgement here"})
self.fields['content'].widget = TinyMCE(attrs={'cols': 80, 'rows': 30, 'id': "content_richtext_field", 'placeholder': f"Enter Article Content here"})
- self.fields['summary'].widget = TinyMCE(attrs={'cols': 80, 'rows': 30, 'id': "summary_richtext_field", 'placeholder': f"Article summary will be generated here"})
+ self.fields['gpt_summary'].widget = TinyMCE(attrs={'cols': 80, 'rows': 15, 'id': "gpt_summary_richtext_field", 'placeholder': f"GPT-generated summary will appear here"})
class ArticleAdmin(admin.ModelAdmin):
"""Admin interface for the Article model."""
@@ -42,7 +42,7 @@ async def _generate_summary(self, content: str) -> str:
"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
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"
+ "- 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)
@@ -51,8 +51,8 @@ def generate_summary_view(self, request):
content = request.POST.get('content')
try:
# Run the async function in the sync view
- summary = asyncio.run(self._generate_summary(content))
- return JsonResponse({'summary': summary})
+ gpt_summary = asyncio.run(self._generate_summary(content))
+ return JsonResponse({'summary': gpt_summary})
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
return JsonResponse({'error': 'Invalid request method'}, status=400)
@@ -62,7 +62,7 @@ def current_slug_history(self, obj):
current_slug_history.short_description = 'Slug Change History'
fieldsets = [
- ('Article Details', {'fields': ['title', 'slug', 'authors', 'acknowledgement', 'categories', 'thumb', 'content', 'summary', 'status', 'scheduled_publish_time']}),
+ ('Article Details', {'fields': ['title', 'slug', 'authors', 'acknowledgement', 'categories', 'thumb', 'content', 'summary', 'gpt_summary', 'status', 'scheduled_publish_time']}),
('Sponsorship Details', {'fields': ['is_sponsored', 'sponsor_color', 'sponsor_text_color']}),
('URL Management', {
'fields': ('current_slug_history',),
diff --git a/server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py b/server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py
index af3fb92..9d2b209 100644
--- a/server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py
+++ b/server/apps/research/migrations/0017_article_gpt_summary_alter_article_summary.py
@@ -1,8 +1,7 @@
# Generated by Django 5.0.8 on 2024-12-10 18:18
import tinymce.models
-from django.db import migrations, models
-
+from django.db import migrations
class Migration(migrations.Migration):
@@ -14,11 +13,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='article',
name='gpt_summary',
- field=models.TextField(blank=True, null=True),
- ),
- migrations.AlterField(
- model_name='article',
- name='summary',
field=tinymce.models.HTMLField(blank=True, null=True),
),
]
diff --git a/server/apps/research/models/article.py b/server/apps/research/models/article.py
index a4d3c1b..5d8883e 100644
--- a/server/apps/research/models/article.py
+++ b/server/apps/research/models/article.py
@@ -25,7 +25,7 @@ class Article(BaseModel):
title = models.TextField()
content = HTMLField(blank=True, null=True)
- summary = HTMLField(blank=True, null=True)
+ summary = models.TextField(blank=True)
gpt_summary = models.TextField(blank=True, null=True)
acknowledgement = HTMLField(blank=True, null=True)
authors = models.ManyToManyField(Author, blank=True, related_name='articles')
diff --git a/server/static/css/article_admin.css b/server/static/css/article_admin.css
index c0a67d4..fef5266 100644
--- a/server/static/css/article_admin.css
+++ b/server/static/css/article_admin.css
@@ -1,5 +1,5 @@
.generate-summary-btn {
- background-color: #417690;
+ background-color: #0C4B33;
color: white;
padding: 10px 15px;
border: none;
@@ -7,10 +7,7 @@
cursor: pointer;
font-weight: bold;
margin-left: 10px;
-}
-
-.generate-summary-btn:hover {
- background-color: #2b5172;
+ align-self: baseline;
}
.generate-summary-btn:disabled {
@@ -22,4 +19,10 @@
margin-left: 10px;
font-style: italic;
color: #666;
+}
+
+.generate-summary-container-btn {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
}
\ No newline at end of file
diff --git a/server/static/js/article_admin.js b/server/static/js/article_admin.js
index ef9aee9..4e28844 100644
--- a/server/static/js/article_admin.js
+++ b/server/static/js/article_admin.js
@@ -1,57 +1,56 @@
document.addEventListener('DOMContentLoaded', function() {
- // Add generate summary button after the content field
- const contentField = document.getElementById('content_richtext_field');
- const summaryField = document.getElementById('summary_richtext_field');
- if (contentField) {
- const button = document.createElement('button');
- button.type = 'button';
- button.id = 'generate-summary-btn';
- button.className = 'generate-summary-btn';
- button.textContent = 'Generate Summary';
- button.style.height = 'min-content';
+ // Wait for TinyMCE to initialize
+ if (typeof tinymce !== 'undefined') {
+ const gptSummaryContainer = document.getElementById('gpt_summary_richtext_field');
+ if (gptSummaryContainer) {
+ const buttonContainer = document.createElement('div');
+ buttonContainer.id = 'generate-summary-container-btn';
- const statusSpan = document.createElement('span');
- statusSpan.className = 'summary-status';
- statusSpan.id = 'summary-status';
+ const button = document.createElement('button');
+ button.type = 'button';
+ button.id = 'generate-summary-btn';
+ button.className = 'generate-summary-btn';
+ button.textContent = 'Generate Summary';
- summaryField.parentNode.insertBefore(button, summaryField.nextSibling);
- button.parentNode.insertBefore(statusSpan, button.nextSibling);
+ const statusSpan = document.createElement('p');
+ statusSpan.className = 'summary-status';
+ statusSpan.id = 'summary-status';
- // Add click event listener to the button
- button.addEventListener('click', function() {
- const editor = tinymce.get('content_richtext_field');
- const summaryEditor = tinymce.get('summary_richtext_field');
-
- if (editor && summaryEditor) {
- const content = editor.getContent();
- if (!content.trim()) {
- alert('Please enter some content before generating a summary.');
- return;
- }
+ buttonContainer.appendChild(button);
+ buttonContainer.appendChild(statusSpan);
+
+ gptSummaryContainer.parentNode.insertBefore(buttonContainer, gptSummaryContainer.nextSibling);
+ button.parentNode.insertBefore(statusSpan, button.nextSibling);
- // Disable both editors and the button
- editor.setMode('readonly');
- summaryEditor.setMode('readonly');
- button.disabled = true;
+ button.addEventListener('click', function() {
+ const contentEditor = tinymce.get('content_richtext_field');
+ const gptSummaryContainer = document.getElementById('gpt_summary_richtext_field');
statusSpan.textContent = ' Generating summary...';
+
+ if (contentEditor && gptSummaryContainer) {
+ const content = contentEditor.getContent();
+ if (!content.trim()) {
+ alert('Please enter some content before generating a summary.');
+ return;
+ }
- // Call the GPT API
- generateSummary(content)
- .then(() => {
- statusSpan.textContent = ' Summary generated successfully!';
- })
- .catch(error => {
- console.error('Error generating summary:', error);
- statusSpan.textContent = ' Error generating summary. Please try again.';
- })
- .finally(() => {
- // Re-enable both editors and the button
- editor.setMode('design');
- summaryEditor.setMode('design');
- button.disabled = false;
- });
- }
- });
+ // Call the GPT API
+ generateSummary(content)
+ .then(() => {
+ statusSpan.textContent = ' Summary generated successfully!';
+ })
+ .catch(error => {
+ statusSpan.textContent = ' Error generating summary. Please try again.';
+ })
+ .finally(() => {
+ // Re-enable editors and button
+ contentEditor.setMode('design');
+ gptSummaryEditor.setMode('design');
+ button.disabled = false;
+ });
+ }
+ });
+ }
}
});
@@ -74,7 +73,7 @@ async function generateSummary(content) {
// Update the summary field
if (typeof tinymce !== 'undefined') {
- const summaryEditor = tinymce.get('summary_richtext_field');
+ const summaryEditor = tinymce.get('gpt_summary_richtext_field');
if (summaryEditor) {
summaryEditor.setContent(data.summary);
}