Exemplo n.º 1
0
def test_csv_export(now, client, answer_type, answer):
    im = ImageFactory()

    rs = ReaderStudyFactory()
    rs.images.add(im)
    rs.save()

    editor = UserFactory()
    rs.add_editor(editor)

    reader = UserFactory()
    rs.add_reader(reader)

    q = QuestionFactory(
        question_text="foo", reader_study=rs, answer_type=answer_type
    )

    a = AnswerFactory(question=q, answer=answer)
    a.images.add(im)
    a.save()

    response = get_view_for_user(
        viewname="api:reader-study-export-answers",
        reverse_kwargs={"pk": rs.pk},
        user=editor,
        client=client,
        method=client.get,
        content_type="application/json",
    )

    headers = str(response.serialize_headers())
    content = str(response.content)

    assert response.status_code == 200
    assert "Content-Type: text/csv" in headers
    assert (
        f'filename="{rs.slug}-answers-2020-01-01T00:00:00+00:00.csv"'
        in headers
    )
    assert a.question.question_text in content
    assert a.question.get_answer_type_display() in content
    assert str(a.question.required) in content
    assert a.question.get_image_port_display() in content
    if isinstance(answer, dict):
        for key in answer:
            assert key in content
    else:
        assert re.sub(r"[\n\r\t]", " ", str(a.answer)) in content
    assert im.name in content
    assert a.creator.username in content

    response = get_view_for_user(
        viewname="api:reader-study-export-answers",
        reverse_kwargs={"pk": rs.pk},
        user=reader,
        client=client,
        method=client.get,
        content_type="application/json",
    )
    assert response.status_code == 404
Exemplo n.º 2
0
def test_assign_score(settings):
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    rs = ReaderStudyFactory(use_display_sets=False)
    im = ImageFactory()
    q1 = QuestionFactory(reader_study=rs)
    q2 = QuestionFactory(reader_study=rs,
                         answer_type=Question.AnswerType.MULTIPLE_CHOICE)
    e, r1, r2 = UserFactory(), UserFactory(), UserFactory()

    rs.images.add(im)
    rs.add_editor(e)
    rs.add_reader(r1)
    rs.add_reader(r2)

    with capture_on_commit_callbacks(execute=True):
        a1 = AnswerFactory(question=q1, creator=r1, answer="foo")
    a1.images.add(im)
    assert a1.score is None

    with capture_on_commit_callbacks(execute=True):
        gt = AnswerFactory(question=q1,
                           creator=e,
                           answer="foo",
                           is_ground_truth=True)
        gt.images.add(im)
    a1.refresh_from_db()
    assert a1.score == 1.0

    with capture_on_commit_callbacks(execute=True):
        a2 = AnswerFactory(question=q1, creator=r2, answer="foo")
        a2.images.add(im)
    a2.refresh_from_db()
    assert a2.score == 1.0

    with capture_on_commit_callbacks(execute=True):
        a1 = AnswerFactory(question=q2, creator=r1, answer=[])
        a1.images.add(im)
    a1.refresh_from_db()
    assert a1.score is None

    with capture_on_commit_callbacks(execute=True):
        gt = AnswerFactory(question=q2,
                           creator=e,
                           answer=[],
                           is_ground_truth=True)
        gt.images.add(im)
    a1.refresh_from_db()
    assert a1.score == 1.0

    with capture_on_commit_callbacks(execute=True):
        a2 = AnswerFactory(question=q2, creator=r2, answer=[])
        a2.images.add(im)
    a2.refresh_from_db()
    assert a2.score == 1.0
