From abaef847ebce766d63353ac8c8107769182275f9 Mon Sep 17 00:00:00 2001 From: Mike <45373284+munkhuushmgl@users.noreply.github.com> Date: Fri, 30 Oct 2020 11:50:10 -0700 Subject: [PATCH] chore: moving vision automl samples (#373) * chore: moving vision automl samples * refactored printout stream * fixed the lint * lint --- automl/snippets/resources/dandelion.jpg | Bin 0 -> 53485 bytes .../vision/ClassificationDeployModel.java | 60 ++++++++ .../ClassificationDeployModelNodeCount.java | 61 ++++++++ .../vision/ClassificationUndeployModel.java | 60 ++++++++ .../com/google/cloud/vision/ModelApi.java | 143 ++++++++++++++++++ .../ObjectDetectionDeployModelNodeCount.java | 60 ++++++++ .../google/cloud/vision/PredictionApi.java | 136 +++++++++++++++++ .../vision/ClassificationDeployModelIT.java | 92 +++++++++++ ...ObjectDetectionDeployModelNodeCountIT.java | 68 +++++++++ .../google/cloud/vision/PredictionApiIT.java | 85 +++++++++++ 10 files changed, 765 insertions(+) create mode 100644 automl/snippets/resources/dandelion.jpg create mode 100644 automl/snippets/src/main/java/com/google/cloud/vision/ClassificationDeployModel.java create mode 100644 automl/snippets/src/main/java/com/google/cloud/vision/ClassificationDeployModelNodeCount.java create mode 100644 automl/snippets/src/main/java/com/google/cloud/vision/ClassificationUndeployModel.java create mode 100644 automl/snippets/src/main/java/com/google/cloud/vision/ModelApi.java create mode 100644 automl/snippets/src/main/java/com/google/cloud/vision/ObjectDetectionDeployModelNodeCount.java create mode 100644 automl/snippets/src/main/java/com/google/cloud/vision/PredictionApi.java create mode 100644 automl/snippets/src/test/java/com/google/cloud/vision/ClassificationDeployModelIT.java create mode 100644 automl/snippets/src/test/java/com/google/cloud/vision/ObjectDetectionDeployModelNodeCountIT.java create mode 100644 automl/snippets/src/test/java/com/google/cloud/vision/PredictionApiIT.java diff --git a/automl/snippets/resources/dandelion.jpg b/automl/snippets/resources/dandelion.jpg new file mode 100644 index 0000000000000000000000000000000000000000..326e4c1bf533799771a86daa32fe4acbd70285aa GIT binary patch literal 53485 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 {J6HT)NUt64jDrt|AY@zqNAdI*}N%BvD zp(4#61Ji9QNu`Pi9h8e&cgYICW@dU?h@1JvlVAjDsb;so#=Y)pDct$MCI4VThrR0K%NC zeq~&rY?{=9eDRfy`3DMjJ}oCc=fA1x+L5{nUI>?WkRtL@OM8h2hfbGB^PZUill1!L zrsUrN#D6E;g}PN2a+GC07~zm?J+Lqj{HBi2EL*YU;iawQj>ty1Uz0j_?VRWJ$)hEV z87ttMJYV5)iTHwZ94jiUap@sHG20?2{MhB&pdA35n_QsMT#!Zm&l8estAh4CHWCJ9 zQ;la}29x_&#^EvHz}-gmB_}U)70Y+RCtYy}?;@7U(%yJ&r)5_wrRBIF?ad?R>#_rZ zWxIVG|%#Dlr=7#O6JZTt-slF1u_%8zq$ zzYMU%kP)gDC^GvTp(T1_%K&18ozy|wkiDI)lH6WRX*z=#8q97Y4BIM3b$E6QjARbG zRn|$S=;MgmOYmGJ+BqPQK{OhbJRk{mDv&iHT`l{=`XrUMQ8b66B2yEzI$Xt#OSoZ; z_8y}+-=OJI+tUkbF3|q~3bm5zHHzjx8f3)z$g7XPynKo6oMC2&>{m|xiNQPM=8l_` zU9&~)$o$qn32pGJCc8od+RE7RM02DcQSzMhBig5{9l7W-SM6``$V1+cKC@&;D60zI{9-6VPfMm}0Sg zp{~j&GCGAJV{k!6&djGLZ?50YtH?n~F$;l)O)hjZZ?VY%y7b7v$gRPwlQ@q0z^njG z3yhn2(T&oRd%eM z0Osb~S{*d;!w=w#CA6{2xZ1Ads5j8+9SP-J&nyDMP#1)KDl$aL-xj*GN-*BEmK4vWNC?za5k*Kh6!#lc;=yz<_Q1>ujy4(U;~-Z|7-Gs+ zGMJ~=3R?+ulm!}0s^>TZJ7>FfuBgrQQ93X1+@_YEMU0M$Zk0RM(lCvwPq0>G0Qd*LSsS~p1bECP3p-eF!lH0yD?~POm8Ei1~3IzFTX%M$=e%d zqp1pZkx0RE%%|dhBM-wpqr}s^z8psokQ{BOV1c1dReZ>18)>LHr0Tzcr#eefBG%q8XggCb7h$?iXte>29(Im-G%@gclI8&Yij{Z zvY!Y85#5ag1`-iTWtNaPZo`uv#-XtP~CnUj#(j0{1k|isqGyYPxcLcACe=$*)kV;Og$(`de@bt0@@mzA+ zK;N^`jDdiDU{6!to)S+85WBI?7|_244`p%0Ay_X0f>0^L#6~t?+Bj}i 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 future = + client.deployModelAsync(deployModelRequest); + + future.get(); + + // Display the deployment details of model. + System.out.println("Model deployment finished"); + } + } +} +// [END automl_vision_classification_deploy_model] diff --git a/automl/snippets/src/main/java/com/google/cloud/vision/ClassificationDeployModelNodeCount.java b/automl/snippets/src/main/java/com/google/cloud/vision/ClassificationDeployModelNodeCount.java new file mode 100644 index 00000000000..07b91d9be44 --- /dev/null +++ b/automl/snippets/src/main/java/com/google/cloud/vision/ClassificationDeployModelNodeCount.java @@ -0,0 +1,61 @@ +/* + * Copyright 2019 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. + */ + +package com.google.cloud.vision; + +// [START automl_vision_classification_deploy_model_node_count] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.automl.v1beta1.AutoMlClient; +import com.google.cloud.automl.v1beta1.DeployModelRequest; +import com.google.cloud.automl.v1beta1.ImageClassificationModelDeploymentMetadata; +import com.google.cloud.automl.v1beta1.ModelName; +import com.google.cloud.automl.v1beta1.OperationMetadata; +import com.google.protobuf.Empty; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +class ClassificationDeployModelNodeCount { + + // Deploy a model with a specified node count + static void classificationDeployModelNodeCount(String projectId, String modelId) + throws IOException, ExecutionException, InterruptedException { + // String projectId = "YOUR_PROJECT_ID"; + // String modelId = "YOUR_MODEL_ID"; + + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. After completing all of your requests, call + // the "close" method on the client to safely clean up any remaining background resources. + try (AutoMlClient client = AutoMlClient.create()) { + // Get the full path of the model. + ModelName modelFullId = ModelName.of(projectId, "us-central1", modelId); + + // Set how many nodes the model is deployed on + ImageClassificationModelDeploymentMetadata deploymentMetadata = + ImageClassificationModelDeploymentMetadata.newBuilder().setNodeCount(2).build(); + + DeployModelRequest request = + DeployModelRequest.newBuilder() + .setName(modelFullId.toString()) + .setImageClassificationModelDeploymentMetadata(deploymentMetadata) + .build(); + // Deploy the model + OperationFuture future = client.deployModelAsync(request); + future.get(); + System.out.println("Model deployment on 2 nodes finished"); + } + } +} +// [END automl_vision_classification_deploy_model_node_count] diff --git a/automl/snippets/src/main/java/com/google/cloud/vision/ClassificationUndeployModel.java b/automl/snippets/src/main/java/com/google/cloud/vision/ClassificationUndeployModel.java new file mode 100644 index 00000000000..73ff19bef00 --- /dev/null +++ b/automl/snippets/src/main/java/com/google/cloud/vision/ClassificationUndeployModel.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019 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. + */ + +package com.google.cloud.vision; + +// [START automl_vision_classification_undeploy_model] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.automl.v1beta1.AutoMlClient; +import com.google.cloud.automl.v1beta1.ModelName; +import com.google.cloud.automl.v1beta1.OperationMetadata; +import com.google.cloud.automl.v1beta1.UndeployModelRequest; +import com.google.protobuf.Empty; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +class ClassificationUndeployModel { + + // Deploy a model + static void classificationUndeployModel(String projectId, String modelId) + throws IOException, ExecutionException, InterruptedException { + // String projectId = "YOUR_PROJECT_ID"; + // String modelId = "YOUR_MODEL_ID"; + + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. After completing all of your requests, call + // the "close" method on the client to safely clean up any remaining background resources. + try (AutoMlClient client = AutoMlClient.create()) { + + // Get the full path of the model. + ModelName modelFullId = ModelName.of(projectId, "us-central1", modelId); + + // Build deploy model request. + UndeployModelRequest undeployModelRequest = + UndeployModelRequest.newBuilder().setName(modelFullId.toString()).build(); + + // Deploy a model with the deploy model request. + OperationFuture future = + client.undeployModelAsync(undeployModelRequest); + + future.get(); + + // Display the deployment details of model. + System.out.println("Model undeploy finished"); + } + } +} +// [END automl_vision_classification_undeploy_model] diff --git a/automl/snippets/src/main/java/com/google/cloud/vision/ModelApi.java b/automl/snippets/src/main/java/com/google/cloud/vision/ModelApi.java new file mode 100644 index 00000000000..7ce19be0caa --- /dev/null +++ b/automl/snippets/src/main/java/com/google/cloud/vision/ModelApi.java @@ -0,0 +1,143 @@ +/* + * Copyright 2018 Google Inc. + * + * 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. + */ + +package com.google.cloud.vision; + +// Imports the Google Cloud client library +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.automl.v1beta1.AutoMlClient; +import com.google.cloud.automl.v1beta1.ClassificationProto.ClassificationEvaluationMetrics; +import com.google.cloud.automl.v1beta1.ClassificationProto.ClassificationEvaluationMetrics.ConfidenceMetricsEntry; +import com.google.cloud.automl.v1beta1.ImageClassificationModelMetadata; +import com.google.cloud.automl.v1beta1.ListModelEvaluationsRequest; +import com.google.cloud.automl.v1beta1.ListModelsRequest; +import com.google.cloud.automl.v1beta1.LocationName; +import com.google.cloud.automl.v1beta1.Model; +import com.google.cloud.automl.v1beta1.ModelEvaluation; +import com.google.cloud.automl.v1beta1.ModelEvaluationName; +import com.google.cloud.automl.v1beta1.ModelName; +import com.google.cloud.automl.v1beta1.OperationMetadata; +import com.google.longrunning.Operation; +import com.google.protobuf.Empty; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.ExecutionException; +import net.sourceforge.argparse4j.ArgumentParsers; +import net.sourceforge.argparse4j.inf.ArgumentParser; +import net.sourceforge.argparse4j.inf.ArgumentParserException; +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; +import net.sourceforge.argparse4j.inf.Subparsers; + +/** + * Google Cloud AutoML Vision API sample application. Example usage: mvn package exec:java + * -Dexec.mainClass ='com.google.cloud.vision.samples.automl.ModelApi' -Dexec.args='create_model + * [datasetId] test_model' + */ +public class ModelApi { + + // [START automl_vision_create_model] + /** + * Demonstrates using the AutoML client to create a model. + * + * @param projectId the Id of the project. + * @param computeRegion the Region name. + * @param dataSetId the Id of the dataset to which model is created. + * @param modelName the Name of the model. + * @param trainBudget the Budget for training the model. + */ + static void createModel( + String projectId, + String computeRegion, + String dataSetId, + String modelName, + String trainBudget) { + // Instantiates a client + try (AutoMlClient client = AutoMlClient.create()) { + + // A resource that represents Google Cloud Platform location. + LocationName projectLocation = LocationName.of(projectId, computeRegion); + + // Set model metadata. + ImageClassificationModelMetadata imageClassificationModelMetadata = + Long.valueOf(trainBudget) == 0 + ? ImageClassificationModelMetadata.newBuilder().build() + : ImageClassificationModelMetadata.newBuilder() + .setTrainBudget(Long.valueOf(trainBudget)) + .build(); + + // Set model name and model metadata for the image dataset. + Model myModel = + Model.newBuilder() + .setDisplayName(modelName) + .setDatasetId(dataSetId) + .setImageClassificationModelMetadata(imageClassificationModelMetadata) + .build(); + + // Create a model with the model metadata in the region. + OperationFuture response = + client.createModelAsync(projectLocation, myModel); + + System.out.println( + String.format( + "Training operation name: %s", response.getInitialFuture().get().getName())); + System.out.println("Training started..."); + } catch (IOException | ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + } + // [END automl_vision_create_model] + + public static void main(String[] args) { + argsHelper(args); + } + + static void argsHelper(String[] args) { + ArgumentParser parser = + ArgumentParsers.newFor("ModelApi") + .build() + .defaultHelp(true) + .description("Model API operations."); + Subparsers subparsers = parser.addSubparsers().dest("command"); + + Subparser createModelParser = subparsers.addParser("create_model"); + createModelParser.addArgument("datasetId"); + createModelParser.addArgument("modelName"); + createModelParser.addArgument("trainBudget"); + + String projectId = System.getenv("GOOGLE_CLOUD_PROJECT"); + String computeRegion = System.getenv("REGION_NAME"); + + if (projectId == null || computeRegion == null) { + System.out.println("Set `GOOGLE_CLOUD_PROJECT` and `REGION_NAME` as specified in the README"); + System.exit(-1); + } + + try { + Namespace ns = parser.parseArgs(args); + if (ns.get("command").equals("create_model")) { + createModel( + projectId, + computeRegion, + ns.getString("datasetId"), + ns.getString("modelName"), + ns.getString("trainBudget")); + } + } catch (ArgumentParserException e) { + parser.handleError(e); + } + } +} diff --git a/automl/snippets/src/main/java/com/google/cloud/vision/ObjectDetectionDeployModelNodeCount.java b/automl/snippets/src/main/java/com/google/cloud/vision/ObjectDetectionDeployModelNodeCount.java new file mode 100644 index 00000000000..a26137a67ff --- /dev/null +++ b/automl/snippets/src/main/java/com/google/cloud/vision/ObjectDetectionDeployModelNodeCount.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019 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. + */ + +package com.google.cloud.vision; + +// [START automl_vision_object_detection_deploy_model_node_count] +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.automl.v1beta1.AutoMlClient; +import com.google.cloud.automl.v1beta1.DeployModelRequest; +import com.google.cloud.automl.v1beta1.ImageObjectDetectionModelDeploymentMetadata; +import com.google.cloud.automl.v1beta1.ModelName; +import com.google.cloud.automl.v1beta1.OperationMetadata; +import com.google.protobuf.Empty; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +class ObjectDetectionDeployModelNodeCount { + + static void objectDetectionDeployModelNodeCount(String projectId, String modelId) + throws IOException, ExecutionException, InterruptedException { + // String projectId = "YOUR_PROJECT_ID"; + // String modelId = "YOUR_MODEL_ID"; + + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. After completing all of your requests, call + // the "close" method on the client to safely clean up any remaining background resources. + try (AutoMlClient client = AutoMlClient.create()) { + // Get the full path of the model. + ModelName modelFullId = ModelName.of(projectId, "us-central1", modelId); + + // Set how many nodes the model is deployed on + ImageObjectDetectionModelDeploymentMetadata deploymentMetadata = + ImageObjectDetectionModelDeploymentMetadata.newBuilder().setNodeCount(2).build(); + + DeployModelRequest request = + DeployModelRequest.newBuilder() + .setName(modelFullId.toString()) + .setImageObjectDetectionModelDeploymentMetadata(deploymentMetadata) + .build(); + // Deploy the model + OperationFuture future = client.deployModelAsync(request); + future.get(); + System.out.println("Model deployment on 2 nodes finished"); + } + } +} +// [END automl_vision_object_detection_deploy_model_node_count] diff --git a/automl/snippets/src/main/java/com/google/cloud/vision/PredictionApi.java b/automl/snippets/src/main/java/com/google/cloud/vision/PredictionApi.java new file mode 100644 index 00000000000..404ee287765 --- /dev/null +++ b/automl/snippets/src/main/java/com/google/cloud/vision/PredictionApi.java @@ -0,0 +1,136 @@ +/* + * Copyright 2018 Google Inc. + * + * 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. + */ + +/* + * This application demonstrates how to perform basic operations on prediction + * with the Google AutoML Vision API. + * + * For more information, the documentation at + * https://cloud.google.com/vision/automl/docs. + */ + +package com.google.cloud.vision; + +// Imports the Google Cloud client library +import com.google.cloud.automl.v1beta1.AnnotationPayload; +import com.google.cloud.automl.v1beta1.ExamplePayload; +import com.google.cloud.automl.v1beta1.Image; +import com.google.cloud.automl.v1beta1.ModelName; +import com.google.cloud.automl.v1beta1.PredictResponse; +import com.google.cloud.automl.v1beta1.PredictionServiceClient; +import com.google.protobuf.ByteString; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import net.sourceforge.argparse4j.ArgumentParsers; +import net.sourceforge.argparse4j.inf.ArgumentParser; +import net.sourceforge.argparse4j.inf.ArgumentParserException; +import net.sourceforge.argparse4j.inf.Namespace; + +/** + * Google Cloud AutoML Vision API sample application. Example usage: mvn package exec:java + * -Dexec.mainClass ='com.google.cloud.vision.samples.automl.PredictionApi' -Dexec.args='predict + * [modelId] [path-to-image] [scoreThreshold]' + */ +public class PredictionApi { + + // [START automl_vision_predict] + /** + * Demonstrates using the AutoML client to predict an image. + * + * @param projectId the Id of the project. + * @param computeRegion the Region name. + * @param modelId the Id of the model which will be used for text classification. + * @param filePath the Local text file path of the content to be classified. + * @param scoreThreshold the Confidence score. Only classifications with confidence score above + * scoreThreshold are displayed. + */ + static void predict( + String projectId, + String computeRegion, + String modelId, + String filePath, + String scoreThreshold) { + + // Instantiate client for prediction service. + try (PredictionServiceClient predictionClient = PredictionServiceClient.create()) { + + // Get the full path of the model. + ModelName name = ModelName.of(projectId, computeRegion, modelId); + + // Read the image and assign to payload. + ByteString content = ByteString.copyFrom(Files.readAllBytes(Paths.get(filePath))); + Image image = Image.newBuilder().setImageBytes(content).build(); + ExamplePayload examplePayload = ExamplePayload.newBuilder().setImage(image).build(); + + // Additional parameters that can be provided for prediction e.g. Score Threshold + Map params = new HashMap<>(); + if (scoreThreshold != null) { + params.put("score_threshold", scoreThreshold); + } + // Perform the AutoML Prediction request + PredictResponse response = predictionClient.predict(name, examplePayload, params); + + System.out.println("Prediction results:"); + for (AnnotationPayload annotationPayload : response.getPayloadList()) { + System.out.println("Predicted class name :" + annotationPayload.getDisplayName()); + System.out.println( + "Predicted class score :" + annotationPayload.getClassification().getScore()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + // [END automl_vision_predict] + + public static void main(String[] args) { + argsHelper(args); + } + + static void argsHelper(String[] args) { + ArgumentParser parser = + ArgumentParsers.newFor("PredictionApi") + .build() + .defaultHelp(true) + .description("Prediction API Operation"); + + parser.addArgument("modelId").required(true); + parser.addArgument("filePath").required(true); + parser.addArgument("scoreThreshold").nargs("?").type(String.class).setDefault(""); + + String projectId = System.getenv("GOOGLE_CLOUD_PROJECT"); + String computeRegion = System.getenv("REGION_NAME"); + + if (projectId == null || computeRegion == null) { + System.out.println("Set `GOOGLE_CLOUD_PROJECT` and `REGION_NAME` as specified in the README"); + System.exit(-1); + } + + try { + Namespace ns = parser.parseArgs(args); + predict( + projectId, + computeRegion, + ns.getString("modelId"), + ns.getString("filePath"), + ns.getString("scoreThreshold")); + } catch (ArgumentParserException e) { + parser.handleError(e); + } + } +} diff --git a/automl/snippets/src/test/java/com/google/cloud/vision/ClassificationDeployModelIT.java b/automl/snippets/src/test/java/com/google/cloud/vision/ClassificationDeployModelIT.java new file mode 100644 index 00000000000..d0683a01384 --- /dev/null +++ b/automl/snippets/src/test/java/com/google/cloud/vision/ClassificationDeployModelIT.java @@ -0,0 +1,92 @@ +/* + * Copyright 2019 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. + */ + +package com.google.cloud.vision; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.concurrent.ExecutionException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public class ClassificationDeployModelIT { + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static final String MODEL_ID = "ICN0000000000000000000"; + private ByteArrayOutputStream bout; + private PrintStream out; + private PrintStream originalPrintStream; + + @Before + public void setUp() { + bout = new ByteArrayOutputStream(); + out = new PrintStream(bout); + originalPrintStream = System.out; + System.setOut(out); + } + + @After + public void tearDown() { + System.out.flush(); + System.setOut(originalPrintStream); + } + + @Test + public void testClassificationDeployModelApi() { + // As model deployment can take a long time, instead try to deploy a + // nonexistent model and confirm that the model was not found, but other + // elements of the request were valid. + try { + ClassificationDeployModel.classificationDeployModel(PROJECT_ID, MODEL_ID); + String got = bout.toString(); + assertThat(got).contains("The model does not exist"); + } catch (IOException | ExecutionException | InterruptedException e) { + assertThat(e.getMessage()).contains("The model does not exist"); + } + } + + @Test + public void testClassificationUndeployModelApi() { + // As model deployment can take a long time, instead try to deploy a + // nonexistent model and confirm that the model was not found, but other + // elements of the request were valid. + try { + ClassificationUndeployModel.classificationUndeployModel(PROJECT_ID, MODEL_ID); + String got = bout.toString(); + assertThat(got).contains("The model does not exist"); + } catch (IOException | ExecutionException | InterruptedException e) { + assertThat(e.getMessage()).contains("The model does not exist"); + } + } + + @Test + public void testClassificationDeployModelNodeCountApi() { + // As model deployment can take a long time, instead try to deploy a + // nonexistent model and confirm that the model was not found, but other + // elements of the request were valid. + try { + ClassificationDeployModelNodeCount.classificationDeployModelNodeCount(PROJECT_ID, MODEL_ID); + String got = bout.toString(); + assertThat(got).contains("The model does not exist"); + } catch (IOException | ExecutionException | InterruptedException e) { + assertThat(e.getMessage()).contains("The model does not exist"); + } + } +} diff --git a/automl/snippets/src/test/java/com/google/cloud/vision/ObjectDetectionDeployModelNodeCountIT.java b/automl/snippets/src/test/java/com/google/cloud/vision/ObjectDetectionDeployModelNodeCountIT.java new file mode 100644 index 00000000000..80e0254caf2 --- /dev/null +++ b/automl/snippets/src/test/java/com/google/cloud/vision/ObjectDetectionDeployModelNodeCountIT.java @@ -0,0 +1,68 @@ +/* + * Copyright 2019 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. + */ + +package com.google.cloud.vision; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.concurrent.ExecutionException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for vision "Deploy Model Node Count" sample. */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:abbreviationaswordinname") +public class ObjectDetectionDeployModelNodeCountIT { + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static final String MODEL_ID = "0000000000000000000000"; + private ByteArrayOutputStream bout; + private PrintStream out; + private PrintStream originalPrintStream; + + @Before + public void setUp() { + bout = new ByteArrayOutputStream(); + out = new PrintStream(bout); + originalPrintStream = System.out; + System.setOut(out); + } + + @After + public void tearDown() { + System.out.flush(); + System.setOut(originalPrintStream); + } + + @Test + public void testObjectDetectionDeployModelNodeCountApi() { + // As model deployment can take a long time, instead try to deploy a + // nonexistent model and confirm that the model was not found, but other + // elements of the request were valid. + try { + ObjectDetectionDeployModelNodeCount.objectDetectionDeployModelNodeCount(PROJECT_ID, MODEL_ID); + String got = bout.toString(); + assertThat(got).contains("The model does not exist"); + } catch (IOException | ExecutionException | InterruptedException e) { + assertThat(e.getMessage()).contains("The model does not exist"); + } + } +} diff --git a/automl/snippets/src/test/java/com/google/cloud/vision/PredictionApiIT.java b/automl/snippets/src/test/java/com/google/cloud/vision/PredictionApiIT.java new file mode 100644 index 00000000000..0464db3b033 --- /dev/null +++ b/automl/snippets/src/test/java/com/google/cloud/vision/PredictionApiIT.java @@ -0,0 +1,85 @@ +/* + * Copyright 2018 Google Inc. + * + * 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. + */ + +package com.google.cloud.vision; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.automl.v1beta1.AutoMlClient; +import com.google.cloud.automl.v1beta1.DeployModelRequest; +import com.google.cloud.automl.v1beta1.Model; +import com.google.cloud.automl.v1beta1.ModelName; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for vision "PredictionAPI" sample. */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:abbreviationaswordinname") +public class PredictionApiIT { + private static final String COMPUTE_REGION = "us-central1"; + private static final String PROJECT_ID = "java-docs-samples-testing"; + private static final String modelId = "ICN620201829169141520"; + private static final String filePath = "./resources/dandelion.jpg"; + private static final String scoreThreshold = "0.7"; + private ByteArrayOutputStream bout; + private PrintStream out; + private PrintStream originalPrintStream; + + @Before + public void setUp() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // Verify that the model is deployed for prediction + try (AutoMlClient client = AutoMlClient.create()) { + ModelName modelFullId = ModelName.of(PROJECT_ID, "us-central1", modelId); + Model model = client.getModel(modelFullId); + if (model.getDeploymentState() == Model.DeploymentState.UNDEPLOYED) { + // Deploy the model if not deployed + DeployModelRequest request = + DeployModelRequest.newBuilder().setName(modelFullId.toString()).build(); + Future future = client.deployModelAsync(request); + future.get(30, TimeUnit.MINUTES); + } + } + + bout = new ByteArrayOutputStream(); + out = new PrintStream(bout); + originalPrintStream = System.out; + System.setOut(out); + } + + @After + public void tearDown() { + System.out.flush(); + System.setOut(originalPrintStream); + } + + @Test + public void testPredict() { + PredictionApi.predict(PROJECT_ID, COMPUTE_REGION, modelId, filePath, scoreThreshold); + String got = bout.toString(); + assertThat(got).contains("dandelion"); + } +}