Exemplo n.º 1
0
        def create_upload(upload_creator):
            riu = RawImageUploadSessionFactory(creator=upload_creator)

            for _ in range(3):
                ImageFactory(origin=riu),

            riu.save()
            return riu
def test_algorithm_execution_session_list_view(client):
    # Session creators and algorithm editors should be able to see these sessions
    alg_set = TwoAlgorithms()

    extra_user1, extra_user2 = UserFactory(), UserFactory()

    alg_set.alg1.add_user(extra_user1)
    alg_set.alg2.add_user(extra_user2)

    s1, s2 = (
        RawImageUploadSessionFactory(creator=extra_user1,
                                     algorithm_image__algorithm=alg_set.alg1),
        RawImageUploadSessionFactory(creator=extra_user2,
                                     algorithm_image__algorithm=alg_set.alg2),
    )

    all_sessions = {s1, s2}

    tests = (
        (None, alg_set.alg1, 302, set()),
        (None, alg_set.alg2, 302, set()),
        (alg_set.creator, alg_set.alg1, 200, set()),
        (alg_set.creator, alg_set.alg2, 200, set()),
        (alg_set.editor1, alg_set.alg1, 200, {s1}),
        (alg_set.editor1, alg_set.alg2, 200, set()),
        (alg_set.user1, alg_set.alg1, 200, set()),
        (alg_set.user1, alg_set.alg2, 200, set()),
        (alg_set.editor2, alg_set.alg1, 200, set()),
        (alg_set.editor2, alg_set.alg2, 200, {s2}),
        (alg_set.user2, alg_set.alg1, 200, set()),
        (alg_set.user2, alg_set.alg2, 200, set()),
        (alg_set.u, alg_set.alg1, 200, set()),
        (alg_set.u, alg_set.alg2, 200, set()),
        (extra_user1, alg_set.alg1, 200, {s1}),
        (extra_user1, alg_set.alg2, 200, set()),
        (extra_user2, alg_set.alg1, 200, set()),
        (extra_user2, alg_set.alg2, 200, {s2}),
    )

    for test in tests:
        response = get_view_for_user(
            viewname="algorithms:execution-session-list",
            reverse_kwargs={"slug": test[1].slug},
            client=client,
            user=test[0],
        )
        assert response.status_code == test[2]

        # Check that the results are filtered
        if response.status_code == 200:
            expected_sessions = test[3]
            excluded_sessions = all_sessions - expected_sessions
            assert all(
                str(j.pk) in response.rendered_content
                for j in expected_sessions)
            assert all(
                str(j.pk) not in response.rendered_content
                for j in excluded_sessions)
Exemplo n.º 3
0
        def create_upload(upload_creator):
            riu = RawImageUploadSessionFactory(creator=upload_creator)

            for _ in range(3):
                ImageFactory(origin=riu),

            riu.save()

            interface = algorithm_image.algorithm.inputs.get()

            return [{
                ComponentInterfaceValueFactory(image=im, interface=interface)
            } for im in riu.image_set.all()]
def test_algorithm_job_post_serializer_create(rf):
    # setup
    user = UserFactory()
    upload, upload_2 = (
        RawImageUploadSessionFactory(creator=user),
        RawImageUploadSessionFactory(creator=user),
    )
    image = ImageFactory()
    upload_2.image_set.set([image])
    assign_perm("view_image", user, image)
    assert user.has_perm("view_image", image)
    algorithm_image = AlgorithmImageFactory(ready=True)
    interfaces = {
        ComponentInterfaceFactory(
            kind=ComponentInterface.Kind.STRING,
            title="TestInterface 1",
            default_value="default",
        ),
        ComponentInterfaceFactory(kind=ComponentInterface.Kind.IMAGE,
                                  title="TestInterface 2"),
        ComponentInterfaceFactory(kind=ComponentInterface.Kind.IMAGE,
                                  title="TestInterface 3"),
    }
    algorithm_image.algorithm.inputs.set(interfaces)
    algorithm_image.algorithm.add_editor(user)

    algorithm_image.algorithm.save()

    job = {"algorithm": algorithm_image.algorithm.api_url, "inputs": []}
    job["inputs"].append({
        "interface": "testinterface-2",
        "upload_session": upload.api_url
    })
    job["inputs"].append({
        "interface": "testinterface-3",
        "image": image.api_url
    })

    # test
    request = rf.get("/foo")
    request.user = user
    serializer = JobPostSerializer(data=job, context={"request": request})

    # verify
    assert serializer.is_valid()
    serializer.create(serializer.validated_data)
    assert len(Job.objects.all()) == 1
    job = Job.objects.first()
    assert job.creator == user
    assert len(job.inputs.all()) == 3
