from collections import OrderedDict
from django.apps import apps
from django.conf import settings
from django.db import transaction
from django.utils import timezone
from django.utils.translation import ugettext as _
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.settings import api_settings
from social_core.utils import setting_name
from . import mixins
from .courses import CourseRoleSerializer
from .. import models
[문서]class AssignmentUserSerializer(serializers.ModelSerializer):
id = serializers.PrimaryKeyRelatedField(
read_only=True,
)
name = serializers.CharField(
source="user.full_name",
read_only=True,
label=_("username"),
help_text=_("사용자 이름"),
)
email = serializers.EmailField(
source="user.email",
read_only=True,
label=_("email address"),
help_text=_("사용자 메일주소"),
)
idNumber = serializers.SerializerMethodField()
# extra_kwargs = {
# "type": {
# "required": True
# }
# }
[문서] def get_idNumber(self, instance):
return None
[문서]class AssignmentRepositoryUpdateSerializer(mixins.RemoveNullFieldsMixin, serializers.Serializer):
commitId = serializers.CharField(
source="commit_id",
label=models.AssignmentRepository._meta.get_field("commit_id").verbose_name,
help_text=models.AssignmentRepository._meta.get_field("commit_id").help_text,
)
[문서] def update(self, instance, validated_data):
pass
[문서] def create(self, validated_data):
pass
[문서]class AssignmentRepositorySerializer(mixins.RemoveNullFieldsMixin, serializers.ModelSerializer):
repoWebUrl = serializers.URLField(
source="web_url",
read_only=True,
)
repoHttpUrl = serializers.URLField(
source="http_url_to_repo",
read_only=True,
)
repoSshUrl = serializers.URLField(
source="ssh_url_to_repo",
read_only=True,
)
commitId = serializers.CharField(
source="commit_id",
required=False,
label=models.AssignmentRepository._meta.get_field("commit_id").verbose_name,
help_text=models.AssignmentRepository._meta.get_field("commit_id").help_text,
)
[문서]class AssignmentSerializer(mixins.RemoveNullFieldsMixin, serializers.ModelSerializer):
parent_lookup_kwargs = {
'course_pk': 'course__pk',
}
nameWithNamespace = serializers.SerializerMethodField(
label=_("name with namespace"),
help_text=_("클래스의 이름을 포함한 이름 '{{class name}} / {{assignment name}}' 입니다.")
)
description = serializers.CharField(
source="short_description",
# read_only=True,
required=False,
allow_null=True,
label=models.Assignment._meta.get_field("short_description").verbose_name,
help_text=models.Assignment._meta.get_field("short_description").help_text,
)
path = serializers.CharField(
source="slug",
# read_only=True,
required=True,
label=models.Assignment._meta.get_field("slug").verbose_name,
help_text=models.Assignment._meta.get_field("slug").help_text,
)
pathWithNamespace = serializers.SerializerMethodField(
label=_("name with namespace"),
help_text=_("클래스의 path를 포함한 경로 '{{class path}} / {{assignment path}}' 입니다.")
)
webUrl = serializers.SerializerMethodField(
help_text=_("이 수업에 대한 Web URL 주소입니다."),
)
readmeFormat = serializers.CharField(
source="readme_format",
required=False,
label=models.Assignment._meta.get_field("readme_format").verbose_name,
help_text=models.Assignment._meta.get_field("readme_format").help_text,
)
startDate = serializers.DateTimeField(
source="start_date",
# read_only=True,
required=False,
allow_null=True,
label=models.Assignment._meta.get_field("start_date").verbose_name,
help_text=models.Assignment._meta.get_field("start_date").help_text,
)
endDate = serializers.DateTimeField(
source="end_date",
# read_only=True,
required=False,
allow_null=True,
label=models.Assignment._meta.get_field("end_date").verbose_name,
help_text=models.Assignment._meta.get_field("end_date").help_text,
)
# Fixme 작성자 모델도 수정필요함.
author = CourseRoleSerializer(
read_only=True,
label=_("작성자"),
help_text=_("과제를 작성한 자"),
)
successBuilds = serializers.IntegerField(
source="metadata.builds",
read_only=True,
label=_("number of success results"),
help_text=_("통과한 빌드의 수. 각 학생들이 제출한 최종 결과중에서 통과한 빌드의 집계"),
)
submits = serializers.IntegerField(
source="metadata.submits",
read_only=True,
label=_("number of submits"),
help_text=_("제출된 과제의 수. 제출 마감일에 과제를 포크한 저장소중 변경사항이 있는 저장소의 수 집계"),
)
modifiedAt = serializers.DateTimeField(
source="metadata.modified_at",
read_only=True,
label=models.AssignmentMeta._meta.get_field("modified_at").verbose_name,
help_text=models.AssignmentMeta._meta.get_field("modified_at").help_text,
)
status = serializers.ChoiceField(
read_only=True,
choices=models.Assignment.STATUS_CHOICES,
label=_("status of assignments"),
help_text=_("과제의 상태. hidden: 숨김(Owner만 보임), dormant: 예정, progress: 진행, closed: 종료"),
)
repository = AssignmentRepositorySerializer()
[문서] @transaction.atomic
def update(self, instance: models.Assignment, validated_data):
validated_data.pop("metadata", {})
validated_repository = validated_data.pop("repository", {})
super(AssignmentSerializer, self).update(instance, validated_data)
gl = apps.get_app_config("classroom").gitlab_admin_api
try:
git_ref = instance.metadata.git_ref
git_ref.refresh(gl)
except models.AssignmentGitlabReference.DoesNotExist:
models.AssignmentGitlabReference.create_sync(instance.metadata, gl)
return instance
[문서] @transaction.atomic
def create(self, validated_data):
errors = OrderedDict()
if "course" in self.context and self.context["course"]:
validated_data["course"] = self.context["course"]
else:
errors.update({
"course": [_("course instance is not found")]
})
validated_data.pop("metadata", {})
validated_repository = validated_data.pop("repository", {})
if errors:
raise ValidationError(errors)
instance = super(AssignmentSerializer, self).create(validated_data)
metadata = models.AssignmentMeta.objects.create(
assignment=instance,
display_order=-1,
modified_at=timezone.now(),
)
gl = apps.get_app_config("classroom").gitlab_admin_api
try:
git_ref = metadata.git_ref
git_ref.refresh(gl)
except models.AssignmentGitlabReference.DoesNotExist:
models.AssignmentGitlabReference.create_sync(metadata, gl)
repository_url = validated_repository.get("origin") if validated_repository else None
if repository_url:
if repository_url.startswith(
getattr(settings, setting_name("GITLAB_API_URL"))):
path = repository_url.split("/")[-1]
for project in gl.projects.list(search=path):
if project.web_url.startswith(repository_url) or project.http_url_to_repo.startswith(
repository_url):
fork = project.forks.create({
"namespace": metadata.git_ref.id
})
fork = gl.projects.get(id=fork.id, lazy=True)
fork.name = _("{name} 템플릿").format(name=instance.name)
fork.path = "template"
fork.description = instance.short_description
fork.jobs_enabled = False
fork.wiki_enabled = False
fork.snippets_enabled = False
fork.issues_enabled = False
fork.resolve_outdated_diff_discussions = False
fork.shared_runners_enabled = False
fork.save()
break
else:
fork = None
else:
fork = gl.projects.create({
"name": _("{name} 템플릿").format(name=instance.name),
"path": "template",
"namespace_id": metadata.git_ref.id,
"description": instance.short_description,
"jobs_enabled": False,
"wiki_enabled": True,
"snippets_enabled": True,
"issues_enabled": False,
"resolve_outdated_diff_discussions": False,
"shared_runners_enabled": False,
"import_url": repository_url,
})
else:
fork = gl.projects.create({
"name": _("{name} 템플릿").format(name=instance.name),
"path": "template",
"namespace_id": metadata.git_ref.id,
"description": instance.short_description,
"jobs_enabled": False,
"wiki_enabled": True,
"snippets_enabled": True,
"issues_enabled": False,
"resolve_outdated_diff_discussions": False,
"shared_runners_enabled": False,
})
if fork is not None:
models.AssignmentRepository.objects.create(
assignment=instance,
origin=validated_repository,
# TODO 업로드 처리는 추후 지원
url=fork.web_url,
branch=validated_repository["branch"] if "branch" in validated_repository and validated_repository[
"branch"] else "master",
data=fork.attributes
)
return instance
[문서] def get_nameWithNamespace(self, instance: models.Assignment):
try:
name = "{} / {}".format(instance.course.info.name, instance.name)
except models.CourseInfo.DoesNotExist:
return None
return name
[문서] def get_pathWithNamespace(self, instance: models.Assignment):
try:
path = "{} / {}".format(instance.course.info.slug, instance.slug)
except models.CourseInfo.DoesNotExist:
return None
return path
[문서] def get_webUrl(self, instance):
try:
url_path = "/class/{}/assignment/{}/".format(instance.course.info.slug, instance.slug)
except models.CourseInfo.DoesNotExist:
return None
request = self.context.get("request")
if request:
return request.build_absolute_uri(url_path)
return url_path
# def save(self, **kwargs):
# parent_pk = self.context['view'].kwargs['course_pk']
#
# v = self.validated_data
#
# a = models.Assignment(
# course=models.Course.objects.get(pk=parent_pk),
# name=v['name'],
# slug=v['slug'],
# short_description=v['short_description'],
# readme=v['readme'],
# readme_format=v['readme_format'],
# start_date=v['start_date'],
# end_date=v['end_date'])
# self.instance = a
# a.clean_fields()
# a.save()
#
# # super(AssignmentSerializer, self).save(**kwargs)
[문서]class AssignmentUserRepositorySerializer(mixins.RemoveNullFieldsMixin, serializers.ModelSerializer):
user = AssignmentUserSerializer(
source="assignment_map",
label=_("student"),
help_text=_("이 과제를 포크한 사용자(학생)"),
)
repoWebUrl = serializers.URLField(
source="web_url",
read_only=True,
label=_("repository web url"),
help_text=_("web url of repository"),
)
repoHttpUrl = serializers.URLField(
source="http_url_to_repo",
read_only=True,
label=_("repository http Url"),
help_text=_("http url for repository clone using http protocol"),
)
repoSshUrl = serializers.URLField(
source="ssh_url_to_repo",
read_only=True,
label=_("repository ssh Url"),
help_text=_("ssh url for repository clone using ssh protocol"),
)
commitId = serializers.CharField(
source="commit_id",
label=models.AssignmentRepository._meta.get_field("commit_id").verbose_name,
help_text=models.AssignmentRepository._meta.get_field("commit_id").help_text,
)
lastSubmittedAt = serializers.DateTimeField(
source="last_submitted_at",
label=models.AssignmentSubmission._meta.get_field("submitted_at").verbose_name,
help_text=models.AssignmentSubmission._meta.get_field("submitted_at").help_text,
)
lastBuildStatus = serializers.CharField(
source="last_build_status",
label=models.AssignmentBuild._meta.get_field("status").verbose_name,
help_text=models.AssignmentBuild._meta.get_field("status").help_text,
)