Files
minecraft-chat-viewer/statistics.js
SimolZimol 8ac625a64d new file: .gitignore
new file:   Dockerfile
	new file:   README.md
	new file:   app.py
	new file:   chat-logs/chat-index.json
	new file:   chat-logs/crea-1-10.08.2020-merged.txt
	new file:   chat-logs/crea-1-11.08.2020-merged.txt
	new file:   chat-logs/crea-1-12.08.2020-merged.txt
	new file:   chat-logs/crea-1-13.08.2020-merged.txt
	new file:   chat-logs/crea-1-14.08.2020-merged.txt
	new file:   chat-logs/crea-1-15.08.2020-merged.txt
	new file:   chat-logs/crea-1-18.08.2020-merged.txt
	new file:   chat-logs/crea-1-20.08.2020-merged.txt
	new file:   chat-logs/crea-1-2020-07-27-1-filtered.txt
	new file:   chat-logs/crea-1-2020-07-28-1-filtered.txt
	new file:   chat-logs/crea-1-2020-07-29-1-filtered.txt
	new file:   chat-logs/crea-1-2020-07-30-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-03-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-04-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-08-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-09-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-10-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-11-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-13-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-16-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-17-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-18-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-20-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-24-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-29-1-filtered.txt
	new file:   chat-logs/crea-1-2020-08-30-1-filtered.txt
	new file:   chat-logs/crea-1-21.08.2020-merged.txt
	new file:   chat-logs/crea-1-22.08.2020-merged.txt
	new file:   chat-logs/crea-1-23.08.2020-merged.txt
	new file:   chat-logs/crea-1-24.07.2020-merged.txt
	new file:   chat-logs/crea-1-25.07.2020-merged.txt
	new file:   chat-logs/crea-1-25.08.2020-merged.txt
	new file:   chat-logs/crea-1-26.07.2020-merged.txt
	new file:   chat-logs/crea-1-26.08.2020-merged.txt
	new file:   chat-logs/crea-1-27.08.2020-merged.txt
	new file:   chat-logs/crea-1-28.08.2020-merged.txt
	new file:   chat-logs/crea-1-crea-1-10.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-11.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-12.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-14.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-15.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-18.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-20.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-21.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-22.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-23.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-24.07.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-25.07.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-25.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-26.07.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-26.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-27.08.2020-merged-filtered.txt
	new file:   chat-logs/crea-1-crea-1-28.08.2020-merged-filtered.txt
	new file:   chat-logs/survival-1-15.08.2020-merged.txt
	new file:   chat-logs/survival-1-2020-07-27-1-filtered.txt
	new file:   chat-logs/survival-1-2020-07-28-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-07-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-08-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-11-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-13-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-14-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-17-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-18-1-filtered.txt
	new file:   chat-logs/survival-1-2020-08-19-1-filtered.txt
	new file:   chat-logs/survival-1-25.07.2020-merged.txt
	new file:   chat-logs/survival-1-survival-1-15.08.2020-merged-filtered.txt
	new file:   chat-logs/survival-1-survival-1-25.07.2020-merged-filtered.txt
	new file:   chat-logs/thesur-1-2020-08-17-1-filtered.txt
	new file:   chat-logs/thesur-1-2020-08-31-1-filtered.txt
	new file:   count_all_sessions.py
	new file:   count_sessions.py
	new file:   index.html
	new file:   local-chat-analyzer.js
	new file:   merge_daily_logs.py
	new file:   process_thesur_logs.py
	new file:   quick_add.py
	new file:   requirements.txt
	new file:   script.js
	new file:   server.py
	new file:   statistics-integration.js
	new file:   statistics.css
	new file:   statistics.js
	new file:   style.css
2025-12-09 15:31:20 +01:00

