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

Share and rating feedback #1761

Merged
merged 5 commits into from
Mar 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion home/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from django.contrib import admin
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register
from django.db.models import Avg, Count
from django.utils.html import format_html
from django.urls import reverse

from home.models import ManifestSettings, SVGToPNGMap
from home.models import ManifestSettings, SVGToPNGMap, Article, ArticleFeedback


class ManifestSettingsAdmin(ModelAdmin):
Expand All @@ -21,3 +24,26 @@ class SVGToPNGMapAdmin(admin.ModelAdmin):
list_display = ('id', 'svg_path', 'fill_color', 'stroke_color', 'png_image_file')


class ArticleAdmin(ModelAdmin):
model = Article
menu_label = "Article Ratings"
menu_icon = "form"
list_display = ("title", "average_rating", "number_of_reviews", "view_feedback_button")
search_fields = ("title",)
ordering = ("-average_rating", "-number_of_reviews") # Sort by rating & reviews in descending order

def view_feedback_button(self, obj):
"""
Creates a button to view feedback for an article.
"""
url = reverse("admin_article_feedback", args=[obj.id])
return format_html('<a class="button button-small" href="{}">View Feedback</a>', url)
view_feedback_button.short_description = "Feedback"

def has_add_permission(self, request):
"""
Hide the 'Add Article' button in the Wagtail admin panel.
"""
return False # Prevents adding new articles from this page

modeladmin_register(ArticleAdmin)
8 changes: 8 additions & 0 deletions home/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.apps import AppConfig

class HomeConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "home"

def ready(self):
import home.signals # Ensure signals are loaded
31 changes: 31 additions & 0 deletions home/migrations/0056_articlefeedback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 3.2.25 on 2025-02-26 05:18

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('home', '0055_enable_use_json_field'),
]

operations = [
migrations.CreateModel(
name='ArticleFeedback',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('session_id', models.CharField(blank=True, max_length=255, null=True)),
('rating', models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)])),
('feedback', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feedbacks', to='home.article')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
options={
'unique_together': {('article', 'user')},
},
),
]
26 changes: 26 additions & 0 deletions home/migrations/0057_feedbacksettings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 3.2.25 on 2025-02-27 08:29

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('wagtailcore', '0069_log_entry_jsonfield'),
('home', '0056_articlefeedback'),
]

operations = [
migrations.CreateModel(
name='FeedbackSettings',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable_feedback', models.BooleanField(default=True, verbose_name='Enable Feedback')),
('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.site')),
],
options={
'verbose_name': 'Rating Settings',
},
),
]
23 changes: 23 additions & 0 deletions home/migrations/0058_auto_20250306_1311.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.25 on 2025-03-06 13:11

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('home', '0057_feedbacksettings'),
]

operations = [
migrations.AddField(
model_name='article',
name='average_rating',
field=models.FloatField(default=0.0, null=True),
),
migrations.AddField(
model_name='article',
name='number_of_reviews',
field=models.PositiveIntegerField(default=0, null=True),
),
]
53 changes: 53 additions & 0 deletions home/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
get_all_renditions_urls,
)
import iogt.iogt_globals as globals_
from django.db.models import Avg, Count

User = get_user_model()
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -432,9 +433,26 @@ class Meta:
verbose_name_plural = _("articles")


@register_setting
class FeedbackSettings(BaseSetting):
"""Global setting to enable or disable feedback submission for all articles."""
enable_feedback = models.BooleanField(default=True, verbose_name="Enable Feedback")

panels = [
FieldPanel("enable_feedback"),
]

class Meta:
verbose_name = "Rating Settings"


class Article(AbstractArticle):
tags = ClusterTaggableManager(through='ArticleTaggedItem', blank=True)

# New fields for precomputed values
average_rating = models.FloatField(default=0.0, null=True)
number_of_reviews = models.PositiveIntegerField(default=0, null=True)

content_panels = AbstractArticle.content_panels + [
MultiFieldPanel([
InlinePanel('recommended_articles',
Expand Down Expand Up @@ -463,13 +481,48 @@ def get_context(self, request):
for recommended_article in self.recommended_articles.all() if recommended_article.article.live
]

# Fetch feedback setting correctly
feedback_settings = FeedbackSettings.for_request(request)
context["feedback_enabled"] = feedback_settings.enable_feedback

# Get the latest 5 feedbacks
context["feedbacks"] = self.feedbacks.order_by('-created_at')[:3]
return context

def serve(self, request):
response = super().serve(request)
if response.status_code == status.HTTP_200_OK:
User.record_article_read(request=request, article=self)
return response

def update_feedback_metrics(self):
"""
Updates the average rating and number of reviews for this article.
"""
feedback_stats = self.feedbacks.aggregate(
avg_rating=Avg("rating"), review_count=Count("id")
)
self.average_rating = feedback_stats["avg_rating"] or 0.0
self.number_of_reviews = feedback_stats["review_count"] or 0
self.save(update_fields=["average_rating", "number_of_reviews"])

def compute_average_rating(self):
return self.average_rating if self.average_rating else 0

def compute_number_of_reviews(self):
return self.number_of_reviews if self.number_of_reviews else 0


class ArticleFeedback(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='feedbacks')
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
session_id = models.CharField(max_length=255, null=True, blank=True)
rating = models.IntegerField(choices=[(i, i) for i in range(1, 6)])
feedback = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)

class Meta:
unique_together = ('article', 'user')


class MiscellaneousIndexPage(Page):
Expand Down
12 changes: 12 additions & 0 deletions home/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from .models import ArticleFeedback

@receiver(post_save, sender=ArticleFeedback)
@receiver(post_delete, sender=ArticleFeedback)
def update_article_feedback_metrics(sender, instance, **kwargs):
"""
Update the article's feedback metrics whenever a feedback entry is added, updated, or deleted.
"""
if instance.article:
instance.article.update_feedback_metrics()
4 changes: 4 additions & 0 deletions home/static/css/custom_admin.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* Hide the "Add Article" button in the Wagtail admin */
a[href*="/admin/home/article/create/"] {
display: none !important;
}
Binary file added home/static/icons/email.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added home/static/icons/facebook.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added home/static/icons/link.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added home/static/icons/linkedin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added home/static/icons/whatsapp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added home/static/icons/x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading