Introduction
This blog is divided into three parts, depending on the part of the application we are building:
- Part 1: data source and backend implementation
- Part 2: frontend implementation
- Part 3: streaming data from Kafka cluster
If you still haven't, you can read the previously published Part
1 and then continue reading this post. Otherwise, you can use the already implemented backend. In this part, you are going to create React application and visualize general
statistics and some interesting insights from the Twitch dataset. All implementation that will be mentioned in this part of the blog you can find in
the frontend folder of the project.
Create a React App
Let's figure out how to visualize all the collected data. First, you have to create React app which, will work with our Flask application. Install Node.js which enables you to use npx
command for creating a React app. Place yourself in the project root folder and run:
npm install -g create-react-app@3.4.1
npm init react-app frontend --use-npm
cd frontend
npm start
Now at http://localhost:3000 you can see a simple React app. You need to configure the React app to work well with the Flask server. In package.json
from the frontend folder, add a line at the end of the file: "proxy": "http://localhost:5000"
This will tell React app to redirect any requests it receives on its port 3000 to port 5000, where the backend is implemented. We will use Semantic UI to build the webpage, so you have to do a few more things before dockerizing the React app. Run the following commands:
npm uninstall semantic-ui semantic-ui-css
npm install @craco/craco @semantic-ui-react/craco-less semantic-ui-less --save-dev
After that, update your package.json
with:
{
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "craco eject"
}
}
Create craco.config.js
in the frontend folder and paste the following content in it:
module.exports = {
plugins: [{ plugin: require('@semantic-ui-react/craco-less') }],
}
Create a semantic-ui/site
folder in the src
folder and then copy the entire node_modules/semantic-ui-less/_site folder
content to src/semantic-ui/site
. Also, create a theme.config
file in the src/semantic-ui/
folder and then
copy the file node_modules/semantic-ui-less/theme.config.example
to src/semantic-ui/theme.config
.
Update the theme.config
file to:
/*******************************
Folders
*******************************/
@themesFolder : 'themes';
@siteFolder : '../../src/semantic-ui/site';
@import (multiple) "~semantic-ui-less/theme.less";
@fontPath : '../../../themes/@{theme}/assets/fonts';
If it's not already installed, run: npm install semantic-ui-react --save-dev
Now you can use Semantic UI components, such as Button
(don’t forget to paste all the imports):
import logo from "./logo.svg";
import "./App.css";
import "semantic-ui-less/semantic.less";
import { Button } from "semantic-ui-react";
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<Button>Click me</Button>
</header>
</div>
);
}
export default App;
How to Dockerize a React App?
You need to add a few new lines in our previously created project's docker-compose.yml
file. At the end of the file, add:
react-app:
build: ./frontend
volumes:
- ./frontend:/app
- /app/node_modules
ports:
- "3000:3000"
depends_on:
- twitch-app
networks:
- app-tier
You should also create a Dockerfile
in the frontend folder like this:
# pull official base image
FROM node:14.17.5-alpine
# set working directory
WORKDIR /app
# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH
# install app dependencies
COPY package.json ./
COPY package-lock.json ./
RUN npm install --silent
RUN npm install react-scripts@3.4.1 -g --silent
# add app
COPY . ./
# start app
CMD ["npm", "start"]
You have to copy package.json
into the container. The best way to have all dependencies installed is to copy package.json
file from frontend folder. All node modules will then be correctly installed in the container and npm start
will run your React app. Node modules volume was added so that all
packages don't have to be installed each time you build your project.
All that is left to do is to create a .dockerignore
file in the frontend
directory:
node_modules
build
.dockerignore
Dockerfile
Also, make a little change in package.json
for the proxy settings:
"proxy": "http://twitch-app:5000"
The backend as a Docker service on port 5000 called twitch-app
(this is defined in the docker-compose.yml
file) and that's why you have to make that change. The project structure now looks like this:
| docker-compose.yml
|
+---backend
| | app.py
| | models.py
| | twitch_data.py
| | requirements.txt
| | Dockerfile
| +---import-data
| | chatters.csv
| | moderators.csv
| | streamers.csv
| | teams.csv
| | vips.csv
|
+---frontend
| | .dockerignore
| | craco.config.js
| | Dockerfile
| | package.json
| | package-lock.json
| +---node_modules
| +---public
| +---src
|
+---memgraph
| | Dockerfile
| +---query_modules
| | twitch.py
| +---mg_log
| +---mg_lib
Now you can hit docker-compose build
from your root project folder and docker-compose up
after that. First memgraph-mage
will run and then twitch-app
. After that react-app
will be run. Now you can make requests from your frontend.
Frontend Implementation with React and D3.js
Create folder components
in your src file. Here you will make your components which you’ll use as puzzles for your web application. Let's make a little part of the puzzle, and add node and edge counters to the webpage by making fetch requests in Counter.js
. You have to make a request depending on the props forwarded from the parent component, like in the code below.
fetch() {
fetch("/" + this.props.count)
.then((res) => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
counter: result[this.props.count],
});
},
(error) => {
this.setState({
isLoaded: true,
error,
});
}
);
}
On the left side, you can see the number of nodes, and on the right the number of edges in your database.
For game statistics, you have to fetch top games from the backend server:
fetchData(number) {
fetch("/top-games/" + number)
.then((res) => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
games: result.games,
players: result.players,
});
},
(error) => {
this.setState({
isLoaded: true,
error,
});
}
);
this.setState({
numOfGames: number,
header: "Top " + number + " games",
});
}
On the right side, you can see the table showing you the names of the games and the number of players that are playing that game (in the dataset).
You can fetch top teams, VIPs, and moderators in a similar way. For streamers, you can rank them by the number of followers or number of views. Because of that, you create a dropdown menu from where you can choose the way of
ranking.
Let’s talk about graph visualization a bit more. Here, you can use D3.js
, a JavaScript library for manipulating documents based on data. You need to set everything up so that you can draw the graphs using D3.js
. Create a folder hooks
in src
folder and create useD3.js
file (hooks are usually named with prefix "use").
import React from "react";
import * as d3 from "d3";
export const useD3 = (renderGraph) => {
const ref = React.useRef();
React.useEffect(() => {
renderGraph(d3.select(ref.current));
return () => {};
});
return ref;
};
This will be your custom hook to allow D3.js
to interact directly with the DOM. You can take advantage of the useRef
and useEffect
hook to link D3.js
with the svg
element that has been created, and specifies when your D3.js
function should be executed. Don’t forget to import d3
. Now you can render the graph using a custom hook useD3.js
. Check the Graph.js
component to see how the graph can be drawn. Using that component you can get information about your favorite streamer - its teams, games, and languages, like in the image below.
It is also possible to search all streamers who are playing some game in a certain language. Using the same Graph.js
component, you get:
Feel free to play with nodes and their forces by dragging them around. In the end, check out the powerful MAGE query modules - PageRank and Betweenness Centrality and visualize your data in a pretty cool way with D3.js
. For calculating PageRank, there is an API GET request in the backend
server and in the frontend, that data is being fetched from the PageRank.js
component.
fetchData(){
fetch("/page-rank")
.then((res) => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
nodes: result.page_rank,
});
},
(error) => {
this.setState({
isLoaded: true,
error,
});
}
);
}
While PageRank results are being calculated, you will see your results loading. After the results are loaded, they are drawn with the graph component GraphPR.js
, which uses D3.js
. Next to the graph, you can see the results table with the names of the streamers and their calculated rank.
You can see the refresh button above the visualized graph. It will be used later on in Part 3 of the tutorial, when you'll learn how to stream the data using Kafka. You will stream new chatters of the user BadBoyHalo and see how
his rank improves with a larger number of chatters in his network. Besides PageRank, you can calculate the betweenness centrality and visualize it as on the image below.
Conclusion
And that's it for now! I hope you got everything right, but if you have any questions or want to give some feedback, feel free to join our Discord Community Server. Make sure to follow up on the last part of this blog, where you can learn how to tackle your streaming data with Kafka and analyze it with Memgraph in real-time.
Top comments (0)