223 lines
8.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Chat Statistics Module */
class ChatStatistics {
constructor() {
this.currentData = null;
this.statsContainer = null;
this.init();
}
init() {
this.createStatsContainer();
// Button wird jetzt von statistics-integration.js hinzugefügt
}
createStatsContainer() {
// Create stats container
const statsHTML = `
<div class="stats-container" id="statsContainer" style="display: none;">
<div class="stats-header">
<h3>📊 Chat Statistics</h3>
<button class="stats-close" id="statsClose">×</button>
</div>
<div class="stats-content" id="statsContent">
<!-- Statistics will be populated here -->
</div>
</div>
<div class="stats-overlay" id="statsOverlay" style="display: none;"></div>
`;
document.body.insertAdjacentHTML('beforeend', statsHTML);
this.statsContainer = document.getElementById('statsContainer');
// Add event listeners
document.getElementById('statsClose').addEventListener('click', () => this.hideStats());
document.getElementById('statsOverlay').addEventListener('click', () => this.hideStats());
}
analyzeChat(messages) {
console.log('Analyzing chat with', messages.length, 'messages');
this.currentData = this.calculateStatistics(messages);
// Trigger custom event that button is ready
const event = new CustomEvent('statsReady', { detail: this.currentData });
document.dispatchEvent(event);
}
calculateStatistics(messages) {
const stats = {
totalMessages: 0,
chatMessages: 0,
joinLeaveMessages: 0,
players: new Map(),
timeDistribution: new Map(),
wordCount: new Map(),
messageLength: []
};
messages.forEach(message => {
stats.totalMessages++;
if (message.type === 'chat') {
stats.chatMessages++;
// Player statistics
const player = message.player;
if (!stats.players.has(player)) {
stats.players.set(player, {
name: player,
role: message.role,
messageCount: 0,
totalChars: 0,
avgMessageLength: 0
});
}
const playerStats = stats.players.get(player);
playerStats.messageCount++;
playerStats.totalChars += message.message.length;
playerStats.avgMessageLength = Math.round(playerStats.totalChars / playerStats.messageCount);
// Time distribution (by hour)
const hour = message.timestamp.split(':')[0];
stats.timeDistribution.set(hour, (stats.timeDistribution.get(hour) || 0) + 1);
// Word analysis
const words = message.message.toLowerCase().match(/\b\w+\b/g) || [];
words.forEach(word => {
if (word.length > 3) { // Only count words longer than 3 characters
stats.wordCount.set(word, (stats.wordCount.get(word) || 0) + 1);
}
});
// Message length
stats.messageLength.push(message.message.length);
} else if (message.type === 'join' || message.type === 'leave') {
stats.joinLeaveMessages++;
}
});
return stats;
}
showStats() {
if (!this.currentData) return;
this.renderStatistics();
document.getElementById('statsContainer').style.display = 'block';
document.getElementById('statsOverlay').style.display = 'block';
document.body.style.overflow = 'hidden';
}
hideStats() {
document.getElementById('statsContainer').style.display = 'none';
document.getElementById('statsOverlay').style.display = 'none';
document.body.style.overflow = 'auto';
}
renderStatistics() {
const data = this.currentData;
const content = document.getElementById('statsContent');
// Convert Maps to sorted arrays
const topPlayers = Array.from(data.players.values())
.sort((a, b) => b.messageCount - a.messageCount)
.slice(0, 10);
const topWords = Array.from(data.wordCount.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 15);
const timeActivity = Array.from(data.timeDistribution.entries())
.sort((a, b) => a[0] - b[0]);
const avgMessageLength = data.messageLength.length > 0
? Math.round(data.messageLength.reduce((a, b) => a + b, 0) / data.messageLength.length)
: 0;
content.innerHTML = `
<div class="stats-grid">
<div class="stats-card">
<h4>📈 Overview</h4>
<div class="stats-item">
<span class="stats-label">Total Messages:</span>
<span class="stats-value">${data.totalMessages.toLocaleString()}</span>
</div>
<div class="stats-item">
<span class="stats-label">Chat Messages:</span>
<span class="stats-value">${data.chatMessages.toLocaleString()}</span>
</div>
<div class="stats-item">
<span class="stats-label">Join/Leave Events:</span>
<span class="stats-value">${data.joinLeaveMessages.toLocaleString()}</span>
</div>
<div class="stats-item">
<span class="stats-label">Unique Players:</span>
<span class="stats-value">${data.players.size}</span>
</div>
<div class="stats-item">
<span class="stats-label">Avg Message Length:</span>
<span class="stats-value">${avgMessageLength} chars</span>
</div>
</div>
<div class="stats-card">
<h4>👥 Top Players</h4>
<div class="top-list">
${topPlayers.map((player, index) => `
<div class="top-item">
<span class="rank">${index + 1}.</span>
<span class="role-badge ${player.role}">${player.role}</span>
<span class="player-name">${player.name}</span>
<span class="player-count">${player.messageCount} msg</span>
</div>
`).join('')}
</div>
</div>
<div class="stats-card">
<h4>🕐 Activity by Hour</h4>
<div class="time-chart">
${timeActivity.map(([hour, count]) => {
const maxCount = Math.max(...timeActivity.map(([, c]) => c));
const percentage = (count / maxCount) * 100;
return `
<div class="time-bar">
<span class="time-label">${hour}:00</span>
<div class="time-bar-container">
<div class="time-bar-fill" style="width: ${percentage}%"></div>
</div>
<span class="time-count">${count}</span>
</div>
`;
}).join('')}
</div>
</div>
<div class="stats-card">
<h4>💬 Popular Words</h4>
<div class="word-cloud">
${topWords.map(([word, count]) => {
const maxCount = topWords[0][1];
const size = Math.max(0.8, (count / maxCount) * 2);
return `
<span class="word-item" style="font-size: ${size}em;" title="${count} times">
${word}
</span>
`;
}).join('')}
</div>
</div>
</div>
`;
}
}
// Initialize statistics module
const chatStats = new ChatStatistics();
// Make it globally available
window.chatStats = chatStats;
console.log('ChatStatistics module loaded and available globally');