import sqlite3 import json from datetime import datetime, timedelta from collections import defaultdict, Counter import sys import time import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') class TikkerVisualizer: def __init__(self, database_path='tikker.db'): self.database_path = database_path self.connection = sqlite3.connect(database_path) self.cursor = self.connection.cursor() def get_total_events(self): start_time = time.time() logging.info("Retrieving total events...") self.cursor.execute("SELECT COUNT(*) FROM kevent") result = self.cursor.fetchone()[0] duration = time.time() - start_time logging.info(f"Total events retrieved in {duration:.2f} seconds") return result, duration def get_pressed_events(self): start_time = time.time() logging.info("Retrieving pressed events...") self.cursor.execute("SELECT COUNT(*) FROM kevent WHERE event='PRESSED'") result = self.cursor.fetchone()[0] duration = time.time() - start_time logging.info(f"Pressed events retrieved in {duration:.2f} seconds") return result, duration def get_released_events(self): start_time = time.time() logging.info("Retrieving released events...") self.cursor.execute("SELECT COUNT(*) FROM kevent WHERE event='RELEASED'") result = self.cursor.fetchone()[0] duration = time.time() - start_time logging.info(f"Released events retrieved in {duration:.2f} seconds") return result, duration def get_date_range(self): start_time = time.time() logging.info("Retrieving date range...") self.cursor.execute("SELECT MIN(timestamp), MAX(timestamp) FROM kevent") result = self.cursor.fetchone() duration = time.time() - start_time logging.info(f"Date range retrieved in {duration:.2f} seconds") return result[0], result[1], duration def get_hourly_activity(self, limit=1000): start_time = time.time() logging.info("Retrieving hourly activity...") self.cursor.execute(""" SELECT strftime('%H', timestamp) as hour, COUNT(*) as count FROM kevent WHERE event='PRESSED' GROUP BY hour ORDER BY hour """) result = self.cursor.fetchall() duration = time.time() - start_time logging.info(f"Hourly activity retrieved in {duration:.2f} seconds") return result, duration def get_daily_activity(self, limit=365): start_time = time.time() logging.info("Retrieving daily activity...") self.cursor.execute(f""" SELECT strftime('%Y-%m-%d', timestamp) as day, COUNT(*) as count FROM kevent WHERE event='PRESSED' GROUP BY day ORDER BY day DESC LIMIT {limit} """) result = list(reversed(self.cursor.fetchall())) duration = time.time() - start_time logging.info(f"Daily activity retrieved in {duration:.2f} seconds") return result, duration def get_weekday_activity(self): start_time = time.time() logging.info("Retrieving weekday activity...") self.cursor.execute(""" SELECT CASE CAST(strftime('%w', timestamp) AS INTEGER) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' WHEN 6 THEN 'Saturday' END as weekday, COUNT(*) as count FROM kevent WHERE event='PRESSED' GROUP BY strftime('%w', timestamp) ORDER BY strftime('%w', timestamp) """) result = self.cursor.fetchall() duration = time.time() - start_time logging.info(f"Weekday activity retrieved in {duration:.2f} seconds") return result, duration def get_top_keys(self, limit=30): start_time = time.time() logging.info("Retrieving top keys...") self.cursor.execute(f""" SELECT char, COUNT(*) as count FROM kevent WHERE event='PRESSED' AND char IS NOT NULL AND char != '' GROUP BY char ORDER BY count DESC LIMIT {limit} """) result = self.cursor.fetchall() duration = time.time() - start_time logging.info(f"Top keys retrieved in {duration:.2f} seconds") return result, duration def get_keyboard_heatmap_data(self): start_time = time.time() logging.info("Retrieving keyboard heatmap data...") self.cursor.execute(""" SELECT char, COUNT(*) as count FROM kevent WHERE event='PRESSED' AND char IS NOT NULL AND char != '' GROUP BY char ORDER BY count DESC """) data = {} for char, count in self.cursor.fetchall(): data[char] = count duration = time.time() - start_time logging.info(f"Keyboard heatmap data retrieved in {duration:.2f} seconds") return data, duration def get_hour_day_heatmap(self, days_back=30): start_time = time.time() logging.info("Retrieving hour-day heatmap...") self.cursor.execute(f""" SELECT strftime('%Y-%m-%d', timestamp) as day, strftime('%H', timestamp) as hour, COUNT(*) as count FROM kevent WHERE event='PRESSED' AND timestamp >= datetime('now', '-{days_back} days') GROUP BY day, hour ORDER BY day, hour """) result = self.cursor.fetchall() duration = time.time() - start_time logging.info(f"Hour-day heatmap retrieved in {duration:.2f} seconds") return result, duration def get_typing_speed_data(self, sample_size=10000): start_time = time.time() logging.info("Retrieving typing speed data...") self.cursor.execute(f""" SELECT strftime('%Y-%m-%d %H:00:00', timestamp) as hour_bucket, COUNT(*) as keypresses FROM kevent WHERE event='PRESSED' GROUP BY hour_bucket ORDER BY hour_bucket DESC LIMIT {sample_size} """) result = list(reversed(self.cursor.fetchall())) duration = time.time() - start_time logging.info(f"Typing speed data retrieved in {duration:.2f} seconds") return result, duration def get_character_frequency(self): start_time = time.time() logging.info("Retrieving character frequency...") letters = Counter() numbers = Counter() special = Counter() self.cursor.execute(""" SELECT char, COUNT(*) as count FROM kevent WHERE event='PRESSED' AND char IS NOT NULL AND char != '' GROUP BY char """) for char, count in self.cursor.fetchall(): if len(char) == 1: if char.isalpha(): letters[char.lower()] += count elif char.isdigit(): numbers[char] += count else: special[char] += count result = { 'letters': dict(letters.most_common(26)), 'numbers': dict(numbers.most_common(10)), 'special': dict(special.most_common(20)) } duration = time.time() - start_time logging.info(f"Character frequency retrieved in {duration:.2f} seconds") return result, duration def get_monthly_stats(self): start_time = time.time() logging.info("Retrieving monthly stats...") self.cursor.execute(""" SELECT strftime('%Y-%m', timestamp) as month, COUNT(*) as count FROM kevent WHERE event='PRESSED' GROUP BY month ORDER BY month """) result = self.cursor.fetchall() duration = time.time() - start_time logging.info(f"Monthly stats retrieved in {duration:.2f} seconds") return result, duration def get_peak_activity_times(self): start_time = time.time() logging.info("Retrieving peak activity times...") self.cursor.execute(""" SELECT strftime('%Y-%m-%d %H:00:00', timestamp) as hour_block, COUNT(*) as count FROM kevent WHERE event='PRESSED' GROUP BY hour_block ORDER BY count DESC LIMIT 10 """) result = self.cursor.fetchall() duration = time.time() - start_time logging.info(f"Peak activity times retrieved in {duration:.2f} seconds") return result, duration # NEW: Words Per Minute (WPM) and Performance Metrics def get_wpm_stats(self): start_time = time.time() logging.info("Calculating WPM and performance metrics...") # Get total key presses and time span self.cursor.execute("SELECT COUNT(*) FROM kevent WHERE event='PRESSED'") total_presses = self.cursor.fetchone()[0] self.cursor.execute("SELECT MIN(timestamp), MAX(timestamp) FROM kevent WHERE event='PRESSED'") min_time, max_time = self.cursor.fetchone() if not min_time or not max_time or total_presses == 0: return { 'wpm_average': 0, 'wpm_peak_hour': 0, 'wpm_peak_day': 0, 'words_typed': 0, 'characters_typed': 0, 'total_typing_time_minutes': 0, 'typing_efficiency': 0, 'peak_hour': None, 'peak_day': None }, 0.0 min_dt = datetime.fromisoformat(min_time) max_dt = datetime.fromisoformat(max_time) total_minutes = (max_dt - min_dt).total_seconds() / 60.0 # Estimate words: 5 characters per word (standard WPM formula) characters_typed = total_presses words_typed = characters_typed / 5.0 wpm_average = words_typed / total_minutes if total_minutes > 0 else 0 # Peak hour WPM self.cursor.execute(""" SELECT strftime('%Y-%m-%d %H:00:00', timestamp) as hour, COUNT(*) as presses FROM kevent WHERE event='PRESSED' GROUP BY hour ORDER BY presses DESC LIMIT 1 """) peak_hour_row = self.cursor.fetchone() peak_hour_wpm = (peak_hour_row[1] / 5.0) if peak_hour_row else 0 # Peak day WPM self.cursor.execute(""" SELECT strftime('%Y-%m-%d', timestamp) as day, COUNT(*) as presses FROM kevent WHERE event='PRESSED' GROUP BY day ORDER BY presses DESC LIMIT 1 """) peak_day_row = self.cursor.fetchone() peak_day_wpm = (peak_day_row[1] / 5.0 / 60.0) if peak_day_row else 0 # per minute # Typing efficiency: presses per minute during active time # Approximate active time as sum of intervals with activity self.cursor.execute(""" SELECT COUNT(DISTINCT strftime('%Y-%m-%d %H', timestamp)) as active_hours FROM kevent WHERE event='PRESSED' """) active_hours = self.cursor.fetchone()[0] typing_efficiency = total_presses / (active_hours * 60) if active_hours > 0 else 0 result = { 'wpm_average': round(wpm_average, 2), 'wpm_peak_hour': round(peak_hour_wpm, 2), 'wpm_peak_day': round(peak_day_wpm, 2), 'words_typed': int(words_typed), 'characters_typed': characters_typed, 'total_typing_time_minutes': round(total_minutes, 1), 'typing_efficiency': round(typing_efficiency, 2), 'peak_hour': peak_hour_row[0] if peak_hour_row else None, 'peak_day': peak_day_row[0] if peak_day_row else None } duration = time.time() - start_time logging.info(f"WPM stats calculated in {duration:.2f} seconds") return result, duration def get_performance_insights(self): start_time = time.time() logging.info("Generating performance insights...") # === 1. Key type distribution and corrections === self.cursor.execute(""" SELECT SUM(CASE WHEN char IS NOT NULL AND length(char) = 1 AND char GLOB '[a-zA-Z]' THEN 1 ELSE 0 END) as letters, SUM(CASE WHEN char IS NOT NULL AND length(char) = 1 AND char GLOB '[0-9]' THEN 1 ELSE 0 END) as numbers, SUM(CASE WHEN char IS NOT NULL AND length(char) = 1 AND char NOT GLOB '[a-zA-Z0-9]' THEN 1 ELSE 0 END) as special, SUM(CASE WHEN char = ' ' THEN 1 ELSE 0 END) as spaces, SUM(CASE WHEN char = 'Backspace' THEN 1 ELSE 0 END) as backspaces, SUM(CASE WHEN char = 'Delete' THEN 1 ELSE 0 END) as deletes FROM kevent WHERE event='PRESSED' """) row = self.cursor.fetchone() letters = row[0] or 0 numbers = row[1] or 0 special = row[2] or 0 spaces = row[3] or 0 backspaces = row[4] or 0 deletes = row[5] or 0 total_edits = backspaces + deletes valid_keystrokes = letters + numbers + special + spaces error_rate = (total_edits / valid_keystrokes * 100) if valid_keystrokes > 0 else 0 # === 2. Daily activity consistency (manual STDDEV) === self.cursor.execute(""" SELECT COUNT(*) as count FROM kevent WHERE event='PRESSED' GROUP BY strftime('%Y-%m-%d', timestamp) """) daily_counts = [row[0] for row in self.cursor.fetchall()] if daily_counts: avg_daily = sum(daily_counts) / len(daily_counts) variance = sum((x - avg_daily) ** 2 for x in daily_counts) / len(daily_counts) std_daily = variance ** 0.5 consistency_score = 100 - (std_daily / avg_daily * 100) if avg_daily > 0 else 0 consistency_score = max(0, min(100, consistency_score)) else: avg_daily = std_daily = 0 consistency_score = 0 result = { 'error_rate_percent': round(error_rate, 2), 'backspace_count': backspaces, 'delete_count': deletes, 'total_corrections': total_edits, 'consistency_score': round(consistency_score, 1), 'letters_typed': letters, 'numbers_typed': numbers, 'special_typed': special, 'spaces_typed': spaces } duration = time.time() - start_time logging.info(f"Performance insights generated in {duration:.2f} seconds") return result, duration def close(self): self.connection.close() def generate_html(self, output_file='tikker_report.html'): logging.info("Starting HTML generation...") overall_start = time.time() total_events, total_events_duration = self.get_total_events() pressed_events, pressed_events_duration = self.get_pressed_events() released_events, released_events_duration = self.get_released_events() date_range_min, date_range_max, date_range_duration = self.get_date_range() date_range = (date_range_min, date_range_max) hourly_data, hourly_duration = self.get_hourly_activity() daily_data, daily_duration = self.get_daily_activity() weekday_data, weekday_duration = self.get_weekday_activity() top_keys, top_keys_duration = self.get_top_keys() keyboard_heatmap, keyboard_heatmap_duration = self.get_keyboard_heatmap_data() hour_day_heatmap, hour_day_heatmap_duration = self.get_hour_day_heatmap() typing_speed, typing_speed_duration = self.get_typing_speed_data() char_frequency, char_frequency_duration = self.get_character_frequency() monthly_stats, monthly_stats_duration = self.get_monthly_stats() peak_times, peak_times_duration = self.get_peak_activity_times() wpm_stats, wpm_duration = self.get_wpm_stats() perf_insights, perf_duration = self.get_performance_insights() durations = { 'total_events': total_events_duration, 'pressed_events': pressed_events_duration, 'released_events': released_events_duration, 'date_range': date_range_duration, 'hourly_activity': hourly_duration, 'daily_activity': daily_duration, 'weekday_activity': weekday_duration, 'top_keys': top_keys_duration, 'keyboard_heatmap': keyboard_heatmap_duration, 'hour_day_heatmap': hour_day_heatmap_duration, 'typing_speed': typing_speed_duration, 'character_frequency': char_frequency_duration, 'monthly_stats': monthly_stats_duration, 'peak_activity_times': peak_times_duration, 'wpm_calculation': wpm_duration, 'performance_insights': perf_duration } logging.info("Generating HTML content...") html = f""" Tikker Keyboard Performance Report

