Skip to content

Commit

Permalink
review backend
Browse files Browse the repository at this point in the history
  • Loading branch information
benjmnxu committed Nov 15, 2024
1 parent 5036993 commit f0d2376
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 5 deletions.
2 changes: 2 additions & 0 deletions backend/ohq/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
Semester,
Tag,
UserStatistic,
Review
)


Expand All @@ -28,3 +29,4 @@
admin.site.register(Announcement)
admin.site.register(Tag)
admin.site.register(UserStatistic)
admin.site.register(Review)
39 changes: 39 additions & 0 deletions backend/ohq/migrations/0022_review_question_review.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 5.0.3 on 2024-11-10 18:14

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


class Migration(migrations.Migration):

dependencies = [
("ohq", "0021_queue_question_timer_enabled_and_more"),
]

operations = [
migrations.CreateModel(
name="Review",
fields=[
(
"id",
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("content", models.TextField(blank=True)),
(
"rating",
models.IntegerField(
choices=[(1, "ONE"), (2, "TWO"), (3, "THREE"), (4, "FOUR"), (5, "FIVE")]
),
),
],
),
migrations.AddField(
model_name="question",
name="review",
field=models.OneToOneField(
blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="ohq.review"
),
),
]
18 changes: 18 additions & 0 deletions backend/ohq/migrations/0023_course_reviews.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.3 on 2024-11-10 18:28

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("ohq", "0022_review_question_review"),
]

operations = [
migrations.AddField(
model_name="course",
name="reviews",
field=models.BooleanField(default=True),
),
]
18 changes: 18 additions & 0 deletions backend/ohq/migrations/0024_rename_reviews_course_allow_reviews.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.3 on 2024-11-15 17:45

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("ohq", "0023_course_reviews"),
]

operations = [
migrations.RenameField(
model_name="course",
old_name="reviews",
new_name="allow_reviews",
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 5.0.3 on 2024-11-15 17:52

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


class Migration(migrations.Migration):

dependencies = [
("ohq", "0024_rename_reviews_course_allow_reviews"),
]

operations = [
migrations.RemoveField(
model_name="question",
name="review",
),
migrations.AddField(
model_name="review",
name="question",
field=models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="ohq.question",
),
),
]
22 changes: 20 additions & 2 deletions backend/ohq/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class Course(models.Model):
archived = models.BooleanField(default=False)
invite_only = models.BooleanField(default=False)
members = models.ManyToManyField(User, through="Membership", through_fields=("course", "user"))

allow_reviews = models.BooleanField(default=True)

# MAX_NUMBER_COURSE_USERS = 1000

Expand Down Expand Up @@ -244,7 +244,6 @@ class Meta:
def __str__(self):
return f"{self.course}: {self.name}"


class Question(models.Model):
"""
A question asked within a queue.
Expand Down Expand Up @@ -287,7 +286,26 @@ class Question(models.Model):
tags = models.ManyToManyField(Tag, blank=True)
student_descriptor = models.CharField(max_length=255, blank=True, null=True)

class Review(models.Model):
"""
TA reviews
"""
RATING_ONE = 1
RATING_TWO = 2
RATING_THREE = 3
RATING_FOUR = 4
RATING_FIVE = 5
RATING_CHOICES = [
(RATING_ONE, "ONE"),
(RATING_TWO, "TWO"),
(RATING_THREE, "THREE"),
(RATING_FOUR, "FOUR"),
(RATING_FIVE, "FIVE")
]

content = models.TextField(blank=True)
rating = models.IntegerField(choices=RATING_CHOICES)
question = models.OneToOneField(Question, on_delete=models.CASCADE, blank=True, null=True)
class CourseStatistic(models.Model):
"""
Most active students/TAs in the past week for a course
Expand Down
31 changes: 31 additions & 0 deletions backend/ohq/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,34 @@ def has_permission(self, request, view):
return True

return True

class ReviewPermission(permissions.BasePermission):
def has_permission(self, request, view):
# Anonymous users can't do anything
if not request.user.is_authenticated:
return False

membership = Membership.objects.filter(
course=view.kwargs["course_pk"], user=request.user
).first()

# Non-Students can't do anything
if not membership or membership == "TA":
return False

# Only students can create, modify and delete reviews
if membership.kind == "TA":
return False # Deny access to TAs

# Allow access for Head TAs, Professors, and Students (additional checks can be added here if needed)
return membership.kind in ["HEAD_TA", "PROFESSOR", "STUDENT"]

def has_object_permission(self, request, view, obj):
membership = Membership.objects.filter(course=view.kwargs["course_pk"], user=request.user).first()

if membership and membership.kind == "STUDENT":
# Students can only access their own questions
return obj.question.asked_by == request.user

# Allow access for Head TAs and Professors
return membership.kind in ["HEAD_TA", "PROFESSOR"]
43 changes: 40 additions & 3 deletions backend/ohq/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.http import JsonResponse
from phonenumber_field.serializerfields import PhoneNumberField
from rest_framework import serializers
from rest_live.signals import save_handler
Expand All @@ -22,6 +23,7 @@
QueueStatistic,
Semester,
Tag,
Review
)
from ohq.sms import sendSMSVerification
from ohq.tasks import sendUpNextNotificationTask
Expand Down Expand Up @@ -50,7 +52,6 @@ def save(self):
self.validated_data["queue"] = Queue.objects.get(pk=self.context["view"].kwargs["queue_pk"])
return super().save()


