From ce0f5d5211e9689b374c03891df461a5c874cf67 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Thu, 20 Apr 2017 13:28:21 -0700 Subject: [PATCH] Adds example usage and tests for v1beta2 (#622) * Adds example usage and tests for v1beta2 * License dates on new files, NPE fix, and renames on some methods. * Updates copyright --- language/analysis/README.md | 7 + language/analysis/demo-beta.sh | 61 ++++++ .../cloud/language/samples/AnalyzeBeta.java | 174 ++++++++++++++++++ .../cloud/language/samples/AnalyzeBetaIT.java | 78 ++++++++ 4 files changed, 320 insertions(+) create mode 100755 language/analysis/demo-beta.sh create mode 100644 language/analysis/src/main/java/com/google/cloud/language/samples/AnalyzeBeta.java create mode 100644 language/analysis/src/test/java/com/google/cloud/language/samples/AnalyzeBetaIT.java diff --git a/language/analysis/README.md b/language/analysis/README.md index 14836f85f44..3d6c1f33ea5 100644 --- a/language/analysis/README.md +++ b/language/analysis/README.md @@ -68,6 +68,13 @@ java -cp target/language-entities-1.0-jar-with-dependencies.jar \ "The quick brown fox jumped over the lazy dog." ``` +Analyze sentiment Beta +``` +java -cp target/language-entities-1.0-jar-with-dependencies.jar \ + com.google.cloud.language.samples.AnalyzeBeta sentiment "Ich habe eine wundervolle Zeit." "DE" +``` + + Included with the sample are `demo.sh` and `demo.bat` which show additional examples of usage. diff --git a/language/analysis/demo-beta.sh b/language/analysis/demo-beta.sh new file mode 100755 index 00000000000..71d5116a142 --- /dev/null +++ b/language/analysis/demo-beta.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# Demonstrates how to run the AnalyzeBeta sample. + +########################################################################## +# Copyright 2017 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. +########################################################################## + + +####################################### +# Performs a language operation on the given text or GCS object. +# Globals: +# None +# Arguments: +# $1 The operation to perform, either entities, sentiment, or syntax. +# $2 The text or GCS object to operate on. +# Returns: +# None +####################################### +function run_nl() { + local main_class=com.google.cloud.language.samples.AnalyzeBeta + local jar_file=target/language-entities-1.0-jar-with-dependencies.jar + java -cp ${jar_file} ${main_class} "$1" "$2" +} + +####################################### +# Exercises the sample code on various example text and GCS objects. +# Globals: +# None +# Arguments: +# None +# Returns: +# None +####################################### +function run_nl_all() { + local quote_de="Bananen sind die köstlichsten Früchte, ich liebe sie zu + essen. Ich mag sie so sehr wie Ananas." + local quote="Larry Page, Google's co-founder, once described the 'perfect + search engine' as something that 'understands exactly what you mean and + gives you back exactly what you want.' Since he spoke those words Google + has grown to offer products beyond search, but the spirit of what he said + remains." + local gs_path="gs://cloud-samples-tests/natural-language/gettysburg.txt" + + run_nl entities-sentiment "${quote}" + run_nl entities-sentiment "${gs_path}" + run_nl sentiment "${quote_de}" "DE" +} + +run_nl_all diff --git a/language/analysis/src/main/java/com/google/cloud/language/samples/AnalyzeBeta.java b/language/analysis/src/main/java/com/google/cloud/language/samples/AnalyzeBeta.java new file mode 100644 index 00000000000..f3d2714be60 --- /dev/null +++ b/language/analysis/src/main/java/com/google/cloud/language/samples/AnalyzeBeta.java @@ -0,0 +1,174 @@ +/* + * Copyright 2017 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.language.samples; + +import com.google.cloud.language.spi.v1beta2.LanguageServiceClient; + +import com.google.cloud.language.v1beta2.AnalyzeEntitySentimentRequest; +import com.google.cloud.language.v1beta2.AnalyzeEntitySentimentResponse; +import com.google.cloud.language.v1beta2.AnalyzeSentimentResponse; +import com.google.cloud.language.v1beta2.Document; +import com.google.cloud.language.v1beta2.Document.Type; +import com.google.cloud.language.v1beta2.EncodingType; +import com.google.cloud.language.v1beta2.Entity; +import com.google.cloud.language.v1beta2.EntityMention; +import com.google.cloud.language.v1beta2.Sentiment; +import com.google.cloud.language.v1beta2.Token; +import com.google.protobuf.Descriptors; + +import java.io.IOException; +import java.io.PrintStream; +import java.security.GeneralSecurityException; +import java.util.List; +import java.util.Map; + +/** + * A sample application that uses the Natural Language API to perform + * entity, sentiment and syntax analysis. + */ +public class AnalyzeBeta { + private final LanguageServiceClient languageApi; + + /** + * Constructs a {@link Analyze} which connects to the Cloud Natural Language API. + */ + public AnalyzeBeta(LanguageServiceClient languageApi) { + this.languageApi = languageApi; + } + + + /** + * Detects entities,sentiment and syntax in a document using the Natural Language API. + */ + public static void main(String[] args) throws IOException, GeneralSecurityException { + if (args.length < 2 || args.length > 4) { + System.err.println("Usage:"); + System.err.printf( + "\tjava %s \"command\" \"text to analyze\" \"language\" \n", + Analyze.class.getCanonicalName()); + System.exit(1); + } + String command = args[0]; + String text = args[1]; + String lang = null; + if (args.length > 2) { + lang = args[2]; + } + + AnalyzeBeta app = new AnalyzeBeta(LanguageServiceClient.create()); + + if (command.equals("entities-sentiment")) { + if (text.startsWith("gs://")) { + printEntities(System.out, app.entitySentimentFile(text)); + } else { + printEntities(System.out, app.entitySentimentText(text)); + } + } else if (command.equals("sentiment")) { + printSentiment(System.out, app.analyzeSentimentText(text, lang)); + } + } + + /** + * Print the Sentiment {@code sentiment}. + */ + public static void printSentiment(PrintStream out, Sentiment sentiment) { + if (sentiment == null) { + out.println("No sentiment found"); + return; + } + out.println("Found sentiment."); + out.printf("\tMagnitude: %.3f\n", sentiment.getMagnitude()); + out.printf("\tScore: %.3f\n", sentiment.getScore()); + } + + /** + * Print a list of {@code entities}. + */ + public static void printEntities(PrintStream out, List entities) { + if (entities == null || entities.size() == 0) { + out.println("No entities found."); + return; + } + out.printf("Found %d entit%s.\n", entities.size(), entities.size() == 1 ? "y" : "ies"); + for (Entity entity : entities) { + out.printf("----\n\"%s\"\n", entity.getName()); + out.printf("\tSalience: %.3f\n", entity.getSalience()); + out.printf("\tSentiment Magnitude: %.3f\n", entity.getSentiment().getMagnitude()); + out.printf("\tSentiment Score: %.3f\n", entity.getSentiment().getScore()); + out.printf("\tType: %s\n", entity.getType()); + if (entity.getMetadataMap() != null) { + for (Map.Entry metadata : entity.getMetadataMap().entrySet()) { + out.printf("\tMetadata: %s = %s\n", metadata.getKey(), metadata.getValue()); + } + } + if (entity.getMentionsList() != null) { + for (EntityMention mention : entity.getMentionsList()) { + for (Map.Entry mentionSetMember : + mention.getAllFields().entrySet()) { + out.printf("\tMention: %s = %s\n", mentionSetMember.getKey(), + mentionSetMember.getValue()); + } + } + } + } + } + + /** + * Gets {@link Sentiment} from the string {@code text}. + */ + public Sentiment analyzeSentimentText(String text, String lang) throws IOException { + if (lang == null || lang.length() < 2) { + lang = "EN"; + } + + // Note: This does not work on App Engine standard. + Document doc = Document.newBuilder() + .setLanguage(lang) + .setContent(text).setType(Type.PLAIN_TEXT).build(); + AnalyzeSentimentResponse response = languageApi.analyzeSentiment(doc); + return response.getDocumentSentiment(); + } + + /** + * Gets {@link Entity}s from the string {@code text} with sentiment. + */ + public List entitySentimentText(String text) throws IOException { + // Note: This does not work on App Engine standard. + Document doc = Document.newBuilder() + .setContent(text).setType(Type.PLAIN_TEXT).build(); + AnalyzeEntitySentimentRequest request = AnalyzeEntitySentimentRequest.newBuilder() + .setDocument(doc) + .setEncodingType(EncodingType.UTF16).build(); + AnalyzeEntitySentimentResponse response = languageApi.analyzeEntitySentiment(request); + return response.getEntitiesList(); + } + + /** + * Gets {@link Entity}s from the contents of the object at the given GCS {@code path} + * with sentiment. + */ + public List entitySentimentFile(String path) throws IOException { + // Note: This does not work on App Engine standard. + Document doc = Document.newBuilder() + .setGcsContentUri(path).setType(Type.PLAIN_TEXT).build(); + AnalyzeEntitySentimentRequest request = AnalyzeEntitySentimentRequest.newBuilder() + .setDocument(doc) + .setEncodingType(EncodingType.UTF16).build(); + AnalyzeEntitySentimentResponse response = languageApi.analyzeEntitySentiment(request); + return response.getEntitiesList(); + } +} diff --git a/language/analysis/src/test/java/com/google/cloud/language/samples/AnalyzeBetaIT.java b/language/analysis/src/test/java/com/google/cloud/language/samples/AnalyzeBetaIT.java new file mode 100644 index 00000000000..183ca7aeae0 --- /dev/null +++ b/language/analysis/src/test/java/com/google/cloud/language/samples/AnalyzeBetaIT.java @@ -0,0 +1,78 @@ +/* + * Copyright 2017 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.language.samples; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.language.spi.v1beta2.LanguageServiceClient; +import com.google.cloud.language.v1beta2.Entity; +import com.google.cloud.language.v1beta2.Sentiment; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Integration (system) tests for {@link Analyze}. + */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:abbreviationaswordinname") +public class AnalyzeBetaIT { + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static final String BUCKET = PROJECT_ID; + + private AnalyzeBeta analyzeApp; + + @Before public void setup() throws Exception { + analyzeApp = new AnalyzeBeta(LanguageServiceClient.create()); + } + + @Test public void analyzeSentiment_returnPositiveGerman() throws Exception { + // Act + Sentiment sentiment = + analyzeApp.analyzeSentimentText( + "Ich hatte die schönste Erfahrung mit euch allen.", "DE"); + + // Assert + assertThat((double)sentiment.getMagnitude()).isGreaterThan(0.0); + assertThat((double)sentiment.getScore()).isGreaterThan(0.0); + } + + @Test public void analyzeSyntax_entitySentimentText() throws Exception { + List entities = analyzeApp.entitySentimentText("Oranges, grapes, and apples can be " + + "found in the cafeterias located in Mountain View, Seattle, and London."); + + List got = entities.stream().map(e -> e.getName()).collect(Collectors.toList()); + + // Assert + assertThat(got).named("entity names").contains("Seattle"); + } + + @Test public void analyzeSyntax_entitySentimentFile() throws Exception { + List entities = + analyzeApp.entitySentimentFile("gs://" + BUCKET + "/natural-language/gettysburg.txt"); + + List got = entities.stream().map(e -> e.getName()).collect(Collectors.toList()); + + // Assert + assertThat(got).named("entity names").contains("God"); + } +}