Exemplo n.º 5
0
def test_process_images_api_view(client, settings):
    # Override the celery settings
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    user = UserFactory()
    us = RawImageUploadSessionFactory(creator=user)

    algorithm_image = AlgorithmImageFactory(ready=True)
    algorithm_image.algorithm.add_user(user)

    f = StagedFileFactory(file__from_path=Path(__file__).parent / "resources" /
                          "image10x10x10.mha")

    RawImageFileFactory(upload_session=us, staged_file_id=f.file_id)

    def request_processing():
        return get_view_for_user(
            viewname="api:upload-session-process-images",
            reverse_kwargs={"pk": us.pk},
            user=user,
            client=client,
            method=client.patch,
            data={"algorithm": algorithm_image.algorithm.slug},
            content_type="application/json",
        )

    # First request should work
    response = request_processing()
    assert response.status_code == 200

    # Jobs should only be run once
    response = request_processing()
    assert response.status_code == 400
Exemplo n.º 6
0
def test_algorithm_input_image_multiple_files(build_images, client,
                                              algorithm_io_image, settings,
                                              component_interfaces):
    # Override the celery settings
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

    creator = UserFactory()

    assert Job.objects.count() == 0

    # Create the algorithm image
    algorithm_container, sha256 = algorithm_io_image
    alg = AlgorithmImageFactory(image__from_path=algorithm_container,
                                image_sha256=sha256,
                                ready=True)
    alg.algorithm.add_editor(creator)

    alg.algorithm.inputs.set(ComponentInterface.objects.all())
    # create the job
    job = Job.objects.create(creator=creator, algorithm_image=alg)
    us = RawImageUploadSessionFactory()

    ImageFactory(origin=us), ImageFactory(origin=us)
    ci = ComponentInterface.objects.get(slug=DEFAULT_INPUT_INTERFACE_SLUG)

    civ = ComponentInterfaceValue.objects.create(interface=ci)
    job.inputs.add(civ)

    with pytest.raises(ValueError):
        with capture_on_commit_callbacks(execute=True):
            run_algorithm_job_for_inputs(job_pk=job.pk,
                                         upload_pks={civ.pk: us.pk})
Exemplo n.º 7
0
def test_keys_used_in_algorithm_session_js(client):
    """All of these values are used in algorithms/js/session.js"""
    u = UserFactory()
    j = AlgorithmJobFactory(creator=u)
    s = RawImageUploadSessionFactory(creator=u)

    # Session API
    response = get_view_for_user(client=client, url=s.api_url, user=u)
    assert response.status_code == 200
    assert response.json()["status"] == "Queued"
    assert response.json()["api_url"] == s.api_url
    assert response.json()["image_set"] == []

    # Evaluation API
    response = get_view_for_user(client=client, url=j.api_url, user=u)
    assert response.status_code == 200
    assert response.json()["status"] == "Queued"
    assert response.json()["api_url"] == j.api_url

    # Image API
    response = get_view_for_user(client=client,
                                 url=j.inputs.first().image.api_url,
                                 user=u)
    assert response.status_code == 200
    assert response.json()["job_set"] == [j.api_url]
def test_algorithm_execution_session_detail(client):
    u1, u2 = UserFactory(), UserFactory()
    a = AlgorithmImageFactory()
    s = RawImageUploadSessionFactory(algorithm_image=a, creator=u1)

    response = get_view_for_user(
        client=client,
        viewname="algorithms:execution-session-detail",
        reverse_kwargs={
            "slug": a.algorithm.slug,
            "pk": s.pk
        },
        user=u1,
    )
    assert response.status_code == 200

    response = get_view_for_user(
        client=client,
        viewname="algorithms:execution-session-detail",
        reverse_kwargs={
            "slug": a.algorithm.slug,
            "pk": s.pk
        },
        user=u2,
    )
    assert response.status_code == 403
Exemplo n.º 9
0
def test_invalid_image_file_post(client):
    user = UserFactory()

    response = get_view_for_user(
        viewname="api:upload-session-file-list",
        user=user,
        client=client,
        method=client.post,
        data={"upload_session": None, "filename": "dummy.bin"},
        content_type="application/json",
    )
    assert response.status_code == 400
    assert response.json() == {
        "upload_session": ["This field may not be null."]
    }

    upload_session = RawImageUploadSessionFactory(creator=user)
    response = get_view_for_user(
        viewname="api:upload-session-file-list",
        user=user,
        client=client,
        method=client.post,
        data={"upload_session": upload_session.api_url, "filename": None},
        content_type="application/json",
    )
    assert response.status_code == 400
    assert response.json() == {"filename": ["This field may not be null."]}
