refactor: improve api calling and var storage in Stat Page
This commit is contained in:
@@ -15,7 +15,15 @@ import { ReactWordcloud } from '@cp949/react-wordcloud';
|
|||||||
import StatsStyling from "../styles/stats_styling";
|
import StatsStyling from "../styles/stats_styling";
|
||||||
import Card from "../components/Card";
|
import Card from "../components/Card";
|
||||||
|
|
||||||
import type { TopUser, SummaryResponse, FrequencyWord } from '../types/ApiTypes'
|
import {
|
||||||
|
type TopUser,
|
||||||
|
type SummaryResponse,
|
||||||
|
type FrequencyWord,
|
||||||
|
type UserAnalysisResponse,
|
||||||
|
type TimeAnalysisResponse,
|
||||||
|
type ContentAnalysisResponse,
|
||||||
|
type FilterResponse
|
||||||
|
} from '../types/ApiTypes'
|
||||||
|
|
||||||
const styles = StatsStyling;
|
const styles = StatsStyling;
|
||||||
|
|
||||||
@@ -33,14 +41,20 @@ function formatDateRange(startUnix: number, endUnix: number) {
|
|||||||
return `${fmt(start)} → ${fmt(end)}`;
|
return `${fmt(start)} → ${fmt(end)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertFrequencyData(data: FrequencyWord[]) {
|
||||||
|
return data.map((d: FrequencyWord) => ({
|
||||||
|
text: d.word,
|
||||||
|
value: d.count,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
const StatPage = () => {
|
const StatPage = () => {
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const [postsPerDay, setPostsPerDay] = useState([]);
|
const [userData, setUserData] = useState<UserAnalysisResponse | null>(null);
|
||||||
const [heatmapData, setHeatmapData] = useState([]);
|
const [timeData, setTimeData] = useState<TimeAnalysisResponse | null>(null);
|
||||||
const [topUserData, setTopUserData] = useState<TopUser[]>([]);
|
const [contentData, setContentData] = useState<ContentAnalysisResponse | null>(null);
|
||||||
const [wordFrequencyData, setWordFrequencyData] = useState([]);
|
|
||||||
const [summary, setSummary] = useState<SummaryResponse | null>(null);
|
const [summary, setSummary] = useState<SummaryResponse | null>(null);
|
||||||
|
|
||||||
|
|
||||||
@@ -53,41 +67,16 @@ const StatPage = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
axios.get("http://localhost:5000/stats/time"),
|
axios.get<TimeAnalysisResponse>("http://localhost:5000/stats/time"),
|
||||||
axios.get("http://localhost:5000/stats/user"),
|
axios.get<UserAnalysisResponse>("http://localhost:5000/stats/user"),
|
||||||
axios.get("http://localhost:5000/stats/content"),
|
axios.get<ContentAnalysisResponse>("http://localhost:5000/stats/content"),
|
||||||
axios.get<SummaryResponse>(`http://localhost:5000/stats/summary`),
|
axios.get<SummaryResponse>(`http://localhost:5000/stats/summary`),
|
||||||
])
|
])
|
||||||
.then(([timeRes, userRes, wordsRes, summaryRes]) => {
|
.then(([timeRes, userRes, contentRes, summaryRes]) => {
|
||||||
const eventsPerDay = Array.isArray(timeRes.data?.events_per_day)
|
setUserData(userRes.data || null);
|
||||||
? timeRes.data.events_per_day.filter((d: any) => new Date(d.date) >= new Date("2026-01-10"))
|
setTimeData(timeRes.data || null);
|
||||||
: [];
|
setContentData(contentRes.data || null);
|
||||||
|
setSummary(summaryRes.data || null);
|
||||||
const topUsers = Array.isArray(userRes.data?.top_users)
|
|
||||||
? userRes.data.top_users.slice(0, 100)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const weekdayHourHeatmap = Array.isArray(timeRes.data?.weekday_hour_heatmap)
|
|
||||||
? timeRes.data.weekday_hour_heatmap
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const wordFrequencies = Array.isArray(wordsRes.data?.word_frequencies)
|
|
||||||
? wordsRes.data.word_frequencies
|
|
||||||
: [];
|
|
||||||
|
|
||||||
setPostsPerDay(
|
|
||||||
eventsPerDay
|
|
||||||
);
|
|
||||||
|
|
||||||
setTopUserData(topUsers);
|
|
||||||
setHeatmapData(weekdayHourHeatmap);
|
|
||||||
setWordFrequencyData(
|
|
||||||
wordFrequencies.map((d: FrequencyWord) => ({
|
|
||||||
text: d.word,
|
|
||||||
value: d.count,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
setSummary(summaryRes.data ?? null);
|
|
||||||
})
|
})
|
||||||
.catch((e) => setError("Failed to load statistics: " + String(e)))
|
.catch((e) => setError("Failed to load statistics: " + String(e)))
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
@@ -249,7 +238,7 @@ return (
|
|||||||
|
|
||||||
<div style={styles.chartWrapper}>
|
<div style={styles.chartWrapper}>
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<LineChart data={postsPerDay}>
|
<LineChart data={timeData?.events_per_day.filter((d) => new Date(d.date) >= new Date('2026-01-10'))}>
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
<XAxis dataKey="date" />
|
<XAxis dataKey="date" />
|
||||||
<YAxis />
|
<YAxis />
|
||||||
@@ -267,7 +256,7 @@ return (
|
|||||||
|
|
||||||
<div style={styles.chartWrapper}>
|
<div style={styles.chartWrapper}>
|
||||||
<ReactWordcloud
|
<ReactWordcloud
|
||||||
words={wordFrequencyData}
|
words={convertFrequencyData(contentData?.word_frequencies ?? [])}
|
||||||
options={{
|
options={{
|
||||||
rotations: 2,
|
rotations: 2,
|
||||||
rotationAngles: [0, 90],
|
rotationAngles: [0, 90],
|
||||||
@@ -286,7 +275,7 @@ return (
|
|||||||
<p style={styles.sectionSubtitle}>Most active authors</p>
|
<p style={styles.sectionSubtitle}>Most active authors</p>
|
||||||
|
|
||||||
<div style={styles.topUsersList}>
|
<div style={styles.topUsersList}>
|
||||||
{topUserData.map((item) => (
|
{userData?.top_users.map((item) => (
|
||||||
<div
|
<div
|
||||||
key={`${item.author}-${item.source}`}
|
key={`${item.author}-${item.source}`}
|
||||||
style={styles.topUserItem}
|
style={styles.topUserItem}
|
||||||
@@ -306,7 +295,7 @@ return (
|
|||||||
<p style={styles.sectionSubtitle}>Activity density across time</p>
|
<p style={styles.sectionSubtitle}>Activity density across time</p>
|
||||||
|
|
||||||
<div style={styles.heatmapWrapper}>
|
<div style={styles.heatmapWrapper}>
|
||||||
<ActivityHeatmap data={heatmapData} />
|
<ActivityHeatmap data={timeData?.weekday_hour_heatmap ?? []} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
// User Code
|
// User Responses
|
||||||
type TopUser = {
|
type TopUser = {
|
||||||
author: string;
|
author: string;
|
||||||
source: string;
|
source: string;
|
||||||
count: number
|
count: number
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type FrequencyWord = {
|
||||||
|
word: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
type Vocab = {
|
type Vocab = {
|
||||||
author: string;
|
author: string;
|
||||||
events: number;
|
events: number;
|
||||||
@@ -12,7 +17,7 @@ type Vocab = {
|
|||||||
unique_words: number;
|
unique_words: number;
|
||||||
vocab_richness: number;
|
vocab_richness: number;
|
||||||
avg_words_per_event: number;
|
avg_words_per_event: number;
|
||||||
top_words: { word: string; count: number }[];
|
top_words: FrequencyWord[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type User = {
|
type User = {
|
||||||
@@ -48,11 +53,6 @@ type TimeAnalysisResponse = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Content Analysis
|
// Content Analysis
|
||||||
type FrequencyWord = {
|
|
||||||
word: string;
|
|
||||||
count: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ContentAnalysisResponse = {
|
type ContentAnalysisResponse = {
|
||||||
word_frequencies: FrequencyWord[];
|
word_frequencies: FrequencyWord[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user