Skip to content

Add BigQuery Remote Vision Function code sample #8426

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
/appengine/standard_python3/spanner/* @GoogleCloudPlatform/api-spanner-python @GoogleCloudPlatform/python-samples-reviewers
/auth/**/* @arithmetic1728 @GoogleCloudPlatform/python-samples-reviewers
/bigquery/**/* @chalmerlowe @GoogleCloudPlatform/python-samples-reviewers
/bigquery/remote_function/**/* @autoerr @GoogleCloudPlatform/python-samples-reviewers
/billing/**/* @GoogleCloudPlatform/billing-samples-maintainers @GoogleCloudPlatform/python-samples-reviewers
/blog/**/* @GoogleCloudPlatform/python-samples-reviewers
/cdn/**/* @mpwarres @GoogleCloudPlatform/python-samples-reviewers
Expand Down
89 changes: 89 additions & 0 deletions bigquery/remote-function/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
.. This file is automatically generated. Do not edit this file directly.

BigQuery Remote Function Python Samples
===============================================================================

.. image:: https://gstatic.com/cloudssh/images/open-btn.png
:target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=/README.rst


This directory contains samples for BigQuery Remote Function. `BigQuery Remote Function`_ allows you to analyze structured and unstructured data by integrating services outside BigQuery including Vision, Document AI, Speech-to-Text etc.




.. _BigQuery Remote Function: https://cloud.google.com/bigquery/docs/reference/standard-sql/remote-functions






Setup
-------------------------------------------------------------------------------


Authentication
++++++++++++++

This sample requires you to have authentication setup. Refer to the
`Authentication Getting Started Guide`_ for instructions on setting up
credentials for applications.

.. _Authentication Getting Started Guide:
https://cloud.google.com/docs/authentication/getting-started

Install Dependencies
++++++++++++++++++++

#. Clone python-docs-samples and change directory to the sample directory you want to use.

.. code-block:: bash

$ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions.

.. _Python Development Environment Setup Guide:
https://cloud.google.com/python/setup

#. Create a virtualenv. Samples are compatible with Python 3.6+.

.. code-block:: bash

$ virtualenv env
$ source env/bin/activate

#. Install the dependencies needed to run the samples.

.. code-block:: bash

$ pip install -r requirements.txt

.. _pip: https://pip.pypa.io/
.. _virtualenv: https://virtualenv.pypa.io/

Samples
-------------------------------------------------------------------------------

- `Vision`_: this sample can detect and extract objects from input images.


.. _Vision: vision/

The client library
-------------------------------------------------------------------------------

This sample uses the `Google Cloud Client Library for Python`_.
You can read the documentation for more details on API usage and use GitHub
to `browse the source`_ and `report issues`_.

.. _Google Cloud Client Library for Python:
https://googlecloudplatform.github.io/google-cloud-python/
.. _browse the source:
https://github.com/GoogleCloudPlatform/google-cloud-python
.. _report issues:
https://github.com/GoogleCloudPlatform/google-cloud-python/issues


.. _Google Cloud SDK: https://cloud.google.com/sdk/
19 changes: 19 additions & 0 deletions bigquery/remote-function/README.rst.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This file is used to generate README.rst
# python3 ../../scripts/readme-gen/readme_gen.py README.rst.in

product:
name: BigQuery Remote Function
short_name: BigQuery Remote Function
url: https://cloud.google.com/bigquery/docs/reference/standard-sql/remote-functions
description: >
BigQuery Remote Function allows you to analyze structured and unstructured data by integrating services outside BigQuery including Vision, Document AI, Speech-to-Text etc.

setup:
- auth
- install_deps

samples:
- name: Vision
file: vision/vision_function.py

cloud_client_library: true
33 changes: 33 additions & 0 deletions bigquery/remote-function/vision/noxfile_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# TEST_CONFIG_OVERRIDE copied from the source of truth:
# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/noxfile_config.py

