예제 #1
0
 def test_timestamp_arbitrary_parse(self):
     tz = pytz.timezone('Africa/Nairobi')
     timezone.activate(tz)
     field = DateTimeTzSerializerField()
     timestamp = aware_datetime()
     self.assertEqual(field.to_internal_value(timestamp.isoformat()).tzinfo, aware_datetime().tzinfo)
     timezone.deactivate()
예제 #2
0
class ExamSerializer(KolibriModelSerializer):

    question_sources = ExamQuestionSourcesField(default=[])
    date_created = DateTimeTzField()
    date_archived = DateTimeTzField()
    date_activated = DateTimeTzField()

    # classes are in here, and filtered out later to create `groups`
    assignments = ExamAssignmentsField(many=True, read_only=True)

    groups = serializers.ListField(default=[])

    class Meta:
        model = Exam
        fields = (
            "id",
            "title",
            "active",
            "question_sources",
            "assignments",
            "groups",
            "data_model_version",
            "question_count",
            "learners_see_fixed_order",
            "seed",
            "date_created",
            "date_archived",
            "date_activated",
            "archive",
        )
예제 #3
0
 def test_timestamp_utc_parse(self):
     timezone.activate(pytz.utc)
     field = DateTimeTzSerializerField()
     timestamp = aware_datetime()
     self.assertEqual(
         field.to_internal_value(timestamp.isoformat()).tzinfo,
         aware_datetime().tzinfo)
     timezone.deactivate()
예제 #4
0
 def test_timestamp_arbitrary_parse(self):
     tz = pytz.timezone('Africa/Nairobi')
     timezone.activate(tz)
     field = DateTimeTzSerializerField()
     timestamp = aware_datetime()
     self.assertEqual(
         field.to_internal_value(timestamp.isoformat()).tzinfo,
         aware_datetime().tzinfo)
     timezone.deactivate()
예제 #5
0
class ExamStatusSerializer(KolibriModelSerializer):
    status = serializers.SerializerMethodField()
    exam_id = serializers.PrimaryKeyRelatedField(source="exam", read_only=True)
    learner_id = serializers.PrimaryKeyRelatedField(source="user", read_only=True)
    last_activity = DateTimeTzField()
    num_correct = serializers.SerializerMethodField()

    def get_status(self, exam_log):
        if exam_log.closed:
            return COMPLETED
        else:
            return STARTED

    def get_num_correct(self, exam_log):
        return (
            exam_log.attemptlogs.values_list("item")
            .order_by("completion_timestamp")
            .distinct()
            .aggregate(Sum("correct"))
            .get("correct__sum")
        )

    class Meta:
        model = logger_models.ExamLog
        fields = ("exam_id", "learner_id", "status", "last_activity", "num_correct")
예제 #6
0
class LessonSerializer(KolibriModelSerializer):
    active = serializers.BooleanField(source="is_active")
    node_ids = serializers.SerializerMethodField()
    date_created = DateTimeTzField(required=False)

    # classrooms are in here, and filtered out later to create `groups`
    assignments = LessonAssignmentsField(many=True,
                                         read_only=True,
                                         source="lesson_assignments")

    groups = serializers.ListField(default=[])

    class Meta:
        model = Lesson
        fields = (
            "id",
            "title",
            "active",
            "node_ids",
            "assignments",
            "groups",
            "description",
            "date_created",
        )

    def get_node_ids(self, obj):
        return [resource["contentnode_id"] for resource in obj.resources]
