Skip to content

Commit

Permalink
.NET v3: Feedback Sentiment Analyzer cross-service example (#5493)
Browse files Browse the repository at this point in the history
  • Loading branch information
rlhagerm authored and ford-at-aws committed Dec 15, 2023
1 parent 8d512fe commit 649a0a1
Show file tree
Hide file tree
Showing 46 changed files with 1,382 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .doc_gen/cross-content/cross_FSA_JavaScript_block.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<para>&CMP; determines the sentiment of the extracted text and its language.</para>
</listitem>
<listitem>
<para>The extracted text is translated to French using &TSL;.</para>
<para>The extracted text is translated to English using &TSL;.</para>
</listitem>
<listitem>
<para>&POL; synthesizes an audio file from the extracted text.</para>
Expand Down
2 changes: 1 addition & 1 deletion .doc_gen/cross-content/cross_FSA_Java_block.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<para>&CMP; determines the sentiment of the extracted text and its language.</para>
</listitem>
<listitem>
<para>The extracted text is translated to French using &TSL;.</para>
<para>The extracted text is translated to English using &TSL;.</para>
</listitem>
<listitem>
<para>&POL; synthesizes an audio file from the extracted text.</para>
Expand Down
33 changes: 33 additions & 0 deletions .doc_gen/cross-content/cross_FSA_NetV3_block.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "file://zonbook/docbookx.dtd" [
<!ENTITY % phrases-shared SYSTEM "file://AWSShared/common/phrases-shared.ent">
%phrases-shared;
]>
<block>
<para>
This example application analyzes and stores customer feedback cards. Specifically,
it fulfills the need of a fictitious hotel in New York City. The hotel receives feedback
from guests in various languages in the form of physical comment cards. That feedback
is uploaded into the app through a web client.

After an image of a comment card is uploaded, the following steps occur:
</para>
<itemizedlist>
<listitem>
<para>Text is extracted from the image using &TEXTRACT;.</para>
</listitem>
<listitem>
<para>&CMP; determines the sentiment of the extracted text and its language.</para>
</listitem>
<listitem>
<para>The extracted text is translated to English using &TSL;.</para>
</listitem>
<listitem>
<para>&POL; synthesizes an audio file from the extracted text.</para>
</listitem>
</itemizedlist>
<para> The full app can be deployed with the &CDK;. For source code and deployment
instructions, see the project in <ulink
url="https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/example_code/cross-services/FeedbackSentimentAnalyzer">
GitHub</ulink>. </para>
</block>
2 changes: 1 addition & 1 deletion .doc_gen/cross-content/cross_FSA_Ruby_block.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<para>&CMP; determines the sentiment of the extracted text and its language.</para>
</listitem>
<listitem>
<para>The extracted text is translated to French using &TSL;.</para>
<para>The extracted text is translated to English using &TSL;.</para>
</listitem>
<listitem>
<para>&POL; synthesizes an audio file from the extracted text.</para>
Expand Down
4 changes: 4 additions & 0 deletions .doc_gen/metadata/cross_metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ cross_FSA:
their original language, determines their sentiment, and generates an audio file
from the translated text.
languages:
.NET:
versions:
- sdk_version: 3
block_content: cross_FSA_NetV3_block.xml
Java:
versions:
- sdk_version: 2
Expand Down
1 change: 1 addition & 0 deletions .github/pre_validate/pre_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@
'role/AmazonSageMakerGeospatialFullAccess',
'VectorEnrichmentJobDataSourceConfigInput',
'com/workdocs/latest/APIReference/Welcome',
'service/FeedbackSentimentAnalyzer/README'
}

def check_files(root, quiet):
Expand Down
1 change: 1 addition & 0 deletions applications/feedback_sentiment_analyzer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This application has been implemented with the following AWS SDKs.
- [Ruby](../../ruby/cross_service_examples/feedback_sentiment_analyzer/README.md)
- [JavaScript](../../javascriptv3/example_code/cross-services/feedback-sentiment-analyzer/README.md)
- [Java](../../javav2/usecases/creating_fsa_app/README.md)
- [.NET](../../dotnetv3/cross-service/FeedbackSentimentAnalyzer/README.md)

