def test_scan_upload_and_conversion(prepare_environment: Any, synchronous_celery: Any) -> None: """Test application for Scan upload and conversion.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 3} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) json_response = json.loads(response.data) scan_id = json_response['scan_id'] # Step 3. Send Slices for file in glob.glob('tests/assets/example_scan/*.dcm'): with open(file, 'rb') as image: response = api_client.post('/api/v1/scans/{}/slices'.format(scan_id), data={ 'image': (image, 'slice_1.dcm'), }, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 201 # Step 4. Check Scan & Slices in the databases z_slices = SlicesRepository.get_slices_by_scan_id(scan_id) assert len(z_slices) == 3 y_slices = SlicesRepository.get_slices_by_scan_id(scan_id, SliceOrientation.Y) assert not y_slices x_slices = SlicesRepository.get_slices_by_scan_id(scan_id, SliceOrientation.X) assert not x_slices # Step 5.1. Slices in Z axis z_slice = SlicesRepository.get_slice_converted_image(z_slices[2].id) z_slice_image = Image.open(io.BytesIO(z_slice)) assert z_slice_image.size == (512, 512)
def test_add_point_label(prepare_environment: Any, synchronous_celery: Any) -> None: """Test for adding a Label made with Point tool.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.POINT], task.id) # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 3} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) scan_id = json_response['scan_id'] # Step 3. Label it with Point Tool payload = { 'elements': [{ 'slice_index': 0, 'x': 0.25, 'y': 0.5, 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.POINT.value, }], 'labeling_time': 12.34, } data = { 'label': json.dumps(payload), } response = api_client.post( '/api/v1/scans/{}/MARK_KIDNEYS/label'.format(scan_id), data=data, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) label_id = json_response['label_id'] assert isinstance(label_id, str) assert len(label_id) >= 1 # Step 4. Fetch details about above Label response = api_client.get('/api/v1/labels/' + label_id, headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert len(json_response['elements']) == 1 assert json_response['elements'][0]['x'] == 0.25 assert json_response['elements'][0]['y'] == 0.5
def test_ownership(prepare_environment: Any, synchronous_celery: Any) -> None: """Test for checking scan and label ownership.""" api_client = get_api_client() # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('LUNGS', 'Lungs') task = TasksRepository.add_task('FIND_NODULES', 'Find Nodules', 'path/to/image', ['LUNGS'], '', [], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.RECTANGLE], task.id) admin_id, _ = create_user(ADMIN_EMAIL, ADMIN_PASSWORD, ADMIN_FIRST_NAME, ADMIN_LAST_NAME) set_user_role(admin_id, 'admin') # Step 2. Admin user logs in payload: Dict[str, Any] = {'email': ADMIN_EMAIL, 'password': ADMIN_PASSWORD} response = api_client.post('/api/v1/auth/sign-in', data=json.dumps(payload), headers=get_headers(json=True)) json_response = json.loads(response.data) admin_user_token = json_response['token'] assert response.status_code == 200 # Step 3. Add Scan to the system payload = {'dataset': 'LUNGS', 'number_of_slices': 1} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=admin_user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) owner_id = json_response['owner_id'] assert owner_id == admin_id scan_id = json_response['scan_id'] # Step 4. Send slices with open('tests/assets/example_scan/slice_1.dcm', 'rb') as image: response = api_client.post('/api/v1/scans/{}/slices'.format(scan_id), data={ 'image': (image, 'slice_1.dcm'), }, content_type='multipart/form-data', headers=get_headers(token=admin_user_token)) assert response.status_code == 201 # Step 5. Label payload = { 'elements': [{ 'x': 0.5, 'y': 0.5, 'slice_index': 0, 'width': 0.1, 'height': 0.1, 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.RECTANGLE.value, }], 'labeling_time': 12.34, } response = api_client.post('/api/v1/scans/{}/FIND_NODULES/label'.format(scan_id), data={'label': json.dumps(payload)}, headers=get_headers(token=admin_user_token, multipart=True)) assert response.status_code == 201 json_response = json.loads(response.data) owner_id = json_response['owner_id'] assert owner_id == admin_id
def test_add_label_with_tag_from_other_task(prepare_environment: Any, synchronous_celery: Any) -> None: """Test for adding a Label with Tag from other Task.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') left_task = TasksRepository.add_task('MARK_LEFT', 'Mark Left', 'path/to/image', ['KIDNEYS'], '', [], []) right_task = TasksRepository.add_task('MARK_RIGHT', 'Mark Left', 'path/to/image', ['KIDNEYS'], '', [], []) LabelTagsRepository.add_new_tag('TAG_LEFT', 'Tag Left', [LabelTool.POINT], left_task.id) LabelTagsRepository.add_new_tag('TAG_RIGHT', 'Tag Right', [LabelTool.POINT], right_task.id) # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 3} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) scan_id = json_response['scan_id'] # Step 3. Label it with an element with Tag from another Task payload = { 'elements': [{ 'slice_index': 0, 'x': 0.25, 'y': 0.5, 'tag': 'TAG_RIGHT', 'tool': LabelTool.POINT.value, }], 'labeling_time': 12.34, } data = { 'label': json.dumps(payload), } response = api_client.post( '/api/v1/scans/{}/MARK_LEFT/label'.format(scan_id), data=data, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 400 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert json_response['message'] == 'Invalid arguments.' assert json_response[ 'details'] == 'Tag TAG_RIGHT is not part of Task MARK_LEFT.'
def test_add_task(prepare_environment: Any) -> None: """Test application with basic flow.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') DatasetsRepository.add_new_dataset('LUNGS', 'Lungs') # Step 2. Add new Task through the REST API payload = { 'key': 'MARK_NODULES', 'name': 'Mark nodules', 'image_path': 'assets/icon/my_icon.svg', 'datasets_keys': ['KIDNEYS', 'LUNGS'], 'tags': [{ 'key': 'SMALL_NODULE', 'name': 'Small nodule', 'tools': ['POINT'], }, { 'key': 'BIG_NODULE', 'name': 'Big nodule', 'tools': ['RECTANGLE', 'BRUSH'], }], } response = api_client.post('/api/v1/tasks', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert json_response['key'] == 'MARK_NODULES' assert json_response['name'] == 'Mark nodules' assert json_response['image_path'] == 'assets/icon/my_icon.svg' assert len(json_response['tags']) == 2 assert len(json_response['datasets_keys']) == 2 # Step 3. Check for available Datasets through the REST API response = api_client.get('/api/v1/datasets', headers=get_headers(token=user_token, json=True)) json_response = json.loads(response.data) datasets = [ dataset for dataset in json_response if any( task for task in dataset['tasks'] if task['key'] == 'MARK_NODULES') ] assert len(datasets) == 2
def test_add_chain_label_not_enough_points(prepare_environment: Any, synchronous_celery: Any) -> None: """Test for adding a Label made with Chain tool.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.CHAIN], task.id) # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 3} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) scan_id = json_response['scan_id'] # Step 3. Label it with Chain Tool payload = { 'elements': [{ 'slice_index': 0, 'points': [ { 'x': 0.2, 'y': 0.3, }, ], 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.CHAIN.value, 'loop': False, }], 'labeling_time': 12.34, } data = { 'label': json.dumps(payload), } response = api_client.post( '/api/v1/scans/{}/MARK_KIDNEYS/label'.format(scan_id), data=data, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 400
def test_get_predefined_label_for_scan_in_task__predefined_label( prepare_environment: Any) -> None: """Test for fetching Predefined Label that exists.""" # Step 1. Prepare a structure for the test dataset = DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.RECTANGLE], task.id) scan = ScansRepository.add_new_scan(dataset, 0) user_id = UsersRepository.add_new_user( User('user@medtagger', 'HASH', 'Admin', 'Admin')) user = UsersRepository.get_user_by_id(user_id) # Step 2. Add Label which is predefined label = LabelsRepository.add_new_label(scan.id, task.key, user, LabelingTime(0), is_predefined=True) # Step 3. Check if there is is Predefined Label predefined_label = LabelsRepository.get_predefined_label_for_scan_in_task( scan, task) assert predefined_label assert predefined_label.id == label.id
def test_get_paginated_scans_with_invalid_arguments( prepare_environment: Any) -> None: """Test for fetching Scans in the paginated way with invalid arguments.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test dataset = DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') # Step 2. Add example Scans to the system for _ in range(50): ScansRepository.add_new_scan(dataset, number_of_slices=3) # Step 3. Fetch them with MedTagger REST API in the wrong way response = api_client.get('/api/v1/scans?dataset_key=KIDNEYS&page=-1', headers=get_headers(token=user_token)) assert response.status_code == 400 json_response = json.loads(response.data) assert json_response['message'] == 'Invalid arguments.' assert json_response['details'] == 'Page cannot be smaller than 1.' # Step 4. Make a mistake again response = api_client.get( '/api/v1/scans?dataset_key=KIDNEYS&per_page=5000', headers=get_headers(token=user_token)) assert response.status_code == 400 json_response = json.loads(response.data) assert json_response['message'] == 'Invalid arguments.' assert json_response[ 'details'] == 'Cannot fetch more than 100 entries at once.'
def test_get_paginated_scans(prepare_environment: Any) -> None: """Test for fetching Scans in the paginated way.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test dataset = DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') # Step 2. Add example Scans to the system for _ in range(50): ScansRepository.add_new_scan(dataset, number_of_slices=3) # Step 3. Fetch them with MedTagger REST API response = api_client.get('/api/v1/scans?dataset_key=KIDNEYS', headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert json_response['pagination']['page'] == 1 assert json_response['pagination']['per_page'] == 25 assert json_response['pagination']['total'] == 50 assert len(json_response['scans']) == 25 # Step 4. Fetch the next page with different size response = api_client.get( '/api/v1/scans?dataset_key=KIDNEYS&page=2&per_page=10', headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert json_response['pagination']['page'] == 2 assert json_response['pagination']['per_page'] == 10 assert json_response['pagination']['total'] == 50 assert len(json_response['scans']) == 10
def test_get_predefined_brush_label_elements(prepare_environment: Any) -> None: """Test for fetching Predefined Brush Label Elements.""" # Step 1. Prepare a structure for the test dataset = DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) label_tag = LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.RECTANGLE], task.id) scan = ScansRepository.add_new_scan(dataset, 3) user_id = UsersRepository.add_new_user( User('user@medtagger', 'HASH', 'Admin', 'Admin')) user = UsersRepository.get_user_by_id(user_id) # Step 2. Add Label with Brush Elements label = LabelsRepository.add_new_label(scan.id, task.key, user, LabelingTime(0), is_predefined=True) LabelsRepository.add_new_brush_label_element(label.id, 0, 0, 0, b'', label_tag) LabelsRepository.add_new_brush_label_element(label.id, 1, 0, 0, b'', label_tag) LabelsRepository.add_new_brush_label_element(label.id, 2, 0, 0, b'', label_tag) # Step 3. Check if there is is Predefined Label brush_label_elements = LabelsRepository.get_predefined_brush_label_elements( scan.id, task.id, 0, 3) assert len(brush_label_elements) == 3 brush_label_elements = LabelsRepository.get_predefined_brush_label_elements( scan.id, task.id, 0, 1) assert len(brush_label_elements) == 1
def create_dataset(key: str, name: str) -> Dataset: """Create new Dataset. :param key: unique key representing Dataset :param name: name which describes this Dataset :return: Dataset object """ return DatasetsRepository.add_new_dataset(key, name)
def test_add_label_non_existing_tag(prepare_environment: Any) -> None: """Test application for situation when users provides non existing tag.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], '', [], []) LabelTagsRepository.add_new_tag('LEFT_KIDNEY', 'Left Kidney', [LabelTool.RECTANGLE], task.id) # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 3} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) scan_id = json_response['scan_id'] # Step 3. Create label payload = { 'elements': [{ 'x': 0.5, 'y': 0.5, 'slice_index': 0, 'width': 0.1, 'height': 0.1, 'tag': 'NON_EXISTING', 'tool': LabelTool.RECTANGLE.value, }], 'labeling_time': 12.34, } response = api_client.post( '/api/v1/scans/{}/MARK_KIDNEYS/label'.format(scan_id), data={'label': json.dumps(payload)}, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 400 json_response = json.loads(response.data) assert json_response[ 'details'] == 'Tag NON_EXISTING is not part of Task MARK_KIDNEYS.'
def test_delete_scan_without_slices(prepare_environment: Any, synchronous_celery: Any) -> None: """Test deleting scan without any slices.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.RECTANGLE], task.id) # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 0} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) scan_id: ScanID = json_response['scan_id'] assert isinstance(scan_id, str) assert len(scan_id) >= 1 # Step 3. Get scan response = api_client.get('/api/v1/scans/{}'.format(scan_id), headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert json_response['scan_id'] == scan_id assert json_response['number_of_slices'] == 0 # Step 4. Delete scan from the system ScansRepository.delete_scan_by_id(scan_id) # Step 5. Check that scan has been deleted response = api_client.get('/api/v1/scans/{}'.format(scan_id), headers=get_headers(token=user_token)) assert response.status_code == 404
def prepare_scan_and_tag_for_labeling() -> Tuple[models.Scan, models.LabelTag]: """Create needed Scan and Label Tag for labeling purpose.""" dataset = DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) label_tag = LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [definitions.LabelTool.POINT], task.id) scan = ScansRepository.add_new_scan(dataset, number_of_slices=3) for _ in range(3): scan.add_slice() return scan, label_tag
def test_scan_upload_with_retrying(fixture_problems_with_storage: Any, prepare_environment: Any, synchronous_celery: Any) -> None: """Test application for Scan upload with retrying.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 3} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) json_response = json.loads(response.data) scan_id = json_response['scan_id'] # Step 3. Send Slices for file in glob.glob('tests/assets/example_scan/*.dcm'): with open(file, 'rb') as image: # First request to the API will fail with unknown error due to Storage issues fixture_problems_with_storage.side_effect = WriteTimeout('Internal Storage Error', write_type=0) response = api_client.post('/api/v1/scans/{}/slices'.format(scan_id), data={ 'image': (image, 'slice_1.dcm'), }, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 500 # After such error, UI will retry the request (this time it will be fine...) # As request will close the image file, we've got to open it again with open(file, 'rb') as image: fixture_problems_with_storage.side_effect = None response = api_client.post('/api/v1/scans/{}/slices'.format(scan_id), data={ 'image': (image, 'slice_1.dcm'), }, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 201 # Step 4. Check number of Slices in the databases z_slices = SlicesRepository.get_slices_by_scan_id(scan_id) assert len(z_slices) == 3
def test_skipping_a_scan(prepare_environment: Any, synchronous_celery: Any) -> None: """Test for skipping a scan.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 3} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) scan_id = json_response['scan_id'] # Step 3. Skip Scan response = api_client.post('/api/v1/scans/' + scan_id + '/skip', headers=get_headers(token=user_token)) assert response.status_code == 200
def test_get_predefined_label_for_scan_in_task__no_predefined_label( prepare_environment: Any) -> None: """Test for fetching Predefined Label that does not exist.""" # Step 1. Prepare a structure for the test dataset = DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.RECTANGLE], task.id) scan = ScansRepository.add_new_scan(dataset, 0) # Step 2. Check if there is no Predefined Label predefined_label = LabelsRepository.get_predefined_label_for_scan_in_task( scan, task) assert not predefined_label
def _sync_datasets(configuration: Dict) -> None: """Synchronize Datasets from configuration file with database entries. Example DataSets in the configuration file: ``` datasets: - name: Kidneys key: KIDNEYS tasks: - KIDNEYS_SEGMENTATION ``` :param configuration: content of YAML configuration file """ datasets = configuration.get('datasets', []) or [] configuration_datasets_keys = {dataset['key'] for dataset in datasets} database_datasets_keys = {dataset.key for dataset in DatasetsRepository.get_all_datasets(include_disabled=True)} datasets_to_add = configuration_datasets_keys - database_datasets_keys datasets_to_disable = database_datasets_keys - configuration_datasets_keys datasets_to_enable = database_datasets_keys & configuration_datasets_keys for dataset_key in datasets_to_add: dataset = next(dataset for dataset in datasets if dataset['key'] == dataset_key) DatasetsRepository.add_new_dataset(dataset['key'], dataset['name']) logger.info('New DataSet added: %s', dataset['key']) for dataset_key in datasets_to_enable: dataset = next(dataset for dataset in datasets if dataset['key'] == dataset_key) DatasetsRepository.enable(dataset['key']) DatasetsRepository.update(dataset['key'], dataset['name']) logger.info('DataSet enabled: %s', dataset['key']) for dataset_key in datasets_to_disable: DatasetsRepository.disable(dataset_key) logger.info('DataSet disabled: %s', dataset_key)
def test_get_paginated_scans_by_volunteer(prepare_environment: Any) -> None: """Test for fetching Scans in the paginated way by volunteers.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('volunteer') # Step 1. Prepare a structure for the test dataset = DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') # Step 2. Add example Scans to the system for _ in range(50): ScansRepository.add_new_scan(dataset, number_of_slices=3) # Step 3. Fetch them with MedTagger REST API response = api_client.get('/api/v1/scans?dataset_key=KIDNEYS', headers=get_headers(token=user_token)) assert response.status_code == 403 json_response = json.loads(response.data) assert json_response['message'] == 'Access forbidden' assert json_response[ 'details'] == 'You don\'t have required roles to access this method.'
def test_get_predefined_label_for_scan_in_task__predefined_label_for_given_task( prepare_environment: Any) -> None: """Test for fetching Predefined Label only for specific Task.""" # Step 1. Prepare a structure for the test dataset = DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task_left = TasksRepository.add_task('MARK_LEFT', 'Mark Left', 'path/to/image', ['KIDNEYS'], '', [], []) task_right = TasksRepository.add_task('MARK_RIGHT', 'Mark Right', 'path/to/image', ['KIDNEYS'], '', [], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.RECTANGLE], task_left.id) scan = ScansRepository.add_new_scan(dataset, 0) user_id = UsersRepository.add_new_user( User('user@medtagger', 'HASH', 'Admin', 'Admin')) user = UsersRepository.get_user_by_id(user_id) # Step 2. Add Labels for each Task label_left = LabelsRepository.add_new_label(scan.id, task_left.key, user, LabelingTime(0), is_predefined=True) label_right = LabelsRepository.add_new_label(scan.id, task_right.key, user, LabelingTime(0), is_predefined=True) # Step 3. Check if there are these Predefined Labels predefined_label = LabelsRepository.get_predefined_label_for_scan_in_task( scan, task_left) assert predefined_label assert predefined_label.id == label_left.id predefined_label = LabelsRepository.get_predefined_label_for_scan_in_task( scan, task_right) assert predefined_label assert predefined_label.id == label_right.id
def test_add_task(prepare_environment: Any, synchronous_celery: Any) -> None: """Test application with basic flow.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') DatasetsRepository.add_new_dataset('LUNGS', 'Lungs') # Step 2. Add new Task through the REST API payload = { 'key': 'MARK_NODULES', 'name': 'Mark nodules', 'description': 'This task will focus on tagging nodules.', 'label_examples': ['assets/labels/label_1.png', 'assets/labels/label_2.png'], 'image_path': 'assets/icon/my_icon.svg', 'datasets_keys': ['KIDNEYS', 'LUNGS'], 'tags': [{ 'key': 'SMALL_NODULE', 'name': 'Small nodule', 'tools': ['POINT'], }, { 'key': 'BIG_NODULE', 'name': 'Big nodule', 'tools': ['RECTANGLE', 'BRUSH'], }], } response = api_client.post('/api/v1/tasks', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert json_response['key'] == 'MARK_NODULES' assert json_response['name'] == 'Mark nodules' assert json_response['image_path'] == 'assets/icon/my_icon.svg' assert json_response['number_of_available_scans'] == 0 assert len(json_response['label_examples']) == 2 assert json_response[ 'description'] == 'This task will focus on tagging nodules.' assert len(json_response['tags']) == 2 assert len(json_response['datasets_keys']) == 2 # Step 3. Check for available Datasets through the REST API response = api_client.get('/api/v1/datasets', headers=get_headers(token=user_token, json=True)) json_response = json.loads(response.data) datasets = [ dataset for dataset in json_response if any( task for task in dataset['tasks'] if task['key'] == 'MARK_NODULES') ] assert len(datasets) == 2 # Step 4. Add Scans to datasets payload = {'dataset': 'KIDNEYS', 'number_of_slices': 1} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) scan_id = json_response['scan_id'] payload = {'dataset': 'LUNGS', 'number_of_slices': 1} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 # Step 3. Send Slices for file in glob.glob('tests/assets/example_scan/*.dcm'): with open(file, 'rb') as image: response = api_client.post( '/api/v1/scans/{}/slices'.format(scan_id), data={ 'image': (image, 'slice_1.dcm'), }, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 201 # Step 4. Check for Task metadata through the REST API response = api_client.get('/api/v1/tasks/MARK_NODULES', headers=get_headers(token=user_token, json=True)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert json_response['key'] == 'MARK_NODULES' assert json_response['name'] == 'Mark nodules' assert json_response['number_of_available_scans'] == 1 assert len(json_response['label_examples']) == 2 assert json_response[ 'description'] == 'This task will focus on tagging nodules.'
def test_basic_flow_with_predefined_label(prepare_environment: Any, synchronous_celery: Any) -> None: """Test application with basic flow that uses Predefined Label in Scan.""" api_client = get_api_client() web_socket_client = get_web_socket_client(namespace='/slices') user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.RECTANGLE, LabelTool.BRUSH], task.id) # Step 2. Get all datasets response = api_client.get('/api/v1/datasets', headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, list) dataset = json_response[0] dataset_key = dataset['key'] task_key = dataset['tasks'][0]['key'] # Step 3. Add Scan to the system payload = {'dataset': dataset_key, 'number_of_slices': 1} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) scan_id = json_response['scan_id'] # Step 4. Send slices with open('tests/assets/example_scan/slice_1.dcm', 'rb') as image: response = api_client.post('/api/v1/scans/{}/slices'.format(scan_id), data={ 'image': (image, 'slice_1.dcm'), }, content_type='multipart/form-data', headers=get_headers(token=user_token)) assert response.status_code == 201 # Step 5. Send Predefined Label payload = { 'elements': [{ 'x': 0.5, 'y': 0.5, 'slice_index': 0, 'width': 0.1, 'height': 0.1, 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.RECTANGLE.value, }, { 'slice_index': 0, 'width': 128, 'height': 128, 'image_key': 'SLICE_1', 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.BRUSH.value, }], 'labeling_time': None, } with open('tests/assets/example_labels/binary_mask.png', 'rb') as image: data = { 'label': json.dumps(payload), 'SLICE_1': (image, 'slice_1'), } response = api_client.post( '/api/v1/scans/{}/MARK_KIDNEYS/label?is_predefined=true'.format( scan_id), data=data, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) predefined_label_id = json_response['label_id'] assert type(predefined_label_id), str assert predefined_label_id # Step 6. Get random scan response = api_client.get('/api/v1/scans/random?task={}'.format(task_key), headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert json_response['scan_id'] == scan_id assert json_response['number_of_slices'] == 1 assert json_response['width'] == 512 assert json_response['height'] == 512 assert json_response['predefined_label_id'] == predefined_label_id # Step 7. Get slices from the server payload = { 'scan_id': scan_id, 'task_key': task_key, 'begin': 0, 'count': 1 } web_socket_client.emit('request_slices', payload, namespace='/slices') responses = web_socket_client.get_received(namespace='/slices') assert len(responses) == 2 slice_response = next(response for response in responses if response['name'] == 'slice') assert slice_response['args'][0]['scan_id'] == scan_id assert slice_response['args'][0]['index'] == 0 assert isinstance(slice_response['args'][0]['image'], bytes) brush_label_response = next(response for response in responses if response['name'] == 'brush_labels') assert brush_label_response['args'][0]['scan_id'] == scan_id assert brush_label_response['args'][0]['tag_key'] == 'EXAMPLE_TAG' assert brush_label_response['args'][0]['index'] == 0 assert isinstance(brush_label_response['args'][0]['image'], bytes) # Step 8. Label it payload = { 'elements': [{ 'x': 0.5, 'y': 0.5, 'slice_index': 0, 'width': 0.1, 'height': 0.1, 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.RECTANGLE.value, }], 'labeling_time': 12.34, } response = api_client.post( '/api/v1/scans/{}/MARK_KIDNEYS/label'.format(scan_id), data={'label': json.dumps(payload)}, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) label_id = json_response['label_id'] assert isinstance(label_id, str) assert len(label_id) >= 1 # Step 9. Get random label for validation response = api_client.get('/api/v1/labels/random', headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert json_response['label_id'] == label_id
def test_adding_new_survey(prepare_environment: Any) -> None: """Test for adding new Survey and fetching it through API.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('volunteer') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) label_tag = LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.RECTANGLE], task.id) # Step 2. Add an example of Survey survey = Survey(name='Colors Survey', initial_element_key=SurveyElementKey('FIRST_QUESTION')) survey.elements = [ SurveySingleChoiceQuestion( key=SurveyElementKey('FIRST_QUESTION'), title='What is your favourite color?', possible_answers={ 'Red': SurveyElementKey('SECOND_QUESTION'), 'Green': None, 'Blue': None, }, ), SurveySingleChoiceQuestion( key=SurveyElementKey('SECOND_QUESTION'), title='Why do you like Red?', possible_answers={ 'It is nice': None, 'Love it': None, 'I do not know': None, }, ), ] label_tag.actions.append(survey) label_tag.save() # Step 3.1 Check if it's available through API response = api_client.get('/api/v1/labels/actions/1', headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert set(json_response) == {'action_id', 'action_type', 'details'} assert json_response['action_id'] == 1 assert json_response['action_type'] == 'Survey' assert isinstance(json_response['details'], dict) # Step 3.2. Make sure that Survey details have all necessary fields assert set(json_response['details']) == { 'name', 'initial_element_key', 'elements' } assert json_response['details']['name'] == 'Colors Survey' assert json_response['details']['initial_element_key'] == 'FIRST_QUESTION' assert isinstance(json_response['details']['elements'], list) # Step 3.3. Check what elements does this Survey contain all_elements = json_response['details']['elements'] assert len(all_elements) == 2 all_element_keys = {element['key'] for element in all_elements} assert all_element_keys == {'FIRST_QUESTION', 'SECOND_QUESTION'} # Step 3.4. Check what does first question has inside first_question = next(element for element in all_elements if element['key'] == 'FIRST_QUESTION') assert first_question == { 'key': 'FIRST_QUESTION', 'instant_next_element': None, 'type': 'SurveySingleChoiceQuestion', 'title': 'What is your favourite color?', 'possible_answers': { 'Red': 'SECOND_QUESTION', 'Green': None, 'Blue': None, }, } # Step 3.5. Check what second question has inside first_question = next(element for element in all_elements if element['key'] == 'SECOND_QUESTION') assert first_question == { 'key': 'SECOND_QUESTION', 'instant_next_element': None, 'type': 'SurveySingleChoiceQuestion', 'title': 'Why do you like Red?', 'possible_answers': { 'It is nice': None, 'Love it': None, 'I do not know': None, }, } # Step 4. Check if we can fetch any unavailable Action through API response = api_client.get('/api/v1/labels/actions/2', headers=get_headers(token=user_token)) assert response.status_code == 404 json_response = json.loads(response.data) assert json_response == { 'message': 'Requested object does not exist.', 'details': 'Action "2" not found.', } # Step 5. Check if above Action is available from Task endpoint response = api_client.get('/api/v1/tasks', headers=get_headers(token=user_token)) json_response = json.loads(response.data) mark_kidneys_task = next(task for task in json_response if task['key'] == 'MARK_KIDNEYS') example_tag = next(tag for tag in mark_kidneys_task['tags'] if tag['key'] == 'EXAMPLE_TAG') assert example_tag['actions_ids'] == [1]
def test_add_brush_label(prepare_environment: Any, synchronous_celery: Any) -> None: """Test for adding a Label made with Brush tool.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.BRUSH], task.id) # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 3} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) scan_id = json_response['scan_id'] # Step 3. Label it with Brush payload = { 'elements': [{ 'slice_index': 0, 'width': 128, 'height': 128, 'image_key': 'SLICE_1', 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.BRUSH.value, }], 'labeling_time': 12.34, 'task_id': TasksRepository.get_task_by_key('MARK_KIDNEYS').id, } with open('tests/assets/example_labels/binary_mask.png', 'rb') as image: data = { 'label': json.dumps(payload), 'SLICE_1': (image, 'slice_1'), } response = api_client.post( '/api/v1/scans/{}/MARK_KIDNEYS/label'.format(scan_id), data=data, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) label_id = json_response['label_id'] assert isinstance(label_id, str) assert len(label_id) >= 1 # Step 4. Fetch details about above Label and check image storage response = api_client.get('/api/v1/labels/' + label_id, headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, dict) label_element_id = json_response['elements'][0]['label_element_id'] brush_label_element = BrushLabelElement.get(id=label_element_id) assert brush_label_element.image
def test_delete_scan_with_slices(prepare_environment: Any, synchronous_celery: Any) -> None: """Test deleting scan with at least 1 slice.""" api_client = get_api_client() user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [LabelTool.RECTANGLE], task.id) # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 1} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) scan_id: ScanID = json_response['scan_id'] assert isinstance(scan_id, str) assert len(scan_id) >= 1 # Step 3. Send slices with open('tests/assets/example_scan/slice_1.dcm', 'rb') as image: response = api_client.post('/api/v1/scans/{}/slices'.format(scan_id), data={ 'image': (image, 'slice_1.dcm'), }, content_type='multipart/form-data', headers=get_headers(token=user_token)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) slice_id: SliceID = json_response['slice_id'] assert isinstance(slice_id, str) assert len(slice_id) >= 1 # Step 4. Get scan response = api_client.get('/api/v1/scans/{}'.format(scan_id), headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert json_response['scan_id'] == scan_id assert json_response['number_of_slices'] == 1 assert json_response['width'] == 512 assert json_response['height'] == 512 # Step 5. Delete scan from the system ScansRepository.delete_scan_by_id(scan_id) # Step 6. Check that scan has been deleted response = api_client.get('/api/v1/scans/{}'.format(scan_id), headers=get_headers(token=user_token)) assert response.status_code == 404 # Step 7. Check that slices has been deleted with pytest.raises(NoResultFound): SliceRepository.get_slice_by_id(slice_id)
def test_delete_scan_with_labels(prepare_environment: Any, synchronous_celery: Any) -> None: """Test deleting scan with at least 1 slice and with labels made with all tools.""" api_client = get_api_client() web_socket_client = get_web_socket_client(namespace='/slices') user_token = get_token_for_logged_in_user('admin') # Step 1. Prepare a structure for the test DatasetsRepository.add_new_dataset('KIDNEYS', 'Kidneys') task = TasksRepository.add_task('MARK_KIDNEYS', 'Mark Kidneys', 'path/to/image', ['KIDNEYS'], []) LabelTagsRepository.add_new_tag('EXAMPLE_TAG', 'Example Tag', [ LabelTool.RECTANGLE, LabelTool.CHAIN, LabelTool.POINT, LabelTool.BRUSH ], task.id) # Step 2. Add Scan to the system payload = {'dataset': 'KIDNEYS', 'number_of_slices': 1} response = api_client.post('/api/v1/scans', data=json.dumps(payload), headers=get_headers(token=user_token, json=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) scan_id: ScanID = json_response['scan_id'] assert isinstance(scan_id, str) assert len(scan_id) >= 1 # Step 3. Send slices with open('tests/assets/example_scan/slice_1.dcm', 'rb') as image: response = api_client.post('/api/v1/scans/{}/slices'.format(scan_id), data={ 'image': (image, 'slice_1.dcm'), }, content_type='multipart/form-data', headers=get_headers(token=user_token)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) slice_id: SliceID = json_response['slice_id'] assert isinstance(slice_id, str) assert len(slice_id) >= 1 # Step 4. Get scan response = api_client.get('/api/v1/scans/{}'.format(scan_id), headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, dict) assert json_response['scan_id'] == scan_id assert json_response['number_of_slices'] == 1 assert json_response['width'] == 512 assert json_response['height'] == 512 # Step 5. Get slices from the server payload = { 'scan_id': scan_id, 'task_key': 'MARK_KIDNEYS', 'begin': 0, 'count': 1 } web_socket_client.emit('request_slices', payload, namespace='/slices') responses = web_socket_client.get_received(namespace='/slices') assert len(responses) == 1 response = responses[0] assert response['name'] == 'slice' assert response['args'][0]['scan_id'] == scan_id assert response['args'][0]['index'] == 0 assert isinstance(response['args'][0]['image'], bytes) # Step 6. Label it with all available tools payload = { 'elements': [{ 'x': 0.5, 'y': 0.5, 'slice_index': 0, 'width': 0.1, 'height': 0.1, 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.RECTANGLE.value, }, { 'slice_index': 0, 'width': 128, 'height': 128, 'image_key': 'SLICE_1', 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.BRUSH.value, }, { 'slice_index': 0, 'x': 0.25, 'y': 0.5, 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.POINT.value, }, { 'slice_index': 0, 'points': [ { 'x': 0.2, 'y': 0.3, }, { 'x': 0.5, 'y': 0.8, }, ], 'tag': 'EXAMPLE_TAG', 'tool': LabelTool.CHAIN.value, 'loop': False, }], 'labeling_time': 12.34, } with open('tests/assets/example_labels/binary_mask.png', 'rb') as image: data = { 'label': json.dumps(payload), 'SLICE_1': (image, 'slice_1'), } response = api_client.post( '/api/v1/scans/{}/MARK_KIDNEYS/label'.format(scan_id), data=data, headers=get_headers(token=user_token, multipart=True)) assert response.status_code == 201 json_response = json.loads(response.data) assert isinstance(json_response, dict) label_id = json_response['label_id'] assert isinstance(label_id, str) assert len(label_id) >= 1 # Step 7. Fetch details about above Label and check image storage response = api_client.get('/api/v1/labels/' + label_id, headers=get_headers(token=user_token)) assert response.status_code == 200 json_response = json.loads(response.data) assert isinstance(json_response, dict) label_element_id = json_response['elements'][1]['label_element_id'] brush_label_element = BrushLabelElement.get(id=label_element_id) assert brush_label_element.image # Step 7. Delete scan from the system ScansRepository.delete_scan_by_id(scan_id) # Step 8. Check that scan has been deleted response = api_client.get('/api/v1/scans/{}'.format(scan_id), headers=get_headers(token=user_token)) assert response.status_code == 404 # Step 9. Check that slices has been deleted with pytest.raises(NoResultFound): SliceRepository.get_slice_by_id(slice_id) # Step 10. Check that slices original image has been deleted from storage with pytest.raises(DoesNotExist): OriginalSlice.get(id=slice_id) # Step 11. Check that slices processed image has been deleted from storage with pytest.raises(DoesNotExist): ProcessedSlice.get(id=slice_id) # Step 12. Check that labels has been deleted response = api_client.get('/api/v1/labels/{}'.format(label_id), headers=get_headers(token=user_token)) assert response.status_code == 404 # Step 13. Check that Brush Label was deleted from storage with pytest.raises(DoesNotExist): BrushLabelElement.get(id=label_element_id)