Exemplo n.º 10
0
    def test_permission_required_views(self, client):
        rius = RawImageUploadSessionFactory()
        u = UserFactory()

        for view_name, kwargs, permission, obj in [
            (
                "raw-image-upload-session-detail",
                {
                    "pk": rius.pk
                },
                "view_rawimageuploadsession",
                rius,
            ),
        ]:
            response = get_view_for_user(
                client=client,
                viewname=f"cases:{view_name}",
                reverse_kwargs=kwargs,
                user=u,
            )

            assert response.status_code == 403

            assign_perm(permission, u, obj)

            response = get_view_for_user(
                client=client,
                viewname=f"cases:{view_name}",
                reverse_kwargs=kwargs,
                user=u,
            )

            assert response.status_code == 200

            remove_perm(permission, u, obj)
Exemplo n.º 11
0
    def test_permission_filtered_views(self, client):
        rius = RawImageUploadSessionFactory()
        u = UserFactory()

        for view_name, kwargs, permission, obj in [
            (
                "raw-image-upload-session-list",
                {},
                "view_rawimageuploadsession",
                rius,
            ),
        ]:
            assign_perm(permission, u, obj)

            response = get_view_for_user(
                client=client,
                viewname=f"cases:{view_name}",
                reverse_kwargs=kwargs,
                user=u,
            )

            assert response.status_code == 200
            assert obj in response.context[-1]["object_list"]

            remove_perm(permission, u, obj)

            response = get_view_for_user(
                client=client,
                viewname=f"cases:{view_name}",
                reverse_kwargs=kwargs,
                user=u,
            )

            assert response.status_code == 200
            assert obj not in response.context[-1]["object_list"]
def test_keys_used_in_algorithm_session_js(client):
    """All of these values are used in algorithms/js/session.js"""
    u = UserFactory()
    j = AlgorithmJobFactory(creator=u)
    s = RawImageUploadSessionFactory(creator=u)

    # Session API
    response = get_view_for_user(client=client, url=s.api_url, user=u)
    assert response.status_code == 200
    assert response.json()["status"] == "Queued"
    assert response.json()["api_url"] == s.api_url
    assert response.json()["image_set"] == []

    # Evaluation API
    response = get_view_for_user(client=client, url=j.api_url, user=u)
    assert response.status_code == 200
    assert response.json()["status"] == "Queued"
    assert response.json()["api_url"] == j.api_url

    # Job API
    response = get_view_for_user(
        client=client,
        viewname="api:algorithms-job-list",
        user=u,
        data={"input_image": str(j.inputs.first().image.pk)},
    )
    assert response.status_code == 200
    assert len(response.json()["results"]) == 1
    assert response.json()["results"][0]["pk"] == str(j.pk)
Exemplo n.º 13
0
 def test_gets_creator_from_session(self):
     riu = RawImageUploadSessionFactory()
     riu.image_set.add(ImageFactory(), ImageFactory())
     create_algorithm_jobs(
         algorithm_image=AlgorithmImageFactory(),
         images=riu.image_set.all(),
         creator=riu.creator,
     )
     j = Job.objects.first()
     assert j.creator == riu.creator
Exemplo n.º 14
0
    def test_permission_required_views(self, client):
        rius = RawImageUploadSessionFactory()
        image_file_dzi = ImageFileFactory(image_type="DZI")
        image_file_mh = ImageFactoryWithImageFile(
            color_space=Image.COLOR_SPACE_GRAY)
        u = UserFactory()

        for view_name, kwargs, permission, obj in [
            (
                "raw-image-upload-session-detail",
                {
                    "pk": rius.pk
                },
                "view_rawimageuploadsession",
                rius,
            ),
            (
                "osd-image-detail",
                {
                    "pk": image_file_dzi.image.pk
                },
                "view_image",
                image_file_dzi.image,
            ),
            (
                "vtk-image-detail",
                {
                    "pk": image_file_mh.pk
                },
                "view_image",
                image_file_mh,
            ),
        ]:
            response = get_view_for_user(
                client=client,
                viewname=f"cases:{view_name}",
                reverse_kwargs=kwargs,
                user=u,
            )

            assert response.status_code == 403

            assign_perm(permission, u, obj)

            response = get_view_for_user(
                client=client,
                viewname=f"cases:{view_name}",
                reverse_kwargs=kwargs,
                user=u,
            )

            assert response.status_code == 200

            remove_perm(permission, u, obj)