To deploy one of these implementations, follow the [Deployment instructions](#deployment-instructions).

Expand Down
2 changes: 1 addition & 1 deletion applications/feedback_sentiment_analyzer/SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ For example:

### SynthesizeAudio

Uses the Amazon Polly [SynthesizeSpeech](https://docs.aws.amazon.com/polly/latest/dg/API_SynthesizeSpeech.html) method to convert input text into life-like speech. Store the synthesized audio in the provided Amazon S3 bucket with a content type of "audio/mp3".
Uses the Amazon Polly [SynthesizeSpeech](https://docs.aws.amazon.com/polly/latest/dg/API_SynthesizeSpeech.html) method to convert input text into life-like speech. Store the synthesized audio in the provided Amazon S3 bucket with a content type of "audio/mpeg".

#### **Input**

Expand Down
108 changes: 108 additions & 0 deletions applications/feedback_sentiment_analyzer/cdk/lib/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,119 @@ const JAVA_FUNCTIONS: AppFunctionConfig[] = [
},
];

const DOTNET_FUNCTIONS = [
{
...BASE_APP_FUNCTION,
name: "ExtractText",
handler: "FsaExtractText::FsaExtractText.ExtractTextFunction::FunctionHandler",
runtime: Runtime.DOTNET_6,
codeAsset() {
const source = resolve(
"../../../dotnetv3/cross-service/FeedbackSentimentAnalyzer"
);
return Code.fromAsset(source, {
bundling: {
command: [
"/bin/sh",
"-c",
" dotnet tool install -g Amazon.Lambda.Tools" +
" && dotnet build" +
" && cd FsaExtractText" +
" && dotnet lambda package --output-package /asset-output/function.zip",
],
image: Runtime.DOTNET_6.bundlingImage,
user: "root",
outputType: BundlingOutput.ARCHIVED,
},
});
},
},
{
...BASE_APP_FUNCTION,
name: "AnalyzeSentiment",
handler: "FsaAnalyzeSentiment::FsaAnalyzeSentiment.AnalyzeSentimentFunction::FunctionHandler",
runtime: Runtime.DOTNET_6,
codeAsset() {
const source = resolve(
"../../../dotnetv3/cross-service/FeedbackSentimentAnalyzer"
);
return Code.fromAsset(source, {
bundling: {
command: [
"/bin/sh",
"-c",
" dotnet tool install -g Amazon.Lambda.Tools" +
" && dotnet build" +
" && cd FsaAnalyzeSentiment" +
" && dotnet lambda package --output-package /asset-output/function.zip",
],
image: Runtime.DOTNET_6.bundlingImage,
user: "root",
outputType: BundlingOutput.ARCHIVED,
},
});
},
},
{
...BASE_APP_FUNCTION,
name: "TranslateText",
handler: "FsaTranslateText::FsaTranslateText.TranslateTextFunction::FunctionHandler",
runtime: Runtime.DOTNET_6,
codeAsset() {
const source = resolve(
"../../../dotnetv3/cross-service/FeedbackSentimentAnalyzer"
);
return Code.fromAsset(source, {
bundling: {
command: [
"/bin/sh",
"-c",
" dotnet tool install -g Amazon.Lambda.Tools" +
" && dotnet build" +
" && cd FsaTranslateText" +
" && dotnet lambda package --output-package /asset-output/function.zip",
],
image: Runtime.DOTNET_6.bundlingImage,
user: "root",
outputType: BundlingOutput.ARCHIVED,
},
});
},
},
{
...BASE_APP_FUNCTION,
name: "SynthesizeAudio",
handler: "FsaSynthesizeAudio::FsaSynthesizeAudio.SynthesizeAudioFunction::FunctionHandler",
runtime: Runtime.DOTNET_6,
codeAsset() {
const source = resolve(
"../../../dotnetv3/cross-service/FeedbackSentimentAnalyzer"
);
return Code.fromAsset(source, {
bundling: {
command: [
"/bin/sh",
"-c",
" dotnet tool install -g Amazon.Lambda.Tools" +
" && dotnet build" +
" && cd FsaSynthesizeAudio" +
" && dotnet lambda package --output-package /asset-output/function.zip",
],
image: Runtime.DOTNET_6.bundlingImage,
user: "root",
outputType: BundlingOutput.ARCHIVED,
},
});
},
},
];

const FUNCTIONS: Record<string, AppFunctionConfig[]> = {
examplelang: EXAMPLE_LANG_FUNCTIONS,
ruby: RUBY_FUNCTIONS,
java: JAVA_FUNCTIONS,
javascript: JAVASCRIPT_FUNCTIONS,
dotnet: DOTNET_FUNCTIONS,
};
export function getFunctions(language: string = ""): AppFunctionConfig[] {
return FUNCTIONS[language] ?? FUNCTIONS.examplelang;
Expand Down
1 change: 1 addition & 0 deletions applications/feedback_sentiment_analyzer/cdk/lib/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export class AppStack extends Stack {
"image/png",
"binary/octet-stream",
"audio/mp3",
"audio/mpeg",
],
cloudWatchRole: true,
deployOptions: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33927.249
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FsaExtractText", "FsaExtractText\FsaExtractText.csproj", "{C0BB9701-7EE8-4A91-B1EB-B3888ED43079}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FsaAnalyzeSentiment", "FsaAnalyzeSentiment\FsaAnalyzeSentiment.csproj", "{9B31C723-D4B0-4953-B5B1-9002E7181DEB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FsaTranslateText", "FsaTranslateText\FsaTranslateText.csproj", "{9886D38A-541E-4BF5-83CE-6CA85088028B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FsaSynthesizeAudio", "FsaSynthesizeAudio\FsaSynthesizeAudio.csproj", "{4613C012-D517-4C57-98A3-F59B727889EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FsaServices", "FsaServices\FsaServices.csproj", "{DA3393CC-DBBF-444E-947C-FBDC4BA66C55}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FsaServicesTest", "FsaServicesTest\FsaServicesTest.csproj", "{3A3503C4-9077-4BB3-90C9-08A2B8ED798D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C0BB9701-7EE8-4A91-B1EB-B3888ED43079}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0BB9701-7EE8-4A91-B1EB-B3888ED43079}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0BB9701-7EE8-4A91-B1EB-B3888ED43079}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0BB9701-7EE8-4A91-B1EB-B3888ED43079}.Release|Any CPU.Build.0 = Release|Any CPU
{9B31C723-D4B0-4953-B5B1-9002E7181DEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9B31C723-D4B0-4953-B5B1-9002E7181DEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B31C723-D4B0-4953-B5B1-9002E7181DEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B31C723-D4B0-4953-B5B1-9002E7181DEB}.Release|Any CPU.Build.0 = Release|Any CPU
{9886D38A-541E-4BF5-83CE-6CA85088028B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9886D38A-541E-4BF5-83CE-6CA85088028B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9886D38A-541E-4BF5-83CE-6CA85088028B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9886D38A-541E-4BF5-83CE-6CA85088028B}.Release|Any CPU.Build.0 = Release|Any CPU
{4613C012-D517-4C57-98A3-F59B727889EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4613C012-D517-4C57-98A3-F59B727889EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4613C012-D517-4C57-98A3-F59B727889EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4613C012-D517-4C57-98A3-F59B727889EE}.Release|Any CPU.Build.0 = Release|Any CPU
{DA3393CC-DBBF-444E-947C-FBDC4BA66C55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA3393CC-DBBF-444E-947C-FBDC4BA66C55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA3393CC-DBBF-444E-947C-FBDC4BA66C55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA3393CC-DBBF-444E-947C-FBDC4BA66C55}.Release|Any CPU.Build.0 = Release|Any CPU
{3A3503C4-9077-4BB3-90C9-08A2B8ED798D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A3503C4-9077-4BB3-90C9-08A2B8ED798D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A3503C4-9077-4BB3-90C9-08A2B8ED798D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A3503C4-9077-4BB3-90C9-08A2B8ED798D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C7E557C4-6C58-4B38-8C84-D71A72BD3F3C}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using Amazon.Comprehend;
using Amazon.Lambda.Core;
using AWS.Lambda.Powertools.Logging;
using FsaServices.Models;
using FsaServices.Services;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace FsaAnalyzeSentiment;

/// <summary>
/// Function to handle analysis of sentiment.
/// </summary>
public class AnalyzeSentimentFunction
{
private readonly SentimentService _sentimentService;

/// <summary>
/// Default constructor. This constructor is used by AWS Lambda to construct the instance. When invoked in a Lambda environment,
/// the AWS credentials will come from the AWS Identity and Access Management (IAM) role associated with the function. The AWS Region will be set to the
/// Region the Lambda function is executed in.
/// </summary>
public AnalyzeSentimentFunction()
{
_sentimentService = new SentimentService(new AmazonComprehendClient());
}

/// <summary>
/// Constructs an instance with a preconfigured Comprehend client. This can be used for testing outside of the Lambda environment.
/// </summary>
/// <param name="s3Client"></param>
public AnalyzeSentimentFunction(IAmazonComprehend comprehendClient)
{
this._sentimentService = new SentimentService(comprehendClient);
}

/// <summary>
/// A function that takes in the source text and returns the sentiment details.
/// </summary>
/// <param name="extractTextOutput">The extracted text output to analyze.</param>
/// <param name="context">The Lambda context</param>
/// <returns>Sentiment details.</returns>
public async Task<SentimentDetails> FunctionHandler(SourceTextDetails extractTextOutput, ILambdaContext context)
{
// Log the object with Lambda PowerTools logger.
Logger.LogInformation(extractTextOutput);
var result = await _sentimentService.AnalyzeTextSentiment(extractTextOutput.source_text);
Logger.LogInformation(result);
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AWSProjectType>Lambda</AWSProjectType>
<!-- This property makes the build directory similar to a publish directory and helps the AWS .NET Lambda Mock Test Tool find project dependencies. -->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- Generate ready to run images during publishing to improve cold start time. -->
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Amazon.Lambda.Core" Version="2.1.0" />
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.3.1" />
<PackageReference Include="AWS.Lambda.Powertools.Logging" Version="1.3.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FsaServices\FsaServices.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"profiles": {
"Mock Lambda Test Tool": {
"commandName": "Executable",
"commandLineArgs": "--port 5050",
"workingDirectory": ".\\bin\\$(Configuration)\\net6.0",
"executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"Information": [
"This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
"To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",
"dotnet lambda help",
"All the command line options for the Lambda command can be specified in this file."
],
"profile": "default",
"region": "us-east-1",
"configuration": "Release",
"function-runtime": "dotnet6",
"function-memory-size": 256,
"function-timeout": 30,
"function-handler": "FsaAnalyzeSentiment::FsaAnalyzeSentiment.AnalyzeSentimentFunction::FunctionHandler"
}
Loading

0 comments on commit 649a0a1

Please sign in to comment.