diff --git a/backend/course/views/professor.py b/backend/course/views/professor.py index 1f6fd3e16..c54fd0684 100644 --- a/backend/course/views/professor.py +++ b/backend/course/views/professor.py @@ -32,6 +32,12 @@ class CourseAPI(APIView): type=openapi.TYPE_STRING, default=0, ), + openapi.Parameter( + name="bookmark", + in_=openapi.IN_QUERY, + description="bookmark state of course", + type=openapi.TYPE_BOOLEAN, + ), ], operation_description="Get course list or specific course generated by requesting admin", responses={200: CourseRegistrationSerializer}, @@ -49,10 +55,10 @@ def get(self, request): return self.error("Course does not exist") if request.GET.get("bookmark") == "true": - courses = Registration.objects.filter(course__created_by=request.user, bookmark=True) + courses = Registration.objects.filter(course__created_by=request.user, bookmark=True, user=request.user) return self.success(self.paginate_data(request, courses, CourseRegistrationSerializer)) - courses = Registration.objects.filter(course__created_by=request.user) + courses = Registration.objects.filter(course__created_by=request.user, user=request.user) return self.success(self.paginate_data(request, courses, CourseRegistrationSerializer)) @swagger_auto_schema( diff --git a/backend/problem/migrations/0019_problem_course.py b/backend/problem/migrations/0019_problem_course.py new file mode 100644 index 000000000..25927c15e --- /dev/null +++ b/backend/problem/migrations/0019_problem_course.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.11 on 2022-02-10 09:21 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0004_registration_bookmark'), + ('problem', '0018_auto_20211226_1458'), + ] + + operations = [ + migrations.AddField( + model_name='problem', + name='course', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='problems', to='course.course'), + ), + ] diff --git a/backend/problem/models.py b/backend/problem/models.py index 9f2d2a40a..1521b74b9 100644 --- a/backend/problem/models.py +++ b/backend/problem/models.py @@ -7,6 +7,7 @@ from utils.constants import Choices from assignment.models import Assignment +from course.models import Course class ProblemTag(models.Model): @@ -44,6 +45,8 @@ def _default_io_mode(): class Problem(models.Model): # display ID _id = models.TextField(db_index=True) + # course ID + course = models.ForeignKey(Course, null=True, on_delete=models.CASCADE, related_name="problems") # assignment ID assignment = models.ForeignKey(Assignment, null=True, on_delete=models.CASCADE, related_name="problems") contest = models.ForeignKey(Contest, null=True, on_delete=models.CASCADE) diff --git a/backend/problem/serializers.py b/backend/problem/serializers.py index b5d0d77bc..49076d718 100644 --- a/backend/problem/serializers.py +++ b/backend/problem/serializers.py @@ -96,12 +96,21 @@ class EditContestProblemSerializer(CreateOrEditProblemSerializer): class CreateAssignmentProblemSerializer(CreateOrEditProblemSerializer): assignment_id = serializers.IntegerField() + course_id = serializers.IntegerField() class EditAssignmentProblemSerializer(CreateOrEditProblemSerializer): id = serializers.IntegerField() +class CreateCourseProblemSerializer(CreateOrEditProblemSerializer): + course_id = serializers.IntegerField() + + +class EditCourseProblemSerializer(CreateOrEditProblemSerializer): + id = serializers.IntegerField() + + class TagSerializer(serializers.ModelSerializer): class Meta: model = ProblemTag @@ -117,11 +126,16 @@ class BaseProblemSerializer(serializers.ModelSerializer): tags = serializers.SlugRelatedField(many=True, slug_field="name", read_only=True) created_by = UsernameSerializer() contest_name = serializers.SerializerMethodField() + assignment_name = serializers.SerializerMethodField() def get_contest_name(self, obj): if obj.contest: return obj.contest.title + def get_assignment_name(self, obj): + if obj.assignment: + return obj.assignment.title + def get_public_template(self, obj): ret = {} for lang, code in obj.template.items(): @@ -135,7 +149,7 @@ class Meta: fields = "__all__" -class ProblemProfessorSerializer(BaseProblemSerializer): +class ProblemAssignmentProfessorSerializer(BaseProblemSerializer): total_submission_assignment = serializers.SerializerMethodField() def get_total_submission_assignment(self, obj): @@ -146,6 +160,17 @@ class Meta: fields = "__all__" +class ProblemCourseProfessorSerializer(BaseProblemSerializer): + total_submission_course = serializers.SerializerMethodField() + + def get_total_submission_course(self, obj): + return Submission.objects.filter(problem=obj, course=obj.course).values("user_id").distinct().count() + + class Meta: + model = Problem + fields = "__all__" + + class ProblemSerializer(BaseProblemSerializer): class Meta: @@ -176,11 +201,18 @@ class AddContestProblemSerializer(serializers.Serializer): class AddAssignmentProblemSerializer(serializers.Serializer): + course_id = serializers.IntegerField() assignment_id = serializers.IntegerField() problem_id = serializers.IntegerField() display_id = serializers.CharField() +class AddCourseProblemSerializer(serializers.Serializer): + course_id = serializers.IntegerField() + problem_id = serializers.IntegerField() + display_id = serializers.CharField() + + class ExportProblemRequestSerialzier(serializers.Serializer): problem_id = serializers.ListField(child=serializers.IntegerField(), allow_empty=False) diff --git a/backend/problem/tests.py b/backend/problem/tests.py index 381ef8204..dc53ee69e 100644 --- a/backend/problem/tests.py +++ b/backend/problem/tests.py @@ -270,6 +270,7 @@ def setUp(self): admin = self.create_admin() course = Course.objects.create(created_by=admin, **DEFAULT_COURSE_DATA) Registration.objects.create(user_id=student.id, course_id=course.id) + self.course_id = course.id self.assignment_id = Assignment.objects.create(created_by=admin, course=course, **DEFAULT_ASSIGNMENT_DATA).id self.url = self.reverse("assignment_problem_professor_api") @@ -286,6 +287,7 @@ def test_get_assignment_problem(self): def test_create_assignment_problem(self): data = copy.deepcopy(DEFAULT_PROBLEM_DATA) data["assignment_id"] = self.assignment_id + data["course_id"] = self.course_id res = self.client.post(self.url, data=data) self.assertSuccess(res) return res.data["data"] @@ -295,6 +297,7 @@ def test_edit_assignment_problem(self): data = copy.deepcopy(DEFAULT_PROBLEM_DATA) data["id"] = problem_id data["title"] = "edit test" + data["course"] = self.course_id data["assignment"] = self.assignment_id res = self.client.put(self.url, data=data) self.assertSuccess(res) @@ -357,12 +360,14 @@ def setUp(self): admin = self.create_admin() course = Course.objects.create(created_by=admin, **DEFAULT_COURSE_DATA) self.assignment_id = Assignment.objects.create(created_by=admin, course=course, **DEFAULT_ASSIGNMENT_DATA).id + self.course_id = course.id self.problem = self.add_problem(DEFAULT_PROBLEM_DATA, admin) self.url = self.reverse("add_assignment_problem_from_public_api") self.data = { "display_id": "1000", "assignment_id": self.assignment_id, - "problem_id": self.problem.id + "problem_id": self.problem.id, + "course_id": self.course_id } def test_add_assignment_problem(self): diff --git a/backend/problem/urls/professor.py b/backend/problem/urls/professor.py index ca8d6a361..4919d2120 100644 --- a/backend/problem/urls/professor.py +++ b/backend/problem/urls/professor.py @@ -1,8 +1,10 @@ from django.urls import path -from ..views.professor import AssignmentProblemAPI, AddAssignmentProblemAPI +from ..views.professor import AssignmentProblemAPI, AddAssignmentProblemAPI, CourseProblemAPI, AddCourseProblemAPI urlpatterns = [ path("course/assignment/problem/", AssignmentProblemAPI.as_view(), name="assignment_problem_professor_api"), path("course/assignment/add_problem_from_public/", AddAssignmentProblemAPI.as_view(), name="add_assignment_problem_from_public_api"), + path("course/problem/", CourseProblemAPI.as_view(), name="course_problem_professor_api"), + path("course/add_problem_from_public/", AddCourseProblemAPI.as_view(), name="add_course_problem_from_public_api"), ] diff --git a/backend/problem/views/admin.py b/backend/problem/views/admin.py index ef48887fa..3f02af3ef 100644 --- a/backend/problem/views/admin.py +++ b/backend/problem/views/admin.py @@ -245,6 +245,8 @@ def get(self, request): ensure_created_by(problem.contest, request.user) elif problem.assignment: ensure_created_by(problem.assignment, request.user) + elif problem.course: + ensure_created_by(problem.course, request.user) else: ensure_created_by(problem, request.user) diff --git a/backend/problem/views/oj.py b/backend/problem/views/oj.py index e87ebcc49..004c1b0a6 100644 --- a/backend/problem/views/oj.py +++ b/backend/problem/views/oj.py @@ -78,7 +78,7 @@ def get(self, request): if problem_id: try: problem = Problem.objects.select_related("created_by") \ - .get(_id=problem_id, contest_id__isnull=True, assignment_id__isnull=True, visible=True) + .get(_id=problem_id, contest_id__isnull=True, assignment_id__isnull=True, course_id__isnull=True, visible=True) problem_data = ProblemSerializer(problem).data self._add_problem_status(request, problem_data) return self.success(problem_data) @@ -89,7 +89,7 @@ def get(self, request): if not limit: return self.error("Limit is needed") - problems = Problem.objects.select_related("created_by").filter(contest_id__isnull=True, assignment_id__isnull=True, visible=True) + problems = Problem.objects.select_related("created_by").filter(contest_id__isnull=True, assignment_id__isnull=True, course_id__isnull=True, visible=True) # filter by label tag_text = request.GET.get("tag") if tag_text: diff --git a/backend/problem/views/professor.py b/backend/problem/views/professor.py index 1f24f5971..18a65665d 100644 --- a/backend/problem/views/professor.py +++ b/backend/problem/views/professor.py @@ -5,11 +5,14 @@ from submission.models import Submission from assignment.models import Assignment +from course.models import Course from .admin import ProblemBase from ..models import Problem, ProblemRuleType, ProblemTag from ..serializers import (CreateAssignmentProblemSerializer, ProblemAdminSerializer, AddAssignmentProblemSerializer, - ProblemProfessorSerializer, EditAssignmentProblemSerializer) + ProblemAssignmentProfessorSerializer, EditAssignmentProblemSerializer, + CreateCourseProblemSerializer, AddCourseProblemSerializer, + ProblemCourseProfessorSerializer, EditCourseProblemSerializer) from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi @@ -32,7 +35,7 @@ class AssignmentProblemAPI(ProblemBase): ), ], operation_description="Get problems of certain assignment. If problem_id is set, certain problem would be returned.", - responses={200: ProblemProfessorSerializer}, + responses={200: ProblemAssignmentProfessorSerializer}, ) @admin_role_required def get(self, request): @@ -45,7 +48,7 @@ def get(self, request): ensure_created_by(problem.assignment, user) except Problem.DoesNotExist: return self.error("Problem does not exist") - return self.success(ProblemProfessorSerializer(problem).data) + return self.success(ProblemAssignmentProfessorSerializer(problem).data) if not assignment_id: return self.error("Invalid parameter, assignment id is required") @@ -56,7 +59,7 @@ def get(self, request): return self.error("Assignment does not exist") problems = Problem.objects.filter(assignment=assignment).order_by("create_time") - return self.success(self.paginate_data(request, problems, ProblemProfessorSerializer)) + return self.success(self.paginate_data(request, problems, ProblemAssignmentProfessorSerializer)) @swagger_auto_schema( request_body=(CreateAssignmentProblemSerializer), @@ -75,6 +78,12 @@ def post(self, request): if assignment.status == AssignmentStatus.ASSIGNMENT_ENDED: return self.error("Assignment deadline has expired") + try: + course = Course.objects.get(id=data.pop("course_id")) + ensure_created_by(course, request.user) + except Course.DoesNotExist: + return self.error("Course does not exist") + _id = data["_id"] if Problem.objects.filter(_id=_id, assignment=assignment).exists(): @@ -85,6 +94,7 @@ def post(self, request): return self.error(error_info) data["assignment"] = assignment + data["course"] = course tags = data.pop("tags") data["created_by"] = request.user problem = Problem.objects.create(**data) @@ -164,7 +174,7 @@ def delete(self, request): if not id: return self.error("Invalid parameter, id is required") try: - problem = Problem.objects.get(id=id, assignment_id__isnull=False) + problem = Problem.objects.get(id=id, assignment_id__isnull=False, course_id__isnull=False) ensure_created_by(problem.assignment, request.user) except Problem.DoesNotExist: return self.error("Problem does not exists") @@ -185,11 +195,14 @@ class AddAssignmentProblemAPI(APIView): def post(self, request): data = request.data try: + course = Course.objects.get(id=data["course_id"]) assignment = Assignment.objects.get(id=data["assignment_id"]) ensure_created_by(assignment, request.user) problem = Problem.objects.get(id=data["problem_id"]) except Assignment.DoesNotExist: return self.error("Assignment does not exist") + except Course.DoesNotExist: + return self.error("Course does not exist") except Problem.DoesNotExist: return self.error("Problem does not exist") @@ -205,6 +218,207 @@ def post(self, request): tags = problem.tags.all() problem.pk = None problem.assignment = assignment + problem.course = course + problem.rule_type = ProblemRuleType.ASSIGNMENT + problem.is_public = True + problem.visible = True + problem._id = request.data["display_id"] + problem.submission_number = problem.accepted_number = 0 + problem.statistic_info = {} + problem.save() + problem.tags.set(tags) + return self.success() + + +class CourseProblemAPI(ProblemBase): + @swagger_auto_schema( + manual_parameters=[ + openapi.Parameter( + name="problem_id", + in_=openapi.IN_QUERY, + type=openapi.TYPE_INTEGER, + description="Unique id of problem.", + ), + openapi.Parameter( + name="course_id", + in_=openapi.IN_QUERY, + type=openapi.TYPE_INTEGER, + required=True, + description="Unique id of course.", + ), + ], + operation_description="Get problems of certain course. If problem_id is set, certain problem would be returned.", + responses={200: ProblemCourseProfessorSerializer}, + ) + @admin_role_required + def get(self, request): + problem_id = request.GET.get("problem_id") + course_id = request.GET.get("course_id") + user = request.user + if problem_id: + try: + problem = Problem.objects.get(id=problem_id) + ensure_created_by(problem.course, user) + except Problem.DoesNotExist: + return self.error("Problem does not exist") + return self.success(ProblemCourseProfessorSerializer(problem).data) + + if not course_id: + return self.error("Invalid parameter, course id is required") + try: + course = Course.objects.get(id=course_id) + ensure_created_by(course, user) + except Course.DoesNotExist: + return self.error("Course does not exist") + + problems = Problem.objects.filter(course=course).order_by("-create_time") + return self.success(self.paginate_data(request, problems, ProblemCourseProfessorSerializer)) + + @swagger_auto_schema( + request_body=(CreateCourseProblemSerializer), + operation_description="Create a problem of course.", + responses={200: ProblemAdminSerializer}, + ) + @validate_serializer(CreateCourseProblemSerializer) + @admin_role_required + def post(self, request): + data = request.data + + try: + course = Course.objects.get(id=data.pop("course_id")) + ensure_created_by(course, request.user) + except Course.DoesNotExist: + return self.error("Course does not exist") + + _id = data["_id"] + + if Problem.objects.filter(_id=_id, course=course).exists(): + return self.error("Duplicate Display id") + + error_info = self.common_checks(request) + if error_info: + return self.error(error_info) + + data["course"] = course + tags = data.pop("tags") + data["created_by"] = request.user + problem = Problem.objects.create(**data) + + if not _id: + problem._id = problem.id + problem.save() + + 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(ProblemAdminSerializer(problem).data) + + @validate_serializer(EditCourseProblemSerializer) + @admin_role_required + def put(self, request): + data = request.data + user = request.user + + problem_id = data.pop("id") + course_id = Problem.objects.get(id=problem_id).course_id + try: + course = Course.objects.get(id=course_id) + ensure_created_by(course, user) + except Course.DoesNotExist: + return self.error("Course does not exist") + + try: + problem = Problem.objects.get(id=problem_id, course=course) + except Problem.DoesNotExist: + return self.error("Problem does not exist") + + _id = data["_id"] + if not _id: + return self.error("Display ID is required") + if Problem.objects.exclude(id=problem_id).filter(_id=_id, course=course).exists(): + return self.error("Display ID already exists") + + error_info = self.common_checks(request) + if error_info: + return self.error(error_info) + + tags = data.pop("tags") + data["languages"] = list(data["languages"]) + + for k, v in data.items(): + setattr(problem, k, v) + problem.save() + + problem.tags.remove(*problem.tags.all()) + for tag in tags: + try: + tag = ProblemTag.objects.get(name=tag) + except ProblemTag.DoesNotExist: + tag = ProblemTag.objects.create(name=tag) + problem.tags.add(tag) + return self.success(ProblemAdminSerializer(problem).data) + + @swagger_auto_schema( + manual_parameters=[ + openapi.Parameter( + name="id", + in_=openapi.IN_QUERY, + type=openapi.TYPE_INTEGER, + description="Unique id of problem.", + required=True + ), + ], + operation_description="Delete certain problem of course." + ) + @admin_role_required + def delete(self, request): + id = request.GET.get("id") + if not id: + return self.error("Invalid parameter, id is required") + try: + problem = Problem.objects.get(id=id, course_id__isnull=False) + ensure_created_by(problem.course, request.user) + except Problem.DoesNotExist: + return self.error("Problem does not exists") + + if Submission.objects.filter(problem=problem).exists(): + return self.error("Can't delete the problem as it has submissions") + + problem.delete() + return self.success() + + +class AddCourseProblemAPI(APIView): + @validate_serializer(AddCourseProblemSerializer) + @swagger_auto_schema( + request_body=AddCourseProblemSerializer, + operation_description="Add problems from public problems into the course." + ) + def post(self, request): + data = request.data + try: + course = Course.objects.get(id=data["course_id"]) + ensure_created_by(course, request.user) + problem = Problem.objects.get(id=data["problem_id"]) + except Course.DoesNotExist: + return self.error("Course does not exist") + except Problem.DoesNotExist: + return self.error("Problem does not exist") + + if Problem.objects.filter(course=course, _id=data["display_id"]).exists(): + return self.error("Duplicate display id in this course") + + total_score = 0 + for item in problem.test_case_score: + total_score += item["score"] + problem.total_score = total_score + tags = problem.tags.all() + problem.pk = None + problem.assignment = None + problem.course = course problem.rule_type = ProblemRuleType.ASSIGNMENT problem.is_public = True problem.visible = True diff --git a/backend/submission/migrations/0017_submission_course.py b/backend/submission/migrations/0017_submission_course.py new file mode 100644 index 000000000..8ba117e09 --- /dev/null +++ b/backend/submission/migrations/0017_submission_course.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.12 on 2022-02-11 06:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0004_registration_bookmark'), + ('submission', '0016_auto_20211226_1458'), + ] + + operations = [ + migrations.AddField( + model_name='submission', + name='course', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='submissions', to='course.course'), + ), + ] diff --git a/backend/submission/models.py b/backend/submission/models.py index 809b0d7f3..c1d5e4f0e 100644 --- a/backend/submission/models.py +++ b/backend/submission/models.py @@ -8,6 +8,7 @@ from utils.shortcuts import rand_str from assignment.models import Assignment +from course.models import Course class JudgeStatus: @@ -28,6 +29,7 @@ class Submission(models.Model): id = models.TextField(default=rand_str, primary_key=True, db_index=True) contest = models.ForeignKey(Contest, null=True, on_delete=models.CASCADE) problem = models.ForeignKey(Problem, on_delete=models.CASCADE) + course = models.ForeignKey(Course, null=True, on_delete=models.CASCADE, related_name="submissions") assignment = models.ForeignKey(Assignment, null=True, on_delete=models.CASCADE, related_name="submissions") create_time = models.DateTimeField(auto_now_add=True) user_id = models.IntegerField(db_index=True) diff --git a/backend/submission/serializers.py b/backend/submission/serializers.py index 5ae336673..be296db58 100644 --- a/backend/submission/serializers.py +++ b/backend/submission/serializers.py @@ -11,6 +11,7 @@ class CreateSubmissionSerializer(serializers.Serializer): code = serializers.CharField(max_length=1024 * 1024) contest_id = serializers.IntegerField(required=False) assignment_id = serializers.IntegerField(required=False) + course_id = serializers.IntegerField(required=False) captcha = serializers.CharField(required=False) @@ -72,4 +73,4 @@ def get_created_by(self, obj): class Meta: model = Submission - exclude = ("contest", "assignment", "shared", "ip") + exclude = ("contest", "assignment", "course", "shared", "ip") diff --git a/backend/submission/tests.py b/backend/submission/tests.py index 099ff2593..9e55c2f77 100644 --- a/backend/submission/tests.py +++ b/backend/submission/tests.py @@ -105,7 +105,7 @@ def setUp(self): def test_get_assignment_submission_list(self): problem_id = self.problem["_id"] - resp = self.client.get(f"{self.url}?assignment_id={self.assignment_id}&problem_id={problem_id}") + resp = self.client.get(f"{self.url}?course_id={self.course_id}&assignment_id={self.assignment_id}&problem_id={problem_id}") self.assertSuccess(resp) def test_get_student_assignment_submission_list(self): @@ -115,7 +115,7 @@ def test_get_student_assignment_submission_list(self): self.submission_data["username"] = student.username Submission.objects.create(**self.submission_data) problem_id = self.problem["_id"] - resp = self.client.get(f"{self.url}?assignment_id={self.assignment_id}&problem_id={problem_id}") + resp = self.client.get(f"{self.url}?course_id={self.course_id}&assignment_id={self.assignment_id}&problem_id={problem_id}") self.assertSuccess(resp) @@ -127,7 +127,7 @@ def setUp(self): def test_get_assignment_submission_list_professor(self): assignment_id = self.assignment_id problem_id = self.problem["id"] - resp = self.client.get(f"{self.url}?assignment_id={assignment_id}&problem_id={problem_id}") + resp = self.client.get(f"{self.url}?course_id={self.course_id}&assignment_id={assignment_id}&problem_id={problem_id}") self.assertSuccess(resp) diff --git a/backend/submission/views.py b/backend/submission/views.py index 3d2b5186f..75e36abaf 100644 --- a/backend/submission/views.py +++ b/backend/submission/views.py @@ -77,7 +77,11 @@ def post(self, request): return self.error(error) try: - problem = Problem.objects.get(id=data["problem_id"], contest_id=data.get("contest_id"), assignment_id=data.get("assignment_id"), visible=True) + problem = Problem.objects.get(id=data["problem_id"], + contest_id=data.get("contest_id"), + assignment_id=data.get("assignment_id"), + course_id=data.get("course_id"), + visible=True) except Problem.DoesNotExist: return self.error("Problem not exist") if data["language"] not in problem.languages: @@ -89,7 +93,8 @@ def post(self, request): problem_id=problem.id, ip=request.session["ip"], contest_id=data.get("contest_id"), - assignment_id=data.get("assignment_id")) + assignment_id=data.get("assignment_id"), + course_id=data.get("course_id")) # use this for debug # JudgeDispatcher(submission.id, problem.id).judge() judge_task.send(submission.id, problem.id) @@ -199,14 +204,14 @@ def get(self, request): if request.GET.get("contest_id"): return self.error("Parameter error") - submissions = Submission.objects.filter(contest_id__isnull=True, assignment_id__isnull=True).select_related("problem__created_by") + submissions = Submission.objects.filter(contest_id__isnull=True, assignment_id__isnull=True, course_id__isnull=True).select_related("problem__created_by") problem_id = request.GET.get("problem_id") myself = request.GET.get("myself") result = request.GET.get("result") username = request.GET.get("username") if problem_id: try: - problem = Problem.objects.get(_id=problem_id, contest_id__isnull=True, assignment_id__isnull=True, visible=True) + problem = Problem.objects.get(_id=problem_id, contest_id__isnull=True, assignment_id__isnull=True, course_id__isnull=True, visible=True) except Problem.DoesNotExist: return self.error("Problem doesn't exist") submissions = submissions.filter(problem=problem) diff --git a/frontend/src/pages/oj/views/problem/Problem.vue b/frontend/src/pages/oj/views/problem/Problem.vue index 0f24a9e5b..7d6ed959e 100644 --- a/frontend/src/pages/oj/views/problem/Problem.vue +++ b/frontend/src/pages/oj/views/problem/Problem.vue @@ -574,7 +574,8 @@ export default { language: this.language, code: this.code, contest_id: this.contestID, - assignment_id: this.assignmentID + assignment_id: this.assignmentID, + course_id: this.courseID } if (this.captchaRequired) { data.captcha = this.captchaCode diff --git a/frontend/src/pages/prof/api.js b/frontend/src/pages/prof/api.js index a25f6bae3..dee7ca4bf 100644 --- a/frontend/src/pages/prof/api.js +++ b/frontend/src/pages/prof/api.js @@ -148,6 +148,36 @@ export default { params: params }) }, + getCourseProblem (courseId, problemId, limit, offset) { + return ajax('lecture/professor/course/problem/', 'get', { + params: { + course_id: courseId, + problem_id: problemId, + limit: limit, + offset: offset + } + }) + }, + createCourseProblem (data) { + return ajax('lecture/professor/course/problem/', 'post', { + data + }) + }, + editCourseProblem (data) { + return ajax('lecture/professor/course/problem/', 'put', { + data + }) + }, + deleteCourseProblem (id) { + return ajax('lecture/professor/course/problem/', 'delete', { + params: { id: id } + }) + }, + addCourseProblemFromPublic (data) { + return ajax('lecture/professor/course/add_problem_from_public/', 'post', { + data + }) + }, // only assignmentId param => return problemList instead getAssignmentProblem (assignmentId, problemId) { const params = { problem_id: problemId, assignment_id: assignmentId } diff --git a/frontend/src/pages/prof/components/SideMenu.vue b/frontend/src/pages/prof/components/SideMenu.vue index 38d912922..d1a5bd0ea 100644 --- a/frontend/src/pages/prof/components/SideMenu.vue +++ b/frontend/src/pages/prof/components/SideMenu.vue @@ -53,6 +53,17 @@ /> Dashboard + + + Problems + @@ -212,7 +214,9 @@ export default { 'Operation' ], selectedAssignmentId: null, - studentTotal: 0 + selectedCourseId: null, + studentTotal: 0, + courseId: '' } }, async mounted () { @@ -283,8 +287,9 @@ export default { createAssignmentProblem (assignment) { if (this.routeName === 'course-assignment-list') { this.$router.push({ - name: 'create-course-problem', + name: 'create-assignment-problem', params: { + courseId: this.courseId, assignmentId: assignment.id, courseInfo: this.pageLocations[0].text, assignmentInfo: assignment.title @@ -295,7 +300,7 @@ export default { editAssignmentProblem (assignment, problemId) { if (this.routeName === 'course-assignment-list') { this.$router.push({ - name: 'edit-course-problem', + name: 'edit-assignment-problem', params: { courseId: this.courseId, assignmentId: assignment.id, @@ -357,6 +362,7 @@ export default { }, showImportPublicProblemModal (assignmentId) { this.selectedAssignmentId = assignmentId + this.selectedCourseId = this.courseId this.$bvModal.show('import-public-problem-modal') }, async getUserTotal () { diff --git a/frontend/src/pages/prof/views/assignment/ImportPublicProblem.vue b/frontend/src/pages/prof/views/assignment/ImportPublicProblem.vue index 6fabe23b1..d523e88d3 100644 --- a/frontend/src/pages/prof/views/assignment/ImportPublicProblem.vue +++ b/frontend/src/pages/prof/views/assignment/ImportPublicProblem.vue @@ -94,7 +94,7 @@ import { DIFFICULTY_COLOR } from '@/utils/constants' export default { name: 'ImportPublicProblem', mixins: [ProblemMixin], - props: ['assignmentId'], + props: ['assignmentId', 'courseId', 'mode'], data () { return { form: { @@ -197,12 +197,17 @@ export default { return } const data = { + course_id: this.courseId, assignment_id: this.assignmentId, problem_id: this.form.selectedProblemId, display_id: this.form.displayId } try { - await profApi.addProblemFromPublic(data) + if (this.mode === 'assignment') { + await profApi.addProblemFromPublic(data) + } else { + await profApi.addCourseProblemFromPublic(data) + } } catch (err) { } this.$set(this.form, 'selectedProblemId', null) diff --git a/frontend/src/pages/prof/views/general/CourseBookmark.vue b/frontend/src/pages/prof/views/general/CourseBookmark.vue index 72c88acda..8708be342 100644 --- a/frontend/src/pages/prof/views/general/CourseBookmark.vue +++ b/frontend/src/pages/prof/views/general/CourseBookmark.vue @@ -95,7 +95,6 @@ export default { try { const resp = await api.getCourseList() this.courseList = resp.data.data.results - console.log(this.courseList) } catch (err) { } }, diff --git a/frontend/src/pages/prof/views/general/CourseDashboard.vue b/frontend/src/pages/prof/views/general/CourseDashboard.vue index cb5274b1c..4f1b399cd 100644 --- a/frontend/src/pages/prof/views/general/CourseDashboard.vue +++ b/frontend/src/pages/prof/views/general/CourseDashboard.vue @@ -15,10 +15,8 @@ cols = "1" class="course-dashboard" > - - + +