Exemplo n.º 3
0
def test_mine(client):
    im1, im2 = ImageFactory(), ImageFactory()
    rs1, rs2 = ReaderStudyFactory(), ReaderStudyFactory()
    rs1.images.add(im1)
    rs2.images.add(im2)

    reader = UserFactory()
    rs1.add_reader(reader)
    rs2.add_reader(reader)

    q1 = QuestionFactory(
        reader_study=rs1, answer_type=Question.ANSWER_TYPE_BOOL
    )
    q2 = QuestionFactory(
        reader_study=rs2, answer_type=Question.ANSWER_TYPE_BOOL
    )

    a1 = AnswerFactory(question=q1, creator=reader, answer=True)
    a1.images.add(im1)

    a2 = AnswerFactory(question=q2, creator=reader, answer=True)
    a2.images.add(im2)

    response = get_view_for_user(
        viewname="api:reader-studies-answer-mine",
        user=reader,
        client=client,
        method=client.get,
        content_type="application/json",
    )
    response = response.json()
    assert response["count"] == 2

    response = get_view_for_user(
        viewname="api:reader-studies-answer-mine",
        user=reader,
        client=client,
        method=client.get,
        data={"question__reader_study": rs1.pk},
        content_type="application/json",
    )
    response = response.json()
    assert response["count"] == 1
    assert response["results"][0]["pk"] == str(a1.pk)

    response = get_view_for_user(
        viewname="api:reader-studies-answer-mine",
        user=reader,
        client=client,
        method=client.get,
        data={"question__reader_study": rs2.pk},
        content_type="application/json",
    )
    response = response.json()
    assert response["count"] == 1
    assert response["results"][0]["pk"] == str(a2.pk)
Exemplo n.º 4
0
def test_help_markdown_is_scrubbed(client):
    rs = ReaderStudyFactory(
        help_text_markdown="<b>My Help Text</b><script>naughty</script>")
    u = UserFactory()
    rs.add_reader(u)

    response = get_view_for_user(client=client, url=rs.api_url, user=u)

    assert response.status_code == 200
    assert response.json()["help_text"] == "<p><b>My Help Text</b>naughty</p>"
Exemplo n.º 5
0
def test_question_delete_disabled_for_questions_with_answers(client):
    rs = ReaderStudyFactory(use_display_sets=False)
    r1, editor = UserFactory(), UserFactory()
    rs.add_reader(r1)
    rs.add_editor(editor)
    q = QuestionFactory(
        reader_study=rs,
        question_text="q1",
        answer_type=Question.AnswerType.BOOL,
    )
    AnswerFactory(creator=r1, question=q, answer=True)

    assert Answer.objects.count() == 1
    assert Question.objects.count() == 1
    assert not q.is_fully_editable

    response = get_view_for_user(
        viewname="reader-studies:question-delete",
        client=client,
        method=client.post,
        reverse_kwargs={
            "slug": rs.slug,
            "pk": q.pk
        },
        user=editor,
    )

    assert response.status_code == 403
    assert Question.objects.count() == 1

    # if answer is deleted, deletion of the question is possible again
    get_view_for_user(
        viewname="reader-studies:answers-remove",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"user": r1.id},
        follow=True,
        user=editor,
    )

    assert Answer.objects.count() == 0

    response = get_view_for_user(
        viewname="reader-studies:question-delete",
        client=client,
        method=client.post,
        reverse_kwargs={
            "slug": rs.slug,
            "pk": q.pk
        },
        user=editor,
    )
    assert response.status_code == 302
    assert Question.objects.count() == 0
Exemplo n.º 6
0
def test_assign_answer_image(client, settings):
    settings.task_eager_propagates = (True,)
    settings.task_always_eager = (True,)
    rs = ReaderStudyFactory()
    im = ImageFactory()
    editor, reader = UserFactory(), UserFactory()

    rs.images.add(im)
    rs.add_editor(editor)
    rs.add_reader(reader)

    question = QuestionFactory(
        reader_study=rs, answer_type=Question.ANSWER_TYPE_POLYGON_IMAGE
    )

    us = RawImageUploadSessionFactory(creator=reader)

    answer = AnswerFactory(
        creator=reader,
        question=question,
        answer={"upload_session_pk": str(us.pk)},
    )

    f = StagedFileFactory(
        file__from_path=Path(__file__).parent.parent
        / "cases_tests"
        / "resources"
        / "image10x10x10.mha"
    )
    RawImageFileFactory(upload_session=us, staged_file_id=f.file_id)

    response = get_view_for_user(
        viewname="api:upload-session-process-images",
        reverse_kwargs={"pk": us.pk},
        user=reader,
        client=client,
        method=client.patch,
        data={"answer": str(answer.pk)},
        content_type="application/json",
    )

    assert response.status_code == 200

    answer.refresh_from_db()
    image = us.image_set.first()

    assert answer.answer_image == image
    assert reader.has_perm("view_image", image)
    assert editor.has_perm("view_image", image)