Exemplo n.º 15
0
def test_add_images_to_component_interface_value():
    # Override the celery settings
    us = RawImageUploadSessionFactory()
    ImageFactory(origin=us), ImageFactory(origin=us)
    ci = ComponentInterface.objects.get(slug=DEFAULT_INPUT_INTERFACE_SLUG)

    civ = ComponentInterfaceValueFactory(interface=ci)

    with pytest.raises(ValueError) as err:
        add_images_to_component_interface_value(
            component_interface_value_pk=civ.pk, upload_pk=us.pk)
    assert "Image imports should result in a single image" in str(err)
    assert civ.image is None

    us2 = RawImageUploadSessionFactory()
    image = ImageFactory(origin=us2)
    civ2 = ComponentInterfaceValueFactory(interface=ci)
    add_images_to_component_interface_value(
        component_interface_value_pk=civ2.pk, upload_pk=us2.pk)
    civ2.refresh_from_db()
    assert civ2.image == image
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.º 17
0
 def test_gets_creator_from_session(self):
     ai = AlgorithmImageFactory()
     riu = RawImageUploadSessionFactory()
     riu.image_set.add(ImageFactory(), ImageFactory())
     civ_sets = [{
         ComponentInterfaceValueFactory(image=image,
                                        interface=ai.algorithm.inputs.get())
     } for image in riu.image_set.all()]
     create_algorithm_jobs(algorithm_image=ai,
                           civ_sets=civ_sets,
                           creator=riu.creator)
     j = Job.objects.first()
     assert j.creator == riu.creator
Exemplo n.º 18
0
def test_image_file_create(client):
    user = UserFactory()
    ai = AlgorithmImageFactory()
    ai.algorithm.add_user(user)
    upload_session = RawImageUploadSessionFactory(creator=user,
                                                  algorithm_image=ai)

    response = get_view_for_user(
        viewname="api:upload-session-file-list",
        user=user,
        client=client,
        method=client.post,
        data={
            "upload_session": upload_session.api_url,
            "filename": "dummy.bin",
        },
        content_type="application/json",
    )
    assert response.status_code == 201

    image_file = RawImageFile.objects.get(pk=response.data.get("pk"))
    assert image_file.upload_session == upload_session

    upload_session = RawImageUploadSessionFactory()

    response = get_view_for_user(
        viewname="api:upload-session-file-list",
        user=user,
        client=client,
        method=client.post,
        data={
            "upload_session": upload_session.api_url,
            "filename": "dummy.bin",
        },
        content_type="application/json",
    )
    assert response.status_code == 400
Exemplo n.º 19
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.º 20
0
def test_image_file_post_permissions(client, is_active, expected_response):
    user = UserFactory(is_active=is_active)
    upload_session = RawImageUploadSessionFactory(creator=user)
    response = get_view_for_user(
        viewname="api:upload-session-file-list",
        user=user,
        client=client,
        method=client.post,
        data={
            "upload_session": upload_session.api_url,
            "filename": "dummy.bin",
        },
        content_type="application/json",
    )
    assert response.status_code == expected_response
Exemplo n.º 21
0
def test_raw_image_file_download(client, settings):
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )
    admin = UserFactory(is_staff=True)
    user = UserFactory()
    us = RawImageUploadSessionFactory(creator=user)
    path = Path(__file__).parent / "resources" / "dicom" / "1.dcm"

    f = StagedFileFactory(file__from_path=path)

    rif = RawImageFileFactory(upload_session=us, staged_file_id=f.file_id)

    response = get_view_for_user(
        client=client,
        user=user,
        viewname="admin:cases_rawimagefile_download",
        reverse_kwargs={"object_id": rif.pk},
        follow=True,
    )
    assert response.status_code == 200
    assert "not authorized to access this page" in response.rendered_content

    response = get_view_for_user(
        client=client,
        user=admin,
        viewname="admin:cases_rawimagefile_download",
        reverse_kwargs={"object_id": rif.pk},
    )

    assert response.status_code == 403

    admin.is_superuser = True
    admin.save()
    response = get_view_for_user(
        client=client,
        user=admin,
        viewname="admin:cases_rawimagefile_download",
        reverse_kwargs={"object_id": rif.pk},
    )

    assert response.status_code == 200

    with path.open("rb") as dcm:
        assert dcm.read() == response.content
Exemplo n.º 22
0
def test_upload_session_detail(client):
    u1, u2 = UserFactory(), UserFactory()
    us = RawImageUploadSessionFactory(creator=u1)

    response = get_view_for_user(
        viewname="api:upload-session-detail",
        reverse_kwargs={"pk": us.pk},
        client=client,
        user=u1,
    )
    assert response.status_code == 200

    response = get_view_for_user(
        viewname="api:upload-session-detail",
        reverse_kwargs={"pk": us.pk},
        client=client,
        user=u2,
    )
    assert response.status_code == 404
