1
Fork 0
mirror of https://github.com/pds-nest/nest.git synced 2024-11-25 14:34:19 +00:00

Add chart visualization

This commit is contained in:
Steffo 2021-05-20 13:36:07 +02:00
parent eb56dbbfc2
commit 5a27a8e734
Signed by: steffo
GPG key ID: 6965406171929D01
8 changed files with 137 additions and 23 deletions

View file

@ -0,0 +1,60 @@
import React, { useRef } from "react"
import BoxFull from "./BoxFull"
import ChartComponent from "react-chartjs-2"
export default function BoxChart({chartProps, ...props}) {
const boxContentsRef = useRef(null)
const getCssVar = (variable) => {
const computedStyle = window.getComputedStyle(boxContentsRef.current)
console.debug(variable, computedStyle.getPropertyValue(variable))
return computedStyle.getPropertyValue(variable).trim()
}
return (
<BoxFull
childrenProps={{ref: boxContentsRef}}
{...props}
>
{boxContentsRef.current ?
<ChartComponent
width={boxContentsRef.current.offsetWidth}
height={boxContentsRef.current.offsetHeight}
options={{
responsive: true,
scales: {
x: {
beginAtZero: true,
grid: {
borderColor: getCssVar("--bg-light"),
color: getCssVar("--bg-light"),
},
ticks: {
color: getCssVar("--fg-primary"),
}
},
y: {
beginAtZero: true,
grid: {
borderColor: getCssVar("--bg-light"),
color: getCssVar("--bg-light"),
},
ticks: {
color: getCssVar("--fg-primary"),
}
},
},
elements: {
bar: {
backgroundColor: getCssVar("--fg-primary"),
borderColor: "transparent",
color: getCssVar("--fg-primary"),
},
},
}}
{...chartProps}
/>
: null}
</BoxFull>
)
}

View file

@ -0,0 +1,36 @@
import React from "react"
import BoxFull from "../base/BoxFull"
import BoxChart from "../base/BoxChart"
export default function BoxVisualizationChart({ tweets, ...props }) {
// TODO: translate this
const hours = [...Array(24).keys()].map(hour => hour.toString())
const hourlyTweetCount = Array(24).fill(0)
for(const tweet of tweets) {
const insertDate = new Date(tweet["insert_time"])
const insertHour = insertDate.getHours()
console.log(insertHour)
hourlyTweetCount[insertHour] += 1
}
return (
<BoxChart
header={"Hourly graph"}
chartProps={{
type: "bar",
data: {
labels: hours.map(hour => hour.toString()),
datasets: [
{
label: "Tweets",
data: hourlyTweetCount,
}
],
}
}}
{...props}
/>
)
}

View file

@ -1,14 +0,0 @@
import React from "react"
import BoxFull from "../base/BoxFull"
export default function BoxVisualizationGraph({ children, className, ...props }) {
// TODO: translate this
// TODO: implement this
return (
<BoxFull header={"Hourly graph"} {...props}>
{children}
</BoxFull>
)
}

View file

