Exemplo n.º 1
0
def test_student_activity__no_questions_done(
    client,
    teacher,
    group,
    students,
    student_group_assignments,
    student_assignments,
):
    assert login_teacher(client, teacher)
    group.teacher.add(teacher)
    teacher.current_groups.add(group)

    resp = client.post(
        reverse("teacher-dashboard--student-activity"),
        json.dumps({}),
        content_type="application/json",
    )
    data = json.loads(resp.content)["groups"][0]
    assert data["title"] == group.title
    assert data["n_students"] == len(students)
    assert data["new"] is True
    assert len(data["assignments"]) == len(student_group_assignments)
    for assignment, assignment_ in zip(data["assignments"],
                                       student_group_assignments):
        assert assignment["title"] == assignment_.assignment.title
        assert assignment["n_completed"] == 0
        assert assignment["mean_grade"] == 0
        assert assignment["min_grade"] == 0
        assert assignment["max_grade"] == 0
        assert assignment["new"] is True
        assert assignment["expired"] is False
        assert assignment["link"].endswith(assignment_.hash + "/")
def test_evaluate_rationale__missing_params(client, teacher, answers):
    answer = answers[0]

    assert login_teacher(client, teacher)

    n = AnswerAnnotation.objects.count()

    resp = client.post(
        reverse("teacher-dashboard--evaluate-rationale"), {"id": answer.pk}
    )

    assert resp.status_code == 400
    assert AnswerAnnotation.objects.count() == n

    resp = client.post(
        reverse("teacher-dashboard--evaluate-rationale"), {"score": 0}
    )

    assert resp.status_code == 400
    assert AnswerAnnotation.objects.count() == n

    resp = client.post(reverse("teacher-dashboard--evaluate-rationale"), {})

    assert resp.status_code == 400
    assert AnswerAnnotation.objects.count() == n
def test_download_gradebook__assignment(client, teacher):
    assert login_teacher(client, teacher)

    RunningTask.objects.create(id=1, description="test", teacher=teacher)

    with mock.patch(
        "peerinst.views.teacher.convert_gradebook_to_csv"
    ) as convert_gradebook_to_csv, mock.patch(
        "peerinst.views.teacher.AsyncResult"
    ) as AsyncResult:
        convert_gradebook_to_csv.return_value = iter(["test\n"])
        result = mock.Mock()
        result.ready.return_value = True
        result.result = {"group": "test", "assignment": "test"}
        AsyncResult.return_value = result

        resp = client.post(
            reverse("teacher-gradebook--download"),
            json.dumps({"task_id": 1}),
            content_type="application/json",
        )

        assert resp.status_code == 200
        assert isinstance(resp, StreamingHttpResponse)
        assert (
            next(resp.streaming_content).strip().decode()
            == "myDALITE_gradebook_test_test.csv"
        )
        assert next(resp.streaming_content).strip().decode() == "test"
        assert not RunningTask.objects.filter(id=1).exists()
Exemplo n.º 4
0
def test_in_teacher_list_permission_in_view(
    client, student_group_assignment, teacher
):
    assert teacher not in student_group_assignment.group.teacher.all()

    assert login_teacher(client, teacher)

    url = reverse(
        "REST:student-group-assigment-answers",
        args=[
            student_group_assignment.pk,
            student_group_assignment.assignment.questions.first().pk,
        ],
    )

    response = client.get(url)

    # 404 based on limited queryset
    assert response.status_code == status.HTTP_404_NOT_FOUND

    student_group_assignment.group.teacher.add(teacher)

    assert teacher in student_group_assignment.group.teacher.all()

    response = client.get(url)

    assert response.status_code == status.HTTP_200_OK
