refactor: improve api calling and var storage in Stat Page

This commit is contained in:
2026-02-03 11:36:43 +00:00
parent 815aaf3481
commit 2af31d3392
2 changed files with 38 additions and 49 deletions

View File

@@ -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>

View File

@@ -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[];
} }