add message and comment

This commit is contained in:
2024-06-29 21:33:22 +08:00
parent 148761c8a2
commit 88a322b248
28 changed files with 431 additions and 144 deletions

View File

@@ -0,0 +1,16 @@
# Generated by Django 5.0.6 on 2024-06-29 15:38
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('announcement', '0006_message'),
]
operations = [
migrations.DeleteModel(
name='Message',
),
]

View File

@@ -1,7 +1,6 @@
from django.db import models
from account.models import User
from submission.models import Submission
from utils.models import RichTextField
@@ -19,15 +18,3 @@ class Announcement(models.Model):
class Meta:
db_table = "announcement"
ordering = ("-top", "-create_time",)
class Message(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sender")
recipient = models.ForeignKey(User, on_delete=models.CASCADE, related_name="recipient")
submission = models.ForeignKey(Submission, on_delete=models.CASCADE)
message = RichTextField()
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = "message"
ordering = ("-create_time",)

View File

@@ -1,8 +1,7 @@
from submission.serializers import SubmissionSafeModelSerializer
from utils.api import serializers
from utils.api._serializers import UsernameSerializer
from .models import Announcement, Message
from .models import Announcement
class CreateAnnouncementSerializer(serializers.Serializer):
@@ -36,18 +35,3 @@ class EditAnnouncementSerializer(serializers.Serializer):
content = serializers.CharField(max_length=1024 * 1024 * 8)
visible = serializers.BooleanField()
top = serializers.BooleanField()
class MessageSerializer(serializers.ModelSerializer):
sender = UsernameSerializer()
submission = SubmissionSafeModelSerializer()
class Meta:
model = Message
exclude = ["recipient"]
class CreateMessageSerializer(serializers.Serializer):
recipient = serializers.IntegerField()
submission = serializers.CharField()
message = serializers.CharField()

View File

@@ -1,8 +1,7 @@
from django.urls import re_path as url
from ..views.oj import AnnouncementAPI, MessageAPI
from ..views.oj import AnnouncementAPI
urlpatterns = [
url(r"^announcement/?$", AnnouncementAPI.as_view(), name="announcement_api"),
url(r"^message/?$", MessageAPI.as_view(), name="message_api"),
]

View File

@@ -1,14 +1,8 @@
from account.decorators import super_admin_required, login_required
from account.models import User
from submission.models import Submission
from utils.api import APIView
from announcement.models import Announcement, Message
from announcement.models import Announcement
from announcement.serializers import (AnnouncementSerializer,
AnnouncementListSerializer,
CreateMessageSerializer,
MessageSerializer)
from utils.api.api import validate_serializer
AnnouncementListSerializer)
class AnnouncementAPI(APIView):
@@ -23,30 +17,3 @@ class AnnouncementAPI(APIView):
announcements = Announcement.objects.filter(visible=True)
return self.success(self.paginate_data(request, announcements, AnnouncementListSerializer))
class MessageAPI(APIView):
@login_required
def get(self, request):
messages = Message.objects.select_related("recipient","sender", "submission").filter(recipient=request.user)
return self.success(self.paginate_data(request, messages, MessageSerializer))
@validate_serializer(CreateMessageSerializer)
@super_admin_required
def post(self, request):
data = request.data
if data["recipient"] == request.user.id:
return self.error("Can not send a message to youself")
try:
recipient = User.objects.get(id=data["recipient"], is_disabled=False)
except User.DoesNotExist:
return self.error("User does not exist")
try:
submission = Submission.objects.get(id=data["submission"])
except Submission.DoesNotExist:
return self.error("Submission does not exist")
Message.objects.create(submission=submission,
message=data["message"],
sender=request.user,
recipient=recipient)
return self.success()

0
comment/__init__.py Normal file
View File

View File