Exemplo n.º 5
0
def test_request_gradebook__assignment__no_celery(client, teacher, group,
                                                  student_group_assignment):
    assert login_teacher(client, teacher)
    group.teacher.add(teacher)

    n = RunningTask.objects.count()

    with mock.patch(
            "peerinst.views.teacher.compute_gradebook_async"
    ) as compute_gradebook_async, mock.patch(
            "peerinst.views.teacher.download_gradebook") as download_gradebook:
        result = mock.Mock()
        compute_gradebook_async.return_value = result
        download_gradebook.return_value = HttpResponse()

        resp = client.post(
            reverse("teacher-gradebook--request"),
            json.dumps({
                "group_id": group.pk,
                "assignment_id": student_group_assignment.pk,
            }),
            content_type="application/json",
        )
        assert resp.status_code == 200
        compute_gradebook_async.assert_called_with(group.pk,
                                                   student_group_assignment.pk)
        download_gradebook.assert_called_with(mock.ANY, results=result)

    assert RunningTask.objects.count() == n
Exemplo n.º 6
0
def test_rationales_to_score(client, teacher, answers, discipline):
    teacher.disciplines.add(discipline)
    for i, answer in enumerate(answers):
        answer.question.discipline = discipline
        answer.question.save()

    assert login_teacher(client, teacher)

    with mock.patch("peerinst.rationale_annotation.Quality") as Quality:
        qualities = [float(i) / len(answers) for i in range(len(answers))]
        Quality.objects.filter.return_value.exists.return_value = True
        Quality.objects.get.return_value.batch_evaluate.return_value = [
            (q, None) for q in qualities
        ]

        resp = client.post(
            reverse("teacher-dashboard--rationales"),
            json.dumps({}),
            content_type="application/json",
        )

        assert resp.status_code == 200
        data = json.loads(resp.content.decode())
        assert len(data["rationales"]) == 5

        for a, a_ in zip(data["rationales"], answers[::-1]):
            assert a["id"] == a_.pk
            assert a["title"] == a_.question.title
            assert a["rationale"] == a_.rationale
            assert a["choice"] == a_.first_answer_choice
            assert (a["text"] == a_.question.answerchoice_set.all()[
                a_.first_answer_choice - 1].text)
            assert a["correct"] == a_.question.is_correct(
                a_.first_answer_choice)
Exemplo n.º 7
0
def test_collections__with_params(client, collections, teachers, discipline):
    teacher = teachers[0]
    teachers = teachers[1:]

    teacher.disciplines.add(discipline)

    assert login_teacher(client, teacher)

    for i, collection in enumerate(collections):
        collection.followers.remove(*teachers[:-i - 1])

    resp = client.post(
        reverse("teacher-dashboard--collections"),
        json.dumps({"n": 1}),
        content_type="application/json",
    )

    assert resp.status_code == 200
    data = json.loads(resp.content)
    assert len(data["collections"]) == 1

    for collection, collection_ in zip(data["collections"],
                                       reversed(collections)):
        assert collection["title"] == collection_.title
        assert collection["description"] == collection_.description
        assert collection["discipline"] == collection_.discipline.title
        assert collection["n_assignments"] == collection_.assignments.count()
        assert collection["n_followers"] == collection_.followers.count()
Exemplo n.º 8
0
def test_student_activity__dashboard(client, teacher, group, questions,
                                     student, student_group_assignments):
    assert login_teacher(client, teacher)
    group.teacher.add(teacher)
    teacher.current_groups.add(group)
    student.groups.add(group)

    add_answers(
        student,
        questions,
        student_group_assignments[0].assignment,
        correct_first=True,
        answer_second=True,
        correct_second=True,
    )

    resp = client.get(reverse("teacher-dashboard"))

    assert resp.status_code == 200
    assert any(t.name == "peerinst/teacher/cards/student_activity_card.html"
               for t in resp.templates)
    assert any(t.name == "peerinst/teacher/dashboard.html"
               for t in resp.templates)
    assert str(student_group_assignments[0].assignment.title) in resp.content
    assert str(group.title) in resp.content
    assert (str(student_group_assignments[1].assignment.title)
            not in resp.content)
