Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/carrawao/SWE_Finanzen into …
Browse files Browse the repository at this point in the history
…feature/activities
  • Loading branch information
cedscho committed May 13, 2022
2 parents f69a14e + 4b73ca0 commit 866a1a1
Show file tree
Hide file tree
Showing 19 changed files with 764 additions and 73 deletions.
338 changes: 338 additions & 0 deletions Frontend/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"prop-types": "^15.8.1",
"react": "^17.0.2",
"react-chartjs-2": "^4.0.0",
"react-chatbot-kit": "^2.0.1",
"react-dom": "^17.0.2",
"react-router-dom": "^6.2.2",
"react-scripts": "5.0.0",
Expand Down
Binary file added Frontend/public/assets/images/chatbot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
186 changes: 186 additions & 0 deletions Frontend/src/benchi/ActionProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
class ActionProvider {

strategies = {
BuyAndHold : {experience: 'false', risk: 'false', active: 'true', effort: 'false', duration: 'true'},
Index : {experience: 'false', risk: 'false', active: 'false', effort: 'false',duration: 'true'},
Size : {experience: 'false', risk: 'medium', active: 'true', effort: 'false', duration: undefined},
Growth : {experience: 'true', risk: 'medium', active: 'true', effort: 'true', duration: 'true'},
Value : {experience: 'true', risk: 'medium', active: 'true', effort: 'true', duration: 'true'},
Long : {experience: 'true', risk: 'medium', active: 'true', effort: 'true', duration: 'true'},
Short :{experience: 'true', risk: 'true', active: 'true', effort: 'true', duration: 'false'},
Trend : {experience: 'true', risk: 'medium', active: 'true', effort: 'true', duration: 'medium'} ,
Dividend : {experience: 'false', risk: 'medium', active: 'true', effort: 'medium', duration: undefined},
Anticyclical : {experience: 'true', risk: 'true', active: 'true', effort: 'true', duration: 'false'},
Cyclical : {experience: undefined, risk: 'true', active: 'true', effort: 'true', duration: undefined}
};

strategieDescriptions = {
BuyAndHold : 'In this strategy, you buy individual stocks and hold them for at least five to 20 years. The hope is for long-term growth through diversified single stock purchases. Short-term fluctuations are avoided by the long investment period.',
Index : 'You do not invest in individual stocks, but in indices or so-called ETFs (Exchange Traded Fund). This strategy works in principle like the buy and hold method, but ETFs contain many different securities according to certain indices and are therefore already diversified. This saves you a lot of effort before and during your loading investment.',
Size : 'In the size strategy you go by the size of the company. It is assumed that the larger the company, the more secure the price gain and the smaller the expected fluctuations. Thus, the strategy is based on stability.',
Growth : 'In the growth strategy you invest in stocks that are likely to increase in price over the next few years. This is judged on the basis of stock market data.',
Value : 'With the Growth strategy you invest in stocks that are likely to rise in price in the next few years. This is judged on the basis of a detailed analysis of the company.',
Long : 'In the Go-Long strategy you buy a stock and hold it in the hope that it will rise in the long term.',
Short :'You sell a stock from your holdings and try to buy it again shortly after at the low. The difference can therefore be considered a profit.',
Trend : 'In the trend determined strategy you try to predict the development of the stock by drawing certain trend lines, average values or by market technical theories.',
Dividend : 'You bet on dividend payments and price gains.',
Anticyclical : 'When everyone is buying, you sell and vice versa. You try to buy at the low and sell at the high, against the current.',
Cyclical : 'You try to go with the flow and invest in securities that will predictably perform well.'
};

questions =
[
`Are you a beginner, intermediate or advanced investor?`,
`How much risk are you willing to take?`,
`Would you like to invest active?`,
`How much effort do you want to put into your investing?`,
`Do you want to invest shortterm, mediumterm or longterm?`
];

answerAfterQuestion =
[
{true: `Nice to hear, let's start!`, medium:`Let's give it a try anyway!`, false: `Okay, you can come back any time!`},
{true:`So you alreay have a lot of experience, impressive!`, medium: `So you are an intermediate investor!`, false: `No problem, there are also great strategies for beginners!`},
{true: `Okay so you are a risk taker!`, medium:`Medium risk, nice choice!`, false: `Just a tiny bit of risk, okay!`},
{true: `Okay, so you are an active investor!`, medium:`Then let's go with investing passivly!`, false: `Passive investing, nice choice!`},
{true: `So you don't mind getting your hands dirty!`, medium:`Medium effort it is!`, false: `So you would rather chill at the beach than look through company reports!`},
{true: `All things come to him who waits!`, medium:`Okay, we're going with mediumterm investing!`, false: `Looks like you're looking for quick money!`},
];

preferencesArray = ['experience', 'risk', 'active', 'effort', 'duration'];

constructor(
createChatBotMessage,
setStateFunc,
createClientMessage,
stateRef,
createCustomMessage,
...rest
) {
this.createChatBotMessage = createChatBotMessage;
this.setState = setStateFunc;
this.createClientMessage = createClientMessage;
this.stateRef = stateRef;
this.createCustomMessage = createCustomMessage;
}

handleChitChat(answer) {
const botMessage = this.createChatBotMessage(answer);

this.setState((prev) => ({
...prev,
messages: [...prev.messages, botMessage],
}));
}

handleAnswer(answer, questionNr, userPreferences) {
if (questionNr === undefined) {
questionNr = 0;
userPreferences = {experience: '', risk: '', active: false, effort: '', duration: ''};
}
if (questionNr === -1) {
const botAnswer = this.createChatBotMessage(`Hi! I'm Benchi! Are you interested in finding the right investment strategy for you together?`);
this.setState((prev) => ({
...prev,
messages: [...prev.messages, botAnswer],
'questionNr': (questionNr + 1)
}));
return;
}
if (questionNr === 5) {
userPreferences = {...userPreferences, duration: answer};
const Strategy = this.findStrategyForUser(userPreferences);
const botAnswer = this.createChatBotMessage(this.answerAfterQuestion[questionNr][answer]);
const botStrategy = this.createChatBotMessage(`The best strategy for you is ${Strategy}`);
const botStrategyExplanation = this.createChatBotMessage(this.strategieDescriptions[Strategy]);
this.setState((prev) => ({
...prev,
messages: [...prev.messages, botAnswer, botStrategy, botStrategyExplanation],
questionNr: questionNr + 1
}));
return;
}
if (questionNr > 5) {
userPreferences = {...userPreferences, duration: answer};
const Strategy = this.findStrategyForUser(userPreferences);
const botStrategy = this.createChatBotMessage(`The best strategy for you is ${Strategy}`);
const botStrategyExplanation = this.createChatBotMessage(this.strategieDescriptions[Strategy]);
this.setState((prev) => ({
...prev,
messages: [...prev.messages, botStrategy, botStrategyExplanation],
questionNr: questionNr + 1
}));
return;
}

if (this.preferencesArray[questionNr-1] === 'active') {
answer = answer === 'medium' ? false : answer;
}
const botAnswer = this.createChatBotMessage(this.answerAfterQuestion[questionNr][answer]);
const botNewQuestion = this.createChatBotMessage(this.questions[questionNr]);

if (questionNr > 0 && questionNr < 5) {
this.setState((prev) => ({
...prev,
messages: [...prev.messages, botAnswer, botNewQuestion],
'questionNr': questionNr + 1,
'userPreferences': {...userPreferences, [this.preferencesArray[questionNr-1]]: answer}
}));
} else {
if (questionNr === 0 && answer === 'false') {
this.setState((prev) => ({
...prev,
messages: [...prev.messages, botAnswer],
'questionNr': -1,
}));
return;
}
this.setState((prev) => ({
...prev,
messages: [...prev.messages, botAnswer, botNewQuestion],
questionNr: questionNr + 1
}));
}
}

findStrategyForUser(userPreferences) {
let strategiesFit = {};
const strategiesKeys = Object.keys(this.strategies);
for (let index = 0; index < strategiesKeys.length; index++) {
const strategieKey = strategiesKeys[index];
const strategy = this.strategies[strategieKey];
let fit = 0;
for (let i = 0; i < this.preferencesArray.length; i++) {
const preference = this.preferencesArray[i];
if (strategy[preference] === undefined || strategy[preference] === userPreferences[preference]) {
fit++;
}
}
strategiesFit[strategieKey] = fit;
}
console.log(strategiesFit);
let bestStrategy = '';
let bestFit = 0;
for (let index = 0; index < strategiesKeys.length; index++) {
const strategieKey = strategiesKeys[index];
if (bestFit < strategiesFit[strategieKey]) {
bestFit = strategiesFit[strategieKey];
bestStrategy = strategieKey;
}
}
console.log(bestStrategy);
return bestStrategy;
}

handleNoAnswer() {
const botMessage = this.createChatBotMessage(`Sorry, but I didn't understand you.`);

this.setState((prev) => ({
...prev,
messages: [...prev.messages, botMessage],
}));
this.messageCnt = this.messageCnt + 1;
}
}