@@ -0,0 +1,40 @@
# Generated by Django 5.0.6 on 2024-06-29 13:32
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('problem', '0015_alter_problem_io_mode_alter_problem_languages_and_more'),
('submission', '0013_alter_submission_info_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Comment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('problem_solved', models.BooleanField()),
('language', models.CharField(blank=True, choices=[('Python', 'Python'), ('C', 'C'), ('C++', 'C++'), ('Java', 'Java')], max_length=10, null=True, verbose_name='解决这道题使用的语言')),
('description_rating', models.PositiveSmallIntegerField(default=5, verbose_name='题目描述的分数')),
('difficulty_rating', models.PositiveSmallIntegerField(default=5, verbose_name='题目难度的分数')),
('comprehensive_rating', models.PositiveSmallIntegerField(default=5, verbose_name='综合的分数')),
('content', models.TextField(blank=True, null=True)),
('visible', models.BooleanField(default=True)),
('create_time', models.DateTimeField(auto_now_add=True)),
('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='problem.problem')),
('submission', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='submission.submission')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'comment',
'ordering': ('create_time',),
},
),
]

View File

47
comment/models.py Normal file
View File

@@ -0,0 +1,47 @@
from django.db import models
from account.models import User
from problem.models import Problem
from submission.models import Submission
class Languages(models.TextChoices):
Python = "Python", "Python"
C = "C", "C"
Cpp = "C++", "C++"
Java = "Java", "Java"
class Comment(models.Model):
problem = models.ForeignKey(Problem, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
submission = models.ForeignKey(Submission, null=True, blank=True, on_delete=models.SET_NULL)
problem_solved = models.BooleanField()
language = models.CharField(
max_length=10,
choices=Languages.choices,
null=True,
blank=True,
verbose_name="解决这道题使用的语言",
)
description_rating = models.PositiveSmallIntegerField(
default=5,
verbose_name="题目描述的分数",
)
difficulty_rating = models.PositiveSmallIntegerField(
default=5,
verbose_name="题目难度的分数",
)
comprehensive_rating = models.PositiveSmallIntegerField(
default=5,
verbose_name="综合的分数",
)
content = models.TextField(null=True, blank=True)
visible = models.BooleanField(default=True)
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = "comment"
ordering = ("create_time",)

19
comment/serializers.py Normal file
View File

@@ -0,0 +1,19 @@
from comment.models import Comment
from utils.api import serializers
class CreateCommentSerializer(serializers.Serializer):
problem_id = serializers.IntegerField()
submission_id = serializers.CharField(max_length=32, allow_null=True)
problem_solved = serializers.BooleanField()
language = serializers.CharField(max_length=10, allow_null=True)
description_rating = serializers.IntegerField()
difficulty_rating = serializers.IntegerField()
comprehensive_rating = serializers.IntegerField()
content = serializers.CharField()
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = "__all__"

0
comment/urls/__init__.py Normal file
View File

8
comment/urls/oj.py Normal file
View File

@@ -0,0 +1,8 @@
from django.urls import re_path as url
from ..views.oj import CommentAPI
urlpatterns = [
url(r"^comment/?$", CommentAPI.as_view(), name="comment_api"),
]

View File

64
comment/views/oj.py Normal file
View File

@@ -0,0 +1,64 @@
from django.db.models import Avg
from comment.models import Comment
from problem.models import Problem
from utils.api import APIView
from account.decorators import login_required
from utils.api.api import validate_serializer
from comment.serializers import CreateCommentSerializer
from submission.models import Submission, JudgeStatus
class CommentAPI(APIView):
@validate_serializer(CreateCommentSerializer)
@login_required
def post(self, request):
data = request.data
try:
problem = Problem.objects.get(id=data.problem_id, visible=True)
except Problem.DoesNotExist:
self.error("problem is not exists")
submission = None
if data.problem_solved and data.submission_id:
try:
data.submission_id
submission = Submission.objects.select_related("problem").get(
id=data.submission_id,
problem_id=data.problem_id,
result=JudgeStatus.ACCEPTED,
)
except Submission.DoesNotExist:
self.error("submission is not exists or not accepted")
if not data.problem_solved:
data.language = None
Comment.objects.create(
user=request.user,
problem=problem,
submission=submission,
problem_solved=data.problem_solved,
language=data.language,
description_rating=data.description_rating,
difficulty_rating=data.difficulty_rating,
comprehensive_rating=data.comprehensive_rating,
content=data.content,
)
return self.success()
def get(self, request):
problem_id = request.GET.get("problem_id")
comments = Comment.objects.select_related("problem").filter(
problem_id=problem_id, visible=True
)
if comments.count() == 0:
return self.success()
rating = comments.aggregate(
description=Avg("description_rating"),
difficulty=Avg("difficulty_rating"),
comprehensive=Avg("comprehensive_rating"),
)
contents = comments.filter(content__isnull=False).values_list(
"content", flat=True
)
return self.success({rating: rating, contents: contents})

0
message/__init__.py Normal file
View File

View File

@@ -0,0 +1,34 @@
# Generated by Django 5.0.6 on 2024-06-29 15:38
import django.db.models.deletion
import utils.models
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('submission', '0013_alter_submission_info_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Message',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', utils.models.RichTextField()),
('create_time', models.DateTimeField(auto_now_add=True)),
('recipient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recipient', to=settings.AUTH_USER_MODEL)),
('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sender', to=settings.AUTH_USER_MODEL)),
('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.submission')),
],
options={
'db_table': 'message',
'ordering': ('-create_time',),
},
),
]

