-
Notifications
You must be signed in to change notification settings - Fork 5
Course Testimonials #46
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
base: master
Are you sure you want to change the base?
Changes from all commits
89b96bb
4d8516d
e458800
068cf42
8ce566d
6f51715
bc12174
6e0b0ab
e77e8c1
fcca3ca
2e5f1bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| from django.contrib import admin | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # Register your models here. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| from django.apps import AppConfig | ||
|
|
||
|
|
||
| class TestimonialsConfig(AppConfig): | ||
| default_auto_field = "django.db.models.BigAutoField" | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| name = "testimonials" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| from django.db import models | ||
| from django.db.models import Q, UniqueConstraint | ||
|
|
||
|
|
||
| class ApprovalStatus(models.IntegerChoices): | ||
| APPROVED = 0, "Approved" | ||
| PENDING = 1, "Pending" | ||
| REJECTED = 2, "Rejected" | ||
|
|
||
|
|
||
| class Testimonial(models.Model): | ||
| id = models.AutoField(primary_key=True) | ||
| author = models.ForeignKey("auth.User", on_delete=models.CASCADE, default="") | ||
| category = models.ForeignKey( # Link Testimonial to a Category | ||
| "categories.Category", | ||
| on_delete=models.CASCADE# Delete testimonials if category is deleted | ||
| ) | ||
| testimonial = models.TextField() | ||
| year_taken = models.IntegerField() | ||
| approval_status = models.IntegerField( | ||
| choices=ApprovalStatus.choices, | ||
| default=ApprovalStatus.PENDING, | ||
| ) | ||
|
|
||
| class Meta: | ||
| #Only one row with (author, course) where approval_status is APPROVED or PENDING can exist. | ||
| #Multiple rejected rows can exist for (author, course) combination. | ||
| constraints = [ | ||
| UniqueConstraint( | ||
| fields=["author", "category"], | ||
| condition=Q(approval_status__in=[ApprovalStatus.APPROVED, ApprovalStatus.PENDING]), | ||
| name="unique_approved_or_pending_per_author_course", | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| from django.test import TestCase | ||
|
|
||
| # Create your tests here. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| from django.urls import path | ||
| from testimonials import views | ||
| urlpatterns = [ | ||
| path("listtestimonials/", views.testimonial_metadata, name="testimonial_list"), | ||
| path("gettestimonial/", views.get_testimonial_metadata_by_code, name="get_testimonial"), | ||
| path('addtestimonial/', views.add_testimonial, name='add_testimonial'), | ||
| path('removetestimonial/', views.remove_testimonial, name='remove_testimonial'), | ||
| path('updatetestimonialapproval/', views.update_testimonial_approval_status, name="update_testimonial_approval_status") | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| from util import response | ||
| from ediauth import auth_check | ||
| from testimonials.models import Testimonial, ApprovalStatus | ||
| from categories.models import Category | ||
| from django.contrib.auth.models import User | ||
| from django.shortcuts import get_object_or_404 | ||
| from datetime import timedelta | ||
| from django.http import JsonResponse | ||
| from django.views.decorators.csrf import csrf_exempt | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| from notifications.notification_util import update_to_testimonial_status | ||
| import ediauth.auth_check as auth_check | ||
|
|
||
| @response.request_get() | ||
| @auth_check.require_login | ||
| def testimonial_metadata(request): | ||
| testimonials = Testimonial.objects.all() | ||
| res = [ | ||
| { | ||
| "author_id": testimonial.author.username, | ||
| "author_diplay_name": testimonial.author.profile.display_username, | ||
| "category_id": testimonial.category.id, | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "euclid_codes": [euclidcode.code for euclidcode in testimonial.category.euclid_codes.all()], | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "course_name": testimonial.category.displayname, | ||
| "testimonial": testimonial.testimonial, | ||
| "testimonial_id": testimonial.id, | ||
| "year_taken": testimonial.year_taken, | ||
| "approval_status": testimonial.approval_status, | ||
| } | ||
| for testimonial in testimonials | ||
| ] | ||
| return response.success(value=res) | ||
|
|
||
| @response.request_get("category_id") | ||
| @auth_check.require_login | ||
| def get_testimonial_metadata_by_code(request): | ||
| category_id = request.POST.get('category_id') | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This endpoint is unused. I can see it being useful though (maybe the category-page tab should be changed to use this endpoint instead), but there's a lot of duplicate with Like: |
||
| try: | ||
| category_obj = Category.objects.get(id=category_id) | ||
| except Category.DoesNotExist: | ||
| return response.not_possible(f"The category with id {category_id} does not exist in the database.") | ||
| testimonials = Testimonial.objects.filter(category=category_obj) | ||
| res = [ | ||
| { | ||
| "author_id": testimonial.author.username, | ||
| "author_diplay_name": testimonial.author.profile.display_username, | ||
| "category_id": testimonial.category.id, | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "euclid_codes": testimonial.category.euclid_codes, | ||
| "course_name": testimonial.category.displayname, | ||
| "testimonial": testimonial.testimonial, | ||
| "testimonial_id": testimonial.id, | ||
| "year_taken": testimonial.year_taken, | ||
| "approval_status": testimonial.approval_status, | ||
| } | ||
| for testimonial in testimonials | ||
| ] | ||
| return response.success(value=res) | ||
|
|
||
| @response.request_post("category_id", "year_taken", optional=True) | ||
| @auth_check.require_login | ||
| def add_testimonial(request): | ||
| author = request.user | ||
| category_id = request.POST.get('category_id') #course code instead of course name | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| year_taken = request.POST.get('year_taken') | ||
| testimonial = request.POST.get('testimonial') | ||
|
|
||
| if not author: | ||
| return response.not_possible("Missing argument: author") | ||
| if not year_taken: | ||
| return response.not_possible("Missing argument: year_taken") | ||
| if not testimonial: | ||
| return response.not_possible("Missing argument: testimonial") | ||
|
|
||
| testimonials = Testimonial.objects.all() | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| category_obj = Category.objects.get(id=category_id) | ||
|
|
||
| for t in testimonials: | ||
| if t.author == author and t.category == category_obj and (t.approval_status == ApprovalStatus.APPROVED): | ||
| return response.not_possible("You have written a testimonial for this course that has been approved.") | ||
| elif t.author == author and t.category == category_obj and (t.approval_status == ApprovalStatus.PENDING): | ||
| return response.not_possible("You have written a testimonial for this course that is currently pending approval.") | ||
|
|
||
| testimonial = Testimonial.objects.create( | ||
| author=author, | ||
| category=category_obj, | ||
| year_taken=year_taken, | ||
| approval_status= ApprovalStatus.PENDING, | ||
| testimonial=testimonial, | ||
| ) | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return response.success(value={"testimonial_id" : testimonial.id, "approved" : False}) | ||
|
|
||
| @response.request_post("username", 'testimonial_id', optional=True) | ||
| @auth_check.require_login | ||
| def remove_testimonial(request): | ||
| username = request.POST.get('username') | ||
| testimonial_id = request.POST.get('testimonial_id') | ||
|
|
||
| testimonial = Testimonial.objects.filter(id=testimonial_id) #Since id is primary key, always returns 1 or none. | ||
nikhen-s marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if not testimonial: | ||
| return response.not_possible("Testimonial not found for author: " + username + " with id " + testimonial_id) | ||
|
|
||
| if not (testimonial[0].author == request.user or auth_check.has_admin_rights(request)): | ||
| return response.not_possible("No permission to delete this.") | ||
|
|
||
| testimonial.delete() | ||
| return response.success(value="Deleted Testimonial " + str(testimonial)) | ||
|
|
||
| @response.request_post("title", "message", optional=True) | ||
| @auth_check.require_login | ||
| def update_testimonial_approval_status(request): | ||
| sender = request.user | ||
| has_admin_rights = auth_check.has_admin_rights(request) | ||
| testimonial_author = request.POST.get('author') | ||
| receiver = get_object_or_404(User, username=testimonial_author) | ||
| testimonial_id = request.POST.get('testimonial_id') | ||
| title = request.POST.get('title') | ||
| message = request.POST.get('message') | ||
| approval_status = request.POST.get('approval_status') | ||
| course_name = request.POST.get('course_name') | ||
|
|
||
| testimonial = Testimonial.objects.filter(id=testimonial_id) | ||
|
|
||
| final_message = "" | ||
| if has_admin_rights: | ||
| testimonial.update(approval_status=approval_status) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want to add checks (frontend & backend) so you can't approve or reject testimonials which already have a decision? Currently admins can approve the same testimonial many times in succession, each one sending a new notification. Maybe the buttons should be disabled and the backend should prevent it.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will do that, I was just wondering if there has been a mistake in approving/disapproving a testimonial, an admin can go back and change their mistake? For example, they accidentally pressed approve on a testimonial, and they could go back and change it to disapproved? Maybe instead, I should add an "are you sure" menu to approving/disapproving a testimonial so that if an admin makes a mistake, they can revert back the error by clicking "No" on the "Are you sure" menu. That way, the user does not get spammed with notifications too. I hope this makes sense. |
||
| if approval_status == str(ApprovalStatus.APPROVED.value): | ||
| final_message = f'Your Testimonial to {course_name}: \n"{testimonial[0].testimonial}" has been Accepted, it is now available to see in the Testimonials tab.' | ||
| if (sender != receiver): | ||
| update_to_testimonial_status(sender, receiver, title, final_message) #notification | ||
| return response.success(value="Testimonial Accepted and the notification has been sent to " + str(receiver) + ".") | ||
| elif approval_status == str(ApprovalStatus.REJECTED.value): | ||
| final_message = f'Your Testimonial to {course_name}: \n"{testimonial[0].testimonial}" has not been accepted due to: {message}' | ||
| if (sender != receiver): | ||
| update_to_testimonial_status(sender, receiver, title, final_message) #notification | ||
| return response.success(value="Testimonial Not Accepted " + "and the notification has been sent to " + str(receiver) + ".") | ||
| else: | ||
| return response.not_possible("Cannot Update the Testimonial to approval_status: " + str(approval_status)) | ||
| else: | ||
| return response.not_possible("No permission to approve/disapprove this testimonial.") | ||
Uh oh!
There was an error while loading. Please reload this page.