def _sync_tasks(configuration: Dict) -> None: """Synchronize Tasks from configuration file with database entries. Example Tasks in the configuration file: ``` tasks: - key: KIDNEYS_SEGMENTATION name: Kidneys segmentation image_path: assets/icon/kidneys_dataset_icon.svg description: Task description label_examples: ['assets/example_1', 'assets/example_2'] tags: - key: LEFT_KIDNEY name: Left Kidney tools: - CHAIN - key: RIGHT_KIDNEY name: Right Kidney tools: - CHAIN ``` :param configuration: content of YAML configuration file """ datasets = configuration.get('datasets', []) or [] tasks = configuration.get('tasks', []) or [] configuration_tasks_keys = {task['key'] for task in tasks} database_tasks_keys = {task.key for task in Task.query.all()} tasks_to_add = configuration_tasks_keys - database_tasks_keys tasks_to_disable = database_tasks_keys - configuration_tasks_keys tasks_to_enable = database_tasks_keys & configuration_tasks_keys # Add all new Tasks that haven't ever been in the DB for task_key in tasks_to_add: task = next(task for task in tasks if task['key'] == task_key) datasets_keys = [dataset['key'] for dataset in datasets if task['key'] in dataset['tasks']] TasksRepository.add_task(task['key'], task['name'], task['image_path'], datasets_keys, task.get('description', ''), task.get("label_examples", []), []) _sync_label_tags_in_task(configuration, task_key) logger.info('New Task added: %s', task['key']) # Enable back all Tasks that were previously commented-out or removed from configuration file for task_key in tasks_to_enable: TasksRepository.enable(task_key) _sync_label_tags_in_task(configuration, task_key) task = next(task for task in tasks if task['key'] == task_key) datasets_keys = [dataset['key'] for dataset in datasets if task['key'] in dataset['tasks']] TasksRepository.update(task_key, task['name'], task['image_path'], datasets_keys, task.get('description', ''), task.get("label_examples", [])) logger.info('Task enabled: %s', task_key) # Disable all Tasks that exists in the DB but are missing in configuration file for task_key in tasks_to_disable: TasksRepository.disable(task_key) task = TasksRepository.get_task_by_key(task_key) for tag in task.available_tags: LabelTagsRepository.disable(tag.key) logger.info('LabelTag disabled: %s', tag.key) logger.info('Task disabled: %s', task_key)
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_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 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_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 create_task(key: str, name: str, image_path: str, datasets_keys: List[str], tags: List[LabelTag]) -> Task: """Create new Task. :param key: unique key representing Task :param name: name which describes this Task :param image_path: path to the image which is located on the frontend :param datasets_keys: Keys of Datasets that Task takes Scans from :param tags: Label Tags that will be created and assigned to Task :return: Task object """ return TasksRepository.add_task(key, name, image_path, datasets_keys, tags)
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_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_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 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_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 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_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)
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]