Exemplo n.º 9
0
def test_request_gradebook__group(client, teacher, group):
    assert login_teacher(client, teacher)
    group.teacher.add(teacher)

    class AsyncResultMock(AsyncResult):
        def __new__(cls):
            return mock.mock(spec=cls)

    with mock.patch("peerinst.views.teacher.compute_gradebook_async"
                    ) as compute_gradebook_async, mock.patch(
                        "peerinst.views.teacher.AsyncResult",
                        new=AsyncResultMock):
        result = mock.Mock(spec=AsyncResultMock)
        result.id = 1
        compute_gradebook_async.return_value = result

        resp = client.post(
            reverse("teacher-gradebook--request"),
            json.dumps({"group_id": group.pk}),
            content_type="application/json",
        )
        compute_gradebook_async.assert_called_with(group.pk, None)

    assert resp.status_code == 201
    data = json.loads(resp.content)
    assert data["id"] == 1
    assert data[
        "description"] == "gradebook for group <strong>{}</strong>".format(
            group.name)
    assert not data["completed"]

    assert RunningTask.objects.filter(id=1).exists()
Exemplo n.º 10
0
def test_messages(client, teacher, thread):
    assert login_teacher(client, teacher)

    notification_type = ContentType.objects.get(
        app_label="pinax_forums", model="ThreadSubscription"
    )

    resp = client.get(reverse("teacher-dashboard--messages"))
    assert resp.status_code == 200
    data = json.loads(resp.content.decode())["threads"]

    assert len(data) == 1
    for thread in data:
        assert "id" in thread
        assert "last_reply" in thread
        assert "author" in thread["last_reply"]
        assert "content" in thread["last_reply"]
        assert (
            thread["n_new"]
            == TeacherNotification.objects.filter(
                teacher=teacher,
                notification_type=notification_type,
                object_id__in=ForumThread.objects.get(
                    id=thread["id"]
                ).subscriptions.values_list("id"),
            ).count()
        )

        resp = client.get(thread["link"])
        assert resp.status_code == 200
        assert "pinax/forums/thread.html" in resp.template_name
Exemplo n.º 11
0
def test_rationales__dashboard(client, teacher, discipline, answers):
    assert login_teacher(client, teacher)

    teacher.disciplines.add(discipline)
    for i, answer in enumerate(answers):
        answer.question.discipline = discipline
        answer.question.save()
        answer.second_answer_choice = 1
        answer.save()

    n = AnswerAnnotation.objects.count()

    resp = client.get(reverse("teacher-dashboard--rationales"))

    assert resp.status_code == 200
    assert any(t.name == "peerinst/teacher/cards/rationale_to_score_card.html"
               for t in resp.templates)

    resp = client.post(
        reverse("teacher-dashboard--evaluate-rationale"),
        {
            "id": answers[0].id,
            "score": 0
        },
        follow=True,
    )

    assert resp.status_code == 200
    assert any(t.name == "peerinst/teacher/cards/rationale_to_score_card.html"
               for t in resp.templates)
    assert AnswerAnnotation.objects.count() == n + 1
Exemplo n.º 12
0
def test_discipline_list(client, disciplines, student, teacher):
    """
    Requirements:
    1. Must be authenticated
    2. Must not be a student to GET
    3. Must be admin for anything else
    """

    # 1. Must be authenticated
    url = reverse("REST:discipline-list")

    response = client.get(url)
    assert response.status_code == status.HTTP_403_FORBIDDEN

    # 2. Must not be a student to GET
    assert login_student(client, student)

    response = client.get(url)
    assert response.status_code == status.HTTP_403_FORBIDDEN

    assert login_teacher(client, teacher)

    response = client.get(url)
    assert response.status_code == status.HTTP_200_OK
    retrieved_disciplines = json.loads(response.content)
    for d in disciplines:
        assert d.pk in [d["pk"] for d in retrieved_disciplines]

    # 3. Must be admin for anything else
    response = client.post(url, {"title": "New discipline"})

    assert response.status_code == status.HTTP_403_FORBIDDEN
