From d4f55d493586a26a50f29a19e1ef293287070de9 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Thu, 27 May 2021 18:31:13 -0400 Subject: [PATCH 1/3] change return type of analyze actions to list of list --- .../azure-ai-textanalytics/CHANGELOG.md | 5 + .../azure-ai-textanalytics/README.md | 114 +++---- .../azure/ai/textanalytics/__init__.py | 10 +- .../azure/ai/textanalytics/_models.py | 64 +--- .../ai/textanalytics/_request_handlers.py | 12 +- .../ai/textanalytics/_response_handlers.py | 119 ++----- .../textanalytics/_text_analytics_client.py | 36 +- .../aio/_text_analytics_client_async.py | 36 +- .../sample_analyze_actions_async.py | 142 ++++---- .../samples/sample_analyze_actions.py | 131 ++++---- .../tests/test_analyze.py | 307 ++++++++--------- .../tests/test_analyze_async.py | 317 +++++++++--------- .../azure-ai-textanalytics/tests/test_repr.py | 51 --- .../azure-ai-textanalytics/tests/testcase.py | 22 ++ 14 files changed, 593 insertions(+), 773 deletions(-) diff --git a/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md b/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md index 4ae23e24a7e7..39a31a7a9a7d 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md +++ b/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md @@ -2,6 +2,11 @@ ## 5.1.0b8 (Unreleased) +**Breaking Changes** +- Changed the response structure of `being_analyze_actions`. Now, we return a list of list of an action result of documents +- Removed `AnalyzeActionsType` +- Removed `AnalyzeActionsResult` +- Removed `AnalyzeActionsError` ## 5.1.0b7 (2021-05-18) diff --git a/sdk/textanalytics/azure-ai-textanalytics/README.md b/sdk/textanalytics/azure-ai-textanalytics/README.md index b1e69be921de..b015b0f4d522 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/README.md +++ b/sdk/textanalytics/azure-ai-textanalytics/README.md @@ -532,72 +532,56 @@ poller = text_analytics_client.begin_analyze_actions( ) # returns multiple actions results in the same order as the inputted actions -result = poller.result() - -first_action_result = next(result) -print("Results of Entities Recognition action:") -docs = [doc for doc in first_action_result.document_results if not doc.is_error] - -for idx, doc in enumerate(docs): - print("\nDocument text: {}".format(documents[idx])) - for entity in doc.entities: - print("Entity: {}".format(entity.text)) - print("...Category: {}".format(entity.category)) - print("...Confidence Score: {}".format(entity.confidence_score)) - print("...Offset: {}".format(entity.offset)) - print("------------------------------------------") - -second_action_result = next(result) -print("Results of PII Entities Recognition action:") -docs = [doc for doc in second_action_result.document_results if not doc.is_error] - -for idx, doc in enumerate(docs): - print("Document text: {}".format(documents[idx])) - for entity in doc.entities: - print("Entity: {}".format(entity.text)) - print("Category: {}".format(entity.category)) - print("Confidence Score: {}\n".format(entity.confidence_score)) - print("------------------------------------------") - -third_action_result = next(result) -print("Results of Key Phrase Extraction action:") -docs = [doc for doc in third_action_result.document_results if not doc.is_error] - -for idx, doc in enumerate(docs): - print("Document text: {}\n".format(documents[idx])) - print("Key Phrases: {}\n".format(doc.key_phrases)) - print("------------------------------------------") - -fourth_action_result = next(result) -print("Results of Linked Entities Recognition action:") -docs = [doc for doc in fourth_action_result.document_results if not doc.is_error] - -for idx, doc in enumerate(docs): - print("Document text: {}\n".format(documents[idx])) - for linked_entity in doc.entities: - print("Entity name: {}".format(linked_entity.name)) - print("...Data source: {}".format(linked_entity.data_source)) - print("...Data source language: {}".format(linked_entity.language)) - print("...Data source entity ID: {}".format(linked_entity.data_source_entity_id)) - print("...Data source URL: {}".format(linked_entity.url)) - print("...Document matches:") +pages = poller.result() + +for doc, document_results in zip(documents, pages): + print("\nDocument text: {}".format(doc)) + recognize_entities_result = document_results[0] + assert not recognize_entities_result.is_error + print("...Results of Recognize Entities Action:") + for entity in recognize_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + print(".........Offset: {}".format(entity.offset)) + + recognize_pii_entities_result = document_results[1] + assert not recognize_pii_entities_result.is_error + print("...Results of Recognize PII Entities action:") + for entity in recognize_pii_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + + extract_key_phrases_result = document_results[2] + assert not extract_key_phrases_result.is_error + print("...Results of Extract Key Phrases action:") + print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) + + recognize_linked_entities_result = document_results[3] + assert not recognize_linked_entities_result.is_error + print("...Results of Recognize Linked Entities action:") + for linked_entity in recognize_linked_entities_result.entities: + print("......Entity name: {}".format(linked_entity.name)) + print(".........Data source: {}".format(linked_entity.data_source)) + print(".........Data source language: {}".format(linked_entity.language)) + print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) + print(".........Data source URL: {}".format(linked_entity.url)) + print(".........Document matches:") for match in linked_entity.matches: - print("......Match text: {}".format(match.text)) - print(".........Confidence Score: {}".format(match.confidence_score)) - print(".........Offset: {}".format(match.offset)) - print(".........Length: {}".format(match.length)) - print("------------------------------------------") - -fifth_action_result = next(result) -print("Results of Sentiment Analysis action:") -docs = [doc for doc in fifth_action_result.document_results if not doc.is_error] - -for doc in docs: - print("Overall sentiment: {}".format(doc.sentiment)) - print("Scores: positive={}; neutral={}; negative={} \n".format( - doc.confidence_scores.positive, - doc.confidence_scores.neutral, - doc.confidence_scores.negative, + print("............Match text: {}".format(match.text)) + print("............Confidence Score: {}".format(match.confidence_score)) + print("............Offset: {}".format(match.offset)) + print("............Length: {}".format(match.length)) + + analyze_sentiment_result = document_results[4] + assert not analyze_sentiment_result.is_error + print("...Results of Analyze Sentiment action:") + print("......Overall sentiment: {}".format(analyze_sentiment_result.sentiment)) + print("......Scores: positive={}; neutral={}; negative={} \n".format( + analyze_sentiment_result.confidence_scores.positive, + analyze_sentiment_result.confidence_scores.neutral, + analyze_sentiment_result.confidence_scores.negative, )) print("------------------------------------------") ``` diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py index 753e2c49ecba..d8c8adc9be1f 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/__init__.py @@ -39,10 +39,7 @@ RecognizeLinkedEntitiesAction, RecognizePiiEntitiesAction, ExtractKeyPhrasesAction, - AnalyzeActionsResult, - RequestStatistics, - AnalyzeActionsType, - AnalyzeActionsError, + _AnalyzeActionsType, HealthcareEntityRelationRoleType, HealthcareRelation, HealthcareRelationRole, @@ -93,10 +90,7 @@ 'RecognizeLinkedEntitiesAction', 'RecognizePiiEntitiesAction', 'ExtractKeyPhrasesAction', - 'AnalyzeActionsResult', - 'RequestStatistics', - 'AnalyzeActionsType', - "AnalyzeActionsError", + '_AnalyzeActionsType', "PiiEntityCategoryType", "HealthcareEntityRelationType", "HealthcareEntityRelationRoleType", diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py index b0b9af28efff..d036ae1bd5f9 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_models.py @@ -1356,7 +1356,7 @@ def __repr__(self): .format(self.positive, self.neutral, self.negative)[:1024] -class AnalyzeActionsType(str, Enum): +class _AnalyzeActionsType(str, Enum): """The type of action that was applied to the documents """ RECOGNIZE_ENTITIES = "recognize_entities" #: Entities Recognition action. @@ -1365,67 +1365,6 @@ class AnalyzeActionsType(str, Enum): RECOGNIZE_LINKED_ENTITIES = "recognize_linked_entities" #: Linked Entities Recognition action. ANALYZE_SENTIMENT = "analyze_sentiment" #: Sentiment Analysis action. - -class AnalyzeActionsResult(DictMixin): - """AnalyzeActionsResult contains the results of a recognize entities action - on a list of documents. Returned by `begin_analyze_actions` - - :ivar document_results: A list of objects containing results for all Entity Recognition actions - included in the analysis. - :vartype document_results: list[~azure.ai.textanalytics.RecognizeEntitiesResult] - :ivar bool is_error: Boolean check for error item when iterating over list of - actions. Always False for an instance of a AnalyzeActionsResult. - :ivar action_type: The type of action this class is a result of. - :vartype action_type: str or ~azure.ai.textanalytics.AnalyzeActionsType - :ivar ~datetime.datetime completed_on: Date and time (UTC) when the result completed - on the service. - :ivar statistics: Overall statistics for the action result. - :vartype statistics: ~azure.ai.RequestStatistics - """ - def __init__(self, **kwargs): - self.document_results = kwargs.get("document_results") - self.is_error = False - self.action_type = kwargs.get("action_type") - self.completed_on = kwargs.get("completed_on") - self.statistics = kwargs.get("statistics") - - def __repr__(self): - return "AnalyzeActionsResult(document_results={}, is_error={}, action_type={}, completed_on={}, " \ - "statistics={})".format( - repr(self.document_results), - self.is_error, - self.action_type, - self.completed_on, - repr(self.statistics) - )[:1024] - - -class AnalyzeActionsError(DictMixin): - """AnalyzeActionsError is an error object which represents an an - error response for an action. - - :ivar error: The action result error. - :vartype error: ~azure.ai.textanalytics.TextAnalyticsError - :ivar bool is_error: Boolean check for error item when iterating over list of - results. Always True for an instance of a DocumentError. - """ - - def __init__(self, **kwargs): - self.error = kwargs.get("error") - self.is_error = True - - def __repr__(self): - return "AnalyzeActionsError(error={}, is_error={}".format( - repr(self.error), self.is_error - ) - - @classmethod - def _from_generated(cls, error): - return cls( - error=TextAnalyticsError(code=error.code, message=error.message, target=error.target) - ) - - class RecognizeEntitiesAction(DictMixin): """RecognizeEntitiesAction encapsulates the parameters for starting a long-running Entities Recognition operation. @@ -1719,7 +1658,6 @@ def to_generated(self): ) ) - class RequestStatistics(DictMixin): def __init__(self, **kwargs): self.documents_count = kwargs.get("documents_count") diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_request_handlers.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_request_handlers.py index baf47085ba0b..b4b4d4466e6e 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_request_handlers.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_request_handlers.py @@ -14,7 +14,7 @@ RecognizePiiEntitiesAction, RecognizeLinkedEntitiesAction, AnalyzeSentimentAction, - AnalyzeActionsType, + _AnalyzeActionsType, ) def _validate_input(documents, hint, whole_input_hint): @@ -71,14 +71,14 @@ def _validate_input(documents, hint, whole_input_hint): def _determine_action_type(action): if isinstance(action, RecognizeEntitiesAction): - return AnalyzeActionsType.RECOGNIZE_ENTITIES + return _AnalyzeActionsType.RECOGNIZE_ENTITIES if isinstance(action, RecognizePiiEntitiesAction): - return AnalyzeActionsType.RECOGNIZE_PII_ENTITIES + return _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES if isinstance(action, RecognizeLinkedEntitiesAction): - return AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES + return _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES if isinstance(action, AnalyzeSentimentAction): - return AnalyzeActionsType.ANALYZE_SENTIMENT - return AnalyzeActionsType.EXTRACT_KEY_PHRASES + return _AnalyzeActionsType.ANALYZE_SENTIMENT + return _AnalyzeActionsType.EXTRACT_KEY_PHRASES def _check_string_index_type_arg(string_index_type_arg, api_version, string_index_type_default="UnicodeCodePoint"): string_index_type = None diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_response_handlers.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_response_handlers.py index bba2c934d37c..f01b82eb1182 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_response_handlers.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_response_handlers.py @@ -31,11 +31,8 @@ RecognizePiiEntitiesResult, PiiEntity, AnalyzeHealthcareEntitiesResultItem, - AnalyzeActionsResult, RequestStatistics, - AnalyzeActionsType, - AnalyzeActionsError, - _get_indices, + _AnalyzeActionsType, ) from ._paging import AnalyzeHealthcareEntitiesResult, AnalyzeResult @@ -87,10 +84,10 @@ def order_lro_results(doc_id_order, combined): def prepare_result(func): def choose_wrapper(*args, **kwargs): - def wrapper(response, obj, response_headers): # pylint: disable=unused-argument + def wrapper(response, obj, response_headers, ordering_function): # pylint: disable=unused-argument if obj.errors: combined = obj.documents + obj.errors - results = order_results(response, combined) + results = ordering_function(response, combined) else: results = obj.documents @@ -102,27 +99,11 @@ def wrapper(response, obj, response_headers): # pylint: disable=unused-argument results[idx] = func(item, results) return results - def lro_wrapper(doc_id_order, obj, response_headers): # pylint: disable=unused-argument - if obj.errors: - combined = obj.documents + obj.errors - - results = order_lro_results(doc_id_order, combined) - else: - results = obj.documents - - for idx, item in enumerate(results): - if hasattr(item, "error"): - results[idx] = DocumentError(id=item.id, error=TextAnalyticsError._from_generated(item.error)) # pylint: disable=protected-access - else: - results[idx] = func(item, results) - return results - lro = kwargs.get("lro", False) if lro: - return lro_wrapper(*args) - - return wrapper(*args) + return wrapper(*args, ordering_function=order_lro_results) + return wrapper(*args, ordering_function=order_results) return choose_wrapper @@ -199,99 +180,57 @@ def healthcare_extract_page_data(doc_id_order, obj, response_headers, health_job healthcare_result(doc_id_order, health_job_state.results, response_headers, lro=True)) def _get_deserialization_callback_from_task_type(task_type): - if task_type == AnalyzeActionsType.RECOGNIZE_ENTITIES: + if task_type == _AnalyzeActionsType.RECOGNIZE_ENTITIES: return entities_result - if task_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES: + if task_type == _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES: return pii_entities_result - if task_type == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES: + if task_type == _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES: return linked_entities_result - if task_type == AnalyzeActionsType.ANALYZE_SENTIMENT: + if task_type == _AnalyzeActionsType.ANALYZE_SENTIMENT: return sentiment_result return key_phrases_result def _get_property_name_from_task_type(task_type): - if task_type == AnalyzeActionsType.RECOGNIZE_ENTITIES: + if task_type == _AnalyzeActionsType.RECOGNIZE_ENTITIES: return "entity_recognition_tasks" - if task_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES: + if task_type == _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES: return "entity_recognition_pii_tasks" - if task_type == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES: + if task_type == _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES: return "entity_linking_tasks" - if task_type == AnalyzeActionsType.ANALYZE_SENTIMENT: + if task_type == _AnalyzeActionsType.ANALYZE_SENTIMENT: return "sentiment_analysis_tasks" return "key_phrase_extraction_tasks" -def _num_tasks_in_current_page(returned_tasks_object): - return ( - len(returned_tasks_object.entity_recognition_tasks or []) + - len(returned_tasks_object.entity_recognition_pii_tasks or []) + - len(returned_tasks_object.key_phrase_extraction_tasks or []) + - len(returned_tasks_object.entity_linking_tasks or []) + - len(returned_tasks_object.sentiment_analysis_tasks or []) - ) - -def _get_task_type_from_error(error): - if "pii" in error.target.lower(): - return AnalyzeActionsType.RECOGNIZE_PII_ENTITIES - if "entityrecognition" in error.target.lower(): - return AnalyzeActionsType.RECOGNIZE_ENTITIES - if "entitylinking" in error.target.lower(): - return AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES - if "sentiment" in error.target.lower(): - return AnalyzeActionsType.ANALYZE_SENTIMENT - return AnalyzeActionsType.EXTRACT_KEY_PHRASES - -def _get_mapped_errors(analyze_job_state): - """ - """ - mapped_errors = defaultdict(list) - if not analyze_job_state.errors: - return mapped_errors - for error in analyze_job_state.errors: - mapped_errors[_get_task_type_from_error(error)].append((_get_error_index(error), error)) - return mapped_errors - -def _get_error_index(error): - return _get_indices(error.target)[-1] - def _get_good_result(current_task_type, index_of_task_result, doc_id_order, response_headers, returned_tasks_object): deserialization_callback = _get_deserialization_callback_from_task_type(current_task_type) property_name = _get_property_name_from_task_type(current_task_type) response_task_to_deserialize = getattr(returned_tasks_object, property_name)[index_of_task_result] - document_results = deserialization_callback( + return deserialization_callback( doc_id_order, response_task_to_deserialize.results, response_headers, lro=True ) - return AnalyzeActionsResult( - document_results=document_results, - statistics=RequestStatistics._from_generated( # pylint: disable=protected-access - response_task_to_deserialize.results.statistics - ) if response_task_to_deserialize.results.statistics else None, - action_type=current_task_type, - completed_on=response_task_to_deserialize.last_update_date_time, - ) def get_iter_items(doc_id_order, task_order, response_headers, analyze_job_state): - iter_items = [] + iter_items = defaultdict(list) # map doc id to action results task_type_to_index = defaultdict(int) # need to keep track of how many of each type of tasks we've seen returned_tasks_object = analyze_job_state.tasks - mapped_errors = _get_mapped_errors(analyze_job_state) for current_task_type in task_order: index_of_task_result = task_type_to_index[current_task_type] + results = _get_good_result( + current_task_type, + index_of_task_result, + doc_id_order, + response_headers, + returned_tasks_object, + ) + for result in results: + iter_items[result.id].append(result) - try: - # try to deserailize as error. If fails, we know it's good - # kind of a weird way to order things, but we can fail when deserializing - # the curr response as an error, not when deserializing as a good response. - - current_task_type_errors = mapped_errors[current_task_type] - error = next(err for err in current_task_type_errors if err[0] == index_of_task_result) - result = AnalyzeActionsError._from_generated(error[1]) # pylint: disable=protected-access - except StopIteration: - result = _get_good_result( - current_task_type, index_of_task_result, doc_id_order, response_headers, returned_tasks_object - ) - iter_items.append(result) task_type_to_index[current_task_type] += 1 - return iter_items + return [ + iter_items[doc_id] + for doc_id in doc_id_order + if doc_id in iter_items + ] def analyze_extract_page_data(doc_id_order, task_order, response_headers, analyze_job_state): # return next link, list of diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py index c8737a4501fc..525e6a379974 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/_text_analytics_client.py @@ -36,7 +36,7 @@ _get_deserialize ) -from ._models import AnalyzeActionsType +from ._models import _AnalyzeActionsType from ._lro import ( TextAnalyticsOperationResourcePolling, @@ -62,7 +62,6 @@ ExtractKeyPhrasesAction, AnalyzeSentimentAction, AnalyzeHealthcareEntitiesResultItem, - AnalyzeActionsResult, ) @@ -830,9 +829,13 @@ def begin_analyze_actions( # type: ignore documents, # type: Union[List[str], List[TextDocumentInput], List[Dict[str, str]]] actions, # type: List[Union[RecognizeEntitiesAction, RecognizeLinkedEntitiesAction, RecognizePiiEntitiesAction, ExtractKeyPhrasesAction, AnalyzeSentimentAction]] # pylint: disable=line-too-long **kwargs # type: Any - ): # type: (...) -> LROPoller[ItemPaged[AnalyzeActionsResult]] + ): # type: (...) -> LROPoller[ItemPaged[List[Union[RecognizeEntitiesResult, RecognizeLinkedEntitiesResult, RecognizePiiEntitiesResult, ExtractKeyPhrasesResult, AnalyzeSentimentResult]]]] # pylint: disable=line-too-long """Start a long-running operation to perform a variety of text analysis actions over a batch of documents. + We recommend you use this function if you're looking to analyze larger documents, and / or + combine multiple Text Analytics actions into one call. Otherwise, we recommend you use + the action specific endpoints, for example :func:`analyze_sentiment`: + :param documents: The set of documents to process as part of this batch. If you wish to specify the ID and language on a per-item basis you must use as input a list[:class:`~azure.ai.textanalytics.TextDocumentInput`] or a list of @@ -858,11 +861,22 @@ def begin_analyze_actions( # type: ignore :keyword int polling_interval: Waiting time between two polls for LRO operations if no Retry-After header is present. Defaults to 30 seconds. :return: An instance of an LROPoller. Call `result()` on the poller - object to return a pageable heterogeneous list of the action results in the order - the actions were sent in this method. + object to return a pageable heterogeneous list of lists. This list of lists is first ordered + by the documents you input, then ordered by the actions you input. For example, + if you have documents input ["Hello", "world"], and actions + :class:`~azure.ai.textanalytics.RecognizeEntitiesAction` and + :class:`~azure.ai.textanalytics.AnalyzeSentimentAction`, when iterating over the list of lists, + you will first iterate over the action results for the "Hello" document, getting the + :class:`~azure.ai.textanalytics.RecognizeEntitiesResult` of "Hello", + then the :class:`~azure.ai.textanalytics.AnalyzeSentimentResult` of "Hello". + Then, you will get the :class:`~azure.ai.textanalytics.RecognizeEntitiesResult` and + :class:`~azure.ai.textanalytics.AnalyzeSentimentResult` of "world". :rtype: ~azure.core.polling.LROPoller[~azure.core.paging.ItemPaged[ - ~azure.ai.textanalytics.AnalyzeActionsResult]] + list[ + RecognizeEntitiesResult or RecognizeLinkedEntitiesResult or RecognizePiiEntitiesResult or + ExtractKeyPhrasesResult or AnalyzeSentimentResult + ]]] :raises ~azure.core.exceptions.HttpResponseError or TypeError or ValueError or NotImplementedError: .. admonition:: Example: @@ -893,26 +907,26 @@ def begin_analyze_actions( # type: ignore analyze_tasks = self._client.models(api_version='v3.1-preview.5').JobManifestTasks( entity_recognition_tasks=[ t.to_generated() for t in - [a for a in actions if _determine_action_type(a) == AnalyzeActionsType.RECOGNIZE_ENTITIES] + [a for a in actions if _determine_action_type(a) == _AnalyzeActionsType.RECOGNIZE_ENTITIES] ], entity_recognition_pii_tasks=[ t.to_generated() for t in - [a for a in actions if _determine_action_type(a) == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES] + [a for a in actions if _determine_action_type(a) == _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES] ], key_phrase_extraction_tasks=[ t.to_generated() for t in - [a for a in actions if _determine_action_type(a) == AnalyzeActionsType.EXTRACT_KEY_PHRASES] + [a for a in actions if _determine_action_type(a) == _AnalyzeActionsType.EXTRACT_KEY_PHRASES] ], entity_linking_tasks=[ t.to_generated() for t in [ a for a in actions - if _determine_action_type(a) == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES + if _determine_action_type(a) == _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES ] ], sentiment_analysis_tasks=[ t.to_generated() for t in - [a for a in actions if _determine_action_type(a) == AnalyzeActionsType.ANALYZE_SENTIMENT] + [a for a in actions if _determine_action_type(a) == _AnalyzeActionsType.ANALYZE_SENTIMENT] ] ) analyze_body = self._client.models(api_version='v3.1-preview.5').AnalyzeBatchInput( diff --git a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py index c64a395f7494..e75751f385ed 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/azure/ai/textanalytics/aio/_text_analytics_client_async.py @@ -43,8 +43,7 @@ RecognizeEntitiesAction, RecognizePiiEntitiesAction, ExtractKeyPhrasesAction, - AnalyzeActionsResult, - AnalyzeActionsType, + _AnalyzeActionsType, RecognizeLinkedEntitiesAction, AnalyzeSentimentAction ) @@ -812,9 +811,13 @@ async def begin_analyze_actions( # type: ignore documents, # type: Union[List[str], List[TextDocumentInput], List[Dict[str, str]]] actions, # type: List[Union[RecognizeEntitiesAction, RecognizeLinkedEntitiesAction, RecognizePiiEntitiesAction, ExtractKeyPhrasesAction, AnalyzeSentimentAction]] # pylint: disable=line-too-long **kwargs # type: Any - ): # type: (...) -> AsyncLROPoller[AsyncItemPaged[AnalyzeActionsResult]] + ): # type: (...) -> AsyncLROPoller[AsyncItemPaged[List[Union[RecognizeEntitiesResult, RecognizeLinkedEntitiesResult, RecognizePiiEntitiesResult, ExtractKeyPhrasesResult, AnalyzeSentimentResult]]]] # pylint: disable=line-too-long """Start a long-running operation to perform a variety of text analysis actions over a batch of documents. + We recommend you use this function if you're looking to analyze larger documents, and / or + combine multiple Text Analytics actions into one call. Otherwise, we recommend you use + the action specific endpoints, for example :func:`analyze_sentiment`: + :param documents: The set of documents to process as part of this batch. If you wish to specify the ID and language on a per-item basis you must use as input a list[:class:`~azure.ai.textanalytics.TextDocumentInput`] or a list of @@ -840,11 +843,22 @@ async def begin_analyze_actions( # type: ignore :keyword int polling_interval: Waiting time between two polls for LRO operations if no Retry-After header is present. Defaults to 30 seconds. :return: An instance of an LROPoller. Call `result()` on the poller - object to return a pageable heterogeneous list of the action results in the order - the actions were sent in this method. + object to return a pageable heterogeneous list of lists. This list of lists is first ordered + by the documents you input, then ordered by the actions you input. For example, + if you have documents input ["Hello", "world"], and actions + :class:`~azure.ai.textanalytics.RecognizeEntitiesAction` and + :class:`~azure.ai.textanalytics.AnalyzeSentimentAction`, when iterating over the list of lists, + you will first iterate over the action results for the "Hello" document, getting the + :class:`~azure.ai.textanalytics.RecognizeEntitiesResult` of "Hello", + then the :class:`~azure.ai.textanalytics.AnalyzeSentimentResult` of "Hello". + Then, you will get the :class:`~azure.ai.textanalytics.RecognizeEntitiesResult` and + :class:`~azure.ai.textanalytics.AnalyzeSentimentResult` of "world". :rtype: ~azure.core.polling.AsyncLROPoller[~azure.core.async_paging.AsyncItemPaged[ - ~azure.ai.textanalytics.AnalyzeActionsResult]] + list[ + RecognizeEntitiesResult or RecognizeLinkedEntitiesResult or RecognizePiiEntitiesResult or + ExtractKeyPhrasesResult or AnalyzeSentimentResult + ]]] :raises ~azure.core.exceptions.HttpResponseError or TypeError or ValueError or NotImplementedError: .. admonition:: Example: @@ -875,26 +889,26 @@ async def begin_analyze_actions( # type: ignore analyze_tasks = self._client.models(api_version='v3.1-preview.5').JobManifestTasks( entity_recognition_tasks=[ t.to_generated() for t in - [a for a in actions if _determine_action_type(a) == AnalyzeActionsType.RECOGNIZE_ENTITIES] + [a for a in actions if _determine_action_type(a) == _AnalyzeActionsType.RECOGNIZE_ENTITIES] ], entity_recognition_pii_tasks=[ t.to_generated() for t in - [a for a in actions if _determine_action_type(a) == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES] + [a for a in actions if _determine_action_type(a) == _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES] ], key_phrase_extraction_tasks=[ t.to_generated() for t in - [a for a in actions if _determine_action_type(a) == AnalyzeActionsType.EXTRACT_KEY_PHRASES] + [a for a in actions if _determine_action_type(a) == _AnalyzeActionsType.EXTRACT_KEY_PHRASES] ], entity_linking_tasks=[ t.to_generated() for t in [ a for a in actions if \ - _determine_action_type(a) == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES + _determine_action_type(a) == _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES ] ], sentiment_analysis_tasks=[ t.to_generated() for t in - [a for a in actions if _determine_action_type(a) == AnalyzeActionsType.ANALYZE_SENTIMENT] + [a for a in actions if _determine_action_type(a) == _AnalyzeActionsType.ANALYZE_SENTIMENT] ] ) analyze_body = self._client.models(api_version='v3.1-preview.5').AnalyzeBatchInput( diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py index d8894f025c51..eee731ad257b 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py @@ -39,7 +39,7 @@ async def analyze_async(self): RecognizePiiEntitiesAction, ExtractKeyPhrasesAction, AnalyzeSentimentAction, - AnalyzeActionsType + _AnalyzeActionsType ) endpoint = os.environ["AZURE_TEXT_ANALYTICS_ENDPOINT"] @@ -51,12 +51,15 @@ async def analyze_async(self): ) documents = [ - "We went to Contoso Steakhouse located at midtown NYC last week for a dinner party, and we adore the spot! \ - They provide marvelous food and they have a great menu. The chief cook happens to be the owner (I think his name is John Doe) \ - and he is super nice, coming out of the kitchen and greeted us all. We enjoyed very much dining in the place! \ - The Sirloin steak I ordered was tender and juicy, and the place was impeccably clean. You can even pre-order from their \ - online menu at www.contososteakhouse.com, call 312-555-0176 or send email to order@contososteakhouse.com! \ - The only complaint I have is the food didn't come fast enough. Overall I highly recommend it!" + 'We went to Contoso Steakhouse located at midtown NYC last week for a dinner party, and we adore the spot!'\ + 'They provide marvelous food and they have a great menu. The chief cook happens to be the owner (I think his name is John Doe)'\ + 'and he is super nice, coming out of the kitchen and greeted us all.'\ + , + + 'We enjoyed very much dining in the place!'\ + 'The Sirloin steak I ordered was tender and juicy, and the place was impeccably clean. You can even pre-order from their'\ + 'online menu at www.contososteakhouse.com, call 312-555-0176 or send email to order@contososteakhouse.com!'\ + 'The only complaint I have is the food didn\'t come fast enough. Overall I highly recommend it!'\ ] async with text_analytics_client: @@ -72,71 +75,66 @@ async def analyze_async(self): ] ) - result = await poller.result() - - async for action_result in result: - if action_result.is_error: - raise ValueError( - "Action has failed with message: {}".format( - action_result.error.message - ) - ) - if action_result.action_type == AnalyzeActionsType.RECOGNIZE_ENTITIES: - print("Results of Entities Recognition action:") - for idx, doc in enumerate(action_result.document_results): - print("\nDocument text: {}".format(documents[idx])) - for entity in doc.entities: - print("Entity: {}".format(entity.text)) - print("...Category: {}".format(entity.category)) - print("...Confidence Score: {}".format(entity.confidence_score)) - print("...Offset: {}".format(entity.offset)) - print("------------------------------------------") - - if action_result.action_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES: - print("Results of PII Entities Recognition action:") - for idx, doc in enumerate(action_result.document_results): - print("Document text: {}".format(documents[idx])) - for entity in doc.entities: - print("Entity: {}".format(entity.text)) - print("Category: {}".format(entity.category)) - print("Confidence Score: {}\n".format(entity.confidence_score)) - print("------------------------------------------") - - if action_result.action_type == AnalyzeActionsType.EXTRACT_KEY_PHRASES: - print("Results of Key Phrase Extraction action:") - for idx, doc in enumerate(action_result.document_results): - print("Document text: {}\n".format(documents[idx])) - print("Key Phrases: {}\n".format(doc.key_phrases)) - print("------------------------------------------") - - if action_result.action_type == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES: - print("Results of Linked Entities Recognition action:") - for idx, doc in enumerate(action_result.document_results): - print("Document text: {}\n".format(documents[idx])) - for linked_entity in doc.entities: - print("Entity name: {}".format(linked_entity.name)) - print("...Data source: {}".format(linked_entity.data_source)) - print("...Data source language: {}".format(linked_entity.language)) - print("...Data source entity ID: {}".format(linked_entity.data_source_entity_id)) - print("...Data source URL: {}".format(linked_entity.url)) - print("...Document matches:") - for match in linked_entity.matches: - print("......Match text: {}".format(match.text)) - print(".........Confidence Score: {}".format(match.confidence_score)) - print(".........Offset: {}".format(match.offset)) - print(".........Length: {}".format(match.length)) - print("------------------------------------------") - - if action_result.action_type == AnalyzeActionsType.ANALYZE_SENTIMENT: - print("Results of Sentiment Analysis action:") - for doc in action_result.document_results: - print("Overall sentiment: {}".format(doc.sentiment)) - print("Scores: positive={}; neutral={}; negative={} \n".format( - doc.confidence_scores.positive, - doc.confidence_scores.neutral, - doc.confidence_scores.negative, - )) - print("------------------------------------------") + pages = await poller.result() + + # To enumerate / zip for async, unless you install a third party library, + # you have to read in all of the elements into memory first. + # If you're not looking to enumerate / zip, we recommend you just asynchronously + # loop over it immediately, without going through this step of reading them into memory + result = [] + async for page in pages: + result.append(page) + + for doc, document_results in zip(documents, result): + print("\nDocument text: {}".format(doc)) + recognize_entities_result = document_results[0] + assert not recognize_entities_result.is_error + print("...Results of Recognize Entities Action:") + for entity in recognize_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + print(".........Offset: {}".format(entity.offset)) + + recognize_pii_entities_result = document_results[1] + assert not recognize_pii_entities_result.is_error + print("...Results of Recognize PII Entities action:") + for entity in recognize_pii_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + + extract_key_phrases_result = document_results[2] + assert not extract_key_phrases_result.is_error + print("...Results of Extract Key Phrases action:") + print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) + + recognize_linked_entities_result = document_results[3] + assert not recognize_linked_entities_result.is_error + print("...Results of Recognize Linked Entities action:") + for linked_entity in recognize_linked_entities_result.entities: + print("......Entity name: {}".format(linked_entity.name)) + print(".........Data source: {}".format(linked_entity.data_source)) + print(".........Data source language: {}".format(linked_entity.language)) + print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) + print(".........Data source URL: {}".format(linked_entity.url)) + print(".........Document matches:") + for match in linked_entity.matches: + print("............Match text: {}".format(match.text)) + print("............Confidence Score: {}".format(match.confidence_score)) + print("............Offset: {}".format(match.offset)) + print("............Length: {}".format(match.length)) + + analyze_sentiment_result = document_results[4] + assert not analyze_sentiment_result.is_error + print("...Results of Analyze Sentiment action:") + print("......Overall sentiment: {}".format(analyze_sentiment_result.sentiment)) + print("......Scores: positive={}; neutral={}; negative={} \n".format( + analyze_sentiment_result.confidence_scores.positive, + analyze_sentiment_result.confidence_scores.neutral, + analyze_sentiment_result.confidence_scores.negative, + )) + print("------------------------------------------") # [END analyze_async] diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_actions.py b/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_actions.py index c613fd6d45a5..7f5d33e15a16 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_actions.py +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_actions.py @@ -50,12 +50,15 @@ def analyze(self): ) documents = [ - "We went to Contoso Steakhouse located at midtown NYC last week for a dinner party, and we adore the spot! \ - They provide marvelous food and they have a great menu. The chief cook happens to be the owner (I think his name is John Doe) \ - and he is super nice, coming out of the kitchen and greeted us all. We enjoyed very much dining in the place! \ - The Sirloin steak I ordered was tender and juicy, and the place was impeccably clean. You can even pre-order from their \ - online menu at www.contososteakhouse.com, call 312-555-0176 or send email to order@contososteakhouse.com! \ - The only complaint I have is the food didn't come fast enough. Overall I highly recommend it!" + 'We went to Contoso Steakhouse located at midtown NYC last week for a dinner party, and we adore the spot!'\ + 'They provide marvelous food and they have a great menu. The chief cook happens to be the owner (I think his name is John Doe)'\ + 'and he is super nice, coming out of the kitchen and greeted us all.'\ + , + + 'We enjoyed very much dining in the place!'\ + 'The Sirloin steak I ordered was tender and juicy, and the place was impeccably clean. You can even pre-order from their'\ + 'online menu at www.contososteakhouse.com, call 312-555-0176 or send email to order@contososteakhouse.com!'\ + 'The only complaint I have is the food didn\'t come fast enough. Overall I highly recommend it!'\ ] poller = text_analytics_client.begin_analyze_actions( @@ -71,76 +74,54 @@ def analyze(self): ) result = poller.result() - action_results = [action_result for action_result in list(result) if not action_result.is_error] - - first_action_result = action_results[0] - print("Results of Entities Recognition action:") - docs = [doc for doc in first_action_result.document_results if not doc.is_error] - - for idx, doc in enumerate(docs): - print("\nDocument text: {}".format(documents[idx])) - for entity in doc.entities: - print("Entity: {}".format(entity.text)) - print("...Category: {}".format(entity.category)) - print("...Confidence Score: {}".format(entity.confidence_score)) - print("...Offset: {}".format(entity.offset)) - print("...Length: {}".format(entity.length)) - print("------------------------------------------") - - second_action_result = action_results[1] - print("Results of PII Entities Recognition action:") - docs = [doc for doc in second_action_result.document_results if not doc.is_error] - - for idx, doc in enumerate(docs): - print("Document text: {}".format(documents[idx])) - print("Document text with redactions: {}".format(doc.redacted_text)) - for entity in doc.entities: - print("Entity: {}".format(entity.text)) - print("...Category: {}".format(entity.category)) - print("...Confidence Score: {}\n".format(entity.confidence_score)) - print("...Offset: {}".format(entity.offset)) - print("...Length: {}".format(entity.length)) - print("------------------------------------------") - - third_action_result = action_results[2] - print("Results of Key Phrase Extraction action:") - docs = [doc for doc in third_action_result.document_results if not doc.is_error] - - for idx, doc in enumerate(docs): - print("Document text: {}\n".format(documents[idx])) - print("Key Phrases: {}\n".format(doc.key_phrases)) - print("------------------------------------------") - - fourth_action_result = action_results[3] - print("Results of Linked Entities Recognition action:") - docs = [doc for doc in fourth_action_result.document_results if not doc.is_error] - - for idx, doc in enumerate(docs): - print("Document text: {}\n".format(documents[idx])) - for linked_entity in doc.entities: - print("Entity name: {}".format(linked_entity.name)) - print("...Data source: {}".format(linked_entity.data_source)) - print("...Data source language: {}".format(linked_entity.language)) - print("...Data source entity ID: {}".format(linked_entity.data_source_entity_id)) - print("...Data source URL: {}".format(linked_entity.url)) - print("...Document matches:") + for doc, document_results in zip(documents, result): + print("\nDocument text: {}".format(doc)) + recognize_entities_result = document_results[0] + assert not recognize_entities_result.is_error + print("...Results of Recognize Entities Action:") + for entity in recognize_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + print(".........Offset: {}".format(entity.offset)) + + recognize_pii_entities_result = document_results[1] + assert not recognize_pii_entities_result.is_error + print("...Results of Recognize PII Entities action:") + for entity in recognize_pii_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + + extract_key_phrases_result = document_results[2] + assert not extract_key_phrases_result.is_error + print("...Results of Extract Key Phrases action:") + print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) + + recognize_linked_entities_result = document_results[3] + assert not recognize_linked_entities_result.is_error + print("...Results of Recognize Linked Entities action:") + for linked_entity in recognize_linked_entities_result.entities: + print("......Entity name: {}".format(linked_entity.name)) + print(".........Data source: {}".format(linked_entity.data_source)) + print(".........Data source language: {}".format(linked_entity.language)) + print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) + print(".........Data source URL: {}".format(linked_entity.url)) + print(".........Document matches:") for match in linked_entity.matches: - print("......Match text: {}".format(match.text)) - print(".........Confidence Score: {}".format(match.confidence_score)) - print(".........Offset: {}".format(match.offset)) - print(".........Length: {}".format(match.length)) - print("------------------------------------------") - - fifth_action_result = action_results[4] - print("Results of Sentiment Analysis action:") - docs = [doc for doc in fifth_action_result.document_results if not doc.is_error] - - for doc in docs: - print("Overall sentiment: {}".format(doc.sentiment)) - print("Scores: positive={}; neutral={}; negative={} \n".format( - doc.confidence_scores.positive, - doc.confidence_scores.neutral, - doc.confidence_scores.negative, + print("............Match text: {}".format(match.text)) + print("............Confidence Score: {}".format(match.confidence_score)) + print("............Offset: {}".format(match.offset)) + print("............Length: {}".format(match.length)) + + analyze_sentiment_result = document_results[4] + assert not analyze_sentiment_result.is_error + print("...Results of Analyze Sentiment action:") + print("......Overall sentiment: {}".format(analyze_sentiment_result.sentiment)) + print("......Scores: positive={}; neutral={}; negative={} \n".format( + analyze_sentiment_result.confidence_scores.positive, + analyze_sentiment_result.confidence_scores.neutral, + analyze_sentiment_result.confidence_scores.negative, )) print("------------------------------------------") diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze.py b/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze.py index 55819cfad94c..62041d0b1f3a 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze.py +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze.py @@ -4,6 +4,7 @@ # Licensed under the MIT License. # ------------------------------------ +from collections import defaultdict import os import pytest import platform @@ -26,7 +27,12 @@ TextDocumentInput, VERSION, TextAnalyticsApiVersion, - AnalyzeActionsType, + _AnalyzeActionsType, + ExtractKeyPhrasesResult, + AnalyzeSentimentResult, + RecognizeLinkedEntitiesResult, + RecognizeEntitiesResult, + RecognizePiiEntitiesResult, ) # pre-apply the client_cls positional argument so it needn't be explicitly passed below @@ -57,19 +63,17 @@ def test_all_successful_passing_dict_key_phrase_task(self, client): polling_interval=self._interval(), ).result() - action_results = list(response) + document_results = list(response) - assert len(action_results) == 1 - action_result = action_results[0] - - assert action_result.action_type == AnalyzeActionsType.EXTRACT_KEY_PHRASES - assert len(action_result.document_results) == len(docs) - - for doc in action_result.document_results: - self.assertIn("Paul Allen", doc.key_phrases) - self.assertIn("Bill Gates", doc.key_phrases) - self.assertIn("Microsoft", doc.key_phrases) - self.assertIsNotNone(doc.id) + assert len(document_results) == 2 + for document_result in document_results: + assert len(document_result) == 1 + for document_result in document_result: + assert isinstance(document_result, ExtractKeyPhrasesResult) + assert "Paul Allen" in document_result.key_phrases + assert "Bill Gates" in document_result.key_phrases + assert "Microsoft" in document_result.key_phrases + assert document_result.id is not None @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() @@ -85,33 +89,31 @@ def test_all_successful_passing_dict_sentiment_task(self, client): polling_interval=self._interval(), ).result() - action_results = list(response) - - assert len(action_results) == 1 - action_result = action_results[0] - - assert action_result.action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - assert len(action_result.document_results) == len(docs) - - self.assertEqual(action_result.document_results[0].sentiment, "neutral") - self.assertEqual(action_result.document_results[1].sentiment, "negative") - self.assertEqual(action_result.document_results[2].sentiment, "positive") - - for doc in action_result.document_results: - self.assertIsNotNone(doc.id) - self.assertIsNotNone(doc.statistics) - self.validateConfidenceScores(doc.confidence_scores) - self.assertIsNotNone(doc.sentences) - - self.assertEqual(len(action_result.document_results[0].sentences), 1) - self.assertEqual(action_result.document_results[0].sentences[0].text, "Microsoft was founded by Bill Gates and Paul Allen.") - self.assertEqual(len(action_result.document_results[1].sentences), 2) - self.assertEqual(action_result.document_results[1].sentences[0].text, "I did not like the hotel we stayed at.") - self.assertEqual(action_result.document_results[1].sentences[1].text, "It was too expensive.") - self.assertEqual(len(action_result.document_results[2].sentences), 2) - self.assertEqual(action_result.document_results[2].sentences[0].text, "The restaurant had really good food.") - self.assertEqual(action_result.document_results[2].sentences[1].text, "I recommend you try it.") - + pages = list(response) + + assert len(pages) == len(docs) + for idx, document_results in enumerate(pages): + assert len(document_results) == 1 + document_result = document_results[0] + assert isinstance(document_result, AnalyzeSentimentResult) + assert document_result.id is not None + assert document_result.statistics is not None + self.validateConfidenceScores(document_result.confidence_scores) + assert document_result.sentences is not None + if idx == 0: + assert document_result.sentiment == "neutral" + assert len(document_result.sentences) == 1 + assert document_result.sentences[0].text == "Microsoft was founded by Bill Gates and Paul Allen." + elif idx == 1: + assert document_result.sentiment == "negative" + assert len(document_result.sentences) == 2 + assert document_result.sentences[0].text == "I did not like the hotel we stayed at." + assert document_result.sentences[1].text == "It was too expensive." + else: + assert document_result.sentiment == "positive" + assert len(document_result.sentences) == 2 + assert document_result.sentences[0].text == "The restaurant had really good food." + assert document_result.sentences[1].text == "I recommend you try it." @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() @@ -128,16 +130,14 @@ def test_sentiment_analysis_task_with_opinion_mining(self, client): polling_interval=self._interval(), ).result() - action_results = list(response) - - assert len(action_results) == 1 - action_result = action_results[0] + pages = list(response) - assert action_result.action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - assert len(action_result.document_results) == len(documents) - - for idx, doc in enumerate(action_result.document_results): - for sentence in doc.sentences: + assert len(pages) == len(documents) + for idx, document_results in enumerate(pages): + assert len(document_results) == 1 + document_result = document_results[0] + assert isinstance(document_result, AnalyzeSentimentResult) + for sentence in document_result.sentences: if idx == 0: for mined_opinion in sentence.mined_opinions: target = mined_opinion.target @@ -206,19 +206,20 @@ def test_all_successful_passing_text_document_input_entities_task(self, client): polling_interval=self._interval(), ).result() - action_results = list(response) - - assert len(action_results) == 1 - action_result = action_results[0] - - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_ENTITIES - assert len(action_result.document_results) == len(docs) - - for doc in action_result.document_results: - self.assertEqual(len(doc.entities), 4) - self.assertIsNotNone(doc.id) - for entity in doc.entities: - self.assertIsNotNone(entity.text) + pages = list(response) + assert len(pages) == len(docs) + + for document_results in pages: + assert len(document_results) == 1 + document_result = document_results[0] + assert isinstance(document_result, RecognizeEntitiesResult) + assert len(document_result.entities) == 4 + assert document_result.id is not None + for entity in document_result.entities: + assert entity.text is not None + assert entity.category is not None + assert entity.offset is not None + assert entity.confidence_score is not None self.assertIsNotNone(entity.category) self.assertIsNotNone(entity.offset) self.assertIsNotNone(entity.confidence_score) @@ -239,29 +240,23 @@ def test_all_successful_passing_string_pii_entities_task(self, client): polling_interval=self._interval(), ).result() - action_results = list(response) - - assert len(action_results) == 1 - action_result = action_results[0] - - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES - assert len(action_result.document_results) == len(docs) - - self.assertEqual(action_result.document_results[0].entities[0].text, "859-98-0987") - self.assertEqual(action_result.document_results[0].entities[0].category, "USSocialSecurityNumber") - self.assertEqual(action_result.document_results[1].entities[0].text, "111000025") - # self.assertEqual(results[1].entities[0].category, "ABA Routing Number") # Service is currently returning PhoneNumber here - - # commenting out brazil cpf, currently service is not returning it - # self.assertEqual(action_result.document_results[2].entities[0].text, "998.214.865-68") - # self.assertEqual(action_result.document_results[2].entities[0].category, "Brazil CPF Number") - for doc in action_result.document_results: - self.assertIsNotNone(doc.id) - for entity in doc.entities: - self.assertIsNotNone(entity.text) - self.assertIsNotNone(entity.category) - self.assertIsNotNone(entity.offset) - self.assertIsNotNone(entity.confidence_score) + pages = list(response) + assert len(pages) == len(docs) + + for idx, document_results in enumerate(pages): + assert len(document_results) == 1 + document_result = document_results[0] + assert isinstance(document_result, RecognizePiiEntitiesResult) + if idx == 0: + assert document_result.entities[0].text == "859-98-0987" + assert document_result.entities[0].category == "USSocialSecurityNumber" + elif idx == 1: + assert document_result.entities[0].text == "111000025" + for entity in document_result.entities: + assert entity.text is not None + assert entity.category is not None + assert entity.offset is not None + assert entity.confidence_score is not None @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() @@ -331,31 +326,36 @@ def test_out_of_order_ids_multiple_tasks(self, client): polling_interval=self._interval(), ).result() - action_results = list(response) - assert len(action_results) == 5 - - assert action_results[0].action_type == AnalyzeActionsType.RECOGNIZE_ENTITIES - assert action_results[1].action_type == AnalyzeActionsType.EXTRACT_KEY_PHRASES - assert action_results[2].action_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES - assert action_results[3].action_type == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES - assert action_results[4].action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - - action_results = [r for r in action_results if not r.is_error] - assert all([action_result for action_result in action_results if len(action_result.document_results) == len(docs)]) - - in_order = ["56", "0", "19", "1"] + results = list(response) + assert len(results) == len(docs) - for action_result in action_results: - for idx, resp in enumerate(action_result.document_results): - self.assertEqual(resp.id, in_order[idx]) + document_order = ["56", "0", "19", "1"] + action_order = [ + _AnalyzeActionsType.RECOGNIZE_ENTITIES, + _AnalyzeActionsType.EXTRACT_KEY_PHRASES, + _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES, + _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES, + _AnalyzeActionsType.ANALYZE_SENTIMENT, + ] + for doc_idx, document_results in enumerate(results): + assert len(document_results) == 5 + for action_idx, document_result in enumerate(document_results): + self.assertEqual(document_result.id, document_order[doc_idx]) + self.assertEqual(self.document_result_to_action_type(document_result), action_order[action_idx]) @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() def test_show_stats_and_model_version_multiple_tasks(self, client): def callback(resp): - if resp.raw_response: - a = "b" + if not resp.raw_response: + # this is the initial post call + request_body = json.loads(resp.http_request.body) + assert len(request_body["tasks"]) == 5 + for task in request_body["tasks"].values(): + assert len(task) == 1 + assert task[0]['parameters']['model-version'] == 'latest' + assert not task[0]['parameters']['loggingOptOut'] docs = [{"id": "56", "text": ":)"}, {"id": "0", "text": ":("}, @@ -378,20 +378,19 @@ def callback(resp): response = poller.result() - action_results = list(response) - assert len(action_results) == 5 - assert action_results[0].action_type == AnalyzeActionsType.RECOGNIZE_ENTITIES - assert action_results[1].action_type == AnalyzeActionsType.EXTRACT_KEY_PHRASES - assert action_results[2].action_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES - assert action_results[3].action_type == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES - assert action_results[4].action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - - assert all([action_result for action_result in action_results if len(action_result.document_results) == len(docs)]) - - for action_result in action_results: - assert action_result.statistics - for doc in action_result.document_results: - assert doc.statistics + pages = list(response) + assert len(pages) == len(docs) + action_order = [ + _AnalyzeActionsType.RECOGNIZE_ENTITIES, + _AnalyzeActionsType.EXTRACT_KEY_PHRASES, + _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES, + _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES, + _AnalyzeActionsType.ANALYZE_SENTIMENT, + ] + for document_results in pages: + assert len(document_results) == len(action_order) + for document_result in document_results: + assert document_result.statistics @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() @@ -407,7 +406,7 @@ def test_poller_metadata(self, client): polling_interval=self._interval(), ) - response = poller.result() + poller.result() assert isinstance(poller.created_on, datetime.datetime) poller._polling_method.display_name @@ -448,8 +447,8 @@ def test_poller_metadata(self, client): # raw_response_hook=callback # ).result()) - # for action_result in response: - # for doc in action_result.document_results: + # for document_result in response: + # for doc in document_result.document_results: # self.assertFalse(doc.is_error) # @GlobalTextAnalyticsAccountPreparer() @@ -483,8 +482,8 @@ def test_poller_metadata(self, client): # polling_interval=self._interval(), # ).result()) - # for action_result in response: - # for doc in action_result.document_results: + # for document_result in response: + # for doc in document_result.document_results: # assert not doc.is_error @GlobalTextAnalyticsAccountPreparer() @@ -503,8 +502,8 @@ def test_invalid_language_hint_method(self, client): polling_interval=self._interval(), ).result()) - for action_result in response: - for doc in action_result.document_results: + for document_results in response: + for doc in document_results: assert doc.is_error @GlobalTextAnalyticsAccountPreparer() @@ -513,7 +512,7 @@ def test_bad_model_version_error_multiple_tasks(self, client): docs = [{"id": "1", "language": "english", "text": "I did not like the hotel we stayed at."}] with pytest.raises(HttpResponseError): - response = client.begin_analyze_actions( + client.begin_analyze_actions( docs, actions=[ RecognizeEntitiesAction(model_version="latest"), @@ -531,7 +530,7 @@ def test_bad_model_version_error_all_tasks(self, client): # TODO: verify behavi docs = [{"id": "1", "language": "english", "text": "I did not like the hotel we stayed at."}] with self.assertRaises(HttpResponseError): - response = client.begin_analyze_actions( + client.begin_analyze_actions( docs, actions=[ RecognizeEntitiesAction(model_version="bad"), @@ -602,41 +601,27 @@ def test_multiple_pages_of_results_returned_successfully(self, client): polling_interval=self._interval(), ).result() - recognize_entities_results = [] - extract_key_phrases_results = [] - recognize_pii_entities_results = [] - recognize_linked_entities_results = [] - analyze_sentiment_results = [] - - action_results = list(result) - - # do 2 pages of 5 task results - for idx, action_result in enumerate(action_results): - if idx % 5 == 0: - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_ENTITIES - recognize_entities_results.append(action_result) - elif idx % 5 == 1: - assert action_result.action_type == AnalyzeActionsType.EXTRACT_KEY_PHRASES - extract_key_phrases_results.append(action_result) - elif idx % 5 == 2: - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES - recognize_pii_entities_results.append(action_result) - elif idx % 5 == 3: - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES - recognize_linked_entities_results.append(action_result) - else: - assert action_result.action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - analyze_sentiment_results.append(action_result) - if idx < 5: # first page of task results - assert len(action_result.document_results) == 20 - else: - assert len(action_result.document_results) == 5 - - assert all([action_result for action_result in recognize_entities_results if len(action_result.document_results) == len(docs)]) - assert all([action_result for action_result in extract_key_phrases_results if len(action_result.document_results) == len(docs)]) - assert all([action_result for action_result in recognize_pii_entities_results if len(action_result.document_results) == len(docs)]) - assert all([action_result for action_result in recognize_linked_entities_results if len(action_result.document_results) == len(docs)]) - assert all([action_result for action_result in analyze_sentiment_results if len(action_result.document_results) == len(docs)]) + document_results = list(result) + assert len(document_results) == len(docs) + action_order = [ + _AnalyzeActionsType.RECOGNIZE_ENTITIES, + _AnalyzeActionsType.EXTRACT_KEY_PHRASES, + _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES, + _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES, + _AnalyzeActionsType.ANALYZE_SENTIMENT, + ] + action_type_to_document_results = defaultdict(list) + + for doc_idx, document_result in enumerate(document_results): + for action_idx, document_result in enumerate(document_result): + self.assertEqual(document_result.id, str(doc_idx)) + action_type = self.document_result_to_action_type(document_result) + self.assertEqual(action_type, action_order[action_idx]) + action_type_to_document_results[action_type].append(document_result) + + assert len(action_type_to_document_results) == len(action_order) + for document_results in action_type_to_document_results.values(): + assert len(document_results) == len(docs) @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze_async.py b/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze_async.py index 32253f51810b..dffae7ad4266 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze_async.py @@ -3,6 +3,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +from collections import defaultdict import datetime import os import pytest @@ -22,14 +23,17 @@ from azure.ai.textanalytics.aio import TextAnalyticsClient from azure.ai.textanalytics import ( TextDocumentInput, - VERSION, - TextAnalyticsApiVersion, RecognizeEntitiesAction, RecognizeLinkedEntitiesAction, RecognizePiiEntitiesAction, ExtractKeyPhrasesAction, AnalyzeSentimentAction, - AnalyzeActionsType + _AnalyzeActionsType, + RecognizePiiEntitiesResult, + RecognizeEntitiesResult, + RecognizeLinkedEntitiesResult, + AnalyzeSentimentResult, + ExtractKeyPhrasesResult, ) # pre-apply the client_cls positional argument so it needn't be explicitly passed below @@ -73,20 +77,19 @@ async def test_all_successful_passing_dict_key_phrase_task(self, client): polling_interval=self._interval() )).result() - action_results = [] + document_results = [] async for p in response: - action_results.append(p) - assert len(action_results) == 1 - action_result = action_results[0] - - assert action_result.action_type == AnalyzeActionsType.EXTRACT_KEY_PHRASES - assert len(action_result.document_results) == len(docs) - - for doc in action_result.document_results: - self.assertIn("Paul Allen", doc.key_phrases) - self.assertIn("Bill Gates", doc.key_phrases) - self.assertIn("Microsoft", doc.key_phrases) - self.assertIsNotNone(doc.id) + document_results.append(p) + assert len(document_results) == 2 + + for document_result in document_results: + assert len(document_result) == 1 + for document_result in document_result: + assert isinstance(document_result, ExtractKeyPhrasesResult) + assert "Paul Allen" in document_result.key_phrases + assert "Bill Gates" in document_result.key_phrases + assert "Microsoft" in document_result.key_phrases + assert document_result.id is not None @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() @@ -103,34 +106,35 @@ async def test_all_successful_passing_dict_sentiment_task(self, client): polling_interval=self._interval(), )).result() - action_results = [] + pages = [] async for p in response: - action_results.append(p) - - assert len(action_results) == 1 - action_result = action_results[0] - - assert action_result.action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - assert len(action_result.document_results) == len(docs) - - self.assertEqual(action_result.document_results[0].sentiment, "neutral") - self.assertEqual(action_result.document_results[1].sentiment, "negative") - self.assertEqual(action_result.document_results[2].sentiment, "positive") - - for doc in action_result.document_results: - self.assertIsNotNone(doc.id) - self.assertIsNotNone(doc.statistics) - self.validateConfidenceScores(doc.confidence_scores) - self.assertIsNotNone(doc.sentences) - - self.assertEqual(len(action_result.document_results[0].sentences), 1) - self.assertEqual(action_result.document_results[0].sentences[0].text, "Microsoft was founded by Bill Gates and Paul Allen.") - self.assertEqual(len(action_result.document_results[1].sentences), 2) - self.assertEqual(action_result.document_results[1].sentences[0].text, "I did not like the hotel we stayed at.") - self.assertEqual(action_result.document_results[1].sentences[1].text, "It was too expensive.") - self.assertEqual(len(action_result.document_results[2].sentences), 2) - self.assertEqual(action_result.document_results[2].sentences[0].text, "The restaurant had really good food.") - self.assertEqual(action_result.document_results[2].sentences[1].text, "I recommend you try it.") + pages.append(p) + + assert len(pages) == len(docs) + assert len(pages) == len(docs) + + for idx, document_results in enumerate(pages): + assert len(document_results) == 1 + document_result = document_results[0] + assert isinstance(document_result, AnalyzeSentimentResult) + assert document_result.id is not None + assert document_result.statistics is not None + self.validateConfidenceScores(document_result.confidence_scores) + assert document_result.sentences is not None + if idx == 0: + assert document_result.sentiment == "neutral" + assert len(document_result.sentences) == 1 + assert document_result.sentences[0].text == "Microsoft was founded by Bill Gates and Paul Allen." + elif idx == 1: + assert document_result.sentiment == "negative" + assert len(document_result.sentences) == 2 + assert document_result.sentences[0].text == "I did not like the hotel we stayed at." + assert document_result.sentences[1].text == "It was too expensive." + else: + assert document_result.sentiment == "positive" + assert len(document_result.sentences) == 2 + assert document_result.sentences[0].text == "The restaurant had really good food." + assert document_result.sentences[1].text == "I recommend you try it." @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() @@ -148,18 +152,17 @@ async def test_sentiment_analysis_task_with_opinion_mining(self, client): polling_interval=self._interval(), )).result() - action_results = [] + pages = [] async for p in response: - action_results.append(p) - - assert len(action_results) == 1 - action_result = action_results[0] + pages.append(p) - assert action_result.action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - assert len(action_result.document_results) == len(documents) + assert len(pages) == len(documents) - for idx, doc in enumerate(action_result.document_results): - for sentence in doc.sentences: + for idx, document_results in enumerate(pages): + assert len(document_results) == 1 + document_result = document_results[0] + assert isinstance(document_result, AnalyzeSentimentResult) + for sentence in document_result.sentences: if idx == 0: for mined_opinion in sentence.mined_opinions: target = mined_opinion.target @@ -211,7 +214,6 @@ async def test_sentiment_analysis_task_with_opinion_mining(self, client): self.assertEqual('food', food_target.text) self.assertEqual('negative', food_target.sentiment) self.assertEqual(0.0, food_target.confidence_scores.neutral) - @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() async def test_all_successful_passing_text_document_input_entities_task(self, client): @@ -230,20 +232,22 @@ async def test_all_successful_passing_text_document_input_entities_task(self, cl ) response = await poller.result() - action_results = [] + pages = [] async for p in response: - action_results.append(p) - assert len(action_results) == 1 - action_result = action_results[0] - - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_ENTITIES - assert len(action_result.document_results) == len(docs) - - for doc in action_result.document_results: - self.assertEqual(len(doc.entities), 4) - self.assertIsNotNone(doc.id) - for entity in doc.entities: - self.assertIsNotNone(entity.text) + pages.append(p) + assert len(pages) == len(docs) + + for document_results in pages: + assert len(document_results) == 1 + document_result = document_results[0] + assert isinstance(document_result, RecognizeEntitiesResult) + assert len(document_result.entities) == 4 + assert document_result.id is not None + for entity in document_result.entities: + assert entity.text is not None + assert entity.category is not None + assert entity.offset is not None + assert entity.confidence_score is not None self.assertIsNotNone(entity.category) self.assertIsNotNone(entity.offset) self.assertIsNotNone(entity.confidence_score) @@ -265,30 +269,25 @@ async def test_all_successful_passing_string_pii_entities_task(self, client): polling_interval=self._interval() )).result() - action_results = [] + pages = [] async for p in response: - action_results.append(p) - assert len(action_results) == 1 - action_result = action_results[0] - - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES - assert len(action_result.document_results) == len(docs) - - self.assertEqual(action_result.document_results[0].entities[0].text, "859-98-0987") - self.assertEqual(action_result.document_results[0].entities[0].category, "USSocialSecurityNumber") - self.assertEqual(action_result.document_results[1].entities[0].text, "111000025") - # self.assertEqual(results[1].entities[0].category, "ABA Routing Number") # Service is currently returning PhoneNumber here - - # commenting out brazil cpf, currently service is not returning it - # self.assertEqual(action_result.document_results[2].entities[0].text, "998.214.865-68") - # self.assertEqual(action_result.document_results[2].entities[0].category, "Brazil CPF Number") - for doc in action_result.document_results: - self.assertIsNotNone(doc.id) - for entity in doc.entities: - self.assertIsNotNone(entity.text) - self.assertIsNotNone(entity.category) - self.assertIsNotNone(entity.offset) - self.assertIsNotNone(entity.confidence_score) + pages.append(p) + assert len(pages) == len(docs) + + for idx, document_results in enumerate(pages): + assert len(document_results) == 1 + document_result = document_results[0] + assert isinstance(document_result, RecognizePiiEntitiesResult) + if idx == 0: + assert document_result.entities[0].text == "859-98-0987" + assert document_result.entities[0].category == "USSocialSecurityNumber" + elif idx == 1: + assert document_result.entities[0].text == "111000025" + for entity in document_result.entities: + assert entity.text is not None + assert entity.category is not None + assert entity.offset is not None + assert entity.confidence_score is not None @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() @@ -297,7 +296,7 @@ async def test_bad_request_on_empty_document(self, client): with self.assertRaises(HttpResponseError): async with client: - response = await (await client.begin_analyze_actions( + await (await client.begin_analyze_actions( docs, actions=[ExtractKeyPhrasesAction()], polling_interval=self._interval() @@ -310,7 +309,7 @@ async def test_bad_request_on_empty_document(self, client): async def test_empty_credential_class(self, client): with self.assertRaises(ClientAuthenticationError): async with client: - response = await (await client.begin_analyze_actions( + await (await client.begin_analyze_actions( ["This is written in English."], actions=[ RecognizeEntitiesAction(), @@ -329,7 +328,7 @@ async def test_empty_credential_class(self, client): async def test_bad_credentials(self, client): with self.assertRaises(ClientAuthenticationError): async with client: - response = await (await client.begin_analyze_actions( + await (await client.begin_analyze_actions( ["This is written in English."], actions=[ RecognizeEntitiesAction(), @@ -362,30 +361,40 @@ async def test_out_of_order_ids_multiple_tasks(self, client): polling_interval=self._interval() )).result() - action_results = [] + results = [] async for p in response: - action_results.append(p) - assert len(action_results) == 5 - - assert action_results[0].action_type == AnalyzeActionsType.RECOGNIZE_ENTITIES - assert action_results[1].action_type == AnalyzeActionsType.EXTRACT_KEY_PHRASES - assert action_results[2].action_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES - assert action_results[3].action_type == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES - assert action_results[4].action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - - action_results = [r for r in action_results if not r.is_error] - - assert all([action_result for action_result in action_results if len(action_result.document_results) == len(docs)]) + results.append(p) + assert len(results) == len(docs) + + document_order = ["56", "0", "19", "1"] + action_order = [ + _AnalyzeActionsType.RECOGNIZE_ENTITIES, + _AnalyzeActionsType.EXTRACT_KEY_PHRASES, + _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES, + _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES, + _AnalyzeActionsType.ANALYZE_SENTIMENT, + ] + for doc_idx, document_results in enumerate(results): + assert len(document_results) == 5 + for action_idx, document_result in enumerate(document_results): + self.assertEqual(document_result.id, document_order[doc_idx]) + self.assertEqual(self.document_result_to_action_type(document_result), action_order[action_idx]) - in_order = ["56", "0", "19", "1"] - - for action_result in action_results: - for idx, resp in enumerate(action_result.document_results): - self.assertEqual(resp.id, in_order[idx]) @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() async def test_show_stats_and_model_version_multiple_tasks(self, client): + + def callback(resp): + if not resp.raw_response: + # this is the initial post call + request_body = json.loads(resp.http_request.body) + assert len(request_body["tasks"]) == 5 + for task in request_body["tasks"].values(): + assert len(task) == 1 + assert task[0]['parameters']['model-version'] == 'latest' + assert not task[0]['parameters']['loggingOptOut'] + docs = [{"id": "56", "text": ":)"}, {"id": "0", "text": ":("}, {"id": "19", "text": ":P"}, @@ -405,22 +414,22 @@ async def test_show_stats_and_model_version_multiple_tasks(self, client): polling_interval=self._interval() )).result() - action_results = [] + pages = [] async for p in response: - action_results.append(p) - assert len(action_results) == 5 - assert action_results[0].action_type == AnalyzeActionsType.RECOGNIZE_ENTITIES - assert action_results[1].action_type == AnalyzeActionsType.EXTRACT_KEY_PHRASES - assert action_results[2].action_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES - assert action_results[3].action_type == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES - assert action_results[4].action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - - assert all([action_result for action_result in action_results if len(action_result.document_results) == len(docs)]) - - for action_result in action_results: - assert action_result.statistics - for doc in action_result.document_results: - assert doc.statistics + pages.append(p) + assert len(pages) == len(docs) + + action_order = [ + _AnalyzeActionsType.RECOGNIZE_ENTITIES, + _AnalyzeActionsType.EXTRACT_KEY_PHRASES, + _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES, + _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES, + _AnalyzeActionsType.ANALYZE_SENTIMENT, + ] + for document_results in pages: + assert len(document_results) == len(action_order) + for document_result in document_results: + assert document_result.statistics @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() @@ -643,42 +652,30 @@ async def test_multiple_pages_of_results_returned_successfully(self, client): polling_interval=self._interval() )).result() - pages = [] + document_results = [] async for p in result: - pages.append(p) - - recognize_entities_results = [] - extract_key_phrases_results = [] - recognize_pii_entities_results = [] - recognize_linked_entities_results = [] - analyze_sentiment_results = [] - - for idx, action_result in enumerate(pages): - if idx % 5 == 0: - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_ENTITIES - recognize_entities_results.append(action_result) - elif idx % 5 == 1: - assert action_result.action_type == AnalyzeActionsType.EXTRACT_KEY_PHRASES - extract_key_phrases_results.append(action_result) - elif idx % 5 == 2: - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_PII_ENTITIES - recognize_pii_entities_results.append(action_result) - elif idx % 5 == 3: - assert action_result.action_type == AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES - recognize_linked_entities_results.append(action_result) - else: - assert action_result.action_type == AnalyzeActionsType.ANALYZE_SENTIMENT - analyze_sentiment_results.append(action_result) - if idx < 5: # first page of task results - assert len(action_result.document_results) == 20 - else: - assert len(action_result.document_results) == 5 - - assert all([action_result for action_result in recognize_entities_results if len(action_result.document_results) == len(docs)]) - assert all([action_result for action_result in extract_key_phrases_results if len(action_result.document_results) == len(docs)]) - assert all([action_result for action_result in recognize_pii_entities_results if len(action_result.document_results) == len(docs)]) - assert all([action_result for action_result in recognize_linked_entities_results if len(action_result.document_results) == len(docs)]) - assert all([action_result for action_result in analyze_sentiment_results if len(action_result.document_results) == len(docs)]) + document_results.append(p) + + assert len(document_results) == len(docs) + action_order = [ + _AnalyzeActionsType.RECOGNIZE_ENTITIES, + _AnalyzeActionsType.EXTRACT_KEY_PHRASES, + _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES, + _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES, + _AnalyzeActionsType.ANALYZE_SENTIMENT, + ] + action_type_to_document_results = defaultdict(list) + + for doc_idx, document_result in enumerate(document_results): + for action_idx, document_result in enumerate(document_result): + self.assertEqual(document_result.id, str(doc_idx)) + action_type = self.document_result_to_action_type(document_result) + self.assertEqual(action_type, action_order[action_idx]) + action_type_to_document_results[action_type].append(document_result) + + assert len(action_type_to_document_results) == len(action_order) + for document_results in action_type_to_document_results.values(): + assert len(document_results) == len(docs) @GlobalTextAnalyticsAccountPreparer() @TextAnalyticsClientPreparer() diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/test_repr.py b/sdk/textanalytics/azure-ai-textanalytics/tests/test_repr.py index 3a0843fd6f22..4dfdb5e39e74 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/tests/test_repr.py +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/test_repr.py @@ -455,57 +455,6 @@ def test_inner_error_takes_precedence(self): assert error.code == "UnsupportedLanguageCode" assert error.message == "Supplied language not supported. Pass in one of: de,en,es,fr,it,ja,ko,nl,pt-PT,zh-Hans,zh-Hant" - def test_analyze_actions_result_recognize_entities(self, recognize_entities_result, request_statistics): - model = _models.AnalyzeActionsResult( - document_results=[recognize_entities_result[0]], - statistics=request_statistics[0], - is_error=False, - action_type=_models.AnalyzeActionsType.RECOGNIZE_ENTITIES, - completed_on=datetime.datetime(1, 1, 1) - ) - - model_repr = ( - "AnalyzeActionsResult(document_results=[{}], is_error={}, action_type={}, completed_on={}, statistics={})".format( - recognize_entities_result[1], False, "recognize_entities", datetime.datetime(1, 1, 1), request_statistics[1] - ) - ) - - assert repr(model) == model_repr - - def test_analyze_actions_result_recognize_pii_entities(self, recognize_pii_entities_result, request_statistics): - model = _models.AnalyzeActionsResult( - document_results=[recognize_pii_entities_result[0]], - statistics=request_statistics[0], - is_error=False, - action_type=_models.AnalyzeActionsType.RECOGNIZE_PII_ENTITIES, - completed_on=datetime.datetime(1, 1, 1) - ) - - model_repr = ( - "AnalyzeActionsResult(document_results=[{}], is_error={}, action_type={}, completed_on={}, statistics={})".format( - recognize_pii_entities_result[1], False, "recognize_pii_entities", datetime.datetime(1, 1, 1), request_statistics[1] - ) - ) - - assert repr(model) == model_repr - - def test_analyze_actions_result_extract_key_phrases(self, extract_key_phrases_result, request_statistics): - model = _models.AnalyzeActionsResult( - document_results=[extract_key_phrases_result[0]], - statistics=request_statistics[0], - is_error=False, - action_type=_models.AnalyzeActionsType.EXTRACT_KEY_PHRASES, - completed_on=datetime.datetime(1, 1, 1) - ) - - model_repr = ( - "AnalyzeActionsResult(document_results=[{}], is_error={}, action_type={}, completed_on={}, statistics={})".format( - extract_key_phrases_result[1], False, "extract_key_phrases", datetime.datetime(1, 1, 1), request_statistics[1] - ) - ) - - assert repr(model) == model_repr - def test_analyze_healthcare_entities_result_item( self, healthcare_entity, healthcare_relation, text_analytics_warning, text_document_statistics ): diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/testcase.py b/sdk/textanalytics/azure-ai-textanalytics/tests/testcase.py index af101458e0c8..8fd51ef0afbc 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/tests/testcase.py +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/testcase.py @@ -5,6 +5,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +from re import L import pytest from azure.core.credentials import AccessToken, AzureKeyCredential @@ -14,6 +15,14 @@ FakeResource, ResourceGroupPreparer, ) +from azure.ai.textanalytics import ( + RecognizeEntitiesResult, + RecognizeLinkedEntitiesResult, + RecognizePiiEntitiesResult, + AnalyzeSentimentResult, + ExtractKeyPhrasesResult, + _AnalyzeActionsType +) from devtools_testutils.cognitiveservices_testcase import CognitiveServicesAccountPreparer from azure_devtools.scenario_tests import ReplayableTest @@ -87,6 +96,19 @@ def assert_healthcare_entities_equal(self, entity_a, entity_b): assert entity_a.length == entity_b.length assert entity_a.offset == entity_b.offset + def document_result_to_action_type(self, document_result): + if isinstance(document_result, RecognizePiiEntitiesResult): + return _AnalyzeActionsType.RECOGNIZE_PII_ENTITIES + if isinstance(document_result, RecognizeEntitiesResult): + return _AnalyzeActionsType.RECOGNIZE_ENTITIES + if isinstance(document_result, RecognizeLinkedEntitiesResult): + return _AnalyzeActionsType.RECOGNIZE_LINKED_ENTITIES + if isinstance(document_result, AnalyzeSentimentResult): + return _AnalyzeActionsType.ANALYZE_SENTIMENT + if isinstance(document_result, ExtractKeyPhrasesResult): + return _AnalyzeActionsType.EXTRACT_KEY_PHRASES + raise ValueError("Your action result doesn't match any of the action types") + class GlobalResourceGroupPreparer(AzureMgmtPreparer): def __init__(self): From 0307a4a103ef087d89e61103f4b945cc4fcc7995 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Tue, 1 Jun 2021 13:16:22 -0400 Subject: [PATCH 2/3] address johan's and krista's comments --- .../azure-ai-textanalytics/CHANGELOG.md | 3 +- .../azure-ai-textanalytics/README.md | 109 ++++++++++------- .../sample_analyze_actions_async.py | 110 +++++++++++------- .../samples/sample_analyze_actions.py | 108 ++++++++++------- .../tests/test_analyze.py | 8 +- .../tests/test_analyze_async.py | 11 +- .../azure-ai-textanalytics/tests/testcase.py | 1 - 7 files changed, 204 insertions(+), 146 deletions(-) diff --git a/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md b/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md index 39a31a7a9a7d..82175f577f06 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md +++ b/sdk/textanalytics/azure-ai-textanalytics/CHANGELOG.md @@ -3,7 +3,8 @@ ## 5.1.0b8 (Unreleased) **Breaking Changes** -- Changed the response structure of `being_analyze_actions`. Now, we return a list of list of an action result of documents + +- Changed the response structure of `being_analyze_actions`. Now, we return a list of results, where each result is a list of the action results for the document, in the order the documents and actions were passed - Removed `AnalyzeActionsType` - Removed `AnalyzeActionsResult` - Removed `AnalyzeActionsError` diff --git a/sdk/textanalytics/azure-ai-textanalytics/README.md b/sdk/textanalytics/azure-ai-textanalytics/README.md index b015b0f4d522..fbe7da50c4b4 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/README.md +++ b/sdk/textanalytics/azure-ai-textanalytics/README.md @@ -532,57 +532,76 @@ poller = text_analytics_client.begin_analyze_actions( ) # returns multiple actions results in the same order as the inputted actions -pages = poller.result() - -for doc, document_results in zip(documents, pages): +document_results = poller.result() +for doc, action_results in zip(documents, document_results): print("\nDocument text: {}".format(doc)) - recognize_entities_result = document_results[0] - assert not recognize_entities_result.is_error + recognize_entities_result = action_results[0] print("...Results of Recognize Entities Action:") - for entity in recognize_entities_result.entities: - print("......Entity: {}".format(entity.text)) - print(".........Category: {}".format(entity.category)) - print(".........Confidence Score: {}".format(entity.confidence_score)) - print(".........Offset: {}".format(entity.offset)) - - recognize_pii_entities_result = document_results[1] - assert not recognize_pii_entities_result.is_error + if recognize_entities_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + recognize_entities_result.code, recognize_entities_result.message + )) + else: + for entity in recognize_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + print(".........Offset: {}".format(entity.offset)) + + recognize_pii_entities_result = action_results[1] print("...Results of Recognize PII Entities action:") - for entity in recognize_pii_entities_result.entities: - print("......Entity: {}".format(entity.text)) - print(".........Category: {}".format(entity.category)) - print(".........Confidence Score: {}".format(entity.confidence_score)) - - extract_key_phrases_result = document_results[2] - assert not extract_key_phrases_result.is_error + if recognize_pii_entities_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + recognize_pii_entities_result.code, recognize_pii_entities_result.message + )) + else: + for entity in recognize_pii_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + + extract_key_phrases_result = action_results[2] print("...Results of Extract Key Phrases action:") - print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) - - recognize_linked_entities_result = document_results[3] - assert not recognize_linked_entities_result.is_error + if extract_key_phrases_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + extract_key_phrases_result.code, extract_key_phrases_result.message + )) + else: + print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) + + recognize_linked_entities_result = action_results[3] print("...Results of Recognize Linked Entities action:") - for linked_entity in recognize_linked_entities_result.entities: - print("......Entity name: {}".format(linked_entity.name)) - print(".........Data source: {}".format(linked_entity.data_source)) - print(".........Data source language: {}".format(linked_entity.language)) - print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) - print(".........Data source URL: {}".format(linked_entity.url)) - print(".........Document matches:") - for match in linked_entity.matches: - print("............Match text: {}".format(match.text)) - print("............Confidence Score: {}".format(match.confidence_score)) - print("............Offset: {}".format(match.offset)) - print("............Length: {}".format(match.length)) - - analyze_sentiment_result = document_results[4] - assert not analyze_sentiment_result.is_error + if recognize_linked_entities_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + recognize_linked_entities_result.code, recognize_linked_entities_result.message + )) + else: + for linked_entity in recognize_linked_entities_result.entities: + print("......Entity name: {}".format(linked_entity.name)) + print(".........Data source: {}".format(linked_entity.data_source)) + print(".........Data source language: {}".format(linked_entity.language)) + print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) + print(".........Data source URL: {}".format(linked_entity.url)) + print(".........Document matches:") + for match in linked_entity.matches: + print("............Match text: {}".format(match.text)) + print("............Confidence Score: {}".format(match.confidence_score)) + print("............Offset: {}".format(match.offset)) + print("............Length: {}".format(match.length)) + + analyze_sentiment_result = action_results[4] print("...Results of Analyze Sentiment action:") - print("......Overall sentiment: {}".format(analyze_sentiment_result.sentiment)) - print("......Scores: positive={}; neutral={}; negative={} \n".format( - analyze_sentiment_result.confidence_scores.positive, - analyze_sentiment_result.confidence_scores.neutral, - analyze_sentiment_result.confidence_scores.negative, - )) + if analyze_sentiment_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + analyze_sentiment_result.code, analyze_sentiment_result.message + )) + else: + print("......Overall sentiment: {}".format(analyze_sentiment_result.sentiment)) + print("......Scores: positive={}; neutral={}; negative={} \n".format( + analyze_sentiment_result.confidence_scores.positive, + analyze_sentiment_result.confidence_scores.neutral, + analyze_sentiment_result.confidence_scores.negative, + )) print("------------------------------------------") ``` diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py index eee731ad257b..316c71d3560d 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py @@ -81,59 +81,79 @@ async def analyze_async(self): # you have to read in all of the elements into memory first. # If you're not looking to enumerate / zip, we recommend you just asynchronously # loop over it immediately, without going through this step of reading them into memory - result = [] + document_results = [] async for page in pages: - result.append(page) + document_results.append(page) - for doc, document_results in zip(documents, result): + for doc, action_results in zip(documents, document_results): print("\nDocument text: {}".format(doc)) - recognize_entities_result = document_results[0] - assert not recognize_entities_result.is_error + recognize_entities_result = action_results[0] print("...Results of Recognize Entities Action:") - for entity in recognize_entities_result.entities: - print("......Entity: {}".format(entity.text)) - print(".........Category: {}".format(entity.category)) - print(".........Confidence Score: {}".format(entity.confidence_score)) - print(".........Offset: {}".format(entity.offset)) - - recognize_pii_entities_result = document_results[1] - assert not recognize_pii_entities_result.is_error + if recognize_entities_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + recognize_entities_result.code, recognize_entities_result.message + )) + else: + for entity in recognize_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + print(".........Offset: {}".format(entity.offset)) + + recognize_pii_entities_result = action_results[1] print("...Results of Recognize PII Entities action:") - for entity in recognize_pii_entities_result.entities: - print("......Entity: {}".format(entity.text)) - print(".........Category: {}".format(entity.category)) - print(".........Confidence Score: {}".format(entity.confidence_score)) - - extract_key_phrases_result = document_results[2] - assert not extract_key_phrases_result.is_error + if recognize_pii_entities_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + recognize_pii_entities_result.code, recognize_pii_entities_result.message + )) + else: + for entity in recognize_pii_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + + extract_key_phrases_result = action_results[2] print("...Results of Extract Key Phrases action:") - print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) - - recognize_linked_entities_result = document_results[3] - assert not recognize_linked_entities_result.is_error + if extract_key_phrases_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + extract_key_phrases_result.code, extract_key_phrases_result.message + )) + else: + print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) + + recognize_linked_entities_result = action_results[3] print("...Results of Recognize Linked Entities action:") - for linked_entity in recognize_linked_entities_result.entities: - print("......Entity name: {}".format(linked_entity.name)) - print(".........Data source: {}".format(linked_entity.data_source)) - print(".........Data source language: {}".format(linked_entity.language)) - print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) - print(".........Data source URL: {}".format(linked_entity.url)) - print(".........Document matches:") - for match in linked_entity.matches: - print("............Match text: {}".format(match.text)) - print("............Confidence Score: {}".format(match.confidence_score)) - print("............Offset: {}".format(match.offset)) - print("............Length: {}".format(match.length)) - - analyze_sentiment_result = document_results[4] - assert not analyze_sentiment_result.is_error + if recognize_linked_entities_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + recognize_linked_entities_result.code, recognize_linked_entities_result.message + )) + else: + for linked_entity in recognize_linked_entities_result.entities: + print("......Entity name: {}".format(linked_entity.name)) + print(".........Data source: {}".format(linked_entity.data_source)) + print(".........Data source language: {}".format(linked_entity.language)) + print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) + print(".........Data source URL: {}".format(linked_entity.url)) + print(".........Document matches:") + for match in linked_entity.matches: + print("............Match text: {}".format(match.text)) + print("............Confidence Score: {}".format(match.confidence_score)) + print("............Offset: {}".format(match.offset)) + print("............Length: {}".format(match.length)) + + analyze_sentiment_result = action_results[4] print("...Results of Analyze Sentiment action:") - print("......Overall sentiment: {}".format(analyze_sentiment_result.sentiment)) - print("......Scores: positive={}; neutral={}; negative={} \n".format( - analyze_sentiment_result.confidence_scores.positive, - analyze_sentiment_result.confidence_scores.neutral, - analyze_sentiment_result.confidence_scores.negative, - )) + if analyze_sentiment_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + analyze_sentiment_result.code, analyze_sentiment_result.message + )) + else: + print("......Overall sentiment: {}".format(analyze_sentiment_result.sentiment)) + print("......Scores: positive={}; neutral={}; negative={} \n".format( + analyze_sentiment_result.confidence_scores.positive, + analyze_sentiment_result.confidence_scores.neutral, + analyze_sentiment_result.confidence_scores.negative, + )) print("------------------------------------------") # [END analyze_async] diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_actions.py b/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_actions.py index 7f5d33e15a16..16f431efb750 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_actions.py +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/sample_analyze_actions.py @@ -73,56 +73,76 @@ def analyze(self): ], ) - result = poller.result() - for doc, document_results in zip(documents, result): + document_results = poller.result() + for doc, action_results in zip(documents, document_results): print("\nDocument text: {}".format(doc)) - recognize_entities_result = document_results[0] - assert not recognize_entities_result.is_error + recognize_entities_result = action_results[0] print("...Results of Recognize Entities Action:") - for entity in recognize_entities_result.entities: - print("......Entity: {}".format(entity.text)) - print(".........Category: {}".format(entity.category)) - print(".........Confidence Score: {}".format(entity.confidence_score)) - print(".........Offset: {}".format(entity.offset)) - - recognize_pii_entities_result = document_results[1] - assert not recognize_pii_entities_result.is_error + if recognize_entities_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + recognize_entities_result.code, recognize_entities_result.message + )) + else: + for entity in recognize_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + print(".........Offset: {}".format(entity.offset)) + + recognize_pii_entities_result = action_results[1] print("...Results of Recognize PII Entities action:") - for entity in recognize_pii_entities_result.entities: - print("......Entity: {}".format(entity.text)) - print(".........Category: {}".format(entity.category)) - print(".........Confidence Score: {}".format(entity.confidence_score)) - - extract_key_phrases_result = document_results[2] - assert not extract_key_phrases_result.is_error + if recognize_pii_entities_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + recognize_pii_entities_result.code, recognize_pii_entities_result.message + )) + else: + for entity in recognize_pii_entities_result.entities: + print("......Entity: {}".format(entity.text)) + print(".........Category: {}".format(entity.category)) + print(".........Confidence Score: {}".format(entity.confidence_score)) + + extract_key_phrases_result = action_results[2] print("...Results of Extract Key Phrases action:") - print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) - - recognize_linked_entities_result = document_results[3] - assert not recognize_linked_entities_result.is_error + if extract_key_phrases_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + extract_key_phrases_result.code, extract_key_phrases_result.message + )) + else: + print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) + + recognize_linked_entities_result = action_results[3] print("...Results of Recognize Linked Entities action:") - for linked_entity in recognize_linked_entities_result.entities: - print("......Entity name: {}".format(linked_entity.name)) - print(".........Data source: {}".format(linked_entity.data_source)) - print(".........Data source language: {}".format(linked_entity.language)) - print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) - print(".........Data source URL: {}".format(linked_entity.url)) - print(".........Document matches:") - for match in linked_entity.matches: - print("............Match text: {}".format(match.text)) - print("............Confidence Score: {}".format(match.confidence_score)) - print("............Offset: {}".format(match.offset)) - print("............Length: {}".format(match.length)) - - analyze_sentiment_result = document_results[4] - assert not analyze_sentiment_result.is_error + if recognize_linked_entities_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + recognize_linked_entities_result.code, recognize_linked_entities_result.message + )) + else: + for linked_entity in recognize_linked_entities_result.entities: + print("......Entity name: {}".format(linked_entity.name)) + print(".........Data source: {}".format(linked_entity.data_source)) + print(".........Data source language: {}".format(linked_entity.language)) + print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) + print(".........Data source URL: {}".format(linked_entity.url)) + print(".........Document matches:") + for match in linked_entity.matches: + print("............Match text: {}".format(match.text)) + print("............Confidence Score: {}".format(match.confidence_score)) + print("............Offset: {}".format(match.offset)) + print("............Length: {}".format(match.length)) + + analyze_sentiment_result = action_results[4] print("...Results of Analyze Sentiment action:") - print("......Overall sentiment: {}".format(analyze_sentiment_result.sentiment)) - print("......Scores: positive={}; neutral={}; negative={} \n".format( - analyze_sentiment_result.confidence_scores.positive, - analyze_sentiment_result.confidence_scores.neutral, - analyze_sentiment_result.confidence_scores.negative, - )) + if analyze_sentiment_result.is_error: + print("...Is an error with code '{}' and message '{}'".format( + analyze_sentiment_result.code, analyze_sentiment_result.message + )) + else: + print("......Overall sentiment: {}".format(analyze_sentiment_result.sentiment)) + print("......Scores: positive={}; neutral={}; negative={} \n".format( + analyze_sentiment_result.confidence_scores.positive, + analyze_sentiment_result.confidence_scores.neutral, + analyze_sentiment_result.confidence_scores.negative, + )) print("------------------------------------------") # [END analyze] diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze.py b/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze.py index 62041d0b1f3a..f0cb3ae68007 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze.py +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze.py @@ -601,8 +601,8 @@ def test_multiple_pages_of_results_returned_successfully(self, client): polling_interval=self._interval(), ).result() - document_results = list(result) - assert len(document_results) == len(docs) + pages = list(result) + assert len(pages) == len(docs) action_order = [ _AnalyzeActionsType.RECOGNIZE_ENTITIES, _AnalyzeActionsType.EXTRACT_KEY_PHRASES, @@ -612,8 +612,8 @@ def test_multiple_pages_of_results_returned_successfully(self, client): ] action_type_to_document_results = defaultdict(list) - for doc_idx, document_result in enumerate(document_results): - for action_idx, document_result in enumerate(document_result): + for doc_idx, page in enumerate(pages): + for action_idx, document_result in enumerate(page): self.assertEqual(document_result.id, str(doc_idx)) action_type = self.document_result_to_action_type(document_result) self.assertEqual(action_type, action_order[action_idx]) diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze_async.py b/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze_async.py index dffae7ad4266..b13a9546e4b8 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/test_analyze_async.py @@ -111,7 +111,6 @@ async def test_all_successful_passing_dict_sentiment_task(self, client): pages.append(p) assert len(pages) == len(docs) - assert len(pages) == len(docs) for idx, document_results in enumerate(pages): assert len(document_results) == 1 @@ -652,11 +651,11 @@ async def test_multiple_pages_of_results_returned_successfully(self, client): polling_interval=self._interval() )).result() - document_results = [] + pages = [] async for p in result: - document_results.append(p) + pages.append(p) - assert len(document_results) == len(docs) + assert len(pages) == len(docs) action_order = [ _AnalyzeActionsType.RECOGNIZE_ENTITIES, _AnalyzeActionsType.EXTRACT_KEY_PHRASES, @@ -666,8 +665,8 @@ async def test_multiple_pages_of_results_returned_successfully(self, client): ] action_type_to_document_results = defaultdict(list) - for doc_idx, document_result in enumerate(document_results): - for action_idx, document_result in enumerate(document_result): + for doc_idx, page in enumerate(pages): + for action_idx, document_result in enumerate(page): self.assertEqual(document_result.id, str(doc_idx)) action_type = self.document_result_to_action_type(document_result) self.assertEqual(action_type, action_order[action_idx]) diff --git a/sdk/textanalytics/azure-ai-textanalytics/tests/testcase.py b/sdk/textanalytics/azure-ai-textanalytics/tests/testcase.py index 8fd51ef0afbc..7b2b5dc6d288 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/tests/testcase.py +++ b/sdk/textanalytics/azure-ai-textanalytics/tests/testcase.py @@ -5,7 +5,6 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- -from re import L import pytest from azure.core.credentials import AccessToken, AzureKeyCredential From 86f82947ab1cb5d075942b42b3435caf944824e2 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Tue, 1 Jun 2021 13:25:31 -0400 Subject: [PATCH 3/3] limit # of action calls in readme example --- .../azure-ai-textanalytics/README.md | 56 ++----------------- .../sample_analyze_actions_async.py | 1 - 2 files changed, 4 insertions(+), 53 deletions(-) diff --git a/sdk/textanalytics/azure-ai-textanalytics/README.md b/sdk/textanalytics/azure-ai-textanalytics/README.md index fbe7da50c4b4..61596afe22dd 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/README.md +++ b/sdk/textanalytics/azure-ai-textanalytics/README.md @@ -506,10 +506,7 @@ from azure.core.credentials import AzureKeyCredential from azure.ai.textanalytics import ( TextAnalyticsClient, RecognizeEntitiesAction, - RecognizePiiEntitiesAction, - ExtractKeyPhrasesAction, - RecognizeLinkedEntitiesAction, - AnalyzeSentimentAction + AnalyzeSentimentAction, ) credential = AzureKeyCredential("") @@ -524,9 +521,6 @@ poller = text_analytics_client.begin_analyze_actions( display_name="Sample Text Analysis", actions=[ RecognizeEntitiesAction(), - RecognizePiiEntitiesAction(), - ExtractKeyPhrasesAction(), - RecognizeLinkedEntitiesAction(), AnalyzeSentimentAction() ] ) @@ -534,11 +528,11 @@ poller = text_analytics_client.begin_analyze_actions( # returns multiple actions results in the same order as the inputted actions document_results = poller.result() for doc, action_results in zip(documents, document_results): + recognize_entities_result, analyze_sentiment_result = action_results print("\nDocument text: {}".format(doc)) - recognize_entities_result = action_results[0] print("...Results of Recognize Entities Action:") if recognize_entities_result.is_error: - print("...Is an error with code '{}' and message '{}'".format( + print("......Is an error with code '{}' and message '{}'".format( recognize_entities_result.code, recognize_entities_result.message )) else: @@ -548,51 +542,9 @@ for doc, action_results in zip(documents, document_results): print(".........Confidence Score: {}".format(entity.confidence_score)) print(".........Offset: {}".format(entity.offset)) - recognize_pii_entities_result = action_results[1] - print("...Results of Recognize PII Entities action:") - if recognize_pii_entities_result.is_error: - print("...Is an error with code '{}' and message '{}'".format( - recognize_pii_entities_result.code, recognize_pii_entities_result.message - )) - else: - for entity in recognize_pii_entities_result.entities: - print("......Entity: {}".format(entity.text)) - print(".........Category: {}".format(entity.category)) - print(".........Confidence Score: {}".format(entity.confidence_score)) - - extract_key_phrases_result = action_results[2] - print("...Results of Extract Key Phrases action:") - if extract_key_phrases_result.is_error: - print("...Is an error with code '{}' and message '{}'".format( - extract_key_phrases_result.code, extract_key_phrases_result.message - )) - else: - print("......Key Phrases: {}".format(extract_key_phrases_result.key_phrases)) - - recognize_linked_entities_result = action_results[3] - print("...Results of Recognize Linked Entities action:") - if recognize_linked_entities_result.is_error: - print("...Is an error with code '{}' and message '{}'".format( - recognize_linked_entities_result.code, recognize_linked_entities_result.message - )) - else: - for linked_entity in recognize_linked_entities_result.entities: - print("......Entity name: {}".format(linked_entity.name)) - print(".........Data source: {}".format(linked_entity.data_source)) - print(".........Data source language: {}".format(linked_entity.language)) - print(".........Data source entity ID: {}".format(linked_entity.data_source_entity_id)) - print(".........Data source URL: {}".format(linked_entity.url)) - print(".........Document matches:") - for match in linked_entity.matches: - print("............Match text: {}".format(match.text)) - print("............Confidence Score: {}".format(match.confidence_score)) - print("............Offset: {}".format(match.offset)) - print("............Length: {}".format(match.length)) - - analyze_sentiment_result = action_results[4] print("...Results of Analyze Sentiment action:") if analyze_sentiment_result.is_error: - print("...Is an error with code '{}' and message '{}'".format( + print("......Is an error with code '{}' and message '{}'".format( analyze_sentiment_result.code, analyze_sentiment_result.message )) else: diff --git a/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py index 316c71d3560d..9f46de7ec3a9 100644 --- a/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py +++ b/sdk/textanalytics/azure-ai-textanalytics/samples/async_samples/sample_analyze_actions_async.py @@ -39,7 +39,6 @@ async def analyze_async(self): RecognizePiiEntitiesAction, ExtractKeyPhrasesAction, AnalyzeSentimentAction, - _AnalyzeActionsType ) endpoint = os.environ["AZURE_TEXT_ANALYTICS_ENDPOINT"]