Exemplo n.º 7
0
def test_rs_list_permissions(client):
    # Users should login
    response = get_view_for_user(viewname="reader-studies:list", client=client)
    assert response.status_code == 200
    assert "Add a new reader study" not in response.rendered_content

    creator = get_rs_creator()

    # Creators should be able to see the create button
    response = get_view_for_user(viewname="reader-studies:list",
                                 client=client,
                                 user=creator)
    assert response.status_code == 200
    assert "Add a new reader study" in response.rendered_content

    rs1, rs2 = ReaderStudyFactory(use_display_sets=False), ReaderStudyFactory(
        use_display_sets=False)
    reader1 = UserFactory()

    # Readers should only be able to see the studies they have access to
    response = get_view_for_user(viewname="reader-studies:list",
                                 client=client,
                                 user=reader1)
    assert response.status_code == 200
    assert "Add a new reader study" not in response.rendered_content
    assert rs1.slug not in response.rendered_content
    assert rs2.slug not in response.rendered_content

    rs1.add_reader(user=reader1)

    response = get_view_for_user(viewname="reader-studies:list",
                                 client=client,
                                 user=reader1)
    assert response.status_code == 200
    assert "Add a new reader study" not in response.rendered_content
    assert rs1.slug in response.rendered_content
    assert rs2.slug not in response.rendered_content

    editor2 = UserFactory()
    rs2.add_editor(user=editor2)

    # Editors should only be able to see the studies that they have access to
    response = get_view_for_user(viewname="reader-studies:list",
                                 client=client,
                                 user=editor2)
    assert response.status_code == 200
    assert "Add a new reader study" not in response.rendered_content
    assert rs1.slug not in response.rendered_content
    assert rs2.slug in response.rendered_content
Exemplo n.º 8
0
def test_validate_answer():
    u = UserFactory()
    im1, im2, im3 = ImageFactory(), ImageFactory(), ImageFactory()
    rs = ReaderStudyFactory(hanging_list=[
        {
            "main": im1.name,
            "main-overlay": im3.name
        },
        {
            "main": im2.name,
            "main-overlay": im3.name
        },
    ])
    rs.images.set([im1, im2, im3])
    rs.add_reader(u)

    q = QuestionFactory(
        reader_study=rs,
        answer_type=Question.AnswerType.BOOL,
        question_text="q1",
    )

    answer = AnswerFactory(
        creator=u,
        question=q,
        answer=True,
    )
    answer.images.set([im1, im3])

    with pytest.raises(ValidationError) as e:
        Answer.validate(
            creator=u,
            question=q,
            answer=True,
            images=[im1, im3],
        )
        assert (
            e.value.message ==
            f"User {u} has already answered this question for this set of images."
        )

    assert (Answer.validate(
        creator=u,
        question=q,
        answer=True,
        images=[im2, im3],
    ) is None)
Exemplo n.º 9
0
def test_reader_study_delete(client):
    rs = ReaderStudyFactory(use_display_sets=False)
    editor = UserFactory()
    reader = UserFactory()
    rs.add_editor(editor)
    rs.add_reader(reader)

    assert ReaderStudy.objects.count() == 1
    assert is_following(user=editor, obj=rs)

    response = get_view_for_user(
        viewname="reader-studies:delete",
        client=client,
        method=client.get,
        reverse_kwargs={"slug": rs.slug},
        follow=True,
        user=reader,
    )

    assert response.status_code == 403
    assert ReaderStudy.objects.count() == 1

    response = get_view_for_user(
        viewname="reader-studies:delete",
        client=client,
        method=client.get,
        reverse_kwargs={"slug": rs.slug},
        follow=True,
        user=editor,
    )

    assert response.status_code == 200
    assert "Confirm Deletion" in response.rendered_content

    response = get_view_for_user(
        viewname="reader-studies:delete",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        follow=True,
        user=editor,
    )

    assert response.status_code == 200
    assert ReaderStudy.objects.count() == 0
    assert not is_following(user=editor, obj=rs)
Exemplo n.º 10
0
def test_display_set_description():
    rs = ReaderStudyFactory()
    reader = UserFactory()
    rs.add_reader(reader)
    images = [ImageFactory() for _ in range(6)]
    ci = ComponentInterface.objects.get(slug="generic-medical-image")
    result = {}
    for image in images:
        ds = DisplaySetFactory(reader_study=rs)
        result[ds.pk] = f"<p>{str(image.pk)}</p>"
        civ = ComponentInterfaceValueFactory(interface=ci, image=image)
        ds.values.add(civ)

    rs.case_text = {im.name: str(im.pk) for im in images}
    rs.case_text["no_image"] = "not an image"
    rs.save()

    for ds in rs.display_sets.all():
        assert ds.description == result[ds.pk]
