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 aec5b8e + 64f65f8 commit 4dc9fa6
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 332 deletions.
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
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
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

This file was deleted.

135 changes: 35 additions & 100 deletions packages/google-cloud-python-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]
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 packages/google-cloud-python-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 packages/google-cloud-python-speech/unit_tests/test_metadata.py

This file was deleted.

Loading

0 comments on commit 4dc9fa6

Please sign in to comment.