def test_unsubscribe_from_thread__wrong_thread(client, teacher):
    assert login_teacher(client, teacher)

    resp = client.post(
        reverse("teacher-dashboard--unsubscribe-thread"),
        json.dumps({"id": 1}),
        content_type="application/json",
    )
    assert resp.status_code == 400
Exemplo n.º 14
0
def test_teacher_favourites(client, questions, teachers):
    """
    Requirements:
    1. Must be authenticated
    2. Only current teacher endpoint is accessible via GET
    3. Can update favourites through PUT
    4. No other http methods
    """

    # Setup
    teachers[0].favourite_questions.add(questions[0], questions[1])

    # 1. Must be authenticated
    url = reverse("REST:teacher", args=[teachers[0].pk])

    response = client.get(url)
    assert response.status_code == status.HTTP_403_FORBIDDEN

    # 2. Only current teacher endpoint is accessible via GET
    assert login_teacher(client, teachers[0])

    response = client.get(url)
    assert response.status_code == status.HTTP_200_OK
    favourites = json.loads(response.content)["favourite_questions"]
    for f in favourites:
        assert f in [fq.pk for fq in teachers[0].favourite_questions.all()]

    url = reverse("REST:teacher", args=[teachers[1].pk])

    response = client.get(url)
    assert response.status_code == status.HTTP_404_NOT_FOUND

    # 3. Can update favourites through PUT
    url = reverse("REST:teacher", args=[teachers[0].pk])

    new_favourites = [questions[0].pk, questions[2].pk]
    response = client.put(
        url,
        {"favourite_questions": new_favourites},
        content_type="application/json",
    )
    retrieved_favourites = json.loads(response.content)["favourite_questions"]

    for q in retrieved_favourites:
        assert q in new_favourites
        assert q in teachers[0].favourite_questions.values_list(
            "pk", flat=True
        )
    assert questions[1].pk not in retrieved_favourites
    assert questions[1] not in teachers[0].favourite_questions.all()

    # 4. No other http methods
    disallowed = ["post", "patch", "delete", "head", "options", "trace"]

    for method in disallowed:
        response = getattr(client, method)(url)
        assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED
def test_remove_gradebook_task__doesn_t_exists(client, teacher):
    assert login_teacher(client, teacher)

    resp = client.post(
        reverse("teacher-gradebook--remove"),
        json.dumps({"task_id": 1}),
        content_type="application/json",
    )

    assert resp.status_code == 200
def test_remove_gradebook_task__missing_params(client, teacher):
    assert login_teacher(client, teacher)

    resp = client.post(
        reverse("teacher-gradebook--remove"),
        json.dumps({}),
        content_type="application/json",
    )

    assert resp.status_code == 400
Exemplo n.º 17
0
def test_evaluate_rationale__wrong_answer_pk(client, teacher):
    assert login_teacher(client, teacher)

    n = AnswerAnnotation.objects.count()

    resp = client.post(
        reverse("teacher-dashboard--evaluate-rationale"), {"id": 0, "score": 0}
    )

    assert resp.status_code == 400
    assert AnswerAnnotation.objects.count() == n
def test_unsubscribe_from_thread(client, teacher, thread):
    assert login_teacher(client, teacher)
    assert ThreadSubscription.objects.filter(user=teacher.user,
                                             thread=thread).count()

    resp = client.post(
        reverse("teacher-dashboard--unsubscribe-thread"),
        json.dumps({"id": thread.pk}),
        content_type="application/json",
    )
    assert resp.status_code == 200
    assert not ThreadSubscription.objects.filter(user=teacher.user,
                                                 thread=thread).count()
Exemplo n.º 19
0
def test_new_questions__wrong_params(client, teacher):
    assert login_teacher(client, teacher)

    with mock.patch(
            "peerinst.views.teacher.get_json_params",
            return_value=HttpResponse("", status=400),
    ):
        resp = client.post(
            reverse("teacher-dashboard--new-questions"),
            json.dumps({}),
            content_type="application/json",
        )
        assert resp.status_code == 400