TEST_CONFIG_OVERRIDE = {
# You can opt out from the test for specific Python versions.
"ignored_versions": ["2.7"],
# Old samples are opted out of enforcing Python type hints
# All new samples should feature them
"enforce_type_hints": True,
# An envvar key for determining the project id to use. Change it
# to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a
# build specific Cloud project. You can also use your own string
# to use your own Cloud project.
"gcloud_project_env": "GOOGLE_CLOUD_PROJECT",
# 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT',
# A dictionary you want to inject into your test. Don't put any
# secrets here. These values will override predefined values.
"envs": {},
}
4 changes: 4 additions & 0 deletions bigquery/remote-function/vision/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Flask==2.2.2
functions-framework==3.2.0
google-cloud-vision==3.1.2
pytest==7.1.3
3 changes: 3 additions & 0 deletions bigquery/remote-function/vision/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Flask==2.2.2
functions-framework==3.2.0
google-cloud-vision==3.1.2
42 changes: 42 additions & 0 deletions bigquery/remote-function/vision/vision_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# [START bigquery_remote_function_vision]
import flask
import functions_framework
from google.cloud import vision_v1


@functions_framework.http
def label_detection(request: flask.Request) -> flask.Response:
"""BigQuery remote function to label input images.
Args:
request: HTTP request from BigQuery
https://cloud.google.com/bigquery/docs/reference/standard-sql/remote-functions#input_format
Returns:
HTTP response to BigQuery
https://cloud.google.com/bigquery/docs/reference/standard-sql/remote-functions#output_format
"""
try:
client = vision_v1.ImageAnnotatorClient()
calls = request.get_json()['calls']
replies = []
for call in calls:
results = client.label_detection(
{'source': {'image_uri': call[0]}})
replies.append(vision_v1.AnnotateImageResponse.to_dict(results))
return flask.make_response(flask.jsonify({'replies': replies}))
except Exception as e:
return flask.make_response(flask.jsonify({'errorMessage': str(e)}), 400)
# [END bigquery_remote_function_vision]
61 changes: 61 additions & 0 deletions bigquery/remote-function/vision/vision_function_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from unittest import mock

import flask
from google.cloud import vision_v1
import pytest

import vision_function


# Create a fake "app" for generating test request contexts.
@pytest.fixture(scope="module")
def app() -> flask.Flask:
return flask.Flask(__name__)


@mock.patch('vision_function.vision_v1')
def test_vision_function(mock_vision_v1: object, app: flask.Flask) -> None:
label_detection_mock = mock.Mock(side_effect=[
vision_v1.AnnotateImageResponse(
{'label_annotations': [{'description': 'apple'}]}),
vision_v1.AnnotateImageResponse(
{'label_annotations': [{'description': 'banana'}]})])
mock_vision_v1.ImageAnnotatorClient = mock.Mock(
return_value=mock.Mock(label_detection=label_detection_mock))
mock_vision_v1.AnnotateImageResponse = vision_v1.AnnotateImageResponse
with app.test_request_context(
json={'calls': [['https://storage.googleapis.com/bucket/apple'],
['https://storage.googleapis.com/bucket/banana']]}):
response = vision_function.label_detection(flask.request)
assert response.status_code == 200
assert len(response.get_json()['replies']) == 2
assert 'apple' in str(response.get_json()['replies'][0])
assert 'banana' in str(response.get_json()['replies'][1])


@mock.patch('vision_function.vision_v1')
def test_vision_function_error(
mock_vision_v1: object, app: flask.Flask) -> None:
label_detection_mock = mock.Mock(side_effect=Exception('API error'))
mock_vision_v1.ImageAnnotatorClient = mock.Mock(
return_value=mock.Mock(label_detection=label_detection_mock))
with app.test_request_context(
json={'calls': [['https://storage.googleapis.com/bucket/apple'],
['https://storage.googleapis.com/bucket/banana']]}):
response = vision_function.label_detection(flask.request)
assert response.status_code == 400
assert 'API error' in str(response.get_data())