diff --git a/nest_frontend/components/interactive/BoxVisualizationStats.js b/nest_frontend/components/interactive/BoxVisualizationStats.js
index 404bbce..f813ff0 100644
--- a/nest_frontend/components/interactive/BoxVisualizationStats.js
+++ b/nest_frontend/components/interactive/BoxVisualizationStats.js
@@ -2,31 +2,95 @@ import React, { useMemo } from "react"
import FormLabelled from "../base/FormLabelled"
import FormLabel from "../base/formparts/FormLabel"
import BoxFullScrollable from "../base/BoxFullScrollable"
-import tokenizeTweetWords from "../../utils/tokenizeTweetWords"
+import tokenizeTweetWords from "../../utils/countTweetWords"
-export default function BoxVisualizationStats({ tweets, totalTweetCount, ...props }) {
+export default function BoxVisualizationStats({ tweets, words, totalTweetCount, ...props }) {
- const words = useMemo(
- () => tokenizeTweetWords(tweets),
+ const tweetCount = useMemo(
+ () => tweets.length,
[tweets]
)
- const tweetCount = tweets.length
- const tweetPct = tweetCount / totalTweetCount * 100
- const tweetLocationCount = tweets.filter(tweet => tweet.location).length
- const tweetLocationPct = tweetLocationCount / tweetCount * 100
- const tweetContent = tweets.filter(tweet => tweet.content)
- const tweetContentCount = tweetContent.length
- const tweetContentPct = tweetContentCount / tweetCount * 100
- const wordCount = words.map(word => word.value).reduce((a, b) => a+b)
- const mostPopularWord = words.sort((wa, wb) => {
- if(wa.value > wb.value) return -1
- if(wa.value < wb.value) return 1
- return 0
- })[0].text
- const users = [...new Set(tweets.map(tweet => tweet.poster))]
- const usersCount = users.length
+ const tweetPct = useMemo(
+ () => tweetCount / totalTweetCount * 100,
+ [tweetCount, totalTweetCount]
+ )
+
+ const tweetLocationCount = useMemo(
+ () => tweets.filter(tweet => tweet.location).length,
+ [tweets]
+ )
+
+ const tweetLocationPct = useMemo(
+ () => tweetLocationCount / tweetCount * 100,
+ [tweetLocationCount, tweetCount]
+ )
+
+ const tweetContent = useMemo(
+ () => tweets.filter(tweet => tweet.content),
+ [tweets]
+ )
+
+ const tweetContentCount = useMemo(
+ () => tweetContent.length,
+ [tweetContent],
+ )
+
+ const tweetContentPct = useMemo(
+ () => tweetContentCount / tweetCount * 100,
+ [tweetContentCount, tweetCount],
+ )
+
+ console.debug(words)
+
+ const wordCount = useMemo(
+ () => words.map(word => word.value).reduce((a, b) => a+b),
+ [words]
+ )
+
+ const mostPopularWord = useMemo(
+ () => {
+ return words.sort((wa, wb) => {
+ if(wa.value > wb.value) return -1
+ if(wa.value < wb.value) return 1
+ return 0
+ })[0].text
+ },
+ [words]
+ )
+
+ const users = useMemo(
+ () => tweets.map(tweet => tweet.poster),
+ [tweets]
+ )
+
+ const uniqueUsers = useMemo(
+ () => [...new Set(users)],
+ [users]
+ )
+
+ const uniqueUsersCount = useMemo(
+ () => uniqueUsers.length,
+ [uniqueUsers]
+ )
+
+ const mostActiveUser = useMemo(
+ () => {
+ if(uniqueUsers.length === 0) return null
+ return uniqueUsers.map(user => {
+ return {
+ user: user,
+ count: tweets.filter(tweet => tweet.poster === user).length
+ }
+ }).sort((a, b) => {
+ if(a.count > b.count) return -1
+ if(a.count < b.count) return 1
+ return 0
+ })[0]
+ },
+ [uniqueUsers, tweets]
+ )
// TODO: tweets with picture count
// TODO: tweets with picture pct
@@ -68,8 +132,11 @@ export default function BoxVisualizationStats({ tweets, totalTweetCount, ...prop
🚧
-
- {usersCount}
+
+ {uniqueUsersCount}
+
+
+ {mostActiveUser.user} ({mostActiveUser.count} tweets)
diff --git a/nest_frontend/components/interactive/BoxVisualizationWordcloud.js b/nest_frontend/components/interactive/BoxVisualizationWordcloud.js
index 6806ff2..531d369 100644
--- a/nest_frontend/components/interactive/BoxVisualizationWordcloud.js
+++ b/nest_frontend/components/interactive/BoxVisualizationWordcloud.js
@@ -1,17 +1,11 @@
-import React, { useContext, useMemo } from "react"
+import React, { useContext } from "react"
import BoxWordcloud from "../base/BoxWordcloud"
import ContextLanguage from "../../contexts/ContextLanguage"
-import tokenizeTweetWords from "../../utils/tokenizeTweetWords"
-export default function BoxVisualizationWordcloud({ tweets = [], ...props }) {
+export default function BoxVisualizationWordcloud({ words, ...props }) {
const {strings} = useContext(ContextLanguage)
- const words = useMemo(
- () => tokenizeTweetWords(tweets),
- [tweets]
- )
-
return (
)
diff --git a/nest_frontend/routes/PageRepository.js b/nest_frontend/routes/PageRepository.js
index 1aa2833..406c309 100644
--- a/nest_frontend/routes/PageRepository.js
+++ b/nest_frontend/routes/PageRepository.js
@@ -17,6 +17,9 @@ import BoxVisualizationMap from "../components/interactive/BoxVisualizationMap"
import BoxVisualizationWordcloud from "../components/interactive/BoxVisualizationWordcloud"
import BoxFull from "../components/base/BoxFull"
import ContextLanguage from "../contexts/ContextLanguage"
+import tokenizeTweetWords from "../utils/countTweetWords"
+import countTweetWords from "../utils/countTweetWords"
+import objectToWordcloudFormat from "../utils/objectToWordcloudFormat"
export default function PageRepository({ className, ...props }) {
@@ -52,6 +55,11 @@ export default function PageRepository({ className, ...props }) {
)
const tweets = tweetsBv.resources && tweetsBv.error ? [] : tweetsBv.resources
+ const words = useMemo(
+ () => objectToWordcloudFormat(countTweetWords(tweets)),
+ [tweets]
+ )
+
let contents;
if(!repositoryBr.firstLoad || !tweetsBv.firstLoad) {
contents = <>
@@ -88,6 +96,7 @@ export default function PageRepository({ className, ...props }) {
: null}
{visualizationTab === "histogram" ?
@@ -106,6 +115,7 @@ export default function PageRepository({ className, ...props }) {
: null}
diff --git a/nest_frontend/utils/countTweetWords.js b/nest_frontend/utils/countTweetWords.js
new file mode 100644
index 0000000..41d892d
--- /dev/null
+++ b/nest_frontend/utils/countTweetWords.js
@@ -0,0 +1,24 @@
+import sw from "stopword"
+
+
+const stopwords = [...sw.it, ...sw.en, "rt"]
+
+
+export default function countTweetWords(tweets = {}) {
+ let words = {}
+ for(const tweet of tweets) {
+ if(!tweet.content) {
+ continue
+ }
+ for(const word of tweet.content.toLowerCase().split(/\s+/)) {
+ if(stopwords.includes(word)) continue
+ if(word.startsWith("https://")) continue
+
+ if(!words.hasOwnProperty(word)) {
+ words[word] = 0
+ }
+ words[word] += 1
+ }
+ }
+ return words
+}
diff --git a/nest_frontend/utils/objectToWordcloudFormat.js b/nest_frontend/utils/objectToWordcloudFormat.js
new file mode 100644
index 0000000..373720f
--- /dev/null
+++ b/nest_frontend/utils/objectToWordcloudFormat.js
@@ -0,0 +1,13 @@
+export default function objectToWordcloudFormat(words) {
+ let result = []
+ for(const word in words) {
+ if(!words.hasOwnProperty(word)) {
+ continue
+ }
+ result.push({
+ text: word,
+ value: words[word]
+ })
+ }
+ return result
+}
\ No newline at end of file
diff --git a/nest_frontend/utils/tokenizeTweetWords.js b/nest_frontend/utils/tokenizeTweetWords.js
deleted file mode 100644
index ce48ff8..0000000
--- a/nest_frontend/utils/tokenizeTweetWords.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import sw from "stopword"
-
-
-const stopwords = [...sw.it, ...sw.en, "rt"]
-
-
-export default function(tweets = {}) {
- let preprocessedWords = {}
- for(const tweet of tweets) {
- if(!tweet.content) {
- continue
- }
- for(const word of tweet.content.toLowerCase().split(/\s+/)) {
- if(stopwords.includes(word)) continue
- if(word.startsWith("https://")) continue
-
- if(!preprocessedWords.hasOwnProperty(word)) {
- preprocessedWords[word] = 0
- }
- preprocessedWords[word] += 1
- }
- }
-
- let processedWords = []
- for(const word in preprocessedWords) {
- if(!preprocessedWords.hasOwnProperty(word)) {
- continue
- }
- processedWords.push({
- text: word,
- value: preprocessedWords[word]
- })
- }
- return processedWords
-}
\ No newline at end of file