View File

18
message/models.py Normal file
View File

@@ -0,0 +1,18 @@
from django.db import models
from account.models import User
from submission.models import Submission
from utils.models import RichTextField
class Message(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sender")
recipient = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="recipient"
)
submission = models.ForeignKey(Submission, on_delete=models.CASCADE)
message = RichTextField()
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = "message"
ordering = ("-create_time",)

19
message/serializers.py Normal file
View File

@@ -0,0 +1,19 @@
from submission.serializers import SubmissionSafeModelSerializer
from utils.api import UsernameSerializer, serializers
from .models import Message
class MessageSerializer(serializers.ModelSerializer):
sender = UsernameSerializer()
submission = SubmissionSafeModelSerializer()
class Meta:
model = Message
exclude = ["recipient"]
class CreateMessageSerializer(serializers.Serializer):
recipient = serializers.IntegerField()
submission = serializers.CharField()
message = serializers.CharField()

0
message/urls/__init__.py Normal file
View File

7
message/urls/oj.py Normal file
View File

@@ -0,0 +1,7 @@
from django.urls import re_path as url
from ..views.oj import MessageAPI
urlpatterns = [
url(r"^message/?$", MessageAPI.as_view(), name="message_api"),
]

View File

40
message/views/oj.py Normal file
View File

@@ -0,0 +1,40 @@
from account.decorators import super_admin_required, login_required
from account.models import User
from message.serializers import CreateMessageSerializer, MessageSerializer
from submission.models import Submission
from utils.api import APIView
from message.models import Message
from utils.api.api import validate_serializer
class MessageAPI(APIView):
@login_required
def get(self, request):
messages = Message.objects.select_related(
"recipient", "sender", "submission"
).filter(recipient=request.user)
return self.success(self.paginate_data(request, messages, MessageSerializer))
@validate_serializer(CreateMessageSerializer)
@super_admin_required
def post(self, request):
data = request.data
if data["recipient"] == request.user.id:
return self.error("Can not send a message to youself")
try:
recipient = User.objects.get(id=data["recipient"], is_disabled=False)
except User.DoesNotExist:
return self.error("User does not exist")
try:
submission = Submission.objects.get(id=data["submission"])
except Submission.DoesNotExist:
return self.error("Submission does not exist")
Message.objects.create(
submission=submission,
message=data["message"],
sender=request.user,
recipient=recipient,
)
return self.success()

View File

@@ -51,6 +51,8 @@ LOCAL_APPS = [
'submission',
'options',
'judge',
'message',
'comment'
]
INSTALLED_APPS = VENDOR_APPS + LOCAL_APPS

View File

@@ -14,4 +14,6 @@ urlpatterns = [
url(r"^api/", include("submission.urls.oj")),
url(r"^api/admin/", include("submission.urls.admin")),
url(r"^api/admin/", include("utils.urls")),
url(r"^api/", include("message.urls.oj")),
url(r"^api/", include("comment.urls.oj")),
]

View File

@@ -31,7 +31,11 @@ class ProblemIOMode(Choices):
def _default_io_mode():
return {"io_mode": ProblemIOMode.standard, "input": "input.txt", "output": "output.txt"}
return {
"io_mode": ProblemIOMode.standard,
"input": "input.txt",
"output": "output.txt",
}
class Problem(models.Model):

View File