Exemplo n.º 11
0
def test_case_text_is_scrubbed(client):
    u = UserFactory()
    im, im1 = ImageFactory(), ImageFactory()
    rs = ReaderStudyFactory(
        case_text={
            im.name: "<b>My Help Text</b><script>naughty</script>",
            "not an image name": "Shouldn't appear in result",
            im1.name: "Doesn't belong to this study so ignore",
        })
    rs.images.add(im)
    rs.add_reader(u)

    response = get_view_for_user(client=client, url=rs.api_url, user=u)

    assert response.status_code == 200
    # Case should be indexed with the api url
    assert response.json()["case_text"] == {
        im.api_url: "<p><b>My Help Text</b>naughty</p>"
    }
Exemplo n.º 12
0
def test_question_delete(client):
    rs = ReaderStudyFactory(use_display_sets=False)
    r1, editor = UserFactory(), UserFactory()
    rs.add_reader(r1)
    rs.add_editor(editor)
    q = QuestionFactory(
        reader_study=rs,
        question_text="q1",
        answer_type=Question.AnswerType.BOOL,
    )
    assert Question.objects.count() == 1

    response = get_view_for_user(
        viewname="reader-studies:question-delete",
        client=client,
        method=client.post,
        reverse_kwargs={
            "slug": rs.slug,
            "pk": q.pk
        },
        follow=True,
        user=r1,
    )

    assert response.status_code == 403

    response = get_view_for_user(
        viewname="reader-studies:question-delete",
        client=client,
        method=client.post,
        reverse_kwargs={
            "slug": rs.slug,
            "pk": q.pk
        },
        user=editor,
    )

    assert response.status_code == 302
    assert Question.objects.count() == 0
    assert str(rs) in response.url
def test_upload_session_owned_by_answer_creator(client, settings, answer_type):
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    rs = ReaderStudyFactory()
    im = ImageFactory()
    editor, reader = UserFactory(), UserFactory()

    rs.images.add(im)
    rs.add_editor(editor)
    rs.add_reader(reader)

    question = QuestionFactory(reader_study=rs, answer_type=answer_type)

    us1 = RawImageUploadSessionFactory(creator=reader)
    us2 = RawImageUploadSessionFactory(creator=editor)

    answer1 = AnswerFactory(
        creator=reader,
        question=question,
        answer={"upload_session_pk": str(us1.pk)},
    )

    f = StagedFileFactory(file__from_path=Path(__file__).parent.parent /
                          "cases_tests" / "resources" / "image10x10x10.mha")
    RawImageFileFactory(upload_session=us1, staged_file_id=f.file_id)

    response = get_view_for_user(
        viewname="api:upload-session-process-images",
        reverse_kwargs={"pk": us2.pk},
        user=editor,
        client=client,
        method=client.patch,
        data={"answer": str(answer1.pk)},
        content_type="application/json",
    )

    assert response.status_code == 400
    assert (b"User does not have permission to add an image to this answer"
            in response.rendered_content)
