def test_custom_base_url(self, high_quality_image: io.BytesIO) -> None: """ It is possible to use query a target to a database under a custom VWQ URL. """ base_vwq_url = 'http://example.com' with MockVWS(base_vwq_url=base_vwq_url) as mock: database = VuforiaDatabase() mock.add_database(database=database) vws_client = VWS( server_access_key=database.server_access_key, server_secret_key=database.server_secret_key, ) target_id = vws_client.add_target( name='x', width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) cloud_reco_client = CloudRecoService( client_access_key=database.client_access_key, client_secret_key=database.client_secret_key, base_vwq_url=base_vwq_url, ) matches = cloud_reco_client.query(image=high_quality_image) assert len(matches) == 1 match = matches[0] assert match.target_id == target_id
def test_query_request( self, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, vws_client: VWS, ) -> None: """ The ``*_recos`` counts seem to be delayed by a significant amount of time. We therefore test that they exist, are integers and do not change between quick requests. """ target_id = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) report_before = vws_client.get_database_summary_report() cloud_reco_client.query(image=high_quality_image) report_after = vws_client.get_database_summary_report() assert report_before.total_recos == report_after.total_recos assert (report_before.current_month_recos == report_after.current_month_recos) assert (report_before.previous_month_recos == report_after.previous_month_recos)
def _wait_for_deletion_processed( image: io.BytesIO, vuforia_database: VuforiaDatabase, ) -> None: """ Wait until the query endpoint "recognizes" the deletion of all targets with an image matching the given image. That is, wait until querying the given image returns a result with no targets. """ _wait_for_deletion_recognized( image=image, vuforia_database=vuforia_database, ) cloud_reco_client = CloudRecoService( client_access_key=vuforia_database.client_access_key, client_secret_key=vuforia_database.client_secret_key, ) while True: try: cloud_reco_client.query(image=image) except MatchProcessing: continue return
def test_give_no_details(self, high_quality_image: io.BytesIO) -> None: """ It is possible to create a database without giving any data. """ databases_url = _EXAMPLE_URL_FOR_TARGET_MANAGER + '/databases' response = requests.post(url=databases_url, json={}) assert response.status_code == HTTPStatus.CREATED data = response.json() assert data['targets'] == [] assert data['state_name'] == 'WORKING' assert 'database_name' in data.keys() vws_client = VWS( server_access_key=data['server_access_key'], server_secret_key=data['server_secret_key'], ) cloud_reco_client = CloudRecoService( client_access_key=data['client_access_key'], client_secret_key=data['client_secret_key'], ) assert not vws_client.list_targets() assert not cloud_reco_client.query(image=high_quality_image)
def test_update_target( self, vws_client: VWS, high_quality_image: io.BytesIO, different_high_quality_image: io.BytesIO, cloud_reco_client: CloudRecoService, ) -> None: """ It is possible to update a target. """ old_name = uuid.uuid4().hex old_width = random.uniform(a=0.01, b=50) target_id = vws_client.add_target( name=old_name, width=old_width, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) [matching_target] = cloud_reco_client.query(image=high_quality_image) assert matching_target.target_id == target_id query_target_data = matching_target.target_data assert query_target_data is not None query_metadata = query_target_data.application_metadata assert query_metadata is None new_name = uuid.uuid4().hex new_width = random.uniform(a=0.01, b=50) new_application_metadata = base64.b64encode(b'a').decode('ascii') vws_client.update_target( target_id=target_id, name=new_name, width=new_width, active_flag=True, image=different_high_quality_image, application_metadata=new_application_metadata, ) vws_client.wait_for_target_processed(target_id=target_id) [ matching_target, ] = cloud_reco_client.query(image=different_high_quality_image) assert matching_target.target_id == target_id query_target_data = matching_target.target_data assert query_target_data is not None query_metadata = query_target_data.application_metadata assert query_metadata == new_application_metadata vws_client.update_target( target_id=target_id, active_flag=False, ) target_details = vws_client.get_target_record(target_id=target_id) assert target_details.target_record.name == new_name assert target_details.target_record.width == new_width assert not target_details.target_record.active_flag
def test_image_too_large( cloud_reco_client: CloudRecoService, png_too_large: io.BytesIO, ) -> None: """ A ``RequestEntityTooLarge`` exception is raised if an image which is too large is given. """ with pytest.raises(RequestEntityTooLarge): cloud_reco_client.query(image=png_too_large)
def test_image_too_large( cloud_reco_client: CloudRecoService, png_too_large: io.BytesIO, ) -> None: """ A ``ConnectionErrorPossiblyImageTooLarge`` exception is raised if an image which is too large is given. """ with pytest.raises(ConnectionErrorPossiblyImageTooLarge) as exc: cloud_reco_client.query(image=png_too_large) assert isinstance(exc.value, requests.ConnectionError)
def test_recognition( self, vws_client: VWS, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ The recognition counts stay at 0 even after recognitions. """ target_id = vws_client.add_target( name='example', width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) results = cloud_reco_client.query(image=high_quality_image) [result] = results assert result.target_id == target_id report = vws_client.get_target_summary_report(target_id=target_id) assert report.status == TargetStatuses.SUCCESS assert report.total_recos == 0 assert report.current_month_recos == 0 assert report.previous_month_recos == 0
def test_default( self, vws_client: VWS, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ By default, target data is only returned in the top match. """ target_id = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) target_id_2 = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) vws_client.wait_for_target_processed(target_id=target_id_2) top_match, second_match = cloud_reco_client.query( image=high_quality_image, max_num_results=2, ) assert top_match.target_data is not None assert second_match.target_data is None
def test_all( self, vws_client: VWS, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ When ``CloudRecoIncludeTargetData.ALL`` is given, target data is returned in all matches. """ target_id = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) target_id_2 = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) vws_client.wait_for_target_processed(target_id=target_id_2) top_match, second_match = cloud_reco_client.query( image=high_quality_image, max_num_results=2, include_target_data=CloudRecoIncludeTargetData.ALL, ) assert top_match.target_data is not None assert second_match.target_data is not None
def test_default( self, vws_client: VWS, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ By default the maximum number of results is 1. """ target_id = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) target_id_2 = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) vws_client.wait_for_target_processed(target_id=target_id_2) matches = cloud_reco_client.query(image=high_quality_image) assert len(matches) == 1
def test_query_request( self, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, vws_client: VWS, ) -> None: """ The ``request_usage`` count does not increase with each query. """ report = vws_client.get_database_summary_report() original_request_usage = report.request_usage cloud_reco_client.query(image=high_quality_image) report = vws_client.get_database_summary_report() new_request_usage = report.request_usage # The request usage goes up for the database summary request, not the # query. assert new_request_usage == original_request_usage
def test_authentication_failure(high_quality_image: io.BytesIO) -> None: """ An ``AuthenticationFailure`` exception is raised when the client access key exists but the client secret key is incorrect. """ database = VuforiaDatabase() cloud_reco_client = CloudRecoService( client_access_key=database.client_access_key, client_secret_key='a', ) with MockVWS() as mock: mock.add_database(database=database) with pytest.raises(AuthenticationFailure) as exc: cloud_reco_client.query(image=high_quality_image) assert exc.value.response.status_code == HTTPStatus.UNAUTHORIZED
def test_inactive_project(high_quality_image: io.BytesIO) -> None: """ An ``InactiveProject`` exception is raised when querying an inactive database. """ database = VuforiaDatabase(state=States.PROJECT_INACTIVE) with MockVWS() as mock: mock.add_database(database=database) cloud_reco_client = CloudRecoService( client_access_key=database.client_access_key, client_secret_key=database.client_secret_key, ) with pytest.raises(InactiveProject) as exc: cloud_reco_client.query(image=high_quality_image) assert exc.value.response.status_code == HTTPStatus.FORBIDDEN
def cloud_reco_client(vuforia_database: VuforiaDatabase) -> CloudRecoService: """ A query client for an active VWS database. """ return CloudRecoService( client_access_key=vuforia_database.client_access_key, client_secret_key=vuforia_database.client_secret_key, )
def test_too_many_max_results( cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ A ``MaxNumResultsOutOfRange`` error is raised if the given ``max_num_results`` is out of range. """ with pytest.raises(MaxNumResultsOutOfRange) as exc: cloud_reco_client.query( image=high_quality_image, max_num_results=51, ) expected_value = ( "Integer out of range (51) in form data part 'max_result'. " 'Accepted range is from 1 to 50 (inclusive).') assert str(exc.value) == exc.value.response.text == expected_value
def cloud_reco_client( mock_database: VuforiaDatabase, ) -> Iterator[CloudRecoService]: """ Yield a ``CloudRecoService`` client which connects to a mock database. """ yield CloudRecoService( client_access_key=mock_database.client_access_key, client_secret_key=mock_database.client_secret_key, )
def test_no_matches( self, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ An empty list is returned if there are no matches. """ result = cloud_reco_client.query(image=high_quality_image) assert result == []
def test_match_processing( vws_client: VWS, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ A ``MatchProcessing`` exception is raised when a target in processing is matched. """ vws_client.add_target( name='x', width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) with pytest.raises(MatchProcessing) as exc: cloud_reco_client.query(image=high_quality_image) assert exc.value.response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
def test_base_exception( vws_client: VWS, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ ``CloudRecoException``s has a response property. """ vws_client.add_target( name='x', width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) with pytest.raises(CloudRecoException) as exc: cloud_reco_client.query(image=high_quality_image) assert exc.value.response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
def test_active_matching_targets_delete_processing( vws_client: VWS, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ A ``ActiveMatchingTargetsDeleteProcessing`` exception is raised when a target which has recently been deleted is matched. """ target_id = vws_client.add_target( name='x', width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) vws_client.delete_target(target_id=target_id) time.sleep(0.2) with pytest.raises(ActiveMatchingTargetsDeleteProcessing): cloud_reco_client.query(image=high_quality_image)
def test_bad_secret_key_query( self, vuforia_database: VuforiaDatabase, high_quality_image: io.BytesIO, ) -> None: """ If the client secret key given is incorrect, an ``UNAUTHORIZED`` response is returned. """ cloud_reco_client = CloudRecoService( client_access_key=vuforia_database.client_access_key, client_secret_key='example', ) with pytest.raises(cloud_reco_exceptions.AuthenticationFailure) as exc: cloud_reco_client.query(image=high_quality_image) response = exc.value.response assert_vwq_failure( response=response, status_code=HTTPStatus.UNAUTHORIZED, content_type='application/json', cache_control=None, www_authenticate='VWS', connection='keep-alive', ) assert response.json().keys() == {'transaction_id', 'result_code'} assert_valid_transaction_id(response=response) assert_valid_date_header(response=response) result_code = response.json()['result_code'] transaction_id = response.json()['transaction_id'] assert result_code == ResultCodes.AUTHENTICATION_FAILURE.value # The separators are inconsistent and we test this. expected_text = ('{"transaction_id":' f'"{transaction_id}",' f'"result_code":"{result_code}"' '}') assert response.text == expected_text
def vuforia_cloud_reco( image: Path, client_access_key: str, client_secret_key: str, max_num_results: int, include_target_data: CloudRecoIncludeTargetData, base_vwq_url: str, ) -> None: """ Make a request to the Vuforia Cloud Recognition Service API. """ client = CloudRecoService( client_access_key=client_access_key, client_secret_key=client_secret_key, base_vwq_url=base_vwq_url, ) query_result = client.query( image=io.BytesIO(image.read_bytes()), max_num_results=max_num_results, include_target_data=include_target_data, ) query_result_dict_list = [dataclasses.asdict(res) for res in query_result] yaml_list = yaml.dump(query_result_dict_list) click.echo(yaml_list)
def _wait_for_deletion_recognized( image: io.BytesIO, vuforia_database: VuforiaDatabase, ) -> None: """ Wait until the query endpoint "recognizes" the deletion of all targets with an image matching the given image. That is, wait until querying the given image does not return a result with targets. """ cloud_reco_client = CloudRecoService( client_access_key=vuforia_database.client_access_key, client_secret_key=vuforia_database.client_secret_key, ) while True: try: results = cloud_reco_client.query(image=image) except ActiveMatchingTargetsDeleteProcessing: return if not results: return
def test_add_target( self, mock_database: VuforiaDatabase, vws_client: VWS, high_quality_image: io.BytesIO, tmp_path: Path, cloud_reco_client: CloudRecoService, ) -> None: """ It is possible to add a target. """ runner = CliRunner() new_file = tmp_path / uuid.uuid4().hex name = uuid.uuid4().hex image_data = high_quality_image.getvalue() new_file.write_bytes(data=image_data) width = random.uniform(a=0.01, b=50) commands = [ 'add-target', '--name', name, '--width', str(width), '--image', str(new_file), '--server-access-key', mock_database.server_access_key, '--server-secret-key', mock_database.server_secret_key, ] result = runner.invoke(vws_group, commands, catch_exceptions=False) assert result.exit_code == 0 target_id = result.stdout.strip() target_details = vws_client.get_target_record(target_id=target_id) target_record = target_details.target_record assert target_record.name == name assert target_record.width == width assert target_record.active_flag is True vws_client.wait_for_target_processed(target_id=target_id) [query_result] = cloud_reco_client.query(image=high_quality_image) assert query_result.target_id == target_id target_data = query_result.target_data assert target_data is not None assert target_data.application_metadata is None
def test_custom_metadata( self, mock_database: VuforiaDatabase, cloud_reco_client: CloudRecoService, vws_client: VWS, tmp_path: Path, high_quality_image: io.BytesIO, ) -> None: """ Custom metadata can be given. """ runner = CliRunner() new_file = tmp_path / uuid.uuid4().hex name = uuid.uuid4().hex image_data = high_quality_image.getvalue() new_file.write_bytes(data=image_data) application_metadata = uuid.uuid4().hex metadata_bytes = application_metadata.encode('ascii') base64_encoded_metadata_bytes = base64.b64encode(metadata_bytes) base64_encoded_metadata = base64_encoded_metadata_bytes.decode('ascii') commands = [ 'add-target', '--name', name, '--width', '0.1', '--image', str(new_file), '--application-metadata', base64_encoded_metadata, '--server-access-key', mock_database.server_access_key, '--server-secret-key', mock_database.server_secret_key, ] result = runner.invoke(vws_group, commands, catch_exceptions=False) assert result.exit_code == 0 target_id = result.stdout.strip() vws_client.wait_for_target_processed(target_id=target_id) [query_result] = cloud_reco_client.query(image=high_quality_image) assert query_result.target_id == target_id target_data = query_result.target_data assert target_data is not None assert target_data.application_metadata == base64_encoded_metadata
def test_match( self, vws_client: VWS, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ Details of matching targets are returned. """ target_id = vws_client.add_target( name='x', width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) [matching_target] = cloud_reco_client.query(image=high_quality_image) assert matching_target.target_id == target_id
def test_add_target( self, vws_client: VWS, high_quality_image: io.BytesIO, active_flag: bool, application_metadata: Optional[bytes], cloud_reco_client: CloudRecoService, ) -> None: """ No exception is raised when adding one target. """ name = 'x' width = 1 if application_metadata is None: encoded_metadata = None else: encoded_metadata_bytes = base64.b64encode(application_metadata) encoded_metadata = encoded_metadata_bytes.decode('utf-8') target_id = vws_client.add_target( name=name, width=width, image=high_quality_image, application_metadata=encoded_metadata, active_flag=active_flag, ) target_record = vws_client.get_target_record( target_id=target_id, ).target_record assert target_record.name == name assert target_record.width == width assert target_record.active_flag is active_flag vws_client.wait_for_target_processed(target_id=target_id) matching_targets = cloud_reco_client.query(image=high_quality_image) if active_flag: [matching_target] = matching_targets assert matching_target.target_id == target_id assert matching_target.target_data is not None query_metadata = matching_target.target_data.application_metadata assert query_metadata == encoded_metadata else: assert matching_targets == []
def test_custom( self, vws_client: VWS, cloud_reco_client: CloudRecoService, high_quality_image: io.BytesIO, ) -> None: """ It is possible to set a custom ``max_num_results``. """ target_id = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) target_id_2 = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) target_id_3 = vws_client.add_target( name=uuid.uuid4().hex, width=1, image=high_quality_image, active_flag=True, application_metadata=None, ) vws_client.wait_for_target_processed(target_id=target_id) vws_client.wait_for_target_processed(target_id=target_id_2) vws_client.wait_for_target_processed(target_id=target_id_3) matches = cloud_reco_client.query( image=high_quality_image, max_num_results=2, ) assert len(matches) == 2
'Accept-Encoding': "gzip, deflate", 'Connection': "keep-alive", 'cache-control': "no-cache" } SERVER_ACCESS_KEY = os.environ['UPLOADER_SERVER_ACCESS_KEY'] SERVER_SECRET_KEY = os.environ['UPLOADER_SERVER_SECRET_KEY'] CLIENT_ACCESS_KEY = os.environ['UPLOADER_CLIENT_ACCESS_KEY'] CLIENT_SECRET_KEY = os.environ['UPLOADER_CLIENT_SECRET_KEY'] VWS_CLIENT = VWS( server_access_key=SERVER_ACCESS_KEY, server_secret_key=SERVER_SECRET_KEY, ) CLOUD_RECO_CLIENT = CloudRecoService( client_access_key=CLIENT_ACCESS_KEY, client_secret_key=CLIENT_SECRET_KEY, ) # checks if the database is empty: if it is, perform the initialisation LIST_OF_TARGETS = VWS_CLIENT.list_targets() if not LIST_OF_TARGETS: shutil.rmtree("/logos/") os.remove("existing_restaurants.txt") os.remove("imagehashes.txt") print("Database is empty, performing initialisation") initialise(VWS_CLIENT, HEADERS) else: # Find the new restaurants FILE = 'existing_restaurants.txt' NEW_RESTAURANTS_LIST = check_for_new_restaurants(HEADERS, FILE)