Skip to content

Commit

Permalink
[FR] add document classifier to operation details (#29732)
Browse files Browse the repository at this point in the history
  • Loading branch information
kristapratico authored Apr 6, 2023
1 parent fa77a41 commit 4197d67
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 18 deletions.
2 changes: 1 addition & 1 deletion sdk/formrecognizer/azure-ai-formrecognizer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
`list_document_classifiers`, `get_document_classifier`, and `delete_document_classifier`.
- Added support for classifying documents on `DocumentAnalysisClient`: `begin_classify_document` and `begin_classify_document_from_url`.
- Added model `QuotaDetails` and property `custom_neural_document_model_builds` on `ResourceDetails`.
- Added kind `documentClassifierBuild` to `OperationSummary`.
- Added kind `documentClassifierBuild` to `OperationSummary` and `OperationDetails`.
- Added property `expires_on` to `DocumentModelDetails` and `DocumentModelSummary`.
- Added kind `formulaBlock` to `DocumentParagraph`.
- Added property `common_name` to `DocumentKeyValuePair`.
Expand Down
2 changes: 1 addition & 1 deletion sdk/formrecognizer/azure-ai-formrecognizer/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/formrecognizer/azure-ai-formrecognizer",
"Tag": "python/formrecognizer/azure-ai-formrecognizer_390e54ecb9"
"Tag": "python/formrecognizer/azure-ai-formrecognizer_311c87465f"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
from enum import Enum
from collections import namedtuple
from azure.core import CaseInsensitiveEnumMeta
from ._generated.v2023_02_28_preview.models import DocumentModelDetails as ModelDetails, Error
from ._generated.v2023_02_28_preview.models import (
DocumentModelDetails as ModelDetails,
DocumentClassifierDetails as ClassifierDetails,
Error
)
from ._generated.models import ClassifierDocumentTypeDetails
from ._helpers import (
adjust_value_type,
Expand Down Expand Up @@ -3700,7 +3704,8 @@ class OperationSummary:
created, and more.
Note that operation information only persists for 24 hours. If the operation was successful,
the model can be accessed using the :func:`~get_document_model` or :func:`~list_document_models` APIs.
the model can be accessed using the :func:`~get_document_model`, :func:`~list_document_models`,
:func:`~get_document_classifier`, :func:`~list_document_classifiers` APIs.
To find out why an operation failed, use :func:`~get_operation` and provide the `operation_id`.
.. versionadded:: 2023-02-28-preview
Expand Down Expand Up @@ -3800,10 +3805,11 @@ class OperationDetails(OperationSummary):
error of the operation if it has completed.
Note that operation information only persists for 24 hours. If the operation was successful,
the model can also be accessed using the :func:`~get_document_model` or :func:`~list_document_models` APIs.
the model can also be accessed using the :func:`~get_document_model`, :func:`~list_document_models`,
:func:`~get_document_classifier`, :func:`~list_document_classifiers` APIs.
.. versionadded:: 2023-02-28-preview
The `documentClassifierBuild` kind.
The `documentClassifierBuild` kind and `DocumentClassifierDetails` result.
"""
operation_id: str
"""Operation ID."""
Expand All @@ -3824,10 +3830,9 @@ class OperationDetails(OperationSummary):
error: Optional[DocumentAnalysisError]
"""Encountered error, includes the error code, message, and details for why
the operation failed."""
result: Optional[DocumentModelDetails]
"""Operation result upon success. Returns a DocumentModelDetails which contains
all information about the model including the doc types
and fields it can analyze from documents."""
result: Optional[Union[DocumentModelDetails, DocumentClassifierDetails]]
"""Operation result upon success. Returns a DocumentModelDetails or DocumentClassifierDetails
which contains all the information about the model."""
api_version: Optional[str]
"""API version used to create this operation."""
tags: Optional[Dict[str, str]]
Expand Down Expand Up @@ -3871,6 +3876,14 @@ def from_dict(cls, data: Dict) -> "OperationDetails":
:return: OperationDetails
:rtype: OperationDetails
"""

kind = data.get("kind", None)
if kind == "documentClassifierBuild":
result = \
DocumentClassifierDetails.from_dict(data.get("result")) if data.get("result") else None # type: ignore
else:
result = \
DocumentModelDetails.from_dict(data.get("result")) if data.get("result") else None # type: ignore
return cls(
operation_id=data.get("operation_id", None),
status=data.get("status", None),
Expand All @@ -3879,7 +3892,7 @@ def from_dict(cls, data: Dict) -> "OperationDetails":
last_updated_on=data.get("last_updated_on", None),
kind=data.get("kind", None),
resource_location=data.get("resource_location", None),
result=DocumentModelDetails.from_dict(data.get("result")) if data.get("result") else None, # type: ignore
result=result,
error=DocumentAnalysisError.from_dict(data.get("error")) if data.get("error") else None, # type: ignore
api_version=data.get("api_version", None),
tags=data.get("tags", {}),
Expand All @@ -3888,6 +3901,12 @@ def from_dict(cls, data: Dict) -> "OperationDetails":
@classmethod
def _from_generated(cls, op, api_version): # pylint: disable=arguments-differ
deserialize = _get_deserialize(api_version)
if op.kind == "documentClassifierBuild":
result = DocumentClassifierDetails._from_generated(deserialize(ClassifierDetails, op.result)) \
if op.result else None
else:
result = DocumentModelDetails._from_generated(deserialize(ModelDetails, op.result)) \
if op.result else None
return cls(
operation_id=op.operation_id,
status=op.status,
Expand All @@ -3896,8 +3915,7 @@ def _from_generated(cls, op, api_version): # pylint: disable=arguments-differ
last_updated_on=op.last_updated_date_time,
kind=op.kind,
resource_location=op.resource_location,
result=DocumentModelDetails._from_generated(deserialize(ModelDetails, op.result))
if op.result else None,
result=result,
error=DocumentAnalysisError._from_generated(deserialize(Error, op.error))
if op.error else None,
api_version=op.api_version,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ async def sample_get_operations_async():
print("My {} operation is completed.".format(operation_info.kind))
result = operation_info.result
if result is not None:
print("Model ID: {}".format(result.model_id))
if operation_info.kind == "documentClassifierBuild":
print(f"Classifier ID: {result.classifier_id}")
else:
print("Model ID: {}".format(result.model_id))
elif operation_info.status == "failed":
print("My {} operation failed.".format(operation_info.kind))
error = operation_info.error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ def sample_get_operations():
print("My {} operation is completed.".format(operation_info.kind))
result = operation_info.result
if result is not None:
print("Model ID: {}".format(result.model_id))
if operation_info.kind == "documentClassifierBuild":
print(f"Classifier ID: {result.classifier_id}")
else:
print("Model ID: {}".format(result.model_id))
elif operation_info.status == "failed":
print("My {} operation failed.".format(operation_info.kind))
error = operation_info.error
Expand Down
24 changes: 22 additions & 2 deletions sdk/formrecognizer/azure-ai-formrecognizer/tests/test_dmac_mgmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,14 @@ def test_get_list_operations(self, client, **kwargs):
assert op.kind
assert op.resource_location
if op.status == "succeeded":
successful_op = op
if op.kind != "documentClassifierBuild":
successful_op = op
else:
successful_classifier_op = op
if op.status == "failed":
failed_op = op

# check successful op
# check successful document model op
if successful_op:
op = client.get_operation(successful_op.operation_id)
# TODO not seeing this returned at the operation level
Expand All @@ -182,8 +185,25 @@ def test_get_list_operations(self, client, **kwargs):
for key, field in doc_type.field_schema.items():
assert key
assert field["type"]
if doc_type.build_mode == "neural":
continue # neural models don't have field confidence
assert doc_type.field_confidence[key] is not None

# check successful classifier model op
if successful_classifier_op:
op = client.get_operation(successful_classifier_op.operation_id)
# test to/from dict
op_dict = op.to_dict()
op = OperationDetails.from_dict(op_dict)
classifier = op.result
assert classifier.api_version
assert classifier.classifier_id
assert classifier.created_on
assert classifier.expires_on
for doc_type, source in classifier.doc_types.items():
assert doc_type
assert source.azure_blob_source or source.azure_blob_file_list_source

# check failed op
if failed_op:
op = client.get_operation(failed_op.operation_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ async def test_get_list_operations(self, client):
assert op.kind
assert op.resource_location
if op.status == "succeeded":
successful_op = op
if op.kind != "documentClassifierBuild":
successful_op = op
else:
successful_classifier_op = op
if op.status == "failed":
failed_op = op

Expand All @@ -188,8 +191,25 @@ async def test_get_list_operations(self, client):
for key, field in doc_type.field_schema.items():
assert key
assert field["type"]
if doc_type.build_mode == "neural":
continue # neural models don't have field confidence
assert doc_type.field_confidence[key] is not None

# check successful classifier model op
if successful_classifier_op:
op = await client.get_operation(successful_classifier_op.operation_id)
# test to/from dict
op_dict = op.to_dict()
op = OperationDetails.from_dict(op_dict)
classifier = op.result
assert classifier.api_version
assert classifier.classifier_id
assert classifier.created_on
assert classifier.expires_on
for doc_type, source in classifier.doc_types.items():
assert doc_type
assert source.azure_blob_source or source.azure_blob_file_list_source

# check failed op
if failed_op:
op = await client.get_operation(failed_op.operation_id)
Expand Down

0 comments on commit 4197d67

Please sign in to comment.