def test_changing_archive_updates_permissions(): ai = ArchiveItemFactory() im = ImageFactory() civ = ComponentInterfaceValueFactory(image=im) with capture_on_commit_callbacks(execute=True): ai.values.set([civ]) assert get_groups_with_set_perms(im) == { ai.archive.editors_group: {"view_image"}, ai.archive.uploaders_group: {"view_image"}, ai.archive.users_group: {"view_image"}, } a2 = ArchiveFactory() ai.archive = a2 with capture_on_commit_callbacks(execute=True): ai.save() assert get_groups_with_set_perms(im) == { a2.editors_group: {"view_image"}, a2.uploaders_group: {"view_image"}, a2.users_group: {"view_image"}, }
def test_api_archive_item_interface_type_update(client, settings): # Override the celery settings settings.task_eager_propagates = (True, ) settings.task_always_eager = (True, ) archive = ArchiveFactory() editor = UserFactory() archive.add_editor(editor) item = ArchiveItemFactory(archive=archive) session, _ = create_raw_upload_image_session(images=["image10x10x10.mha"], user=editor) session.refresh_from_db() im = session.image_set.get() ci = ComponentInterfaceFactory( kind=InterfaceKind.InterfaceKindChoices.IMAGE) civ = ComponentInterfaceValueFactory(interface=ci, image=im) item.values.add(civ) civ.image.update_viewer_groups_permissions() assert item.values.count() == 1 # change interface type from generic medical image to generic overlay # for the already uploaded image with capture_on_commit_callbacks(execute=True): response = get_view_for_user( viewname="api:archives-item-detail", reverse_kwargs={"pk": item.pk}, data={ "values": [{ "interface": "generic-overlay", "image": im.api_url }] }, user=editor, client=client, method=client.patch, content_type="application/json", HTTP_X_FORWARDED_PROTO="https", ) assert response.status_code == 200 assert response.json()["pk"] == str(item.pk) item.refresh_from_db() # check that the old item was removed and a new one was added with the same # image but the new interface type assert item.values.count() == 1 new_civ = item.values.get() assert new_civ.interface.slug == "generic-overlay" assert new_civ.image == im assert new_civ != civ
def test_api_archive_item_update_permissions(client, settings, add_to_group, status): # Override the celery settings settings.task_eager_propagates = (True, ) settings.task_always_eager = (True, ) archive = ArchiveFactory() user = UserFactory() item = ArchiveItemFactory(archive=archive) if add_to_group: add_to_group(archive, user) ci = ComponentInterfaceFactory( kind=InterfaceKind.InterfaceKindChoices.BOOL) with capture_on_commit_callbacks(execute=True): response = get_view_for_user( viewname="api:archives-item-detail", reverse_kwargs={"pk": item.pk}, data={"values": [{ "interface": ci.slug, "value": True }]}, user=user, client=client, method=client.patch, content_type="application/json", HTTP_X_FORWARDED_PROTO="https", ) assert response.status_code == status
def test_user_upload_to_archive_item_without_interface(client, settings): # Override the celery settings settings.task_eager_propagates = (True, ) settings.task_always_eager = (True, ) user = UserFactory() archive = ArchiveFactory() archive.add_editor(user=user) ci = ComponentInterface.objects.filter(slug="generic-overlay").get() civ = ComponentInterfaceValueFactory(interface=ci) item = ArchiveItemFactory(archive=archive) item.values.add(civ) assert item.values.count() == 1 upload = create_upload_from_file( file_path=Path(__file__).parent / "resources" / "image10x10x10.mha", creator=user, ) with capture_on_commit_callbacks(execute=True): response = get_view_for_user( viewname="api:upload-session-list", user=user, client=client, method=client.post, content_type="application/json", data={ "uploads": [upload.api_url], "archive_item": item.pk }, HTTP_X_FORWARDED_PROTO="https", ) assert response.status_code == 400 assert ("An interface needs to be defined to upload to an archive item." in response.json()["non_field_errors"])
def test_job_permissions_for_challenge(self): ai = AlgorithmImageFactory(ready=True) archive = ArchiveFactory() evaluation = EvaluationFactory(submission__phase__archive=archive, submission__algorithm_image=ai) # Fake an image upload via a session u = UserFactory() s = UploadSessionFactory(creator=u) im = ImageFactory() s.image_set.set([im]) civ = ComponentInterfaceValueFactory(image=im) archive_item = ArchiveItemFactory(archive=archive) with capture_on_commit_callbacks(execute=True): archive_item.values.add(civ) create_algorithm_jobs_for_evaluation(evaluation_pk=evaluation.pk) job = Job.objects.get() # Only the challenge admins and job viewers should be able to view the # job. NOTE: NOT THE ALGORITHM EDITORS, they are the participants # to the challenge and should not be able to see the test data assert get_groups_with_set_perms(job) == { evaluation.submission.phase.challenge.admins_group: {"view_job"}, job.viewers: {"view_job"}, } # No-one should be able to change the job assert (get_users_with_perms(job, attach_perms=True, with_group_users=False) == {}) # No-one should be in the viewers group assert {*job.viewers.user_set.all()} == set()
def test_view_permission_when_reused(in_archive, in_rs, in_job): """When an image is reused it should have view_image set correctly""" im = ImageFactory() job = AlgorithmJobFactory() rs = ReaderStudyFactory(use_display_sets=False) archive = ArchiveFactory() if in_archive: civ = ComponentInterfaceValueFactory(image=im) ai = ArchiveItemFactory(archive=archive) with capture_on_commit_callbacks(execute=True): ai.values.add(civ) if in_rs: rs.images.add(im) if in_job: civ = ComponentInterfaceValueFactory(image=im) job.inputs.add(civ) assert ("view_image" in get_perms(archive.editors_group, im)) is in_archive assert ("view_image" in get_perms(archive.uploaders_group, im)) is in_archive assert ("view_image" in get_perms(archive.users_group, im)) is in_archive assert ("view_image" in get_perms(rs.editors_group, im)) is in_rs assert ("view_image" in get_perms(rs.readers_group, im)) is in_rs for g in job.viewer_groups.all(): assert ("view_image" in get_perms(g, im)) is in_job
def test_civ_file_download(client): """Only viewers of the job should be allowed to download result files.""" detection_interface = ComponentInterface( store_in_database=False, relative_path="detection_results.json", slug="detection-results", title="Detection Results", kind=ComponentInterface.Kind.ANY, ) detection_interface.save() output_civ = ComponentInterfaceValue.objects.create( interface=detection_interface) detection = { "detected points": [{ "type": "Point", "start": [0, 1, 2], "end": [3, 4, 5] }] } output_civ.file.save( "detection_results.json", ContentFile( bytes(json.dumps(detection, ensure_ascii=True, indent=2), "utf-8")), ) user1, user2 = UserFactory(), UserFactory() def has_correct_access(user_allowed, user_denied, url): tests = [(403, None), (302, user_allowed), (403, user_denied)] for test in tests: response = get_view_for_user(url=url, client=client, user=test[1]) assert response.status_code == test[0] # test algorithm job = AlgorithmJobFactory(creator=user1) job.algorithm_image.algorithm.outputs.add(detection_interface) job.outputs.add(output_civ) has_correct_access(user1, user2, job.outputs.first().file.url) job.outputs.remove(output_civ) # test evaluation evaluation = EvaluationFactory() evaluation.output_interfaces.add(detection_interface) evaluation.outputs.add(output_civ) assign_perm("view_evaluation", user1, evaluation) has_correct_access(user1, user2, evaluation.outputs.first().file.url) evaluation.outputs.remove(output_civ) # test archive archive = ArchiveFactory() archive_item = ArchiveItemFactory(archive=archive) archive_item.values.add(output_civ) archive.add_editor(user1) has_correct_access(user1, user2, archive_item.values.first().file.url) archive.remove_editor(user1) archive.add_user(user1) has_correct_access(user1, user2, archive_item.values.first().file.url) archive.remove_user(user1)
def test_api_archive_item_retrieve_permissions(client): archive = ArchiveFactory() editor, user = UserFactory(), UserFactory() archive.add_editor(editor) i1 = ArchiveItemFactory(archive=archive) # editor can retrieve archive item response = get_view_for_user( viewname="api:archives-item-detail", reverse_kwargs={"pk": i1.pk}, user=editor, client=client, ) assert response.status_code == 200 assert response.json()["pk"] == str(i1.pk) # user cannot retrieve archive item response = get_view_for_user( viewname="api:archives-item-detail", reverse_kwargs={"pk": i1.pk}, user=user, client=client, ) assert response.status_code == 404 # add user to archive archive.add_user(user) response = get_view_for_user( viewname="api:archives-item-detail", reverse_kwargs={"pk": i1.pk}, user=user, client=client, ) assert response.status_code == 200 assert response.json()["pk"] == str(i1.pk)
def test_deleting_archive_item_removes_permissions(): ai1, ai2 = ArchiveItemFactory.create_batch(2) im = ImageFactory() civ = ComponentInterfaceValueFactory(image=im) with capture_on_commit_callbacks(execute=True): ai1.values.set([civ]) ai2.values.set([civ]) assert get_groups_with_set_perms(im) == { ai1.archive.editors_group: {"view_image"}, ai1.archive.uploaders_group: {"view_image"}, ai1.archive.users_group: {"view_image"}, ai2.archive.editors_group: {"view_image"}, ai2.archive.uploaders_group: {"view_image"}, ai2.archive.users_group: {"view_image"}, } with capture_on_commit_callbacks(execute=True): ai1.delete() assert get_groups_with_set_perms(im) == { ai2.archive.editors_group: {"view_image"}, ai2.archive.uploaders_group: {"view_image"}, ai2.archive.users_group: {"view_image"}, }
def setUp(self): interface = ComponentInterface.objects.get( slug="generic-medical-image") archive = ArchiveFactory() ais = ArchiveItemFactory.create_batch(2) archive.items.set(ais) input_civs = ComponentInterfaceValueFactory.create_batch( 2, interface=interface) output_civs = ComponentInterfaceValueFactory.create_batch( 2, interface=interface) for ai, civ in zip(ais, input_civs): ai.values.set([civ]) alg = AlgorithmImageFactory() submission = SubmissionFactory(algorithm_image=alg) submission.phase.archive = archive submission.phase.save() submission.phase.algorithm_inputs.set([interface]) jobs = [] for inpt, output in zip(input_civs, output_civs): j = AlgorithmJobFactory(status=Job.SUCCESS, algorithm_image=alg) j.inputs.set([inpt]) j.outputs.set([output]) jobs.append(j) self.evaluation = EvaluationFactory( submission=submission, status=Evaluation.EXECUTING_PREREQUISITES) self.jobs = jobs self.output_civs = output_civs
def test_user_upload_to_archive_item_with_new_interface(client, settings): # Override the celery settings settings.task_eager_propagates = (True, ) settings.task_always_eager = (True, ) user = UserFactory() archive = ArchiveFactory() archive.add_editor(user=user) ci = ComponentInterfaceFactory(kind=ComponentInterface.Kind.STRING, title="Test") civ = ComponentInterfaceValueFactory(interface=ci) item = ArchiveItemFactory(archive=archive) item.values.add(civ) assert item.values.count() == 1 upload = create_upload_from_file( file_path=Path(__file__).parent / "resources" / "image10x10x10.mha", creator=user, ) with capture_on_commit_callbacks(execute=True): response = get_view_for_user( viewname="api:upload-session-list", user=user, client=client, method=client.post, content_type="application/json", data={ "uploads": [upload.api_url], "archive_item": item.pk, "interface": "generic-overlay", }, HTTP_X_FORWARDED_PROTO="https", ) assert response.status_code == 201 upload_session = response.json() assert upload_session["uploads"] == [upload.api_url] item.refresh_from_db() assert item.values.count() == 2 assert "generic-overlay" in [ item.interface.slug for item in item.values.all() ]
def test_user_upload_to_archive_item_with_existing_interface(client, settings): # Override the celery settings settings.task_eager_propagates = (True, ) settings.task_always_eager = (True, ) user = UserFactory() archive = ArchiveFactory() archive.add_editor(user=user) ci = ComponentInterface.objects.filter(slug="generic-overlay").get() civ = ComponentInterfaceValueFactory(interface=ci) item = ArchiveItemFactory(archive=archive) item.values.add(civ) assert item.values.count() == 1 # upload another generic-overlay to the same item upload = create_upload_from_file( file_path=Path(__file__).parent / "resources" / "image10x10x10.mha", creator=user, ) with capture_on_commit_callbacks(execute=True): response = get_view_for_user( viewname="api:upload-session-list", user=user, client=client, method=client.post, content_type="application/json", data={ "uploads": [upload.api_url], "archive_item": item.pk, "interface": "generic-overlay", }, HTTP_X_FORWARDED_PROTO="https", ) assert response.status_code == 201 item.refresh_from_db() # check that there is only one civ with the generic-overlay interface assert item.values.filter(interface__slug="generic-overlay").count() == 1 # and that the previously added one is no longer associated with the item assert civ not in item.values.all()
def test_api_archive_item_list_is_filtered(client): a1, a2 = ArchiveFactory(), ArchiveFactory() a1_editor, user = UserFactory(), UserFactory() a1.add_editor(a1_editor) _, _ = (ArchiveItemFactory(archive=a1), ArchiveItemFactory(archive=a1)) i3 = ArchiveItemFactory(archive=a2) # user does not see any archive items response = get_view_for_user(viewname="api:archives-item-list", user=user, client=client) assert response.status_code == 200 assert response.json()["count"] == 0 # editor of archive 1 sees the items from archive 1, but not from archive 2 response = get_view_for_user(viewname="api:archives-item-list", user=a1_editor, client=client) assert response.status_code == 200 assert response.json()["count"] == 2 assert i3.id not in ( response.json()["results"][0]["pk"], response.json()["results"][1]["pk"], )
def setUp(self) -> None: self.method = MethodFactory(ready=True, phase__archive=ArchiveFactory()) self.algorithm_image = AlgorithmImageFactory() interface = ComponentInterfaceFactory() self.algorithm_image.algorithm.inputs.set([interface]) self.images = ImageFactory.create_batch(3) for image in self.images[:2]: civ = ComponentInterfaceValueFactory(image=image, interface=interface) ai = ArchiveItemFactory(archive=self.method.phase.archive) ai.values.add(civ)
def test_archive_item_permissions_signal(client, reverse): # noqa: C901 ai1, ai2 = ArchiveItemFactory.create_batch(2) im1, im2, im3, im4 = ImageFactory.create_batch(4) civ1, civ2, civ3, civ4 = ( ComponentInterfaceValueFactory(image=im1), ComponentInterfaceValueFactory(image=im2), ComponentInterfaceValueFactory(image=im3), ComponentInterfaceValueFactory(image=im4), ) with capture_on_commit_callbacks(execute=True): if reverse: for civ in [civ1, civ2, civ3, civ4]: civ.archive_items.add(ai1, ai2) for civ in [civ3, civ4]: civ.archive_items.remove(ai1, ai2) for civ in [civ1, civ2]: civ.archive_items.remove(ai2) else: # Test that adding images works ai1.values.add(civ1, civ2, civ3, civ4) # Test that removing images works ai1.values.remove(civ3, civ4) assert get_groups_with_set_perms(im1) == { ai1.archive.editors_group: {"view_image"}, ai1.archive.uploaders_group: {"view_image"}, ai1.archive.users_group: {"view_image"}, } assert get_groups_with_set_perms(im2) == { ai1.archive.editors_group: {"view_image"}, ai1.archive.uploaders_group: {"view_image"}, ai1.archive.users_group: {"view_image"}, } assert get_groups_with_set_perms(im3) == {} assert get_groups_with_set_perms(im4) == {} # Test clearing with capture_on_commit_callbacks(execute=True): if reverse: civ1.archive_items.clear() civ2.archive_items.clear() else: ai1.values.clear() assert get_groups_with_set_perms(im1) == {} assert get_groups_with_set_perms(im2) == {}
def test_api_archive_item_add_and_update_value(client, settings): # Override the celery settings settings.task_eager_propagates = (True, ) settings.task_always_eager = (True, ) archive = ArchiveFactory() editor = UserFactory() archive.add_editor(editor) item = ArchiveItemFactory(archive=archive) ci = ComponentInterfaceFactory( kind=InterfaceKind.InterfaceKindChoices.BOOL) # add civ with capture_on_commit_callbacks(execute=True): response = get_view_for_user( viewname="api:archives-item-detail", reverse_kwargs={"pk": item.pk}, data={"values": [{ "interface": ci.slug, "value": True }]}, user=editor, client=client, method=client.patch, content_type="application/json", HTTP_X_FORWARDED_PROTO="https", ) assert response.status_code == 200 assert response.json()["pk"] == str(item.pk) item.refresh_from_db() assert item.values.count() == 1 civ = item.values.get() assert civ.interface.slug == ci.slug assert civ.value # update civ with capture_on_commit_callbacks(execute=True): response = get_view_for_user( viewname="api:archives-item-detail", reverse_kwargs={"pk": item.pk}, data={"values": [{ "interface": ci.slug, "value": False }]}, user=editor, client=client, method=client.patch, content_type="application/json", HTTP_X_FORWARDED_PROTO="https", ) assert response.status_code == 200 assert response.json()["pk"] == str(item.pk) item.refresh_from_db() assert item.values.count() == 1 new_civ = item.values.get() assert new_civ.interface.slug == ci.slug assert new_civ != civ
def test_job_permissions_for_archive(self): ai = AlgorithmImageFactory(ready=True) archive = ArchiveFactory() # Fake an image upload via a session u = UserFactory() s = UploadSessionFactory(creator=u) im = ImageFactory() s.image_set.set([im]) civ = ComponentInterfaceValueFactory( image=im, interface=ai.algorithm.inputs.get()) archive_item = ArchiveItemFactory(archive=archive) with capture_on_commit_callbacks(execute=True): archive_item.values.add(civ) archive.algorithms.set([ai.algorithm]) create_algorithm_jobs_for_archive(archive_pks=[archive.pk]) job = Job.objects.get() # The archive editors, users and uploaders and job # viewers should be able to view the job. # NOTE: NOT THE ALGORITHM EDITORS, if they need # access the job can be shared with them. assert get_groups_with_set_perms(job) == { archive.editors_group: {"view_job"}, archive.users_group: {"view_job"}, archive.uploaders_group: {"view_job"}, job.viewers: {"view_job"}, } # No-one should be able to change the job assert (get_users_with_perms(job, attach_perms=True, with_group_users=False) == {}) # No-one should be in the viewers group assert {*job.viewers.user_set.all()} == set()
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
def test_archive_items_to_reader_study_update_form(client, settings): settings.task_eager_propagates = (True,) settings.task_always_eager = (True,) archive = ArchiveFactory() rs1, rs2 = ReaderStudyFactory(use_display_sets=True), ReaderStudyFactory( use_display_sets=False ) editor, reader = UserFactory(), UserFactory() archive.editors_group.user_set.add(editor) rs1.add_editor(editor) rs1.add_reader(reader) rs2.add_editor(editor) im1, im2, im3, im4 = ImageFactory.create_batch(4) overlay = ComponentInterface.objects.get(slug="generic-overlay") image = ComponentInterface.objects.get(slug="generic-medical-image") civ1, civ2, civ3, civ4 = ( ComponentInterfaceValueFactory(interface=image, image=im1), ComponentInterfaceValueFactory(interface=image, image=im2), ComponentInterfaceValueFactory(interface=overlay, image=im3), ComponentInterfaceValueFactory(interface=overlay, image=im4), ) ai1 = ArchiveItemFactory(archive=archive) ai2 = ArchiveItemFactory(archive=archive) ai1.values.add(civ1) ai2.values.add(civ2) assert rs1.display_sets.count() == 0 response = get_view_for_user( viewname="archives:items-reader-study-update", client=client, method=client.post, data={"items": [ai1.pk, ai2.pk], "reader_study": rs1.pk}, reverse_kwargs={"slug": archive.slug}, follow=True, user=reader, ) assert response.status_code == 403 assert rs1.display_sets.count() == 0 response = get_view_for_user( viewname="archives:items-reader-study-update", client=client, method=client.post, data={"items": [ai1.pk, ai2.pk], "reader_study": rs1.pk}, reverse_kwargs={"slug": archive.slug}, follow=True, user=editor, ) assert response.status_code == 200 assert rs1.display_sets.count() == 2 assert sorted( list(rs1.display_sets.values_list("values", flat=True)) ) == sorted([civ1.pk, civ2.pk]) assert rs2.display_sets.count() == 0 response = get_view_for_user( viewname="archives:items-reader-study-update", client=client, method=client.post, data={"items": [ai1.pk, ai2.pk], "reader_study": rs2.pk}, reverse_kwargs={"slug": archive.slug}, follow=True, user=editor, ) assert response.status_code == 200 assert rs2.display_sets.count() == 0 ai1.values.add(civ3) ai2.values.add(civ4) response = get_view_for_user( viewname="archives:items-reader-study-update", client=client, method=client.post, data={"items": [ai1.pk, ai2.pk], "reader_study": rs1.pk}, reverse_kwargs={"slug": archive.slug}, follow=True, user=editor, ) assert response.status_code == 200 assert rs1.display_sets.count() == 4 assert sorted( sorted(list(ds.values.values_list("pk", flat=True))) for ds in rs1.display_sets.all() ) == sorted([[civ1.pk], [civ2.pk], [civ1.pk, civ3.pk], [civ2.pk, civ4.pk]])
def add_image_to_archive(image, archive): interface = ComponentInterfaceFactory() civ = ComponentInterfaceValueFactory(interface=interface, image=image) item = ArchiveItemFactory(archive=archive) item.values.set([civ])
def test_archive_items_to_reader_study_update(client, settings): settings.task_eager_propagates = (True, ) settings.task_always_eager = (True, ) archive = ArchiveFactory() rs1 = ReaderStudyFactory(use_display_sets=True) rs2 = ReaderStudyFactory(use_display_sets=False) editor, user = UserFactory(), UserFactory() archive.add_user(user) archive.add_editor(editor) rs1.add_editor(editor) rs2.add_editor(editor) im1, im2, im3, im4 = ImageFactory.create_batch(4) overlay = ComponentInterface.objects.get(slug="generic-overlay") image = ComponentInterface.objects.get(slug="generic-medical-image") civ1, civ2, civ3, civ4 = ( ComponentInterfaceValueFactory(interface=image, image=im1), ComponentInterfaceValueFactory(interface=image, image=im2), ComponentInterfaceValueFactory(interface=overlay, image=im3), ComponentInterfaceValueFactory(interface=overlay, image=im4), ) ai1 = ArchiveItemFactory(archive=archive) ai2 = ArchiveItemFactory(archive=archive) ai1.values.add(civ1) ai2.values.add(civ2) response = get_view_for_user( viewname="archives:items-reader-study-update", client=client, reverse_kwargs={"slug": archive.slug}, user=user, ) assert response.status_code == 200 assert str(rs1.pk) not in response.rendered_content assert str(rs2.pk) not in response.rendered_content response = get_view_for_user( viewname="archives:items-reader-study-update", client=client, reverse_kwargs={"slug": archive.slug}, follow=True, user=editor, ) assert response.status_code == 200 assert str(rs1.pk) in response.rendered_content assert str(rs2.pk) not in response.rendered_content assert im1.name in response.rendered_content assert im2.name in response.rendered_content assert im3.name not in response.rendered_content assert im4.name not in response.rendered_content ai1.values.add(civ3) ai2.values.add(civ4) response = get_view_for_user( viewname="archives:items-reader-study-update", client=client, reverse_kwargs={"slug": archive.slug}, follow=True, user=editor, ) assert response.status_code == 200 assert f"{im1.name}, {im3.name}" in response.rendered_content assert f"{im2.name}, {im4.name}" in response.rendered_content
def test_adding_images_triggers_task(reverse, mocker): mocker.patch( "grandchallenge.algorithms.tasks.create_algorithm_jobs_for_archive.apply_async" ) create_algorithm_jobs_for_archive.apply_async.assert_not_called() arch_set = TwoArchives() with capture_on_commit_callbacks(execute=True): ai1, ai2 = ( ArchiveItemFactory(archive=arch_set.arch1), ArchiveItemFactory(archive=arch_set.arch2), ) ai1.values.set([ComponentInterfaceValueFactory()]) ai2.values.set([ComponentInterfaceValueFactory()]) create_algorithm_jobs_for_archive.apply_async.assert_has_calls( [ call( kwargs={ "archive_pks": [arch_set.arch1.pk], "archive_item_pks": [ai1.pk], } ), call( kwargs={ "archive_pks": [arch_set.arch2.pk], "archive_item_pks": [ai2.pk], } ), ] ) create_algorithm_jobs_for_archive.apply_async.reset_mock() ai1, ai2, ai3, ai4 = ( ArchiveItemFactory(archive=arch_set.arch1), ArchiveItemFactory(archive=arch_set.arch1), ArchiveItemFactory(archive=arch_set.arch1), ArchiveItemFactory(archive=arch_set.arch1), ) civ1, civ2, civ3, civ4 = ( ComponentInterfaceValueFactory(), ComponentInterfaceValueFactory(), ComponentInterfaceValueFactory(), ComponentInterfaceValueFactory(), ) if not reverse: for ai, civ in [(ai1, civ1), (ai2, civ2), (ai3, civ3), (ai4, civ4)]: with capture_on_commit_callbacks(execute=True): ai.values.set([civ]) kwargs = create_algorithm_jobs_for_archive.apply_async.call_args.kwargs[ "kwargs" ] create_algorithm_jobs_for_archive.apply_async.assert_called_once() assert {*kwargs["archive_pks"]} == {arch_set.arch1.pk} assert {*kwargs["archive_item_pks"]} == {ai.pk} create_algorithm_jobs_for_archive.apply_async.reset_mock() with capture_on_commit_callbacks(execute=True): ai.values.remove(civ) ai.values.clear() create_algorithm_jobs_for_archive.apply_async.assert_not_called() else: for ai in [ai1, ai2, ai3, ai4]: with capture_on_commit_callbacks(execute=True): civ = ComponentInterfaceValueFactory() civ.archive_items.add(ai) kwargs = create_algorithm_jobs_for_archive.apply_async.call_args.kwargs[ "kwargs" ] create_algorithm_jobs_for_archive.apply_async.assert_called_once() assert {*kwargs["archive_pks"]} == {arch_set.arch1.pk} assert {*kwargs["archive_item_pks"]} == {ai.pk} create_algorithm_jobs_for_archive.apply_async.reset_mock() with capture_on_commit_callbacks(execute=True): civ3.archive_items.remove(ai3) civ1.archive_items.clear() create_algorithm_jobs_for_archive.apply_async.assert_not_called()
def create_archive_items_for_images(images, archive): for image in images: civ = ComponentInterfaceValueFactory(image=image) ai = ArchiveItemFactory(archive=archive) ai.values.add(civ)
def test_archive_item_form(client, settings): # Override the celery settings settings.task_eager_propagates = (True,) settings.task_always_eager = (True,) archive = ArchiveFactory() editor = UserFactory() archive.editors_group.user_set.add(editor) ci = ComponentInterfaceFactory( kind=InterfaceKind.InterfaceKindChoices.BOOL ) civ = ComponentInterfaceValueFactory( interface=ci, value=True, file=None, image=None ) ai = ArchiveItemFactory(archive=archive) ai.values.add(civ) response = get_view_for_user( viewname="archives:item-edit", client=client, method=client.get, reverse_kwargs={"slug": archive.slug, "id": ai.pk}, follow=True, user=editor, ) assert response.status_code == 200 for _ci in ComponentInterface.objects.all(): assert _ci.slug in response.rendered_content assert f'id="id_{ci.slug}" checked' in response.rendered_content assert Job.objects.count() == 0 alg = AlgorithmFactory() AlgorithmImageFactory(algorithm=alg, ready=True) alg.inputs.set([ci]) with capture_on_commit_callbacks(execute=True): archive.algorithms.add(alg) assert Job.objects.count() == 1 civ_count = ComponentInterfaceValue.objects.count() with capture_on_commit_callbacks(execute=True): with capture_on_commit_callbacks(execute=True): response = get_view_for_user( viewname="archives:item-edit", client=client, method=client.post, reverse_kwargs={"slug": archive.slug, "id": ai.pk}, data={ci.slug: False}, follow=True, user=editor, ) assert ai.values.filter(pk=civ.pk).count() == 0 # This should created a new CIV as they are immutable assert ComponentInterfaceValue.objects.count() == civ_count + 1 # A new job should have been created, because the value for 'bool' # has changed assert Job.objects.count() == 2 with capture_on_commit_callbacks(execute=True): with capture_on_commit_callbacks(execute=True): response = get_view_for_user( viewname="archives:item-edit", client=client, method=client.post, reverse_kwargs={"slug": archive.slug, "id": ai.pk}, data={ci.slug: True}, follow=True, user=editor, ) # New jobs should be created as there is a new CIV assert Job.objects.count() == 3 assert ComponentInterfaceValue.objects.count() == civ_count + 2