@ -41,8 +41,6 @@ export default function BoxVisualizationStats({ tweets, words, totalTweetCount,
[tweetContentCount, tweetCount], [tweetContentCount, tweetCount],
) )
console.debug(words)
const wordCount = useMemo( const wordCount = useMemo(
() => words.map(word => word.value).reduce((a, b) => a + b), () => words.map(word => word.value).reduce((a, b) => a + b),
[words], [words],

View file

@ -6,6 +6,10 @@ import { faChartBar, faCloud, faMap, faStar } from "@fortawesome/free-solid-svg-
export default function PickerVisualization({ currentTab, setTab, ...props }) { export default function PickerVisualization({ currentTab, setTab, ...props }) {
return ( return (
<div {...props}> <div {...props}>
<ButtonIconOnly
onClick={() => setTab("stats")} disabled={currentTab ===
"stats"} color={"Grey"} icon={faStar}
/>
<ButtonIconOnly <ButtonIconOnly
onClick={() => setTab("wordcloud")} disabled={currentTab === onClick={() => setTab("wordcloud")} disabled={currentTab ===
"wordcloud"} color={"Grey"} icon={faCloud} "wordcloud"} color={"Grey"} icon={faCloud}
@ -15,10 +19,6 @@ export default function PickerVisualization({ currentTab, setTab, ...props }) {
"histogram"} color={"Grey"} icon={faChartBar} "histogram"} color={"Grey"} icon={faChartBar}
/> />
<ButtonIconOnly onClick={() => setTab("map")} disabled={currentTab === "map"} color={"Grey"} icon={faMap}/> <ButtonIconOnly onClick={() => setTab("map")} disabled={currentTab === "map"} color={"Grey"} icon={faMap}/>
<ButtonIconOnly
onClick={() => setTab("stats")} disabled={currentTab ===
"stats"} color={"Grey"} icon={faStar}
/>
</div> </div>
) )
} }

View file

@ -12,7 +12,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { useParams } from "react-router" import { useParams } from "react-router"
import Loading from "../components/base/Loading" import Loading from "../components/base/Loading"
import BoxVisualizationStats from "../components/interactive/BoxVisualizationStats" import BoxVisualizationStats from "../components/interactive/BoxVisualizationStats"
import BoxVisualizationGraph from "../components/interactive/BoxVisualizationGraph" import BoxVisualizationChart from "../components/interactive/BoxVisualizationChart"
import BoxVisualizationMap from "../components/interactive/BoxVisualizationMap" import BoxVisualizationMap from "../components/interactive/BoxVisualizationMap"
import BoxVisualizationWordcloud from "../components/interactive/BoxVisualizationWordcloud" import BoxVisualizationWordcloud from "../components/interactive/BoxVisualizationWordcloud"
import BoxFull from "../components/base/BoxFull" import BoxFull from "../components/base/BoxFull"
@ -25,7 +25,7 @@ export default function PageRepository({ className, ...props }) {
const { id } = useParams() const { id } = useParams()
const { strings } = useContext(ContextLanguage) const { strings } = useContext(ContextLanguage)
const [visualizationTab, setVisualizationTab] = useState("wordcloud") const [visualizationTab, setVisualizationTab] = useState("stats")
const [addFilterTab, setAddFilterTab] = useState("hashtag") const [addFilterTab, setAddFilterTab] = useState("hashtag")
const repositoryBr = useBackendResource( const repositoryBr = useBackendResource(
@ -99,7 +99,7 @@ export default function PageRepository({ className, ...props }) {
/> />
: null} : null}
{visualizationTab === "histogram" ? {visualizationTab === "histogram" ?
<BoxVisualizationGraph <BoxVisualizationChart
className={Style.Wordcloud} className={Style.Wordcloud}
tweets={tweets} tweets={tweets}
/> />

33
package-lock.json generated
View file

@ -22,6 +22,7 @@
"is-string": "^1.0.5", "is-string": "^1.0.5",
"leaflet": "^1.7.1", "leaflet": "^1.7.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-chartjs-2": "^3.0.3",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-leaflet": ">=3.1.0 <3.2.0 || ^3.2.1", "react-leaflet": ">=3.1.0 <3.2.0 || ^3.2.1",
"react-router": "^5.2.0", "react-router": "^5.2.0",
@ -5132,6 +5133,12 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/chart.js": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.2.1.tgz",
"integrity": "sha512-XsNDf3854RGZkLCt+5vWAXGAtUdKP2nhfikLGZqud6G4CvRE2ts64TIxTTfspOin2kEZvPgomE29E6oU02dYjQ==",
"peer": true
},
"node_modules/check-types": { "node_modules/check-types": {
"version": "11.1.2", "version": "11.1.2",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz",
@ -16155,6 +16162,18 @@
"react-scripts": ">=2.1.3" "react-scripts": ">=2.1.3"
} }
}, },
"node_modules/react-chartjs-2": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-3.0.3.tgz",
"integrity": "sha512-jOFZKwZ8sMLkddewZ/tToxuu4pYimAvvY5I6uK+hCpSFT16Pvo2bdHhUoZ0X87zu9I+dx2I+JCqaLN6XhmrbDg==",
"dependencies": {
"lodash": "^4.17.19"
},
"peerDependencies": {
"chart.js": "^3.1.0",
"react": "^16.8.0 || ^17.0.0"
}
},
"node_modules/react-dev-utils": { "node_modules/react-dev-utils": {
"version": "11.0.4", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
@ -26645,6 +26664,12 @@
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="
}, },
"chart.js": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.2.1.tgz",
"integrity": "sha512-XsNDf3854RGZkLCt+5vWAXGAtUdKP2nhfikLGZqud6G4CvRE2ts64TIxTTfspOin2kEZvPgomE29E6oU02dYjQ==",
"peer": true
},
"check-types": { "check-types": {
"version": "11.1.2", "version": "11.1.2",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz",
@ -35334,6 +35359,14 @@
"semver": "^5.6.0" "semver": "^5.6.0"
} }
}, },
"react-chartjs-2": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-3.0.3.tgz",
"integrity": "sha512-jOFZKwZ8sMLkddewZ/tToxuu4pYimAvvY5I6uK+hCpSFT16Pvo2bdHhUoZ0X87zu9I+dx2I+JCqaLN6XhmrbDg==",
"requires": {
"lodash": "^4.17.19"
}
},
"react-dev-utils": { "react-dev-utils": {
"version": "11.0.4", "version": "11.0.4",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",

View file

@ -17,6 +17,7 @@
"is-string": "^1.0.5", "is-string": "^1.0.5",
"leaflet": "^1.7.1", "leaflet": "^1.7.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-chartjs-2": "^3.0.3",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-leaflet": ">=3.1.0 <3.2.0 || ^3.2.1", "react-leaflet": ">=3.1.0 <3.2.0 || ^3.2.1",
"react-router": "^5.2.0", "react-router": "^5.2.0",