Exemplo n.º 23
0
def test_upload_session_list(client):
    u1, u2 = UserFactory(), UserFactory()
    RawImageUploadSessionFactory(creator=u1)

    response = get_view_for_user(
        viewname="api:upload-session-list",
        client=client,
        user=u1,
        content_type="application/json",
    )
    assert response.status_code == 200
    assert response.json()["count"] == 1

    response = get_view_for_user(
        viewname="api:upload-session-list",
        client=client,
        user=u2,
        content_type="application/json",
    )
    assert response.status_code == 200
    assert response.json()["count"] == 0
Exemplo n.º 24
0
def test_question_accepts_image_type_answers(client, settings):
    settings.task_eager_propagates = (True, )
    settings.task_always_eager = (True, )

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

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

    question = QuestionFactory(reader_study=rs,
                               answer_type=Question.AnswerType.BOOL)

    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 == 400
    assert (b"This question does not accept image type answers"
            in response.rendered_content)
Exemplo n.º 25
0
    def test_permission_required_views(self, client):
        ai = AlgorithmImageFactory(ready=True)
        s = RawImageUploadSessionFactory()
        u = UserFactory()
        j = AlgorithmJobFactory(algorithm_image=ai)
        p = AlgorithmPermissionRequestFactory(algorithm=ai.algorithm)

        VerificationFactory(user=u, is_verified=True)

        for view_name, kwargs, permission, obj, redirect in [
            ("create", {}, "algorithms.add_algorithm", None, None),
            (
                "detail",
                {"slug": ai.algorithm.slug},
                "view_algorithm",
                ai.algorithm,
                reverse(
                    "algorithms:permission-request-create",
                    kwargs={"slug": ai.algorithm.slug},
                ),
            ),
            (
                "update",
                {"slug": ai.algorithm.slug},
                "change_algorithm",
                ai.algorithm,
                None,
            ),
            (
                "image-create",
                {"slug": ai.algorithm.slug},
                "change_algorithm",
                ai.algorithm,
                None,
            ),
            (
                "image-detail",
                {"slug": ai.algorithm.slug, "pk": ai.pk},
                "view_algorithmimage",
                ai,
                None,
            ),
            (
                "image-update",
                {"slug": ai.algorithm.slug, "pk": ai.pk},
                "change_algorithmimage",
                ai,
                None,
            ),
            (
                "execution-session-create-batch",
                {"slug": ai.algorithm.slug},
                "execute_algorithm",
                ai.algorithm,
                None,
            ),
            (
                "execution-session-create",
                {"slug": ai.algorithm.slug},
                "execute_algorithm",
                ai.algorithm,
                None,
            ),
            (
                "execution-session-detail",
                {"slug": ai.algorithm.slug, "pk": s.pk},
                "view_rawimageuploadsession",
                s,
                None,
            ),
            (
                "job-experiment-detail",
                {"slug": ai.algorithm.slug, "pk": j.pk},
                "view_job",
                j,
                None,
            ),
            (
                "job-detail",
                {"slug": ai.algorithm.slug, "pk": j.pk},
                "view_job",
                j,
                None,
            ),
            (
                "job-update",
                {"slug": ai.algorithm.slug, "pk": j.pk},
                "change_job",
                j,
                None,
            ),
            (
                "job-viewers-update",
                {"slug": ai.algorithm.slug, "pk": j.pk},
                "change_job",
                j,
                None,
            ),
            (
                "editors-update",
                {"slug": ai.algorithm.slug},
                "change_algorithm",
                ai.algorithm,
                None,
            ),
            (
                "users-update",
                {"slug": ai.algorithm.slug},
                "change_algorithm",
                ai.algorithm,
                None,
            ),
            (
                "permission-request-update",
                {"slug": ai.algorithm.slug, "pk": p.pk},
                "change_algorithm",
                ai.algorithm,
                None,
            ),
        ]:

            def _get_view():
                return get_view_for_user(
                    client=client,
                    viewname=f"algorithms:{view_name}",
                    reverse_kwargs=kwargs,
                    user=u,
                )

            response = _get_view()
            if redirect is not None:
                assert response.status_code == 302
                assert response.url == redirect
            else:
                assert response.status_code == 403

            assign_perm(permission, u, obj)

            response = _get_view()
            assert response.status_code == 200

            remove_perm(permission, u, obj)