export default ActionProvider;
52 changes: 52 additions & 0 deletions Frontend/src/benchi/MessageParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
class MessageParser {
constructor(actionProvider, state) {
this.actionProvider = actionProvider;
this.state = {...state};
}

parse(message) {
console.log(this.state)
this.fetchAnswer(message);
}

interpret(data) {
const chitChatAnswer = data[0].answer;
const answer = data[1] !== undefined ? data[1].answer : undefined;
if (answer !== undefined) {
this.actionProvider.handleAnswer(answer, this.state.questionNr, this.state.userPreferences);
return;
}
if (chitChatAnswer === 'true' || chitChatAnswer === 'false' || chitChatAnswer === 'medium') {
this.actionProvider.handleAnswer(chitChatAnswer, this.state.questionNr, this.state.userPreferences);
return;
}
if (chitChatAnswer === 'No answer found') {
this.actionProvider.handleNoAnswer();
return;
}
this.actionProvider.handleChitChat(chitChatAnswer);
}

fetchAnswer(message) {
var url = "https://westeurope.api.cognitive.microsoft.com/language/:query-knowledgebases?projectName=benchi-QnA&api-version=2021-10-01&deploymentName=production";

var xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.responseType = 'json';

xhr.setRequestHeader("Ocp-Apim-Subscription-Key", "6ee6c9b8f982470abc210317cbf8d89c");
xhr.setRequestHeader("Content-Type", "application/json");

xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
this.interpret(xhr.response.answers);
}
};