예제 #7
0
class ExamSerializer(ModelSerializer):

    assignments = ListField(
        child=PrimaryKeyRelatedField(read_only=False, queryset=Collection.objects.all())
    )
    learner_ids = ListField(
        child=PrimaryKeyRelatedField(
            read_only=False, queryset=FacilityUser.objects.all()
        ),
        required=False,
    )
    question_sources = ListField(child=QuestionSourceSerializer(), required=False)
    creator = PrimaryKeyRelatedField(
        read_only=False, queryset=FacilityUser.objects.all()
    )
    date_archived = DateTimeTzField(allow_null=True)
    date_activated = DateTimeTzField(allow_null=True)

    class Meta:
        model = Exam
        fields = (
            "id",
            "title",
            "question_count",
            "question_sources",
            "seed",
            "active",
            "collection",
            "archive",
            "date_archived",
            "date_activated",
            "assignments",
            "creator",
            "data_model_version",
            "learners_see_fixed_order",
            "learner_ids",
        )
        read_only_fields = ("data_model_version",)

    def validate(self, attrs):
        title = attrs.get("title")
        # first condition is for creating object, second is for updating
        collection = attrs.get("collection") or getattr(self.instance, "collection")
        if "learner_ids" in self.initial_data and self.initial_data["learner_ids"]:
            if (
                len(self.initial_data["learner_ids"])
                != FacilityUser.objects.filter(
                    memberships__collection=collection,
                    id__in=self.initial_data["learner_ids"],
                ).count()
            ):
                raise ValidationError(
                    "Some learner_ids are not members of the collection that this quiz is contained in",
                    code=error_constants.INVALID,
                )
        # if obj doesn't exist, return data
        try:
            obj = Exam.objects.get(title__iexact=title, collection=collection)
        except Exam.DoesNotExist:
            return attrs
        # if we are updating object, and this `instance` is the same object, return data
        if self.instance and obj.id == self.instance.id:
            return attrs
        else:
            raise ValidationError(
                "The fields title, collection must make a unique set.",
                code=error_constants.UNIQUE,
            )

    def to_internal_value(self, data):
        # Make a new OrderedDict from the input, which could be an immutable QueryDict
        data = OrderedDict(data)
        if "creator" not in data:
            if self.context["view"].action == "create":
                data["creator"] = self.context["request"].user.id
            else:
                # Otherwise we are just updating the exam, so allow a partial update
                self.partial = True
        return super(ExamSerializer, self).to_internal_value(data)

    def create(self, validated_data):
        collections = validated_data.pop("assignments")
        learners = validated_data.pop("learner_ids", [])
        new_exam = Exam.objects.create(**validated_data)
        # Create all of the new ExamAssignment
        for collection in collections:
            self._create_exam_assignment(exam=new_exam, collection=collection)

        if learners:
            adhoc_group = create_adhoc_group_for_learners(new_exam.collection, learners)
            self._create_exam_assignment(exam=new_exam, collection=adhoc_group)

        return new_exam

    def _create_exam_assignment(self, **params):
        return ExamAssignment.objects.create(
            assigned_by=self.context["request"].user, **params
        )

    def update(self, instance, validated_data):  # noqa
        # Update the scalar fields
        instance.title = validated_data.get("title", instance.title)
        instance.active = validated_data.get("active", instance.active)
        instance.archive = validated_data.get("archive", instance.archive)
        instance.date_activated = validated_data.get(
            "date_activated", instance.date_activated
        )
        instance.date_archived = validated_data.get(
            "date_archived", instance.date_archived
        )

        # Add/delete any new/removed Assignments
        if "assignments" in validated_data:
            collections = validated_data.pop("assignments")
            current_group_ids = set(
                instance.assignments.exclude(
                    collection__kind=ADHOCLEARNERSGROUP
                ).values_list("collection__id", flat=True)
            )
            new_group_ids = set(x.id for x in collections)

            for cid in new_group_ids - current_group_ids:
                collection = Collection.objects.get(id=cid)
                if collection.kind != ADHOCLEARNERSGROUP:
                    self._create_exam_assignment(exam=instance, collection=collection)

            ExamAssignment.objects.filter(
                exam_id=instance.id,
                collection_id__in=(current_group_ids - new_group_ids),
            ).exclude(collection__kind=ADHOCLEARNERSGROUP).delete()

        # Update adhoc assignment
        if "learner_ids" in validated_data:
            self._update_learner_ids(instance, validated_data["learner_ids"])

        instance.save()
        return instance

    def _update_learner_ids(self, instance, learners):
        try:
            adhoc_group_assignment = ExamAssignment.objects.select_related(
                "collection"
            ).get(exam=instance, collection__kind=ADHOCLEARNERSGROUP)
        except ExamAssignment.DoesNotExist:
            adhoc_group_assignment = None
        if not learners:
            # Setting learner_ids to empty, so only need to do something
            # if there is already an adhoc_group_assignment defined
            if adhoc_group_assignment is not None:
                # Adhoc group already exists delete it and the assignment
                # cascade deletion should also delete the adhoc_group_assignment
                adhoc_group_assignment.collection.delete()
        else:
            if adhoc_group_assignment is None:
                # There is no adhoc group right now, so just make a new one
                adhoc_group = create_adhoc_group_for_learners(
                    instance.collection, learners
                )
                self._create_exam_assignment(exam=instance, collection=adhoc_group)
            else:
                # There is an adhoc group, so we need to potentially update its membership
                original_learner_ids = Membership.objects.filter(
                    collection=adhoc_group_assignment.collection
                ).values_list("user_id", flat=True)
                original_learner_ids_set = set(original_learner_ids)
                learner_ids_set = set(learner.id for learner in learners)
                if original_learner_ids_set != learner_ids_set:
                    # Only bother to do anything if these are different
                    new_learner_ids = learner_ids_set - original_learner_ids_set
                    deleted_learner_ids = original_learner_ids_set - learner_ids_set

                    if deleted_learner_ids:
                        Membership.objects.filter(
                            collection=adhoc_group_assignment.collection,
                            user_id__in=deleted_learner_ids,
                        ).delete()

                    for new_learner_id in new_learner_ids:
                        Membership.objects.create(
                            user_id=new_learner_id,
                            collection=adhoc_group_assignment.collection,
                        )
