HH/apps/social/ui_views.py
2026-01-15 14:31:58 +03:00

642 lines
22 KiB
Python

"""
Social Media UI views - Server-rendered templates for social media monitoring
"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Q, Count, Avg, Sum
from django.http import JsonResponse
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods
from .models import SocialMediaComment, SocialPlatform
@login_required
def social_comment_list(request):
"""
Social media comments list view with advanced filters and pagination.
Features:
- Server-side pagination
- Advanced filters (platform, sentiment, date range, etc.)
- Search by comment text, author
- Export capability
"""
# Base queryset
queryset = SocialMediaComment.objects.all()
# Apply filters from request
platform_filter = request.GET.get('platform')
if platform_filter:
queryset = queryset.filter(platform=platform_filter)
sentiment_filter = request.GET.get('sentiment')
if sentiment_filter:
# Filter by sentiment in ai_analysis JSONField
queryset = queryset.filter(
ai_analysis__sentiment__classification__en=sentiment_filter
)
analyzed_filter = request.GET.get('analyzed')
if analyzed_filter == 'true':
queryset = queryset.exclude(ai_analysis__isnull=True).exclude(ai_analysis={})
elif analyzed_filter == 'false':
queryset = queryset.filter(ai_analysis__isnull=True) | queryset.filter(ai_analysis={})
# Date range filters
date_from = request.GET.get('date_from')
if date_from:
queryset = queryset.filter(published_at__gte=date_from)
date_to = request.GET.get('date_to')
if date_to:
queryset = queryset.filter(published_at__lte=date_to)
# Minimum likes
min_likes = request.GET.get('min_likes')
if min_likes:
queryset = queryset.filter(like_count__gte=min_likes)
# Search
search_query = request.GET.get('search')
if search_query:
queryset = queryset.filter(
Q(comments__icontains=search_query) |
Q(author__icontains=search_query) |
Q(comment_id__icontains=search_query)
)
# Ordering
order_by = request.GET.get('order_by', '-published_at')
queryset = queryset.order_by(order_by)
# Pagination
page_size = int(request.GET.get('page_size', 25))
paginator = Paginator(queryset, page_size)
page_number = request.GET.get('page', 1)
page_obj = paginator.get_page(page_number)
# Get platform choices
platforms = SocialPlatform.choices
# Calculate statistics from queryset (using ai_analysis)
total_comments = queryset.count()
analyzed_comments = 0
positive_count = 0
negative_count = 0
neutral_count = 0
for comment in queryset:
if comment.ai_analysis:
analyzed_comments += 1
sentiment = comment.ai_analysis.get('sentiment', {}).get('classification', {}).get('en', 'neutral')
if sentiment == 'positive':
positive_count += 1
elif sentiment == 'negative':
negative_count += 1
else:
neutral_count += 1
stats = {
'total': total_comments,
'analyzed': analyzed_comments,
'unanalyzed': total_comments - analyzed_comments,
'positive': positive_count,
'negative': negative_count,
'neutral': neutral_count,
}
# Add platform-specific counts
for platform_code, platform_name in platforms:
stats[platform_code] = SocialMediaComment.objects.filter(platform=platform_code).count()
context = {
'page_obj': page_obj,
'comments': page_obj.object_list,
'stats': stats,
'platforms': platforms,
'filters': request.GET,
}
return render(request, 'social/social_comment_list.html', context)
@login_required
def social_comment_detail(request, pk):
"""
Social media comment detail view.
Features:
- Full comment details
- Raw data view
- AI analysis results
- Keywords and topics
- Entities extracted
"""
from django.shortcuts import get_object_or_404
comment = get_object_or_404(SocialMediaComment, pk=pk)
context = {
'comment': comment,
}
return render(request, 'social/social_comment_detail.html', context)
@login_required
def social_platform(request, platform):
"""
Platform-specific social media comments view.
Features:
- Filtered comments for specific platform
- Platform-specific branding and metrics
- Time-based filtering
- Platform-specific trends
"""
# Validate platform
valid_platforms = [choice[0] for choice in SocialPlatform.choices]
if platform not in valid_platforms:
messages.error(request, f"Invalid platform: {platform}")
return redirect('social:social_comment_list')
# Base queryset filtered by platform
queryset = SocialMediaComment.objects.filter(platform=platform)
# Apply additional filters
sentiment_filter = request.GET.get('sentiment')
if sentiment_filter:
queryset = queryset.filter(
ai_analysis__sentiment__classification__en=sentiment_filter
)
date_from = request.GET.get('date_from')
if date_from:
queryset = queryset.filter(published_at__gte=date_from)
date_to = request.GET.get('date_to')
if date_to:
queryset = queryset.filter(published_at__lte=date_to)
search_query = request.GET.get('search')
if search_query:
queryset = queryset.filter(
Q(comments__icontains=search_query) |
Q(author__icontains=search_query)
)
# Time-based view filter
time_filter = request.GET.get('time_filter', 'all')
from datetime import datetime, timedelta
if time_filter == 'today':
queryset = queryset.filter(published_at__date=datetime.now().date())
elif time_filter == 'week':
queryset = queryset.filter(published_at__gte=datetime.now() - timedelta(days=7))
elif time_filter == 'month':
queryset = queryset.filter(published_at__gte=datetime.now() - timedelta(days=30))
# Ordering
order_by = request.GET.get('order_by', '-published_at')
queryset = queryset.order_by(order_by)
# Pagination
page_size = int(request.GET.get('page_size', 25))
paginator = Paginator(queryset, page_size)
page_number = request.GET.get('page', 1)
page_obj = paginator.get_page(page_number)
# Platform-specific statistics (using ai_analysis)
total_comments = queryset.count()
analyzed_comments = 0
positive_count = 0
negative_count = 0
neutral_count = 0
sentiment_scores = []
for comment in queryset:
if comment.ai_analysis:
analyzed_comments += 1
sentiment = comment.ai_analysis.get('sentiment', {}).get('classification', {}).get('en', 'neutral')
score = comment.ai_analysis.get('sentiment', {}).get('score', 0)
if sentiment == 'positive':
positive_count += 1
elif sentiment == 'negative':
negative_count += 1
else:
neutral_count += 1
if score:
sentiment_scores.append(score)
avg_sentiment = sum(sentiment_scores) / len(sentiment_scores) if sentiment_scores else 0
stats = {
'total': total_comments,
'analyzed': analyzed_comments,
'positive': positive_count,
'negative': negative_count,
'neutral': neutral_count,
'avg_sentiment': float(avg_sentiment),
'total_likes': int(queryset.aggregate(total=Sum('like_count'))['total'] or 0),
'total_replies': int(queryset.aggregate(total=Sum('reply_count'))['total'] or 0),
}
# Platform name for display
platform_display = dict(SocialPlatform.choices).get(platform, platform)
# Platform color for styling
platform_colors = {
'facebook': '#1877F2',
'instagram': '#C13584',
'youtube': '#FF0000',
'twitter': '#1DA1F2',
'linkedin': '#0077B5',
'tiktok': '#000000',
'google': '#4285F4',
}
platform_color = platform_colors.get(platform, '#6c757d')
context = {
'page_obj': page_obj,
'comments': page_obj.object_list,
'stats': stats,
'platform': platform,
'platform_display': platform_display,
'platform_color': platform_color,
'time_filter': time_filter,
'filters': request.GET,
}
return render(request, 'social/social_platform.html', context)
@login_required
def social_analytics(request):
"""
Social media analytics dashboard.
Features:
- Sentiment distribution
- Platform distribution
- Daily trends
- Top keywords
- Top topics
- Engagement metrics
"""
queryset = SocialMediaComment.objects.all()
# Platform filter
platform_filter = request.GET.get('platform')
if platform_filter:
queryset = queryset.filter(platform=platform_filter)
# Apply date range filter
from datetime import datetime, timedelta
# Check for custom date range first
start_date = request.GET.get('start_date')
end_date = request.GET.get('end_date')
if start_date and end_date:
# Custom date range specified
queryset = queryset.filter(published_at__gte=start_date, published_at__lte=end_date)
else:
# Fall back to preset date range (backwards compatibility)
date_range = int(request.GET.get('date_range', 30))
days_ago = datetime.now() - timedelta(days=date_range)
queryset = queryset.filter(published_at__gte=days_ago)
# Sentiment distribution (from ai_analysis)
sentiment_counts = {'positive': 0, 'negative': 0, 'neutral': 0}
sentiment_scores = {'positive': [], 'negative': [], 'neutral': []}
for comment in queryset:
if comment.ai_analysis:
sentiment = comment.ai_analysis.get('sentiment', {}).get('classification', {}).get('en', 'neutral')
score = comment.ai_analysis.get('sentiment', {}).get('score', 0)
if sentiment in sentiment_counts:
sentiment_counts[sentiment] += 1
if score:
sentiment_scores[sentiment].append(score)
sentiment_dist = []
for sentiment, count in sentiment_counts.items():
scores = sentiment_scores[sentiment]
avg_score = sum(scores) / len(scores) if scores else 0
sentiment_dist.append({
'sentiment': sentiment,
'count': count,
'avg_sentiment_score': avg_score
})
# Platform distribution (add platform_display manually) - using ai_analysis
platform_dist = []
for platform in SocialPlatform.choices:
platform_code = platform[0]
platform_name = platform[1]
platform_data = queryset.filter(platform=platform_code)
if platform_data.exists():
# Calculate avg sentiment from ai_analysis
sentiment_scores = []
for comment in platform_data:
if comment.ai_analysis:
score = comment.ai_analysis.get('sentiment', {}).get('score', 0)
if score:
sentiment_scores.append(score)
avg_sentiment = sum(sentiment_scores) / len(sentiment_scores) if sentiment_scores else 0
platform_dist.append({
'platform': platform_code,
'platform_display': platform_name,
'count': platform_data.count(),
'avg_sentiment': float(avg_sentiment),
'total_likes': int(platform_data.aggregate(total=Sum('like_count'))['total'] or 0),
'total_replies': int(platform_data.aggregate(total=Sum('reply_count'))['total'] or 0),
})
# Daily trends (from ai_analysis)
from collections import defaultdict
daily_data = defaultdict(lambda: {'count': 0, 'positive': 0, 'negative': 0, 'neutral': 0, 'total_likes': 0})
for comment in queryset:
if comment.published_at:
day = comment.published_at.date()
daily_data[day]['count'] += 1
daily_data[day]['total_likes'] += comment.like_count
if comment.ai_analysis:
sentiment = comment.ai_analysis.get('sentiment', {}).get('classification', {}).get('en', 'neutral')
if sentiment in ['positive', 'negative', 'neutral']:
daily_data[day][sentiment] += 1
daily_trends = [
{
'day': day,
**stats
}
for day, stats in sorted(daily_data.items())
]
# Top keywords (from ai_analysis)
all_keywords = []
for comment in queryset.exclude(ai_analysis__isnull=True).exclude(ai_analysis={}):
keywords = comment.ai_analysis.get('keywords', {}).get('en', [])
all_keywords.extend(keywords)
from collections import Counter
keyword_counts = Counter(all_keywords)
top_keywords = [{'keyword': k, 'count': v} for k, v in keyword_counts.most_common(20)]
# Top topics (from ai_analysis)
all_topics = []
for comment in queryset.exclude(ai_analysis__isnull=True).exclude(ai_analysis={}):
topics = comment.ai_analysis.get('topics', {}).get('en', [])
all_topics.extend(topics)
topic_counts = Counter(all_topics)
top_topics = [{'topic': k, 'count': v} for k, v in topic_counts.most_common(10)]
# Top entities (from ai_analysis)
all_entities = []
for comment in queryset.exclude(ai_analysis__isnull=True).exclude(ai_analysis={}):
entities = comment.ai_analysis.get('entities', [])
for entity in entities:
if isinstance(entity, dict):
text_en = entity.get('text', {}).get('en', entity.get('text'))
if text_en:
all_entities.append(text_en)
entity_counts = Counter(all_entities)
top_entities = [{'entity': k, 'count': v} for k, v in entity_counts.most_common(15)]
# Overall statistics (from ai_analysis)
total_comments = queryset.count()
analyzed_comments = 0
for comment in queryset:
if comment.ai_analysis:
analyzed_comments += 1
# Engagement metrics
engagement_metrics = {
'avg_likes': float(queryset.aggregate(avg=Avg('like_count'))['avg'] or 0),
'avg_replies': float(queryset.aggregate(avg=Avg('reply_count'))['avg'] or 0),
'total_likes': int(queryset.aggregate(total=Sum('like_count'))['total'] or 0),
'total_replies': int(queryset.aggregate(total=Sum('reply_count'))['total'] or 0),
}
context = {
'sentiment_distribution': sentiment_dist,
'platform_distribution': platform_dist,
'daily_trends': daily_trends,
'top_keywords': top_keywords,
'top_topics': top_topics,
'top_entities': top_entities,
'total_comments': total_comments,
'analyzed_comments': analyzed_comments,
'unanalyzed_comments': total_comments - analyzed_comments,
'engagement_metrics': engagement_metrics,
'date_range': int(request.GET.get('date_range', 30)),
'start_date': start_date,
'end_date': end_date,
}
return render(request, 'social/social_analytics.html', context)
@login_required
@require_http_methods(["POST"])
def social_scrape_now(request):
"""
Trigger manual scraping for a platform.
"""
platform = request.POST.get('platform')
if not platform:
messages.error(request, "Please select a platform.")
return redirect('social:social_analytics')
try:
# Trigger Celery task for scraping
from .tasks import scrape_platform_comments
task = scrape_platform_comments.delay(platform)
messages.success(
request,
f"Scraping task initiated for {platform}. Task ID: {task.id}"
)
except Exception as e:
messages.error(request, f"Error initiating scraping: {str(e)}")
return redirect('social:social_analytics')
@login_required
def social_export_csv(request):
"""Export social media comments to CSV"""
import csv
from django.http import HttpResponse
from datetime import datetime
# Get filtered queryset (reuse list view logic)
queryset = SocialMediaComment.objects.all()
# Apply filters
platform_filter = request.GET.get('platform')
if platform_filter:
queryset = queryset.filter(platform=platform_filter)
sentiment_filter = request.GET.get('sentiment')
if sentiment_filter:
queryset = queryset.filter(
ai_analysis__sentiment__classification__en=sentiment_filter
)
date_from = request.GET.get('date_from')
if date_from:
queryset = queryset.filter(published_at__gte=date_from)
date_to = request.GET.get('date_to')
if date_to:
queryset = queryset.filter(published_at__lte=date_to)
# Create CSV response
response = HttpResponse(content_type='text/csv')
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
response['Content-Disposition'] = f'attachment; filename="social_comments_{timestamp}.csv"'
writer = csv.writer(response)
writer.writerow([
'ID', 'Platform', 'Comment ID', 'Author', 'Comment',
'Published At', 'Scraped At', 'Sentiment', 'Sentiment Score',
'Confidence', 'Likes', 'Replies', 'Keywords', 'Topics'
])
for comment in queryset:
# Extract data from ai_analysis
sentiment = None
sentiment_score = None
confidence = None
keywords = []
topics = []
if comment.ai_analysis:
sentiment = comment.ai_analysis.get('sentiment', {}).get('classification', {}).get('en')
sentiment_score = comment.ai_analysis.get('sentiment', {}).get('score')
confidence = comment.ai_analysis.get('sentiment', {}).get('confidence')
keywords = comment.ai_analysis.get('keywords', {}).get('en', [])
topics = comment.ai_analysis.get('topics', {}).get('en', [])
writer.writerow([
comment.id,
comment.get_platform_display(),
comment.comment_id,
comment.author,
comment.comments,
comment.published_at,
comment.scraped_at,
sentiment,
sentiment_score,
confidence,
comment.like_count,
comment.reply_count,
', '.join(keywords),
', '.join(topics),
])
return response
@login_required
def social_export_excel(request):
"""Export social media comments to Excel"""
import openpyxl
from django.http import HttpResponse
from datetime import datetime
# Get filtered queryset
queryset = SocialMediaComment.objects.all()
# Apply filters
platform_filter = request.GET.get('platform')
if platform_filter:
queryset = queryset.filter(platform=platform_filter)
sentiment_filter = request.GET.get('sentiment')
if sentiment_filter:
queryset = queryset.filter(
ai_analysis__sentiment__classification__en=sentiment_filter
)
date_from = request.GET.get('date_from')
if date_from:
queryset = queryset.filter(published_at__gte=date_from)
date_to = request.GET.get('date_to')
if date_to:
queryset = queryset.filter(published_at__lte=date_to)
# Create workbook
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "Social Media Comments"
# Headers
headers = [
'ID', 'Platform', 'Comment ID', 'Author', 'Comment',
'Published At', 'Scraped At', 'Sentiment', 'Sentiment Score',
'Confidence', 'Likes', 'Replies', 'Keywords', 'Topics', 'Entities'
]
ws.append(headers)
# Data rows
for comment in queryset:
# Extract data from ai_analysis
sentiment = None
sentiment_score = None
confidence = None
keywords = []
topics = []
entities_text = []
if comment.ai_analysis:
sentiment = comment.ai_analysis.get('sentiment', {}).get('classification', {}).get('en')
sentiment_score = comment.ai_analysis.get('sentiment', {}).get('score')
confidence = comment.ai_analysis.get('sentiment', {}).get('confidence')
keywords = comment.ai_analysis.get('keywords', {}).get('en', [])
topics = comment.ai_analysis.get('topics', {}).get('en', [])
entities = comment.ai_analysis.get('entities', [])
for entity in entities:
if isinstance(entity, dict):
text_en = entity.get('text', {}).get('en', entity.get('text'))
if text_en:
entities_text.append(text_en)
ws.append([
comment.id,
comment.get_platform_display(),
comment.comment_id,
comment.author,
comment.comments,
comment.published_at.strftime('%Y-%m-%d %H:%M:%S') if comment.published_at else '',
comment.scraped_at.strftime('%Y-%m-%d %H:%M:%S') if comment.scraped_at else '',
sentiment,
sentiment_score,
confidence,
comment.like_count,
comment.reply_count,
', '.join(keywords),
', '.join(topics),
', '.join(entities_text),
])
# Create response
response = HttpResponse(
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
response['Content-Disposition'] = f'attachment; filename="social_comments_{timestamp}.xlsx"'
wb.save(response)
return response