var data = `{"top":3,"question":"${message}","includeUnstructuredSources":true,"confidenceScoreThreshold":"0.5","answerSpanRequest":{"enable":true,"topAnswersWithSpan":1,"confidenceScoreThreshold":"0.5"}}`;

xhr.send(data);
}
}

export default MessageParser;
22 changes: 22 additions & 0 deletions Frontend/src/benchi/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createChatBotMessage } from 'react-chatbot-kit';

const botName = 'Benchi';

const config = {
initialMessages: [createChatBotMessage(`Hi! I'm ${botName}! Are you interested in finding the right investment strategy for you together?`)],
customComponents: {
// Replaces the default header
header: () => <div style={{ backgroundColor: '#493f35', padding: '5px', borderRadius: '3px' , color: 'white'}}>Talk to Benchi!</div>
},
botName: botName,
customStyles: {
botMessageBox: {
backgroundColor: '#493f35',
},
chatButton: {
backgroundColor: '#5ccc9d',
},
},
};

export default config;
12 changes: 11 additions & 1 deletion Frontend/src/components/ScreensTemplate.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {AppBar, Box, CssBaseline, Grid, Toolbar, Alert} from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import IconButton from '@mui/material/IconButton';
import PropTypes from 'prop-types';
import {SideNavLeft, Footer, SearchField} from './common/index';
import {SideNavLeft, Footer, SearchField, Benchi} from './common/index';
import SearchResultsTable from './common/SearchResultsTable';

const drawerWidth = 14; // This is the value in rem units, for responsiveness
Expand All @@ -18,6 +18,7 @@ const ScreensTemplate = props => {
const location = useLocation()
const [openInMobile, setOpenInMobile] = useState(false);
const [searchQuery, setSearchQuery] = useState('');


// Resetting the search when changing routes
useEffect(() => {
Expand Down Expand Up @@ -146,6 +147,15 @@ const ScreensTemplate = props => {
</Box>
{props.messageType !== undefined && props.statusMessage !== undefined && showStatusMessage(props.messageType, props.statusMessage)}
</Box>
<Box sx={{
position: 'fixed',
bottom: '5%',
right: '5%',
zIndex: '20000',
transform: 'translate(0 0)',
}}>
<Benchi/>
</Box>
<Box sx={{
position: 'absolute',
bottom: 0,
Expand Down
88 changes: 88 additions & 0 deletions Frontend/src/components/common/Benchi.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, {useState} from 'react';
import PropTypes from 'prop-types';
import {
Box,
Modal,
Container,
Typography,
Avatar,
IconButton,
Grid,
Paper
} from '@mui/material';

import Chatbot from 'react-chatbot-kit';
import 'react-chatbot-kit/build/main.css';
import config from '../../benchi/config.js';
import MessageParser from '../../benchi/MessageParser.js';
import ActionProvider from '../../benchi/ActionProvider.js';

/**
* Template to show modals throughout the app
* @param props
* @returns {JSX.Element}
* @constructor
*/
const Benchi = props => {
const [showBot, toggleBot] = useState(false);

const saveMessages = (messages, HTMLString) => {
console.log(messages)
localStorage.setItem('chatMessages', JSON.stringify(HTMLString));
};

const loadMessages = () => {
const messages = JSON.parse(localStorage.getItem('chatMessages'));
return messages;
};

return (
<React.Fragment>
<Grid container direction='column' alignItems='flex-end'>
<Grid item>
{showBot && (
<Paper elevation={6}>
<Chatbot
config={config}
actionProvider={ActionProvider}
messageHistory={loadMessages()}
messageParser={MessageParser}
saveMessages={saveMessages}
/>
</Paper>
)}
</Grid>
<Grid item>
<IconButton
aria-label='Open Benchi Chat'
size='small'
onClick={() => toggleBot((prev) => !prev)}
sx={{

}}
>
<Avatar
className='me-xs-2 me-md-0'
alt={`Benchi-icon`}
src={`${process.env.PUBLIC_URL}/assets/images/chatbot.png`}
sx={{
backgroundColor: 'white',
border: '1px solid #493f35',
width: {
xs: '4rem',
md: '5rem'
},
height: {
xs: '4rem',
md: '5rem'
},
}}
/>
</IconButton>
</Grid>
</Grid>
</React.Fragment>
);
}

export default Benchi;
Loading

0 comments on commit 866a1a1

Please sign in to comment.