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
This commit is contained in:
SimolZimol
2025-12-09 15:31:20 +01:00
commit 8ac625a64d
88 changed files with 169343 additions and 0 deletions

222
statistics.js Normal file
View File

@@ -0,0 +1,222 @@
/* 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');