def test_remove_gradebook_task__exists(client, teacher):
    assert login_teacher(client, teacher)

    RunningTask.objects.create(id=1, description="test", teacher=teacher)

    resp = client.post(
        reverse("teacher-gradebook--remove"),
        json.dumps({"task_id": 1}),
        content_type="application/json",
    )

    assert resp.status_code == 200
    assert not RunningTask.objects.filter(id=1).exists()
Exemplo n.º 21
0
def test_request_gradebook__no_teacher_access(client, teacher, group):
    assert login_teacher(client, teacher)

    n = RunningTask.objects.count()

    resp = client.post(
        reverse("teacher-gradebook--request"),
        json.dumps({"group_id": group.pk}),
        content_type="application/json",
    )

    assert resp.status_code == 403
    assert RunningTask.objects.count() == n
Exemplo n.º 22
0
def test_request_gradebook__missing_params(client, teacher, group):
    assert login_teacher(client, teacher)
    group.teacher.add(teacher)

    n = RunningTask.objects.count()

    resp = client.post(
        reverse("teacher-gradebook--request"),
        json.dumps({}),
        content_type="application/json",
    )

    assert resp.status_code == 400
    assert RunningTask.objects.count() == n
def test_gradebook_task_result__ready(client, teacher):
    assert login_teacher(client, teacher)

    with mock.patch("peerinst.views.teacher.AsyncResult") as AsyncResult:
        result = mock.Mock()
        result.ready.return_value = True
        AsyncResult.return_value = result

        resp = client.post(
            reverse("teacher-gradebook--result"),
            json.dumps({"task_id": 1}),
            content_type="application/json",
        )

        assert resp.status_code == 200
