177 lines
5.7 KiB
Python
177 lines
5.7 KiB
Python
from django.contrib import admin
|
|
from django.utils.html import format_html
|
|
from .models import SocialMediaComment
|
|
from .services.analysis_service import AnalysisService
|
|
|
|
|
|
@admin.register(SocialMediaComment)
|
|
class SocialMediaCommentAdmin(admin.ModelAdmin):
|
|
"""
|
|
Admin interface for SocialMediaComment model with bilingual AI analysis features.
|
|
"""
|
|
list_display = [
|
|
'platform',
|
|
'author',
|
|
'comments_preview',
|
|
'rating_display',
|
|
'sentiment_badge',
|
|
'confidence_display',
|
|
'like_count',
|
|
'is_analyzed',
|
|
'published_at',
|
|
'scraped_at'
|
|
]
|
|
list_filter = [
|
|
'platform',
|
|
'published_at',
|
|
'scraped_at'
|
|
]
|
|
search_fields = ['author', 'comments', 'comment_id', 'post_id']
|
|
readonly_fields = [
|
|
'scraped_at',
|
|
'is_analyzed',
|
|
'ai_analysis_display',
|
|
'raw_data'
|
|
]
|
|
date_hierarchy = 'published_at'
|
|
actions = ['trigger_analysis']
|
|
|
|
fieldsets = (
|
|
('Basic Information', {
|
|
'fields': ('platform', 'comment_id', 'post_id', 'media_url')
|
|
}),
|
|
('Content', {
|
|
'fields': ('comments', 'author')
|
|
}),
|
|
('Engagement Metrics', {
|
|
'fields': ('like_count', 'reply_count', 'rating')
|
|
}),
|
|
('AI Bilingual Analysis', {
|
|
'fields': ('is_analyzed', 'ai_analysis_display'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Timestamps', {
|
|
'fields': ('published_at', 'scraped_at')
|
|
}),
|
|
('Technical Data', {
|
|
'fields': ('raw_data',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def comments_preview(self, obj):
|
|
"""
|
|
Display a preview of the comment text.
|
|
"""
|
|
return obj.comments[:100] + '...' if len(obj.comments) > 100 else obj.comments
|
|
comments_preview.short_description = 'Comment Preview'
|
|
|
|
def rating_display(self, obj):
|
|
"""
|
|
Display star rating (for Google Reviews).
|
|
"""
|
|
if obj.rating is None:
|
|
return '-'
|
|
stars = '★' * obj.rating + '☆' * (5 - obj.rating)
|
|
return format_html('<span title="{} stars">{}</span>', obj.rating, stars)
|
|
rating_display.short_description = 'Rating'
|
|
|
|
def sentiment_badge(self, obj):
|
|
"""
|
|
Display sentiment as a colored badge from ai_analysis.
|
|
"""
|
|
if not obj.ai_analysis:
|
|
return format_html('<span style="color: gray;">Not analyzed</span>')
|
|
|
|
sentiment = obj.ai_analysis.get('sentiment', {}).get('classification', {}).get('en', 'neutral')
|
|
|
|
colors = {
|
|
'positive': 'green',
|
|
'negative': 'red',
|
|
'neutral': 'blue'
|
|
}
|
|
color = colors.get(sentiment, 'gray')
|
|
return format_html(
|
|
'<span style="color: {}; font-weight: bold;">{}</span>',
|
|
color,
|
|
sentiment.capitalize()
|
|
)
|
|
sentiment_badge.short_description = 'Sentiment'
|
|
|
|
def confidence_display(self, obj):
|
|
"""
|
|
Display confidence score from ai_analysis.
|
|
"""
|
|
if not obj.ai_analysis:
|
|
return '-'
|
|
|
|
confidence = obj.ai_analysis.get('sentiment', {}).get('confidence', 0)
|
|
return format_html('{:.2f}', confidence)
|
|
confidence_display.short_description = 'Confidence'
|
|
|
|
def ai_analysis_display(self, obj):
|
|
"""
|
|
Display formatted AI analysis data.
|
|
"""
|
|
if not obj.ai_analysis:
|
|
return format_html('<p>No AI analysis available</p>')
|
|
|
|
sentiment = obj.ai_analysis.get('sentiment', {})
|
|
summary_en = obj.ai_analysis.get('summaries', {}).get('en', '')
|
|
summary_ar = obj.ai_analysis.get('summaries', {}).get('ar', '')
|
|
keywords = obj.ai_analysis.get('keywords', {}).get('en', [])
|
|
|
|
html = format_html('<h4>Sentiment Analysis</h4>')
|
|
html += format_html('<p><strong>Classification:</strong> {} ({})</p>',
|
|
sentiment.get('classification', {}).get('en', 'N/A'),
|
|
sentiment.get('classification', {}).get('ar', 'N/A')
|
|
)
|
|
html += format_html('<p><strong>Score:</strong> {}</p>',
|
|
sentiment.get('score', 0)
|
|
)
|
|
html += format_html('<p><strong>Confidence:</strong> {}</p>',
|
|
sentiment.get('confidence', 0)
|
|
)
|
|
|
|
if summary_en:
|
|
html += format_html('<h4>Summary (English)</h4><p>{}</p>', summary_en)
|
|
if summary_ar:
|
|
html += format_html('<h4>الملخص (Arabic)</h4><p dir="rtl">{}</p>', summary_ar)
|
|
|
|
if keywords:
|
|
html += format_html('<h4>Keywords</h4><p>{}</p>', ', '.join(keywords))
|
|
|
|
return html
|
|
ai_analysis_display.short_description = 'AI Analysis'
|
|
|
|
def is_analyzed(self, obj):
|
|
"""
|
|
Display whether comment has been analyzed.
|
|
"""
|
|
return bool(obj.ai_analysis)
|
|
is_analyzed.boolean = True
|
|
is_analyzed.short_description = 'Analyzed'
|
|
|
|
def trigger_analysis(self, request, queryset):
|
|
"""
|
|
Admin action to trigger AI analysis for selected comments.
|
|
"""
|
|
service = AnalysisService()
|
|
analyzed = 0
|
|
failed = 0
|
|
|
|
for comment in queryset:
|
|
if not comment.ai_analysis: # Only analyze unanalyzed comments
|
|
result = service.reanalyze_comment(comment.id)
|
|
if result.get('success'):
|
|
analyzed += 1
|
|
else:
|
|
failed += 1
|
|
|
|
self.message_user(
|
|
request,
|
|
f'Analysis complete: {analyzed} analyzed, {failed} failed',
|
|
level='SUCCESS' if failed == 0 else 'WARNING'
|
|
)
|
|
trigger_analysis.short_description = 'Analyze selected comments'
|