示例#1
0
 def test_job_group_created(self):
     j = AlgorithmJobFactory()
     assert j.viewers is not None
     assert j.viewers.name.startswith("algorithms_job_")
     assert j.viewers.name.endswith("_viewers")
示例#2
0
 def test_creator_in_viewers_group(self):
     j = AlgorithmJobFactory()
     assert {*j.viewers.user_set.all()} == {j.creator}
def test_algorithm_jobs_list_view(client):
    # This view is a bit special, everyone should be able to
    # view it, but the results should be filtered

    alg_set = TwoAlgorithms()

    extra_user1, extra_user2 = UserFactory(), UserFactory()

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

    j1, j2 = (
        AlgorithmJobFactory(
            algorithm_image__algorithm=alg_set.alg1,
            creator=extra_user1,
            status=Job.SUCCESS,
        ),
        AlgorithmJobFactory(
            algorithm_image__algorithm=alg_set.alg2,
            creator=extra_user2,
            status=Job.SUCCESS,
        ),
    )

    all_jobs = {j1, j2}

    tests = (
        (None, alg_set.alg1, 200, set()),
        (None, alg_set.alg2, 200, set()),
        (alg_set.creator, alg_set.alg1, 200, set()),
        (alg_set.creator, alg_set.alg2, 200, set()),
        (alg_set.editor1, alg_set.alg1, 200, {j1}),
        (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, {j2}),
        (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, {j1}),
        (extra_user1, alg_set.alg2, 200, set()),
        (extra_user2, alg_set.alg1, 200, set()),
        (extra_user2, alg_set.alg2, 200, {j2}),
    )

    for test in tests:
        response = get_view_for_user(
            viewname="algorithms:jobs-list",
            reverse_kwargs={"slug": test[1].slug},
            client=client,
            user=test[0],
            data={
                "length": 50,
                "draw": 1
            },
            **{"HTTP_X_REQUESTED_WITH": "XMLHttpRequest"},
        )
        assert response.status_code == test[2]

        # Check that the results are filtered
        if response.status_code == 200:
            expected_jobs = test[3]
            excluded_jobs = all_jobs - expected_jobs
            data = response.json()["data"]
            assert all(str(j.pk) in str(data) for j in expected_jobs)
            assert all(str(j.pk) not in str(data) for j in excluded_jobs)
示例#4
0
def test_average_duration():
    alg = AlgorithmFactory()
    now = timezone.now()

    assert alg.average_duration is None

    j = AlgorithmJobFactory(algorithm_image__algorithm=alg)

    j.started_at = now - timedelta(minutes=5)
    j.completed_at = now
    j.status = j.SUCCESS
    j.save()

    assert alg.average_duration == timedelta(minutes=5)

    # Unsuccessful jobs should not count
    j = AlgorithmJobFactory(algorithm_image__algorithm=alg)
    j.started_at = now - timedelta(minutes=10)
    j.completed_at = now
    j.status = j.FAILURE
    j.save()

    assert alg.average_duration == timedelta(minutes=5)

    # Nor should jobs for other algorithms
    j = AlgorithmJobFactory()
    j.started_at = now - timedelta(minutes=15)
    j.completed_at = now
    j.status = j.SUCCESS
    j.save()

    assert alg.average_duration == timedelta(minutes=5)