@@ -3,8 +3,14 @@ from django.db.models import Q, Count
from utils.api import APIView
from account.decorators import check_contest_permission
from ..models import ProblemTag, Problem, ProblemRuleType
from ..serializers import ProblemSerializer, TagSerializer, ProblemSafeSerializer, ProblemListSerializer
from ..serializers import (
ProblemSerializer,
TagSerializer,
ProblemSafeSerializer,
ProblemListSerializer,
)
from contest.models import ContestRuleType
from submission.models import JudgeStatus
class ProblemTagAPI(APIView):
@@ -38,20 +44,27 @@ class ProblemAPI(APIView):
if results is not None:
problems = results
else:
problems = [queryset_values, ]
problems = [
queryset_values,
]
for problem in problems:
if problem["rule_type"] == ProblemRuleType.ACM:
problem["my_status"] = acm_problems_status.get(str(problem["id"]), {}).get("status")
problem["my_status"] = acm_problems_status.get(
str(problem["id"]), {}
).get("status")
else:
problem["my_status"] = oi_problems_status.get(str(problem["id"]), {}).get("status")
problem["my_status"] = oi_problems_status.get(
str(problem["id"]), {}
).get("status")
def get(self, request):
# 问题详情页
problem_id = request.GET.get("problem_id")
if problem_id:
try:
problem = Problem.objects.select_related("created_by") \
.get(_id=problem_id, contest_id__isnull=True, visible=True)
problem = Problem.objects.select_related("created_by").get(
_id=problem_id, contest_id__isnull=True, visible=True
)
problem_data = ProblemSerializer(problem).data
self._add_problem_status(request, problem_data)
return self.success(problem_data)
@@ -62,7 +75,11 @@ class ProblemAPI(APIView):
if not limit:
return self.error("Limit is needed")
problems = Problem.objects.select_related("created_by").filter(contest_id__isnull=True, visible=True).order_by("-create_time")
problems = (
Problem.objects.select_related("created_by")
.filter(contest_id__isnull=True, visible=True)
.order_by("-create_time")
)
# 按照标签筛选
tag_text = request.GET.get("tag")
if tag_text:
@@ -71,7 +88,9 @@ class ProblemAPI(APIView):
# 搜索的情况
keyword = request.GET.get("keyword", "").strip()
if keyword:
problems = problems.filter(Q(title__icontains=keyword) | Q(_id__icontains=keyword))
problems = problems.filter(
Q(title__icontains=keyword) | Q(_id__icontains=keyword)
)
# 难度筛选
difficulty = request.GET.get("difficulty")
@@ -88,30 +107,41 @@ class ContestProblemAPI(APIView):
if request.user.is_authenticated:
profile = request.user.userprofile
if self.contest.rule_type == ContestRuleType.ACM:
problems_status = profile.acm_problems_status.get("contest_problems", {})
problems_status = profile.acm_problems_status.get(
"contest_problems", {}
)
else:
problems_status = profile.oi_problems_status.get("contest_problems", {})
for problem in queryset_values:
problem["my_status"] = problems_status.get(str(problem["id"]), {}).get("status")
problem["my_status"] = problems_status.get(str(problem["id"]), {}).get(
"status"
)
@check_contest_permission(check_type="problems")
def get(self, request):
problem_id = request.GET.get("problem_id")
if problem_id:
try:
problem = Problem.objects.select_related("created_by").get(_id=problem_id,
contest=self.contest,
visible=True)
problem = Problem.objects.select_related("created_by").get(
_id=problem_id, contest=self.contest, visible=True
)
except Problem.DoesNotExist:
return self.error("Problem does not exist.")
if self.contest.problem_details_permission(request.user):
problem_data = ProblemSerializer(problem).data
self._add_problem_status(request, [problem_data, ])
self._add_problem_status(
request,
[
problem_data,
],
)
else:
problem_data = ProblemSafeSerializer(problem).data
return self.success(problem_data)
contest_problems = Problem.objects.select_related("created_by").filter(contest=self.contest, visible=True)
contest_problems = Problem.objects.select_related("created_by").filter(
contest=self.contest, visible=True
)
if self.contest.problem_details_permission(request.user):
data = ProblemListSerializer(contest_problems, many=True).data
self._add_problem_status(request, data)