From 0fdaab7e8893f3bac9da23cdce4428e889c58859 Mon Sep 17 00:00:00 2001 From: virusdefender Date: Fri, 17 Feb 2017 20:14:03 +0800 Subject: [PATCH] add create contest problem --- contest/migrations/0003_auto_20170217_0820.py | 34 ++++++++ contest/models.py | 12 --- problem/migrations/0003_auto_20170217_0820.py | 63 ++++++++++++++ problem/models.py | 12 +++ problem/serializers.py | 4 + problem/urls/admin.py | 5 +- problem/views/admin.py | 87 ++++++++++++++++++- 7 files changed, 201 insertions(+), 16 deletions(-) create mode 100644 contest/migrations/0003_auto_20170217_0820.py create mode 100644 problem/migrations/0003_auto_20170217_0820.py diff --git a/contest/migrations/0003_auto_20170217_0820.py b/contest/migrations/0003_auto_20170217_0820.py new file mode 100644 index 0000000..4776df9 --- /dev/null +++ b/contest/migrations/0003_auto_20170217_0820.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.12 on 2017-02-17 08:20 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0002_auto_20170209_0845'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='contestproblem', + unique_together=set([]), + ), + migrations.RemoveField( + model_name='contestproblem', + name='contest', + ), + migrations.RemoveField( + model_name='contestproblem', + name='created_by', + ), + migrations.RemoveField( + model_name='contestproblem', + name='tags', + ), + migrations.DeleteModel( + name='ContestProblem', + ), + ] diff --git a/contest/models.py b/contest/models.py index d2fd537..72e60cb 100644 --- a/contest/models.py +++ b/contest/models.py @@ -3,7 +3,6 @@ from django.utils.timezone import now from jsonfield import JSONField from account.models import User -from problem.models import AbstractProblem from utils.models import RichTextField @@ -61,17 +60,6 @@ class Contest(models.Model): db_table = "contest" -class ContestProblem(AbstractProblem): - _id = models.CharField(max_length=24, db_index=True) - contest = models.ForeignKey(Contest) - # 是否已经公开了题目,防止重复公开 - is_public = models.BooleanField(default=False) - - class Meta: - db_table = "contest_problem" - unique_together = (("_id", "contest"), ) - - class ContestRank(models.Model): user = models.ForeignKey(User) contest = models.ForeignKey(Contest) diff --git a/problem/migrations/0003_auto_20170217_0820.py b/problem/migrations/0003_auto_20170217_0820.py new file mode 100644 index 0000000..70d66f2 --- /dev/null +++ b/problem/migrations/0003_auto_20170217_0820.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.12 on 2017-02-17 08:20 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import jsonfield.fields +import utils.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0003_auto_20170217_0820'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('problem', '0002_problem__id'), + ] + + operations = [ + migrations.CreateModel( + name='ContestProblem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=128)), + ('description', utils.models.RichTextField()), + ('input_description', utils.models.RichTextField()), + ('output_description', utils.models.RichTextField()), + ('samples', jsonfield.fields.JSONField()), + ('test_case_id', models.CharField(max_length=32)), + ('test_case_score', jsonfield.fields.JSONField()), + ('hint', utils.models.RichTextField(blank=True, null=True)), + ('languages', jsonfield.fields.JSONField()), + ('template', jsonfield.fields.JSONField()), + ('create_time', models.DateTimeField(auto_now_add=True)), + ('last_update_time', models.DateTimeField(blank=True, null=True)), + ('time_limit', models.IntegerField()), + ('memory_limit', models.IntegerField()), + ('spj', models.BooleanField(default=False)), + ('spj_language', models.CharField(blank=True, max_length=32, null=True)), + ('spj_code', models.TextField(blank=True, null=True)), + ('spj_version', models.CharField(blank=True, max_length=32, null=True)), + ('rule_type', models.CharField(max_length=32)), + ('visible', models.BooleanField(default=True)), + ('difficulty', models.CharField(max_length=32)), + ('source', models.CharField(blank=True, max_length=200, null=True)), + ('total_submit_number', models.IntegerField(default=0)), + ('total_accepted_number', models.IntegerField(default=0)), + ('_id', models.CharField(db_index=True, max_length=24)), + ('is_public', models.BooleanField(default=False)), + ('contest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contest.Contest')), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('tags', models.ManyToManyField(to='problem.ProblemTag')), + ], + options={ + 'db_table': 'contest_problem', + }, + ), + migrations.AlterUniqueTogether( + name='contestproblem', + unique_together=set([('_id', 'contest')]), + ), + ] diff --git a/problem/models.py b/problem/models.py index faddb6a..17d4e5d 100644 --- a/problem/models.py +++ b/problem/models.py @@ -2,6 +2,7 @@ from django.db import models from jsonfield import JSONField from account.models import User +from contest.models import Contest from utils.models import RichTextField @@ -66,3 +67,14 @@ class AbstractProblem(models.Model): class Problem(AbstractProblem): _id = models.CharField(max_length=24, unique=True, db_index=True) + + +class ContestProblem(AbstractProblem): + _id = models.CharField(max_length=24, db_index=True) + contest = models.ForeignKey(Contest) + # 是否已经公开了题目,防止重复公开 + is_public = models.BooleanField(default=False) + + class Meta: + db_table = "contest_problem" + unique_together = (("_id", "contest"), ) diff --git a/problem/serializers.py b/problem/serializers.py index e9bdf38..0425f3d 100644 --- a/problem/serializers.py +++ b/problem/serializers.py @@ -64,6 +64,10 @@ class EditProblemSerializer(CreateOrEditProblemSerializer): id = serializers.IntegerField() +class CreateContestProblemSerializer(CreateOrEditProblemSerializer): + contest_id = serializers.IntegerField() + + class TagSerializer(serializers.ModelSerializer): class Meta: model = ProblemTag diff --git a/problem/urls/admin.py b/problem/urls/admin.py index b9a4ec2..a0e0b66 100644 --- a/problem/urls/admin.py +++ b/problem/urls/admin.py @@ -1,8 +1,9 @@ from django.conf.urls import url -from ..views.admin import ProblemAPI, TestCaseUploadAPI +from ..views.admin import ProblemAPI, TestCaseUploadAPI, ContestProblemAPI urlpatterns = [ url(r"^test_case/upload$", TestCaseUploadAPI.as_view(), name="test_case_upload_api"), - url(r"^problem$", ProblemAPI.as_view(), name="problem_api") + url(r"^problem$", ProblemAPI.as_view(), name="problem_api"), + url(r'contest/problem$', ContestProblemAPI.as_view(), name="contest_problem_api") ] diff --git a/problem/views/admin.py b/problem/views/admin.py index 108cace..2fc8cf7 100644 --- a/problem/views/admin.py +++ b/problem/views/admin.py @@ -6,12 +6,13 @@ import zipfile from django.conf import settings from account.decorators import problem_permission_required +from contest.models import Contest from utils.api import APIView, CSRFExemptAPIView, validate_serializer from utils.shortcuts import rand_str -from ..models import Problem, ProblemRuleType, ProblemTag +from ..models import Problem, ProblemRuleType, ProblemTag, ContestProblem from ..serializers import (CreateProblemSerializer, EditProblemSerializer, - ProblemSerializer, TestCaseUploadForm) + ProblemSerializer, TestCaseUploadForm, CreateContestProblemSerializer) class TestCaseUploadAPI(CSRFExemptAPIView): @@ -170,6 +171,9 @@ class ProblemAPI(APIView): problems = Problem.objects.all().order_by("-create_time") if not user.can_mgmt_all_problem(): problems = problems.filter(created_by=request.user) + keyword = request.GET.get("keyword") + if keyword: + problems = problems.filter(title__contains=keyword) return self.success(self.paginate_data(request, problems, ProblemSerializer)) @validate_serializer(EditProblemSerializer) @@ -203,6 +207,7 @@ class ProblemAPI(APIView): else: data["spj_language"] = None data["spj_code"] = None + if data["rule_type"] == ProblemRuleType.OI: for item in data["test_case_score"]: if item["score"] <= 0: @@ -223,3 +228,81 @@ class ProblemAPI(APIView): problem.tags.add(tag) return self.success() + + +class ContestProblemAPI(APIView): + @validate_serializer(CreateContestProblemSerializer) + def post(self, request): + data = request.data + + try: + contest = Contest.objects.get(id=data.pop("contest_id")) + if request.user.is_admin() and contest.created_by != request.user: + return self.error("Contest does not exist") + except Contest.DoesNotExist: + return self.error("Contest does not exist") + + if data["rule_type"] != contest.rule_type: + return self.error("Invalid rule type") + + _id = data["_id"] + if not _id: + return self.error("Display id is required for contest problem") + try: + ContestProblem.objects.get(_id=_id, contest=contest) + return self.error("Duplicate Display id") + except ContestProblem.DoesNotExist: + pass + + if data["spj"]: + if not data["spj_language"] or not data["spj_code"]: + return self.error("Invalid spj") + data["spj_version"] = hashlib.md5((data["spj_language"] + ":" + data["spj_code"]).encode("utf-8")).hexdigest() + else: + data["spj_language"] = None + data["spj_code"] = None + + if data["rule_type"] == ProblemRuleType.OI: + for item in data["test_case_score"]: + if item["score"] <= 0: + return self.error("Invalid score") + # todo check filename and score info + + data["created_by"] = request.user + data["contest"] = contest + tags = data.pop("tags") + data["languages"] = list(data["languages"]) + + problem = ContestProblem.objects.create(**data) + + for item in tags: + try: + tag = ProblemTag.objects.get(name=item) + except ProblemTag.DoesNotExist: + tag = ProblemTag.objects.create(name=item) + problem.tags.add(tag) + return self.success(ProblemSerializer(problem).data) + + def get(self, request): + problem_id = request.GET.get("id") + contest_id = request.GET.get("contest_id") + user = request.user + if problem_id: + try: + problem = ContestProblem.objects.get(id=problem_id) + if request.user.is_admin() and problem.contest.created_by != user: + return self.error("Problem does not exist") + except ContestProblem.DoesNotExist: + return self.error("Problem does not exist") + return self.success(ProblemSerializer(problem).data) + + if not contest_id: + return self.error("Contest id is required") + + problems = ContestProblem.objects.filter(contest_id=contest_id).order_by("-create_time") + if user.is_admin(): + problems = problems.filter(contest__created_by=user) + keyword = request.GET.get("keyword") + if keyword: + problems = problems.filter(title__contains=keyword) + return self.success(self.paginate_data(request, problems, ProblemSerializer))