def test_algorithm_jobs_list_view(client):
    editor = UserFactory()

    alg = AlgorithmFactory(public=True)
    alg.add_editor(editor)
    im = AlgorithmImageFactory(algorithm=alg)
    for x in range(50):
        created = timezone.now() - datetime.timedelta(days=x + 365)
        job = AlgorithmJobFactory(algorithm_image=im, status=Job.SUCCESS)
        job.created = created
        job.save()
        job.viewer_groups.add(alg.editors_group)

    response = get_view_for_user(
        viewname="algorithms:job-list",
        reverse_kwargs={"slug": slugify(alg.slug)},
        client=client,
        user=editor,
        method=client.get,
        follow=True,
    )

    assert response.status_code == 200

    response = get_view_for_user(
        viewname="algorithms:job-list",
        reverse_kwargs={"slug": slugify(alg.slug)},
        client=client,
        user=editor,
        method=client.get,
        follow=True,
        data={
            "length": 10,
            "draw": 1,
            "order[0][dir]": "desc",
            "order[0][column]": 0,
        },
        **{"HTTP_X_REQUESTED_WITH": "XMLHttpRequest"},
    )

    resp = response.json()
    assert resp["recordsTotal"] == 50
    assert len(resp["data"]) == 10

    response = get_view_for_user(
        viewname="algorithms:job-list",
        reverse_kwargs={"slug": slugify(alg.slug)},
        client=client,
        user=editor,
        method=client.get,
        follow=True,
        data={
            "length": 50,
            "draw": 1,
            "order[0][dir]": "desc",
            "order[0][column]": 0,
        },
        **{"HTTP_X_REQUESTED_WITH": "XMLHttpRequest"},
    )

    resp = response.json()
    assert resp["recordsTotal"] == 50
    assert len(resp["data"]) == 50

    response = get_view_for_user(
        viewname="algorithms:job-list",
        reverse_kwargs={"slug": slugify(alg.slug)},
        client=client,
        user=editor,
        method=client.get,
        follow=True,
        data={
            "length": 50,
            "draw": 1,
            "order[0][dir]": "asc",
            "order[0][column]": 0,
        },
        **{"HTTP_X_REQUESTED_WITH": "XMLHttpRequest"},
    )

    resp_new = response.json()
    assert resp_new["recordsTotal"] == 50
    assert resp_new["data"] == resp["data"][::-1]

    response = get_view_for_user(
        viewname="algorithms:job-list",
        reverse_kwargs={"slug": slugify(alg.slug)},
        client=client,
        user=editor,
        method=client.get,
        follow=True,
        data={
            "length": 50,
            "draw": 1,
            "search[value]": job.creator.username,
            "order[0][column]": 0,
        },
        **{"HTTP_X_REQUESTED_WITH": "XMLHttpRequest"},
    )

    resp = response.json()
    assert resp["recordsTotal"] == 50
    assert resp["recordsFiltered"] == 1
    assert len(resp["data"]) == 1
    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",
                {
                    "slug": ai.algorithm.slug
                },
                "execute_algorithm",
                ai.algorithm,
                None,
            ),
            (
                "execution-session-create-new",
                {
                    "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)
示例#7
0
def test_outputs_are_set():
    j = AlgorithmJobFactory()
    j.create_result(result={"dsaf": 35421})

    outputs = j.outputs.all()
    assert len(outputs) == 1
    assert outputs[0].interface.kind == InterfaceKindChoices.JSON
    assert outputs[0].value == {"dsaf": 35421}

    job = AlgorithmJobFactory()
    job.create_result(result={"foo": 13.37})

    outputs = job.outputs.all()
    assert len(outputs) == 1
    assert outputs[0].interface.kind == InterfaceKindChoices.JSON
    assert outputs[0].value == {"foo": 13.37}

    job.create_result(result={"bar": 13.37})
    job.refresh_from_db()

    outputs = job.outputs.all()
    assert len(outputs) == 1
    assert outputs[0].interface.kind == InterfaceKindChoices.JSON
    assert outputs[0].value == {"bar": 13.37}

    # the original job should not be modified
    j.refresh_from_db()
    outputs = j.outputs.all()
    assert len(outputs) == 1
    assert outputs[0].interface.kind == InterfaceKindChoices.JSON
    assert outputs[0].value == {"dsaf": 35421}
def test_user_can_download_images(client, reverse):
    alg_set = TwoAlgorithms()

    j1_creator, j2_creator = UserFactory(), UserFactory()

    alg1_job = AlgorithmJobFactory(algorithm_image__algorithm=alg_set.alg1,
                                   creator=j1_creator)
    alg2_job = AlgorithmJobFactory(algorithm_image__algorithm=alg_set.alg2,
                                   creator=j2_creator)

    alg1_job.viewer_groups.add(alg_set.alg1.editors_group)
    alg2_job.viewer_groups.add(alg_set.alg2.editors_group)

    iv1, iv2, iv3, iv4 = (
        ComponentInterfaceValueFactory(image=ImageFactory()),
        ComponentInterfaceValueFactory(image=ImageFactory()),
        ComponentInterfaceValueFactory(image=ImageFactory()),
        ComponentInterfaceValueFactory(image=ImageFactory()),
    )

    if reverse:
        for im in [iv1, iv2, iv3, iv4]:
            im.algorithms_jobs_as_output.add(alg1_job, alg2_job)
        for im in [iv3, iv4]:
            im.algorithms_jobs_as_output.remove(alg1_job, alg2_job)
        for im in [iv1, iv2]:
            im.algorithms_jobs_as_output.remove(alg2_job)
    else:
        # Test that adding images works
        alg1_job.outputs.add(iv1, iv2, iv3, iv4)
        # Test that removing images works
        alg1_job.outputs.remove(iv3, iv4)

    tests = (
        (None, 200, []),
        (alg_set.creator, 200, []),
        (
            alg_set.editor1,
            200,
            [
                *[i.image.pk for i in alg1_job.inputs.all()],
                iv1.image.pk,
                iv2.image.pk,
            ],
        ),
        (alg_set.user1, 200, []),
        (
            j1_creator,
            200,
            [
                *[i.image.pk for i in alg1_job.inputs.all()],
                iv1.image.pk,
                iv2.image.pk,
            ],
        ),
        (
            alg_set.editor2,
            200,
            [i.image.pk for i in alg2_job.inputs.all()],
        ),
        (alg_set.user2, 200, []),
        (j2_creator, 200, [i.image.pk for i in alg2_job.inputs.all()]),
        (alg_set.u, 200, []),
    )

    for test in tests:
        response = get_view_for_user(
            viewname="api:image-list",
            client=client,
            user=test[0],
            content_type="application/json",
        )
        assert response.status_code == test[1]

        assert response.json()["count"] == len(test[2])

        pks = {obj["pk"] for obj in response.json()["results"]}
        assert {str(pk) for pk in test[2]} == pks

    # Test clearing
    if reverse:
        iv1.algorithms_jobs_as_output.clear()
        iv2.algorithms_jobs_as_output.clear()
    else:
        alg1_job.outputs.clear()

    response = get_view_for_user(
        viewname="api:image-list",
        client=client,
        user=j1_creator,
        content_type="application/json",
    )
    assert response.status_code == 200
    assert response.json()["count"] == 1
示例#9
0
 def test_viewer_group_in_m2m(self):
     j = AlgorithmJobFactory()
     assert {*j.viewer_groups.all()} == {j.viewers}
def test_user_can_download_input_images(client, reverse):
    alg_set = TwoAlgorithms()

    j1_creator, j2_creator = UserFactory(), UserFactory()

    alg1_job = AlgorithmJobFactory(
        algorithm_image__algorithm=alg_set.alg1, creator=j1_creator
    )
    alg2_job = AlgorithmJobFactory(
        algorithm_image__algorithm=alg_set.alg2, creator=j2_creator
    )

    iv1, iv2, iv3, iv4 = (
        ComponentInterfaceValueFactory(image=ImageFactory()),
        ComponentInterfaceValueFactory(image=ImageFactory()),
        ComponentInterfaceValueFactory(image=ImageFactory()),
        ComponentInterfaceValueFactory(image=ImageFactory()),
    )

    alg1_origin_input = [i.image.pk for i in alg1_job.inputs.all()]
    alg2_origin_input = [i.image.pk for i in alg2_job.inputs.all()]

    if reverse:
        for iv in [iv1, iv2, iv3, iv4]:
            iv.algorithms_jobs_as_input.add(alg1_job, alg2_job)
        for iv in [iv3, iv4]:
            iv.algorithms_jobs_as_input.remove(alg1_job, alg2_job)
        for iv in [iv1, iv2]:
            iv.algorithms_jobs_as_input.remove(alg2_job)
    else:
        # Test that adding images works
        alg1_job.inputs.add(iv1, iv2, iv3, iv4)
        # Test that removing images works
        alg1_job.inputs.remove(iv3, iv4)

    tests = (
        (None, 401, []),
        (alg_set.creator, 200, []),
        (
            alg_set.editor1,
            200,
            [*alg1_origin_input, iv1.image.pk, iv2.image.pk],
        ),
        (alg_set.user1, 200, []),
        (j1_creator, 200, [*alg1_origin_input, iv1.image.pk, iv2.image.pk],),
        (alg_set.editor2, 200, alg2_origin_input),
        (alg_set.user2, 200, []),
        (j2_creator, 200, alg2_origin_input),
        (alg_set.u, 200, []),
    )

    for test in tests:
        response = get_view_for_user(
            viewname="api:image-list",
            client=client,
            user=test[0],
            content_type="application/json",
        )
        assert response.status_code == test[1]

        if test[1] != 401:
            # We provided auth details and get a response
            assert response.json()["count"] == len(test[2])

            pks = [obj["pk"] for obj in response.json()["results"]]

            for pk in test[2]:
                assert str(pk) in pks

    # Test clearing
    if reverse:
        iv1.algorithms_jobs_as_input.clear()
        iv2.algorithms_jobs_as_input.clear()
    else:
        alg1_job.inputs.clear()

    response = get_view_for_user(
        viewname="api:image-list",
        client=client,
        user=j1_creator,
        content_type="application/json",
    )
    assert response.status_code == 200

    if reverse:
        assert response.json()["count"] == 1
    else:
        assert response.json()["count"] == 0
示例#11
0
    def test_group_deletion_reverse(self):
        j = AlgorithmJobFactory()
        g = j.viewers

        with pytest.raises(ProtectedError):
            g.delete()
def test_outputs_are_set():
    j = AlgorithmJobFactory()
    j.create_result(result={"dsaf": 35421})

    outputs = j.outputs.all()
    assert len(outputs) == 1
    assert outputs[0].interface.kind == InterfaceKindChoices.JSON
    assert outputs[0].value == {"dsaf": 35421}

    job = AlgorithmJobFactory()
    job.create_result(result={"foo": 13.37})

    outputs = job.outputs.all()
    assert len(outputs) == 1
    assert outputs[0].interface.kind == InterfaceKindChoices.JSON
    assert outputs[0].value == {"foo": 13.37}

    job.create_result(result={"bar": 13.37})
    job.refresh_from_db()

    outputs = job.outputs.all()
    assert len(outputs) == 1
    assert outputs[0].interface.kind == InterfaceKindChoices.JSON
    assert outputs[0].value == {"bar": 13.37}

    # the original job should not be modified
    j.refresh_from_db()
    outputs = j.outputs.all()
    assert len(outputs) == 1
    assert outputs[0].interface.kind == InterfaceKindChoices.JSON
    assert outputs[0].value == {"dsaf": 35421}

    job = AlgorithmJobFactory()
    job.algorithm_image.algorithm.result_template = (
        "foo score: {{result_dict.foo}}")

    assert job.rendered_result_text == ""
    job.create_result(result={"foo": 13.37})
    assert job.rendered_result_text == "<p>foo score: 13.37</p>"

    job.algorithm_image.algorithm.result_template = "{% for key, value in dict.metrics.items() -%}{{ key }}  {{ value }}{% endfor %}"
    assert job.rendered_result_text == "Jinja template is invalid"

    job.algorithm_image.algorithm.result_template = "{{ str.__add__('test')}}"
    assert job.rendered_result_text == "Jinja template is invalid"
示例#13
0
def test_get_metrics():
    AlgorithmJobFactory()
    EvaluationFactory()
    SessionFactory()
    s = UploadSessionFactory()
    s.status = s.REQUEUED
    s.save()

    # Note, this is the format expected by CloudWatch,
    # consult the API when changing this
    result = _get_metrics()

    assert result == [
        {
            "Namespace":
            "testserver/algorithms",
            "MetricData": [
                {
                    "MetricName": "JobsQueued",
                    "Value": 1,
                    "Unit": "Count"
                },
                {
                    "MetricName": "JobsStarted",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "JobsReQueued",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "JobsFailed",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "JobsSucceeded",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "JobsCancelled",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "JobsProvisioning",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "JobsProvisioned",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "JobsExecuting",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "JobsExecuted",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "JobsParsingOutputs",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "JobsExecutingAlgorithm",
                    "Value": 0,
                    "Unit": "Count",
                },
            ],
        },
        {
            "Namespace":
            "testserver/evaluation",
            "MetricData": [
                {
                    "MetricName": "EvaluationsQueued",
                    "Value": 1,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsStarted",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsReQueued",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsFailed",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsSucceeded",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsCancelled",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsProvisioning",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsProvisioned",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsExecuting",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsExecuted",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsParsingOutputs",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "EvaluationsExecutingAlgorithm",
                    "Value": 0,
                    "Unit": "Count",
                },
            ],
        },
        {
            "Namespace":
            "testserver/workstations",
            "MetricData": [
                {
                    "MetricName": "SessionsQueued",
                    "Value": 1,
                    "Unit": "Count"
                },
                {
                    "MetricName": "SessionsStarted",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "SessionsRunning",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "SessionsFailed",
                    "Value": 0,
                    "Unit": "Count"
                },
                {
                    "MetricName": "SessionsStopped",
                    "Value": 0,
                    "Unit": "Count"
                },
            ],
        },
        {
            "Namespace":
            "testserver/cases",
            "MetricData": [
                {
                    "MetricName": "RawImageUploadSessionsQueued",
                    "Value": 1,
                    "Unit": "Count",
                },
                {
                    "MetricName": "RawImageUploadSessionsStarted",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "RawImageUploadSessionsReQueued",
                    "Value": 1,
                    "Unit": "Count",
                },
                {
                    "MetricName": "RawImageUploadSessionsFailed",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "RawImageUploadSessionsSucceeded",
                    "Value": 0,
                    "Unit": "Count",
                },
                {
                    "MetricName": "RawImageUploadSessionsCancelled",
                    "Value": 0,
                    "Unit": "Count",
                },
            ],
        },
    ]
示例#14
0
def test_algorithm_title_on_job_serializer(rf):
    job = AlgorithmJobFactory()
    serializer = HyperlinkedJobSerializer(job,
                                          context={"request": rf.get("/foo")})
    assert (serializer.data["algorithm_title"] ==
            job.algorithm_image.algorithm.title)
示例#15
0
def test_workstation_query(settings):
    image, overlay = ImageFactory(), ImageFactory()
    reader_study = ReaderStudyFactory(
        workstation_config=WorkstationConfigFactory())
    algorithm_job = AlgorithmJobFactory()
    config = WorkstationConfigFactory()
    archive_item = ArchiveItemFactory()

    qs = workstation_query(image=image)
    assert "&" not in qs
    assert f"{settings.WORKSTATIONS_BASE_IMAGE_QUERY_PARAM}={image.pk}" in qs
    assert (f"{settings.WORKSTATIONS_OVERLAY_QUERY_PARAM}={overlay.pk}"
            not in qs)
    assert f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}={config.pk}" not in qs

    qs = workstation_query(image=image, overlay=overlay)
    assert "&" in qs
    assert f"{settings.WORKSTATIONS_BASE_IMAGE_QUERY_PARAM}={image.pk}" in qs
    assert f"{settings.WORKSTATIONS_OVERLAY_QUERY_PARAM}={overlay.pk}" in qs
    assert f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}={config.pk}" not in qs

    qs = workstation_query(image=image, config=config)
    assert "&" in qs
    assert f"{settings.WORKSTATIONS_BASE_IMAGE_QUERY_PARAM}={image.pk}" in qs
    assert (f"{settings.WORKSTATIONS_OVERLAY_QUERY_PARAM}={overlay.pk}"
            not in qs)
    assert f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}={config.pk}" in qs

    qs = workstation_query(reader_study=reader_study)
    assert "&" in qs
    assert (
        f"{settings.WORKSTATIONS_READY_STUDY_QUERY_PARAM}={reader_study.pk}"
        in qs)
    assert (
        f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}={reader_study.workstation_config.pk}"
        in qs)
    assert f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}={config.pk}" not in qs

    qs = workstation_query(reader_study=reader_study, config=config)
    assert "&" in qs
    assert (
        f"{settings.WORKSTATIONS_READY_STUDY_QUERY_PARAM}={reader_study.pk}"
        in qs)
    assert (
        f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}={reader_study.workstation_config.pk}"
        not in qs)
    assert f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}={config.pk}" in qs

    reader_study.workstation_config = None

    qs = workstation_query(reader_study=reader_study)
    assert "&" not in qs
    assert (
        f"{settings.WORKSTATIONS_READY_STUDY_QUERY_PARAM}={reader_study.pk}"
        in qs)
    assert f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}" not in qs

    qs = workstation_query(algorithm_job=algorithm_job)
    assert "&" not in qs
    assert (
        f"{settings.WORKSTATIONS_ALGORITHM_JOB_QUERY_PARAM}={algorithm_job.pk}"
        in qs)

    qs = workstation_query(algorithm_job=algorithm_job, config=config)
    assert "&" in qs
    assert (
        f"{settings.WORKSTATIONS_ALGORITHM_JOB_QUERY_PARAM}={algorithm_job.pk}"
        in qs)
    assert f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}={config.pk}" in qs

    qs = workstation_query(archive_item=archive_item, config=config)
    assert "&" in qs
    assert (
        f"{settings.WORKSTATIONS_ARCHIVE_ITEM_QUERY_PARAM}={archive_item.pk}"
        in qs)
    assert f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}={config.pk}" in qs

    qs = workstation_query(archive_item=archive_item)
    assert "&" not in qs
    assert (
        f"{settings.WORKSTATIONS_ARCHIVE_ITEM_QUERY_PARAM}={archive_item.pk}"
        in qs)
    assert f"{settings.WORKSTATIONS_CONFIG_QUERY_PARAM}" not in qs