Exemplo n.º 14
0
def test_reader_study_copy(client):
    rs = ReaderStudyFactory(title="copied")
    editor = UserFactory()
    editor2 = UserFactory()
    reader = UserFactory()
    rs.add_reader(reader)
    rs.add_editor(editor)
    rs.add_editor(editor2)
    QuestionFactory(
        reader_study=rs,
        answer_type=Question.AnswerType.BOOL,
        question_text="q1",
    ),
    QuestionFactory(
        reader_study=rs,
        answer_type=Question.AnswerType.BOOL,
        question_text="q2",
    )

    im1, im2 = ImageFactory(), ImageFactory()

    rs.images.set([im1, im2])
    rs.hanging_list = [{"main": im1.name}, {"main": im2.name}]
    rs.case_text = {im1.name: "test", im2.name: "test2"}
    rs.save()

    assert ReaderStudy.objects.count() == 1

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "1"},
        user=reader,
        follow=True,
    )

    assert response.status_code == 403
    assert ReaderStudy.objects.count() == 1

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "1"},
        user=editor,
        follow=True,
    )

    assert response.status_code == 403
    assert ReaderStudy.objects.count() == 1

    add_perm = Permission.objects.get(
        codename=f"add_{ReaderStudy._meta.model_name}"
    )
    editor.user_permissions.add(add_perm)

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "1"},
        user=editor,
        follow=True,
    )

    assert response.status_code == 200
    assert ReaderStudy.objects.count() == 2

    _rs = ReaderStudy.objects.order_by("created").last()
    assert _rs.title == "1"
    assert _rs.images.count() == 0
    assert _rs.questions.count() == 0
    assert _rs.readers_group.user_set.count() == 0
    assert _rs.editors_group.user_set.count() == 1
    assert _rs.hanging_list == []
    assert _rs.case_text == {}

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "2", "copy_questions": True},
        user=editor,
        follow=True,
    )

    assert response.status_code == 200
    assert ReaderStudy.objects.count() == 3

    _rs = ReaderStudy.objects.order_by("created").last()
    assert _rs.title == "2"
    assert _rs.questions.count() == 2
    assert _rs.images.count() == 0
    assert _rs.hanging_list == []
    assert _rs.case_text == {}
    assert _rs.readers_group.user_set.count() == 0
    assert _rs.editors_group.user_set.count() == 1

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "3", "copy_images": True},
        user=editor,
        follow=True,
    )

    assert response.status_code == 200
    assert ReaderStudy.objects.count() == 4

    _rs = ReaderStudy.objects.order_by("created").last()
    assert _rs.title == "3"
    assert _rs.questions.count() == 0
    assert _rs.images.count() == 2
    assert _rs.hanging_list == []
    assert _rs.case_text == {}
    assert _rs.readers_group.user_set.count() == 0
    assert _rs.editors_group.user_set.count() == 1

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "4", "copy_hanging_list": True},
        user=editor,
        follow=True,
    )

    assert response.status_code == 200
    assert (
        "Hanging list and case text can only be copied if the images are copied as well"
        in response.rendered_content
    )
    assert ReaderStudy.objects.count() == 4

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "4", "copy_images": True, "copy_hanging_list": True},
        user=editor,
        follow=True,
    )

    assert response.status_code == 200
    assert ReaderStudy.objects.count() == 5

    _rs = ReaderStudy.objects.order_by("created").last()
    assert _rs.title == "4"
    assert _rs.questions.count() == 0
    assert _rs.images.count() == 2
    assert _rs.hanging_list == rs.hanging_list
    assert _rs.case_text == {}
    assert _rs.readers_group.user_set.count() == 0
    assert _rs.editors_group.user_set.count() == 1

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "5", "copy_images": True, "copy_case_text": True},
        user=editor,
        follow=True,
    )

    assert response.status_code == 200
    assert ReaderStudy.objects.count() == 6

    _rs = ReaderStudy.objects.order_by("created").last()
    assert _rs.title == "5"
    assert _rs.questions.count() == 0
    assert _rs.images.count() == 2
    assert _rs.hanging_list == []
    assert _rs.case_text == rs.case_text
    assert _rs.readers_group.user_set.count() == 0
    assert _rs.editors_group.user_set.count() == 1

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "6", "copy_readers": True},
        user=editor,
        follow=True,
    )

    assert response.status_code == 200
    assert ReaderStudy.objects.count() == 7

    _rs = ReaderStudy.objects.order_by("created").last()
    assert _rs.title == "6"
    assert _rs.questions.count() == 0
    assert _rs.images.count() == 0
    assert _rs.hanging_list == []
    assert _rs.case_text == {}
    assert _rs.readers_group.user_set.count() == 1
    assert _rs.editors_group.user_set.count() == 1

    response = get_view_for_user(
        viewname="reader-studies:copy",
        client=client,
        method=client.post,
        reverse_kwargs={"slug": rs.slug},
        data={"title": "7", "copy_editors": True},
        user=editor,
        follow=True,
    )

    assert response.status_code == 200
    assert ReaderStudy.objects.count() == 8

    _rs = ReaderStudy.objects.order_by("created").last()
    assert _rs.title == "7"
    assert _rs.questions.count() == 0
    assert _rs.images.count() == 0
    assert _rs.hanging_list == []
    assert _rs.case_text == {}
    assert _rs.readers_group.user_set.count() == 0
    assert _rs.editors_group.user_set.count() == 2