├── public ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html ├── src ├── .DS_Store ├── setupTests.js ├── App.test.js ├── index.css ├── index.js ├── App.css ├── logo.svg ├── serviceWorker.js └── App.js ├── images ├── SignIn.png ├── SignUp.png ├── Dashboard.png ├── Cloud9Preview.png └── Preview_in_Browser.png ├── setup.sh ├── cleanup.sh ├── .github └── dependabot.yml ├── package.json └── README.md /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulbaisla/sentimentAnalysisLab/HEAD/src/.DS_Store -------------------------------------------------------------------------------- /images/SignIn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulbaisla/sentimentAnalysisLab/HEAD/images/SignIn.png -------------------------------------------------------------------------------- /images/SignUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulbaisla/sentimentAnalysisLab/HEAD/images/SignUp.png -------------------------------------------------------------------------------- /images/Dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulbaisla/sentimentAnalysisLab/HEAD/images/Dashboard.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulbaisla/sentimentAnalysisLab/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulbaisla/sentimentAnalysisLab/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulbaisla/sentimentAnalysisLab/HEAD/public/logo512.png -------------------------------------------------------------------------------- /images/Cloud9Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulbaisla/sentimentAnalysisLab/HEAD/images/Cloud9Preview.png -------------------------------------------------------------------------------- /images/Preview_in_Browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulbaisla/sentimentAnalysisLab/HEAD/images/Preview_in_Browser.png -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo Clone GitHubRepo 4 | 5 | git clone https://github.com/rahulbaisla/sentimentAnalysisLab.git 6 | cd sentimentAnalysisLab 7 | 8 | echo Install Dependencies 9 | npm install 10 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | read -p "This will remove any prior lab environments and set up a new one. Proceed (y/n)? " -n 1 -r 4 | echo # (optional) move to a new line 5 | if [[ $REPLY =~ ^[Yy]$ ]] 6 | then 7 | echo Cleaning up previous environment. Please wait... 8 | cd ~/Desktop/devlabs/sentimentAnalysisLab 9 | yes | amplify delete 10 | rm -rf ~/Desktop/devlabs/sentimentAnalysisLab 11 | echo Done cleaning up 12 | END 13 | else 14 | echo OK. No changes were made 15 | fi 16 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: #282c34; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | # Basic set up for three package managers 7 | 8 | version: 2 9 | updates: 10 | 11 | # Maintain dependencies for GitHub Actions 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | 17 | # Maintain dependencies for npm 18 | - package-ecosystem: "npm" 19 | directory: "/" 20 | schedule: 21 | interval: "daily" 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devlabsummit", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@aws-amplify/ui-react": "^1.2.6", 7 | "@material-ui/core": "^4.3.3", 8 | "@material-ui/icons": "^4.2.1", 9 | "@testing-library/jest-dom": "^4.2.4", 10 | "@testing-library/react": "^9.3.2", 11 | "@testing-library/user-event": "^7.1.2", 12 | "aws-amplify": "^4.2.0", 13 | "microphone-stream": "^5.0.1", 14 | "react": "^16.12.0", 15 | "react-dom": "^16.12.0", 16 | "react-gauge-chart": "^0.2.5", 17 | "react-json-pretty": "^2.2.0", 18 | "react-scripts": "3.4.0" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": "react-app" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' } 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import "./App.css"; 3 | 4 | // Amplify 5 | import Amplify, { Predictions } from "aws-amplify"; 6 | import { AmazonAIPredictionsProvider } from "@aws-amplify/predictions"; 7 | import { withAuthenticator } from "@aws-amplify/ui-react"; 8 | import awsconfig from "./aws-exports"; 9 | 10 | // Utilities 11 | import mic from "microphone-stream"; 12 | import GaugeChart from "react-gauge-chart"; 13 | import JSONPretty from "react-json-pretty"; 14 | 15 | // material-ui 16 | import { makeStyles } from "@material-ui/core/styles"; 17 | import { createMuiTheme, MuiThemeProvider } from "@material-ui/core/styles"; 18 | import orange from "@material-ui/core/colors/orange"; 19 | import Button from "@material-ui/core/Button"; 20 | import Typography from "@material-ui/core/Typography"; 21 | 22 | const theme = createMuiTheme({ 23 | palette: { 24 | primary: orange 25 | } 26 | }); 27 | 28 | const useStyles = makeStyles(theme => ({ 29 | root: { 30 | flexGrow: 1, 31 | height: "100vh" 32 | }, 33 | container: { 34 | fontFamily: 35 | '-apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n Roboto,\n "Helvetica Neue",\n Arial,\n sans-serif,\n "Apple Color Emoji",\n "Segoe UI Emoji",\n "Segoe UI Symbol"', 36 | fontWeight: "400", 37 | lineHeight: "1.5", 38 | color: "#212529", 39 | textAlign: "center", 40 | paddingLeft: "15px", 41 | paddingRight: "15px", 42 | height: "100%" 43 | }, 44 | recBtnContainer: { 45 | display: "flex", 46 | justifyContent: "space-evenly", 47 | padding: "30px 10px 30px 10px" 48 | }, 49 | gaugeContainer: { 50 | display: "flex", 51 | justifyContent: "space-evenly", 52 | padding: "30px 10px 30px 10px", 53 | height: "100%" 54 | }, 55 | textContainer: { 56 | padding: "5px 5px 5px 5px", 57 | border: "3px solid transparent", 58 | borderColor: "#ccc", 59 | width: "70%", 60 | height: "65%", 61 | textAlign: "left", 62 | overflow:"auto" 63 | }, 64 | chartStyle: { 65 | height: 250 66 | }, 67 | chartDivStyle: { 68 | height: 300, 69 | width: 540, 70 | border: 3 71 | } 72 | })); 73 | 74 | Amplify.configure(awsconfig); 75 | 76 | Amplify.addPluggable(new AmazonAIPredictionsProvider()); 77 | 78 | /** Function to convert speech to text */ 79 | function SpeechToText(props) { 80 | const classes = props.classes; 81 | console.log("SpeechToText :: props :: " + props); 82 | console.log("SpeechToText :: classes :: " + classes); 83 | 84 | const [response, setResponse] = useState(""); 85 | 86 | const [percent, setPercent] = useState(0); 87 | 88 | /** Function to Start/Stop Audio Recording */ 89 | function AudioRecorder(props) { 90 | const [recording, setRecording] = useState(false); 91 | const [micStream, setMicStream] = useState(); 92 | const [audioBuffer] = useState( 93 | (function() { 94 | let buffer = []; 95 | function add(raw) { 96 | buffer = buffer.concat(...raw); 97 | return buffer; 98 | } 99 | function newBuffer() { 100 | console.log("reseting buffer"); 101 | buffer = []; 102 | } 103 | 104 | return { 105 | reset: function() { 106 | newBuffer(); 107 | }, 108 | addData: function(raw) { 109 | return add(raw); 110 | }, 111 | getData: function() { 112 | return buffer; 113 | } 114 | }; 115 | })() 116 | ); 117 | 118 | async function startRecording() { 119 | console.log("start recording"); 120 | audioBuffer.reset(); 121 | 122 | window.navigator.mediaDevices 123 | .getUserMedia({ video: false, audio: true }) 124 | .then(stream => { 125 | const startMic = new mic(); 126 | 127 | startMic.setStream(stream); 128 | startMic.on("data", chunk => { 129 | var raw = mic.toRaw(chunk); 130 | if (raw == null) { 131 | return; 132 | } 133 | audioBuffer.addData(raw); 134 | }); 135 | 136 | setRecording(true); 137 | setMicStream(startMic); 138 | }); 139 | } 140 | 141 | async function stopRecording() { 142 | console.log("stop recording"); 143 | const { finishRecording } = props; 144 | 145 | micStream.stop(); 146 | setMicStream(null); 147 | setRecording(false); 148 | 149 | const resultBuffer = audioBuffer.getData(); 150 | 151 | if (typeof finishRecording === "function") { 152 | finishRecording(resultBuffer); 153 | } 154 | } 155 | 156 | return ( 157 |
158 |
159 | 160 | 170 | 180 | 181 |
182 |
183 | ); 184 | } 185 | 186 | /** Function to convert recorded audio to text using Amazon Transcribe */ 187 | function convertFromBuffer(bytes) { 188 | setResponse("Performing Sentiment Analysis..."); 189 | 190 | Predictions.convert({ 191 | transcription: { 192 | source: { 193 | bytes 194 | }, 195 | language: "en-US" // other options are "en-GB", "fr-FR", "fr-CA", "es-US" 196 | } 197 | }) 198 | .then(({ transcription: { fullText } }) => { 199 | console.log(fullText); 200 | interpretFromPredictions(JSON.stringify(fullText, null, 2)); 201 | }) 202 | .catch(err => console.log(JSON.stringify(err, null, 2))); 203 | } 204 | 205 | /** Function to apply sentiment analysis on converted text using Amazon Comprehend */ 206 | function interpretFromPredictions(textToInterpret) { 207 | console.log("inside interpretFromPredictions"); 208 | Predictions.interpret({ 209 | text: { 210 | source: { 211 | text: textToInterpret 212 | }, 213 | type: "ALL" 214 | } 215 | }) 216 | .then(result => { 217 | var textToDisplay = textToInterpret + "\n\n" + JSON.stringify(result, null, 2); 218 | setResponse(textToDisplay); 219 | setGauge(result); 220 | }) 221 | .catch(err => setResponse(JSON.stringify(err, null, 2))); 222 | } 223 | 224 | function setGauge(result) { 225 | if (result.textInterpretation.sentiment.predominant === "POSITIVE") { 226 | console.log("Inside condition POSITIVE :: "); 227 | setPercent(0.83); 228 | } else if (result.textInterpretation.sentiment.predominant === "NEGATIVE") { 229 | console.log("Inside condition NEGATIVE :: "); 230 | setPercent(0.17); 231 | } else if (result.textInterpretation.sentiment.predominant === "NEUTRAL") { 232 | console.log("Inside condition NEUTRAL :: "); 233 | setPercent(0.5); 234 | } 235 | } 236 | return ( 237 |
238 | 239 |
240 |
241 | 248 |
249 |
250 | 257 | 258 | 259 |
260 |
261 |
262 | ); 263 | } 264 | 265 | function App() { 266 | const classes = useStyles(); 267 | 268 | return ( 269 |
270 |
271 |

Voice Sentiment Analysis

272 | 273 |
274 |
275 | ); 276 | } 277 | 278 | export default withAuthenticator(App, { 279 | includeGreetings: true, 280 | signUpConfig: { 281 | hiddenDefaults: ["phone_number"] 282 | } 283 | }); 284 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This lab is provided as part of **[AWS Innovate Data Edition](https://aws.amazon.com/events/aws-innovate/data/)** 2 | Click [here](https://github.com/phonghuule/aws-innovate-data) to explore the full list of hands-on labs. 3 | :information_source: You will run this lab in your own AWS account in **us-east-1**. Please follow directions at the end of the lab to remove resources to avoid future costs. 4 | 5 | 6 | # Dev Labs: Build a Sentiment Analysis App in minutes using Amplify Framework 7 | 8 | In this workshop we will demonstrate how to add the AI and ML cloud service feature to your web application with [React](https://reactjs.org/) and the [Amplify Framework](https://aws-amplify.github.io/). We will learn to integrate following 3 AWS Services in your web application in few minutes 9 | 10 | 1. **Amazon Cognito**- Fully managed User Management 11 | 2. **Amazon Transcribe** - Adds speech-to-text capability 12 | 3. **Amazon Comprehend** - Uses ML to find insights and relationships in text. 13 | 14 | The above mentioned are a subset of services that can be added using AWS Amplify. Apart from these, You can provision, attach and use AWS AppSync(GraphQL API), API Gateway (REST API), Amazon S3(Storage), AWS Lambda (Functions), Amazon Pinpoint (Analytics), Amazon Lex (Interactions/Chatbots) etc to your application using AWS Amplify. 15 | 16 | # Setting Up the Lab Environment 17 | 18 | To run this lab, you will require an AWS account. You will be using a Cloud9, which is a web-based development environment that provides a terminal program running on a virtual machine that has the AWS CLI pre-installed and configured. 19 | 20 | 21 | 22 | 1. Login to your AWS Account. 23 | 24 | 2. From the Services menu, select Cloud9. 25 | 26 | If you are prompted for a region, select **us-east-1**. 27 | 28 | You will now create a Cloud9 environment. 29 | 30 | 3. Click Create environment. 31 | 32 | 4. For Name, enter: amplify-sentimentAnalysis-lab 33 | 34 | 5. Click Next and choose following configuration 35 | 36 | Environment type : **Create a new EC2 instance for environment (direct access)** 37 | 38 | Instance type : **Other instance type -> t3.medium** 39 | 40 | Platform : **Amazon Linux 2 (recommended)** 41 | 42 | For rest of config use default values and click Next Step. 43 | 44 | 6. Finally select Create Environment button. 45 | Cloud9 will take a few minutes to launch the environment. Once it is ready, continue to the next step. 46 | 47 | 7. In the bash terminal at the bottom of the screen (showing ~/environment $), run the following commands: 48 | 49 | ``` 50 | region=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed 's/\(.*\)[a-z]/\1/') 51 | 52 | cat < ~/.aws/config 53 | [default] 54 | region=$region 55 | END 56 | 57 | git clone https://github.com/rahulbaisla/sentimentAnalysisLab.git 58 | 59 | ``` 60 | 61 | 62 | 8. Switch to projects root directory 63 | 64 | ``` 65 | cd sentimentAnalysisLab 66 | 67 | npm install 68 | ``` 69 | 70 | 9. You will now install the AWS Amplify CLI. 71 | 72 | ` 73 | npm install -g @aws-amplify/cli 74 | ` 75 | 76 | **IMPORTANT NOTE : If during any part of lab you may face issues related to no space left on device. Follow the instructions in below AWS Documentation to modify the storage volume.** 77 | 78 | [Resize an Amazon EBS volume used by an environment ](https://docs.aws.amazon.com/cloud9/latest/user-guide/move-environment.html#move-environment-resize) 79 | 80 | ## Initialize Amplify 81 | 82 | 83 | Inside root directory of project run following command and provide the provided values to set up Amplify project. 84 | 85 | ` 86 | amplify init 87 | ` 88 | 89 | `Enter a name for the project:` **sentimentAnalysisLab** 90 | 91 | `Enter a name for the environment:` **dev** 92 | 93 | `Choose your default editor:` **Visual Studio Code** 94 | 95 | `Choose the type of app that you're building:` **javascript** 96 | 97 | `What javascript framework are you using:` **react** 98 | 99 | `Source Directory Path:` **src** 100 | 101 | `Distribution Directory Path:` **build** 102 | 103 | `Build Command:` **npm run-script build** 104 | 105 | `Start Command:` **npm run-script start** 106 | 107 | `Do you want to use an AWS profile?` **Yes** 108 | 109 | `Please choose the profile you want to use` **default** 110 | 111 | 112 | The AWS Amplify CLI will initialize a new project inside your React project & you will see a new folder: `amplify`. The files in this folder hold your project configuration. 113 | 114 | ## Add Authentication to the Web Application 115 | 116 | Amplify CLI provisions [Amazon Cognito](https://aws.amazon.com/cognito/) as backend to provide authN/authZ support for your application. Below command and selected options will create a cloudformation template to provision the Amazon Cognito resource locally under PROJECT_ROOT_DIR/amplify/auth/NAME_OF_COGNITO_RESOURCE folder 117 | 118 | `amplify add auth` 119 | 120 | `Do you want to use the default authentication and security configuration?` **Default configuration** 121 | 122 | `Warning: you will not be able to edit these selections. 123 | How do you want users to be able to sign in?` **Username** 124 | 125 | `Do you want to configure advanced settings?` **No, I am done.** 126 | 127 | ## Add functionality to Transcribe text from audio 128 | 129 | Amplify CLI provisions [Amazon Transcribe](https://aws.amazon.com/transcribe/) as backend to add speech-to-text functionality to your application. Below command and options will create a cloudformation template to provision the the Amazon Transcribe resource locally in your project under PROJECT_ROOT_DIR/amplify/predictions/NAME_OF_CONVERT_RESOURCE folder. 130 | 131 | `amplify add Predictions` 132 | 133 | `Please select from one of the categories below:` **Convert** 134 | 135 | `What would you like to convert?` **Transcribe text from audio** 136 | 137 | `Provide a friendly name for your resource:` **transcription** 138 | 139 | `What is the source language?` **US English** 140 | 141 | `Who should have access?` **Auth users only** 142 | 143 | 144 | 145 | ## Add functionality to Interpret the text 146 | 147 | Amplify CLI provisions [Amazon Comprehend](https://aws.amazon.com/comprehend/) a natural language processing (NLP) service as backend to provide ability to interpret text and perform sentiment analysis. No machine learning experience is required for this feature. Below command and options will create a cloudformation template to provision the Amazon Comprehend resource locally in your project under PROJECT_ROOT_DIR/amplify/predictions/NAME_OF_INTERPRET_RESOURCE folder. 148 | 149 | `amplify add Predictions` 150 | 151 | `Please select from one of the categories below:` **Interpret** 152 | 153 | `What would you like to interpret Interpret:` **Text** 154 | 155 | `Provide a friendly name for your resource:` **interpret** 156 | 157 | `What kind of interpretation would you like?` **All** 158 | 159 | `Who should have access? ` **Auth users only** 160 | 161 | 162 | ## Push the Backend to AWS Cloud 163 | 164 | amplify push 165 | ``` 166 | ✔ Successfully pulled backend environment dev from the cloud. 167 | 168 | Current Environment: dev 169 | 170 | | Category | Resource name | Operation | Provider plugin | 171 | | ----------- | ---------------------------- | --------- | ----------------- | 172 | | Auth | sentimentanalysislab | Create | awscloudformation | 173 | | Predictions | transcription | Create | awscloudformation | 174 | | Predictions | interpret | Create | awscloudformation | 175 | 176 | Are you sure you want to continue? Yes 177 | ``` 178 | 179 | **Note:** Amplify CLI uses AWS CloudFormation as default provider to manage the backend attached to the application. Sometimes it can take additional time to create the AWS resources. In order to confirm the status of the Cloud Formation stack and resources being created, login to AWS Console -> CloudFormation and locate the stack being deployed for your app. 180 | 181 | ## Test the Application 182 | 183 | 1. In your project directory run following command 184 | ``` 185 | npm run start 186 | ``` 187 | 188 | You should see something like this: 189 | 190 | ``` 191 | You can now view sentimentAnalysisLab in the browser. 192 | 193 | Local: http://localhost:8080/ 194 | On Your Network: http://172.31.40.28:8080/ 195 | ``` 196 | 197 | 2. In the Cloud9 **Preview** menu, click **Preview Running Application**. 198 | 199 | 3. Click the 'popout' icon to open it in a new browser tab, as shown below: 200 | ![](images/Cloud9Preview.png) 201 | 202 | 4. Application launched in new tab in browser 203 | ![](images/Preview_in_Browser.png) 204 | 205 | 5. Create an user account 206 | ![](images/SignUp.png) 207 | 208 | 6. Sign-in to the Application 209 | 210 | ![](images/SignIn.png "Sign In") 211 | 212 | 7. Start Recording and speak some text. Once finished stop the recording. 213 | 214 | ![](images/Dashboard.png "App Dashboard") 215 | 216 | The audio will be converted into text using Amazon Transcribe Service and the converted text will be interpreted to perform sentiment analysis using Amazon Comprehend Service. 217 | 218 | 219 | ## Understanding the code 220 | 221 | Importing Amplify into your Front-end application. 222 | 223 | **How it Works:** Amplify supports configuration of your connected AWS resources through a centralized file called aws-exports.js which defines all the regions and service endpoints to communicate. Whenever you run amplify push, this file is automatically created allowing you to focus on your application code. The Amplify CLI will place this file in the appropriate source directory configured with amplify init. 224 | 225 | ``` 226 | import Amplify, { Predictions } from 'aws-amplify'; 227 | import { AmazonAIPredictionsProvider } from '@aws-amplify/predictions'; 228 | import { withAuthenticator } from "@aws-amplify/ui-react"; 229 | import awsconfig from './aws-exports'; //aws-exports 230 | ``` 231 | 232 | 233 | It’s recommended to add the Amplify configuration step to your app’s root entry point. In case of React it would be App.js. 234 | 235 | ``` 236 | Amplify.configure(awsconfig); 237 | 238 | Amplify.addPluggable(new AmazonAIPredictionsProvider()); 239 | ``` 240 | 241 | Alternatively, You can also manually specify your existing Amazon AI and ML resources in your app using [Manual Setup](https://aws-amplify.github.io/docs/js/predictions#manual-setup) 242 | 243 | Convert recorder audio to text Predictions.convert 244 | ``` 245 | function convertFromBuffer(bytes) { 246 | setResponse('Performing Sentiment Analysis...'); 247 | 248 | Predictions.convert({ 249 | transcription: { 250 | source: { 251 | bytes 252 | }, 253 | language: "en-US", // other options are "en-GB", "fr-FR", "fr-CA", "es-US" 254 | }, 255 | }).then(({ transcription: { fullText } }) => {interpretFromPredictions(JSON.stringify(fullText, null, 2))}) 256 | .catch(err => console.log(JSON.stringify(err, null, 2))) 257 | } 258 | ``` 259 | 260 | Performing sentiment analysis on text using Predictions.interpret 261 | ``` 262 | function interpretFromPredictions(textToInterpret) { 263 | console.log("inside interpretFromPredictions") 264 | Predictions.interpret({ 265 | text: { 266 | source: { 267 | text: textToInterpret, 268 | }, 269 | type: "ALL" 270 | } 271 | }).then(result => {setResponse(JSON.stringify(result, null, 2));setGauge(result);}) 272 | .catch(err => setResponse(JSON.stringify(err, null, 2))) 273 | } 274 | ``` 275 | 276 | ## Host your web application using the AWS Amplify Console 277 | 278 | AWS also provides a CI/CD solution named [Amplify Console](https://aws.amazon.com/amplify/console/getting-started/) for single page web applications that follows a git-based workflow to deploy and host fullstack serverless web applications which can include frontend and backend both. Using Amplify Console to host your app can accelerate the release cycle of your product by providing a simple workflow for deploying full-stack serverless applications. Here are few [Fullstack serverless example projects](https://aws.amazon.com/amplify/console/getting-started/) to start with. 279 | 280 | 281 | ## Recap 282 | 283 | In a few minutes you were able to create a Sentiment Analysis application from scratch with: 284 | 285 | A scalable serverless back-end: 286 | 287 | Amazon Cognito - Fully managed authN/authZ service 288 | Amazon Transcribe - Functionality to convert speech-to-text 289 | Amazon Comprehend - Natural language processing (NLP) service that uses machine learning to find insights and relationships in text. 290 | 291 | A browser-based React front-end: 292 | 293 | Use case-centric open source libraries that require minimal code to use for invoking the APIs and connect to backend resources. 294 | 295 | Overall, Developers can focus on the business logic and use AWS Amplify to provision and manage the backend services for your app. 296 | 297 | ## Clean Up 298 | 299 | Please follow these instructions to clean-up your account so that there will be no on-going charges for any services used. 300 | 301 | Run this command to remove resources that were created during the lab (Press Ctrl+C first to stop the running app): 302 | 303 | amplify delete 304 | 305 | Return to the Cloud9 console by clicking AWS Cloud9 in the top left, then click Go To Your Dashboard. 306 | 307 | Delete the amplify-sentimentAnalysis-lab. 308 | 309 | You will need to enter Delete to delete the environment. 310 | 311 | ## Further reading on AWS Amplify 312 | 313 | AWS Amplify framework: https://aws-amplify.github.io/ 314 | AWS Amplify product page: https://aws.amazon.com/amplify/ 315 | AWS Amplify GitHub: https://github.com/aws-amplify 316 | AWS Amplify Community: https://amplify.aws/community/posts 317 | AWS Amplify Lobby: https://gitter.im/AWS-Amplify/Lobby 318 | 319 | ## SurveySurvey: 320 | Please help us to provide your feedback [here](https://amazonmr.au1.qualtrics.com/jfe/form/SV_3a6rNirgLrWYRW6?Session=HOL02). Participants who complete the surveys from AWS Innovate Online Conference - Data Edition will receive a gift code for USD25 in AWS credits. AWS credits will be sent via email by 30 September, 2021. 321 | --------------------------------------------------------------------------------