Tikker Keyboard Performance Report

Comprehensive Analysis of Typing Speed, Accuracy, and Productivity Metrics

Total Events

{total_events:,}
Recorded Actions

Key Presses

{pressed_events:,}
Keys Pressed

Average WPM

{wpm_stats['wpm_average']}
Words Per Minute

Peak Hour WPM

{wpm_stats['wpm_peak_hour']}
Max Speed

Tracking Period

{(datetime.fromisoformat(date_range[1]) - datetime.fromisoformat(date_range[0])).days if date_range[0] and date_range[1] else 0}
Days of Data

Words Typed

{wpm_stats['words_typed']:,}
Total Output

Data Retrieval Performance

    {''.join(f'
  • {key.replace("_", " ").title()}: {value:.3f}s
  • ' for key, value in durations.items())}

Typing Speed & Performance

Detailed breakdown of words per minute, efficiency, and accuracy metrics.

Average WPM

{wpm_stats['wpm_average']}
Over {wpm_stats['total_typing_time_minutes']} minutes

Peak Hour WPM

{wpm_stats['wpm_peak_hour']}
{wpm_stats['peak_hour'] or 'N/A'}

Typing Efficiency

{wpm_stats['typing_efficiency']}
Presses per active minute

Consistency Score

{perf_insights['consistency_score']}%
Daily activity stability

Error Rate

{perf_insights['error_rate_percent']}%
{perf_insights['total_corrections']:,} corrections

Characters Typed

{wpm_stats['characters_typed']:,}
{perf_insights['letters_typed']:,} letters • {perf_insights['spaces_typed']:,} spaces

Hourly Activity Distribution

Average key press volume by hour of day (00:00–23:00) across the complete dataset.

Daily Activity Timeline (Last 365 Days)

Daily key press totals over the past year, illustrating long-term usage patterns.

Activity by Day of Week

Distribution of typing activity across weekdays, highlighting weekly patterns.

Monthly Activity Trends

Monthly key press totals showing seasonal and long-term productivity trends.

Top 30 Most Frequently Used Keys

Ranking of the most pressed keys by total count, excluding system keys.

Keyboard Usage Heatmap

Visual intensity map of key usage. Color intensity corresponds to press frequency.

Low Activity
High Activity

Hour × Day Activity Matrix (Last 30 Days)

Detailed view of typing patterns by hour and day. Each cell represents one hour of activity.

00:00 06:00 12:00 18:00 23:00

Keypresses Per Hour Timeline

Hourly key press volume over time, highlighting peak productivity periods.

Letter Frequency Analysis

Relative frequency of letter usage (A-Z), case-insensitive.

Number Key Usage

Frequency distribution of numeric key usage (0-9).

Peak Activity Periods

Top 10 one-hour periods with highest key press volume.

{''.join(f'' for i, (time, count) in enumerate(peak_times))}
Rank Time Period Key Presses
{i+1}{time}{count:,}
""" with open(output_file, 'w', encoding='utf-8') as f: f.write(html) overall_duration = time.time() - overall_start logging.info(f"Enterprise HTML report generated in {overall_duration:.2f} seconds: {output_file}") logging.info(f"Total events analyzed: {total_events:,}") print(f"Enterprise performance report generated: {output_file}") if __name__ == '__main__': db_path = sys.argv[1] if len(sys.argv) > 1 else 'tikker.db' output_path = sys.argv[2] if len(sys.argv) > 2 else 'tikker_report.html' visualizer = TikkerVisualizer(db_path) visualizer.generate_html(output_path) visualizer.close()