#ifndef TIKKER_STATS_H #define TIKKER_STATS_H #include #include #include #include "sormc.h" #include "tikker_types.h" #include "tikker_db.h" #include "tikker_decode.h" #include "tikker_words.h" static inline int tikker_stats_presses_today(int db) { sorm_ptr result = sormq(db, "SELECT pressed FROM kevent_daily WHERE date = DATE('now')"); if (result) { char *csv = (char *)result; char *line = csv; char *next = strchr(line, '\n'); if (next) line = next + 1; char *end = strchr(line, ';'); if (end) *end = '\0'; end = strchr(line, '\n'); if (end) *end = '\0'; printf("%s\n", line); free(result); } else { printf("0\n"); } return 0; } static inline int tikker_stats_daily(int db) { sorm_ptr result = sormq(db, "SELECT date, pressed FROM kevent_daily ORDER BY date"); printf("date,count\n"); if (!result) return 0; tikker_csv_iter_t iter = tikker_csv_iter_init((char *)result); char *date, *count; while (tikker_csv_iter_next(&iter, &date, &count)) { if (date && count) { printf("%s,%s\n", date, count); } } free(result); return 0; } static inline int tikker_stats_hourly(int db, const char *date) { sorm_ptr result = sormq(db, "SELECT SUBSTR(date_hour, 12, 2) as hour, pressed " "FROM kevent_hourly WHERE date_hour LIKE %s || '.%%' " "ORDER BY hour", date); printf("hour,count\n"); int hour_counts[24] = {0}; if (result) { tikker_csv_iter_t iter = tikker_csv_iter_init((char *)result); char *hour_str, *count_str; while (tikker_csv_iter_next(&iter, &hour_str, &count_str)) { if (hour_str && count_str) { int hour = atoi(hour_str); if (hour >= 0 && hour < 24) { hour_counts[hour] = atoi(count_str); } } } free(result); } for (int h = 0; h < 24; h++) { printf("%02d,%d\n", h, hour_counts[h]); } return 0; } static inline int tikker_stats_weekly(int db) { sorm_ptr result = sormq(db, "SELECT STRFTIME('%%Y-%%U', date) as week, SUM(pressed) as count " "FROM kevent_daily GROUP BY week ORDER BY week"); printf("week,count\n"); if (!result) return 0; tikker_csv_iter_t iter = tikker_csv_iter_init((char *)result); char *week, *count; while (tikker_csv_iter_next(&iter, &week, &count)) { if (week && count) { printf("%s,%s\n", week, count); } } free(result); return 0; } static inline int tikker_stats_weekday(int db) { static const char *weekday_names[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; sorm_ptr result = sormq(db, "SELECT STRFTIME('%%w', date) as weekday, SUM(pressed) as count " "FROM kevent_daily GROUP BY weekday ORDER BY weekday"); printf("weekday,name,count\n"); int weekday_counts[7] = {0}; if (result) { tikker_csv_iter_t iter = tikker_csv_iter_init((char *)result); char *wday_str, *count_str; while (tikker_csv_iter_next(&iter, &wday_str, &count_str)) { if (wday_str && count_str) { int wday = atoi(wday_str); if (wday >= 0 && wday < 7) { weekday_counts[wday] = atoi(count_str); } } } free(result); } for (int d = 0; d < 7; d++) { printf("%d,%s,%d\n", d, weekday_names[d], weekday_counts[d]); } return 0; } static inline int tikker_stats_top_keys(int db, int limit) { char sql[512]; snprintf(sql, sizeof(sql), "SELECT code, count FROM kevent_key_counts " "ORDER BY count DESC LIMIT %d", limit); sorm_ptr result = sormq(db, sql); printf("code,name,count\n"); if (!result) return 0; tikker_csv_iter_t iter = tikker_csv_iter_init((char *)result); char *code_str, *count_str; while (tikker_csv_iter_next(&iter, &code_str, &count_str)) { if (code_str && count_str) { int code = atoi(code_str); const char *name = "UNKNOWN"; if (code < (int)(sizeof(tikker_keycode_to_name) / sizeof(tikker_keycode_to_name[0])) && tikker_keycode_to_name[code]) { name = tikker_keycode_to_name[code]; } printf("%s,%s,%s\n", code_str, name, count_str); } } free(result); return 0; } static inline int tikker_stats_summary(int db) { sorm_ptr result = sormq(db, "SELECT " "SUM(pressed), SUM(released), SUM(repeated), " "MIN(date), MAX(date), COUNT(*) " "FROM kevent_daily"); printf("total_pressed,total_released,total_repeated,first_event,last_event,days\n"); if (!result) { printf("0,0,0,N/A,N/A,0\n"); return 0; } char *csv = (char *)result; char *line = csv; char *next = strchr(line, '\n'); if (next) { line = next + 1; } char values[6][64] = {"0", "0", "0", "N/A", "N/A", "0"}; int idx = 0; char *start = line; while (*start && idx < 6) { char *end = strchr(start, ';'); if (!end) end = start + strlen(start); size_t len = end - start; if (len >= sizeof(values[0])) len = sizeof(values[0]) - 1; strncpy(values[idx], start, len); values[idx][len] = '\0'; idx++; if (*end) start = end + 1; else break; } printf("%s,%s,%s,%s,%s,%s\n", values[0], values[1], values[2], values[3], values[4], values[5]); free(result); return 0; } static inline int tikker_stats_top_words(int db, int limit) { sorm_ptr result = sormq(db, "SELECT STRFTIME('%%Y-%%m-%%d.%%H', timestamp) as date_hour, " "GROUP_CONCAT(char, '') as chars " "FROM kevent WHERE event = 'PRESSED' " "GROUP BY date_hour ORDER BY date_hour"); printf("word,count\n"); if (!result) return 0; size_t total_len = strlen((char *)result); char *decoded = malloc(total_len + 1); if (!decoded) { free(result); return 1; } char *csv = (char *)result; char *line = csv; char *next; size_t decoded_pos = 0; while (line && *line) { next = strchr(line, '\n'); if (next) *next = '\0'; char *chars = strchr(line, ';'); if (chars) { chars++; char *end = strchr(chars, ';'); if (end) *end = '\0'; int len = tikker_decode_buffer(chars, decoded + decoded_pos, total_len - decoded_pos); decoded_pos += len; if (decoded_pos < total_len) { decoded[decoded_pos++] = ' '; } } if (next) line = next + 1; else break; } decoded[decoded_pos] = '\0'; tikker_word_count_t *words = calloc(TIKKER_MAX_WORDS, sizeof(tikker_word_count_t)); if (!words) { free(decoded); free(result); return 1; } int word_count = tikker_extract_words(decoded, words, TIKKER_MAX_WORDS); tikker_sort_words(words, word_count); int output_count = (limit < word_count) ? limit : word_count; for (int i = 0; i < output_count; i++) { printf("%s,%d\n", words[i].word, words[i].count); } free(words); free(decoded); free(result); return 0; } #endif