Skip to content

Commit

Permalink
Merge pull request #2664 from dhermes/refactor-speech-operation
Browse files Browse the repository at this point in the history
Re-factoring Speech operation to use the base class.
  • Loading branch information
dhermes authored Nov 2, 2016
2 parents 3ed1ab9 + ff5a126 commit 4d6615c
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 332 deletions.
1 change: 1 addition & 0 deletions google-cloud-speech/google/cloud/speech/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
from google.cloud.speech.client import Client
from google.cloud.speech.connection import Connection
from google.cloud.speech.encoding import Encoding
from google.cloud.speech.operation import Operation
from google.cloud.speech.transcript import Transcript
15 changes: 9 additions & 6 deletions google-cloud-speech/google/cloud/speech/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
from base64 import b64encode
import os

from google.cloud.client import Client as BaseClient
from google.cloud._helpers import _to_bytes
from google.cloud._helpers import _bytes_to_unicode
from google.cloud.client import Client as BaseClient
from google.cloud.environment_vars import DISABLE_GRPC

from google.cloud.speech.connection import Connection
from google.cloud.speech.encoding import Encoding
from google.cloud.speech.operation import Operation
Expand Down Expand Up @@ -111,8 +112,8 @@ def async_recognize(self, sample, language_code=None,
and phrases. This can also be used to add new
words to the vocabulary of the recognizer.
:rtype: `~google.cloud.speech.operation.Operation`
:returns: ``Operation`` for asynchronous request to Google Speech API.
:rtype: :class:`~google.cloud.speech.operation.Operation`
:returns: Operation for asynchronous request to Google Speech API.
"""
if sample.encoding is not Encoding.LINEAR16:
raise ValueError('Only LINEAR16 encoding is supported by '
Expand Down Expand Up @@ -279,15 +280,17 @@ def async_recognize(self, sample, language_code=None,
and phrases. This can also be used to add new
words to the vocabulary of the recognizer.
:rtype: `~google.cloud.speech.operation.Operation`
:returns: ``Operation`` for asynchronous request to Google Speech API.
:rtype: :class:`~google.cloud.speech.operation.Operation`
:returns: Operation for asynchronous request to Google Speech API.
"""
data = _build_request_data(sample, language_code, max_alternatives,
profanity_filter, speech_context)
api_response = self._connection.api_request(
method='POST', path='speech:asyncrecognize', data=data)

return Operation.from_api_repr(self, api_response)
operation = Operation.from_dict(api_response, self._client)
operation.caller_metadata['request_type'] = 'AsyncRecognize'
return operation

def sync_recognize(self, sample, language_code=None, max_alternatives=None,
profanity_filter=None, speech_context=None):
Expand Down
2 changes: 1 addition & 1 deletion google-cloud-speech/google/cloud/speech/encoding.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2016 Google Inc. All rights reserved.
# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
78 changes: 0 additions & 78 deletions google-cloud-speech/google/cloud/speech/metadata.py

This file was deleted.

135 changes: 35 additions & 100 deletions google-cloud-speech/google/cloud/speech/operation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2016 Google Inc. All rights reserved.
# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -14,120 +14,55 @@

"""Long running operation representation for Google Speech API"""

from google.cloud.speech.metadata import Metadata
from google.cloud.speech.transcript import Transcript
from google.cloud.grpc.speech.v1beta1 import cloud_speech_pb2

from google.cloud import operation
from google.cloud.speech.transcript import Transcript


class Operation(operation.Operation):
"""Representation of a Google API Long-Running Operation.
operation.register_type(cloud_speech_pb2.AsyncRecognizeMetadata)
operation.register_type(cloud_speech_pb2.AsyncRecognizeResponse)

:type client: :class:`~google.cloud.speech.client.Client`
:param client: Instance of speech client.

:type name: int
:param name: ID assigned to an operation.
class Operation(operation.Operation):
"""Custom Long-Running Operation for Google Speech API.
:type complete: bool
:param complete: True if operation is complete, else False.
:type name: str
:param name: The fully-qualified path naming the operation.
:type metadata: :class:`~google.cloud.speech.metadata.Metadata`
:param metadata: Instance of ``Metadata`` with operation information.
:type client: :class:`~google.cloud.speech.client.Client`
:param client: Client that created the current operation.
:type results: dict
:param results: Dictionary with transcript and score of operation.
:type caller_metadata: dict
:param caller_metadata: caller-assigned metadata about the operation
"""
def __init__(self, client, name, complete=False, metadata=None,
results=None):
self.client = client
self.name = name
self._complete = complete
self._metadata = metadata
self._results = results

@classmethod
def from_api_repr(cls, client, response):
"""Factory: construct an instance from Google Speech API.
:type client: :class:`~google.cloud.speech.client.Client`
:param client: Instance of speech client.
:type response: dict
:param response: Dictionary response from Google Speech Operations API.
:rtype: :class:`Operation`
:returns: Instance of `~google.cloud.speech.operations.Operation`.
"""
name = response['name']
complete = response.get('done', False)

operation_instance = cls(client, name, complete)
operation_instance._update(response)
return operation_instance
results = None
"""List of transcriptions from the speech-to-text process."""

@property
def complete(self):
"""Completion state of the `Operation`.
def _update_state(self, operation_pb):
"""Update the state of the current object based on operation.
:rtype: bool
:returns: True if already completed, else false.
"""
return self._complete
This mostly does what the base class does, but all populates
results.
@property
def metadata(self):
"""Metadata of operation.
:type operation_pb:
:class:`~google.longrunning.operations_pb2.Operation`
:param operation_pb: Protobuf to be parsed.
:rtype: :class:`~google.cloud.speech.metadata.Metadata`
:returns: Instance of ``Metadata``.
:raises ValueError: If there is more than one entry in ``results``.
"""
return self._metadata
super(Operation, self)._update_state(operation_pb)

@property
def results(self):
"""Results dictionary with transcript information.
result_type = operation_pb.WhichOneof('result')
if result_type != 'response':
return

:rtype: dict
:returns: Dictionary with transcript and confidence score.
"""
return self._results

def poll(self):
"""Check if the operation has finished.
:rtype: bool
:returns: A boolean indicating if the current operation has completed.
:raises: :class:`ValueError <exceptions.ValueError>` if the operation
has already completed.
"""
if self.complete:
raise ValueError('The operation has completed.')
pb_results = self.response.results
if len(pb_results) != 1:
raise ValueError('Expected exactly one result, found:',
pb_results)

path = 'operations/%s' % (self.name,)
api_response = self.client.connection.api_request(method='GET',
path=path)
self._update(api_response)
return self.complete

def _update(self, response):
"""Update Operation instance with latest data from Speech API.
.. _speech_operations: https://cloud.google.com/speech/reference/\
rest/v1beta1/operations
:type response: dict
:param response: Response from Speech API Operations endpoint.
See: `speech_operations`_.
"""
metadata = response.get('metadata', None)
raw_results = response.get('response', {}).get('results', None)
results = []
if raw_results:
for result in raw_results:
for alternative in result['alternatives']:
results.append(Transcript.from_api_repr(alternative))
if metadata:
self._metadata = Metadata.from_api_repr(metadata)

self._results = results
self._complete = response.get('done', False)
result = pb_results[0]
self.results = [Transcript.from_pb(alternative)
for alternative in result.alternatives]
5 changes: 4 additions & 1 deletion google-cloud-speech/google/cloud/speech/transcript.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ def from_pb(cls, transcript):
:rtype: :class:`Transcript`
:returns: Instance of ``Transcript``.
"""
return cls(transcript.transcript, transcript.confidence)
confidence = transcript.confidence
if confidence == 0.0: # In the protobof 0.0 means unset.
confidence = None
return cls(transcript.transcript, confidence)

@property
def transcript(self):
Expand Down
3 changes: 3 additions & 0 deletions google-cloud-speech/unit_tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ def test_async_recognize_no_gax(self):
sample_rate=self.SAMPLE_RATE)
operation = client.async_recognize(sample)
self.assertIsInstance(operation, Operation)
self.assertIs(operation.client, client)
self.assertEqual(operation.caller_metadata,
{'request_type': 'AsyncRecognize'})
self.assertFalse(operation.complete)
self.assertIsNone(operation.metadata)

Expand Down
50 changes: 0 additions & 50 deletions google-cloud-speech/unit_tests/test_metadata.py

This file was deleted.

Loading

0 comments on commit 4d6615c

Please sign in to comment.