Exemplo n.º 24
0
def test_student_activity__some_questions_done_correct_first_wrong_second(
    client,
    teacher,
    group,
    students,
    student_group_assignments,
    student_assignments,
):
    assert login_teacher(client, teacher)
    group.teacher.add(teacher)
    teacher.current_groups.add(group)
    for assignment in student_group_assignments:
        for question in assignment.questions:
            question.grading_scheme = GradingScheme.ADVANCED
            question.save()
        for assignment_ in assignment.studentassignment_set.all()[::2]:
            add_answers(
                student=assignment_.student,
                questions=assignment.questions,
                assignment=assignment.assignment,
                correct_first=True,
                correct_second=False,
            )

    resp = client.post(
        reverse("teacher-dashboard--student-activity"),
        json.dumps({}),
        content_type="application/json",
    )
    data = json.loads(resp.content)["groups"][0]
    assert data["title"] == group.title
    assert data["n_students"] == len(students)
    assert data["new"] is True
    assert len(data["assignments"]) == len(student_group_assignments)
    for assignment, assignment_ in zip(data["assignments"],
                                       student_group_assignments):
        assert assignment["title"] == assignment_.assignment.title
        assert assignment["n_completed"] == float(len(students[::2]))
        assert (
            assignment["mean_grade"] == float(len(assignment_.questions)) // 2)
        assert (assignment["min_grade"] == float(len(assignment_.questions)) //
                2)
        assert (assignment["max_grade"] == float(len(assignment_.questions)) //
                2)
        assert assignment["new"] is True
        assert assignment["expired"] is False
        assert assignment["link"].endswith(assignment_.hash + "/")
Exemplo n.º 25
0
def test_evaluate_rationale(client, teacher, answers):
    answer = answers[0]

    assert login_teacher(client, teacher)

    n = AnswerAnnotation.objects.count()

    resp = client.post(
        reverse("teacher-dashboard--evaluate-rationale"),
        {"id": answer.pk, "score": 0},
        follow=True,
    )

    assert resp.status_code == 200
    assert any(
        t.name == "peerinst/teacher/cards/rationale_to_score_card.html"
        for t in resp.templates
    )
    assert AnswerAnnotation.objects.count() == n + 1
Exemplo n.º 26
0
def test_new_questions__dashboard(client, teacher, questions, assignment,
                                  disciplines):
    assert login_teacher(client, teacher)

    for question in questions[:len(questions) // 2]:
        question.discipline = disciplines[0]
        question.save()
    for question in questions[len(questions) // 2:]:
        question.discipline = disciplines[1]
        question.save()

    teacher.disciplines.add(disciplines[0])

    resp = client.get(reverse("teacher-dashboard--new-questions"))

    assert resp.status_code == 200
    assert any(t.name == "peerinst/question/cards/question_card.html"
               for t in resp.templates)
    assert str(disciplines[0]) in resp.content
    assert str(disciplines[1]) not in resp.content
Exemplo n.º 27
0
def test_download_gradebook__celery_error(client, teacher):
    assert login_teacher(client, teacher)

    RunningTask.objects.create(id=1, description="test", teacher=teacher)

    with mock.patch("peerinst.views.teacher.convert_gradebook_to_csv"
                    ) as convert_gradebook_to_csv, mock.patch(
                        "peerinst.views.teacher.AsyncResult") as AsyncResult:
        convert_gradebook_to_csv.return_value = iter(["test\n"])
        result = mock.Mock()
        result.ready.side_effect = AttributeError()
        AsyncResult.return_value = result

        resp = client.post(
            reverse("teacher-gradebook--download"),
            json.dumps({"task_id": 1}),
            content_type="application/json",
        )

        assert resp.status_code == 500
        assert RunningTask.objects.filter(id=1).exists()
Exemplo n.º 28
0
def test_get_tasks(client, teacher):
    assert login_teacher(client, teacher)

    for i in range(1, 11):
        RunningTask.objects.create(
            id=i, description="test{}".format(i), teacher=teacher
        )

    with mock.patch("peerinst.views.teacher.AsyncResult") as AsyncResult:
        result = mock.Mock()
        completed = (i % 2 == 0 for i in reversed(list(range(1, 11))))
        result.ready = lambda: next(completed)
        AsyncResult.return_value = result
        resp = client.get(reverse("teacher-tasks"))

    assert resp.status_code == 200
    data = json.loads(resp.content.decode())
    for task, i in zip(data["tasks"], reversed(list(range(1, 11)))):
        assert task["id"] == str(i)
        assert task["description"] == "test{}".format(i)
        assert task["completed"] == (i % 2 == 0)
Exemplo n.º 29
0
def test_clone_assignment(client, assignment, teacher):
    assert login_teacher(client, teacher)

    resp = client.post(
        reverse("assignment-copy", args=[assignment.pk]),
        {
            "identifier": "unique",
            "title": "title",
            "description": "a bucket of my questions",
        },
        follow=True,
    )

    assert resp.status_code == 200

    new_assignment = Assignment.objects.last()

    for q in new_assignment.questions.all():
        assert q in assignment.questions.all()

    assert new_assignment.questions.count() == assignment.questions.count()
    assert new_assignment.parent == assignment
Exemplo n.º 30
0
def test_download_gradebook__no_running_task(client, teacher):
    assert login_teacher(client, teacher)

    with mock.patch("peerinst.views.teacher.convert_gradebook_to_csv"
                    ) as convert_gradebook_to_csv, mock.patch(
                        "peerinst.views.teacher.AsyncResult") as AsyncResult:
        convert_gradebook_to_csv.return_value = iter(["test\n"])
        result = mock.Mock()
        result.ready.return_value = True
        result.result = {"group": "test"}
        AsyncResult.return_value = result

        resp = client.post(
            reverse("teacher-gradebook--download"),
            json.dumps({"task_id": 1}),
            content_type="application/json",
        )

        assert resp.status_code == 200
        assert isinstance(resp, StreamingHttpResponse)
        assert (next(
            resp.streaming_content).strip() == "myDALITE_gradebook_test.csv")
        assert next(resp.streaming_content).strip() == "test"