class SemesterSerializer(serializers.ModelSerializer):
pretty = serializers.SerializerMethodField()

Expand Down Expand Up @@ -209,8 +210,6 @@ class TagSerializer(CourseRouteMixin):
class Meta:
model = Tag
fields = ("id", "name")


class QuestionSerializer(QueueRouteMixin):
asked_by = UserSerializer(read_only=True)
responded_to_by = UserSerializer(read_only=True)
Expand Down Expand Up @@ -246,6 +245,7 @@ class Meta:
"should_send_up_soon_notification",
"resolved_note",
"position",
"review"
)

def update(self, instance, validated_data):
Expand Down Expand Up @@ -345,6 +345,43 @@ def create(self, validated_data):
continue
return question

def get_review(self, obj):
review = Review.objects.get(question=obj)
serializer = ReviewSerializer(review)
return serializer.data


class ReviewSerializer(serializers.ModelSerializer):
"""
Serializer for review, allowing input of a question object via a nested serializer.
"""
question = serializers.PrimaryKeyRelatedField(
queryset=Question.objects.none(),
write_only=True
)
class Meta:
model = Review
fields = ("id", "content", "rating", "question")

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
course_pk = self.context.get('course_pk')
if course_pk:
course = Course.objects.filter(pk=course_pk).first()
self.fields['question'].queryset = Question.objects.filter(queue__course=course)

def create(self, validated_data):
# Extract the nested question data
question = validated_data.pop("question")
validated_data["question"] = question
return super().create(validated_data)

def update(self, instance, validated_data):
# Handle question updates if applicable
question = validated_data.pop("question", None)
validated_data["question"] = question
return super().update(instance, validated_data)


class MembershipPrivateSerializer(CourseRouteMixin):
"""
Expand Down
2 changes: 2 additions & 0 deletions backend/ohq/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
SemesterViewSet,
TagViewSet,
UserView,
ReviewViewSet,
)


Expand All @@ -36,6 +37,7 @@
course_router.register("invites", MembershipInviteViewSet, basename="invite")
course_router.register("announcements", AnnouncementViewSet, basename="announcement")
course_router.register("tags", TagViewSet, basename="tag")
course_router.register("reviews", ReviewViewSet, basename = "review")

queue_router = routers.NestedSimpleRouter(course_router, "queues", lookup="queue")
queue_router.register("questions", QuestionViewSet, basename="question")
Expand Down
Loading

0 comments on commit f0d2376

Please sign in to comment.