Skip to content

Commit

Permalink
Merge pull request googleapis#2664 from dhermes/refactor-speech-opera…
Browse files Browse the repository at this point in the history
…tion

Re-factoring Speech operation to use the base class.
  • Loading branch information
dhermes authored Nov 2, 2016
2 parents 80d2c5a + 9b422fe commit adbbd68
Show file tree
Hide file tree
Showing 15 changed files with 189 additions and 373 deletions.
1 change: 0 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@
speech-usage
Client <speech-client>
speech-encoding
speech-metadata
speech-operation
speech-sample
speech-transcript
Expand Down
7 changes: 0 additions & 7 deletions docs/speech-metadata.rst

This file was deleted.

6 changes: 0 additions & 6 deletions scripts/run_pylint.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,6 @@ def get_python_files(all_files=None):

def lint_fileset(filenames, rcfile, description):
"""Lints a group of files using a given rcfile."""
# Only lint filenames that exist. For example, 'git diff --name-only'
# could spit out deleted / renamed files. Another alternative could
# be to use 'git diff --name-status' and filter out files with a
# status of 'D'.
filenames = [filename for filename in filenames
if os.path.exists(filename)]
if filenames:
rc_flag = '--rcfile=%s' % (rcfile,)
pylint_shell_command = ['pylint', rc_flag]
Expand Down
9 changes: 8 additions & 1 deletion scripts/script_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,14 @@ def get_affected_files(allow_limited=True):
print('Diff base not specified, listing all files in repository.')
result = subprocess.check_output(['git', 'ls-files'])

return result.rstrip('\n').split('\n'), diff_base
# Only return filenames that exist. For example, 'git diff --name-only'
# could spit out deleted / renamed files. Another alternative could
# be to use 'git diff --name-status' and filter out files with a
# status of 'D'.
filenames = [filename
for filename in result.rstrip('\n').split('\n')
if os.path.exists(filename)]
return filenames, diff_base


def get_required_packages(file_contents):
Expand Down
1 change: 1 addition & 0 deletions 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 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 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 speech/google/cloud/speech/metadata.py

This file was deleted.

135 changes: 35 additions & 100 deletions 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 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 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
Loading

0 comments on commit adbbd68

Please sign in to comment.