예제 #8
0
class ExamSerializer(serializers.ModelSerializer):

    assignments = ExamAssignmentNestedSerializer(many=True)
    question_sources = serializers.JSONField(default="[]")
    creator = serializers.PrimaryKeyRelatedField(
        read_only=False, queryset=FacilityUser.objects.all())
    date_archived = DateTimeTzField(allow_null=True)
    date_activated = DateTimeTzField(allow_null=True)

    class Meta:
        model = Exam
        fields = (
            "id",
            "title",
            "question_count",
            "question_sources",
            "seed",
            "active",
            "collection",
            "archive",
            "date_archived",
            "date_activated",
            "assignments",
            "creator",
            "data_model_version",
            "learners_see_fixed_order",
        )
        read_only_fields = ("data_model_version", )

    def validate(self, attrs):
        title = attrs.get("title")
        # first condition is for creating object, second is for updating
        collection = attrs.get("collection") or getattr(
            self.instance, "collection")
        # if obj doesn't exist, return data
        try:
            obj = Exam.objects.get(title__iexact=title, collection=collection)
        except Exam.DoesNotExist:
            return attrs
        # if we are updating object, and this `instance` is the same object, return data
        if self.instance and obj.id == self.instance.id:
            return attrs
        else:
            raise serializers.ValidationError(
                "The fields title, collection must make a unique set.",
                code=error_constants.UNIQUE,
            )

    def validate_question_sources(self, value):
        for question in value:
            required_fields = [
                "exercise_id",
                "question_id",
                "title",
                "counter_in_exercise",
            ]
            for field in required_fields:
                if field not in question:
                    raise serializers.ValidationError(
                        "Question missing '{}'".format(field))
        return value

    def to_internal_value(self, data):
        # Make a new OrderedDict from the input, which could be an immutable QueryDict
        data = OrderedDict(data)
        if "creator" not in data:
            if self.context["view"].action == "create":
                data["creator"] = self.context["request"].user.id
            else:
                # Otherwise we are just updating the exam, so allow a partial update
                self.partial = True
        return super(ExamSerializer, self).to_internal_value(data)

    def create(self, validated_data):
        assignees = validated_data.pop("assignments")
        new_exam = Exam.objects.create(**validated_data)
        # Create all of the new ExamAssignment
        for assignee in assignees:
            self._create_exam_assignment(exam=new_exam,
                                         collection=assignee["collection"])
        return new_exam

    def _create_exam_assignment(self, **params):
        return ExamAssignment.objects.create(
            assigned_by=self.context["request"].user, **params)

    def update(self, instance, validated_data):
        # Update the scalar fields
        instance.title = validated_data.get("title", instance.title)
        instance.active = validated_data.get("active", instance.active)
        instance.archive = validated_data.get("archive", instance.archive)
        instance.date_activated = validated_data.get("date_activated",
                                                     instance.date_activated)
        instance.date_archived = validated_data.get("date_archived",
                                                    instance.date_archived)

        # Add/delete any new/removed Assignments
        if "assignments" in validated_data:
            assignees = validated_data.pop("assignments")
            current_group_ids = set(
                instance.assignments.values_list("collection__id", flat=True))
            new_group_ids = set(x["collection"].id for x in assignees)

            for id in new_group_ids - current_group_ids:
                self._create_exam_assignment(
                    exam=instance, collection=Collection.objects.get(id=id))

            ExamAssignment.objects.filter(
                exam_id=instance.id,
                collection_id__in=(current_group_ids - new_group_ids),
            ).delete()

        instance.save()
        return instance
예제 #9
0
 def test_timestamp_utc_parse(self):
     timezone.activate(pytz.utc)
     field = DateTimeTzSerializerField()
     timestamp = aware_datetime()
     self.assertEqual(field.to_internal_value(timestamp.isoformat()).tzinfo, aware_datetime().tzinfo)
     timezone.deactivate()