| from collections import Counter, defaultdict |
| from datetime import datetime |
| import sqlite3 |
|
|
|
|
| STOP_WORDS = { |
| "the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for", |
| "of", "with", "by", "from", "up", "about", "into", "over", "after", |
| "is", "are", "was", "were", "be", "been", "being", "have", "has", |
| "had", "do", "does", "did", "will", "would", "could", "should", |
| "may", "might", "shall", "can", "need", "dare", "ought", "used", |
| "i", "me", "my", "myself", "we", "our", "ours", "ourselves", |
| "you", "your", "yours", "yourself", "yourselves", "he", "him", |
| "his", "himself", "she", "her", "hers", "herself", "it", "its", |
| "itself", "they", "them", "their", "theirs", "themselves", |
| "what", "which", "who", "whom", "this", "that", "these", "those", |
| "not", "no", "nor", "so", "very", "just", "as", "if", "than", |
| "then", "now", "once", "here", "there", "when", "where", "why", |
| "how", "all", "each", "every", "both", "few", "more", "most", |
| "other", "some", "such", "only", "own", "same", "too", "also", |
| "get", "got", "go", "goes", "went", "see", "seen", "know", |
| "like", "make", "take", "think", "come", "came", "want", "give", |
| "use", "find", "tell", "ask", "work", "seem", "feel", "try", |
| "leave", "call", "keep", "let", "begin", "show", "hear", "play", |
| "run", "move", "live", "said", "say", "says", "thing", "things", |
| "one", "two", "time", "way", "people", "day", "year", "back", |
| "well", "even", "still", "much", "really", "long", "new", "good", |
| "first", "last", "great", "little", "right", "old", "big", |
| "high", "different", "small", "large", "next", "early", "young", |
| "important", "because", "while", "since", "before", "after", |
| "though", "although", "until", "during", "always", "never", |
| "ever", "often", "sometimes", "usually", "maybe", |
| } |
|
|
|
|
| def get_entries_per_period(db_path, period="day"): |
| conn = sqlite3.connect(db_path) |
| rows = conn.execute("SELECT day FROM entries ORDER BY ts").fetchall() |
| conn.close() |
|
|
| counts = defaultdict(int) |
| for (day_str,) in rows: |
| dt = datetime.fromisoformat(day_str) |
| if period == "week": |
| label = f"{dt.isocalendar()[0]}-W{dt.isocalendar()[1]:02d}" |
| elif period == "month": |
| label = f"{dt.year}-{dt.month:02d}" |
| else: |
| label = day_str |
| counts[label] += 1 |
|
|
| labels = sorted(counts.keys()) |
| return {"labels": labels, "counts": [counts[l] for l in labels]} |
|
|
|
|
| def get_writing_streak(db_path): |
| conn = sqlite3.connect(db_path) |
| rows = conn.execute("SELECT DISTINCT day FROM entries ORDER BY day").fetchall() |
| conn.close() |
|
|
| days = [r[0] for r in rows] |
| total_days = len(days) |
|
|
| if total_days == 0: |
| return {"current": 0, "longest": 0, "total_days": 0} |
|
|
| longest = 1 |
| current_run = 1 |
| for i in range(1, len(days)): |
| prev = datetime.fromisoformat(days[i - 1]) |
| cur = datetime.fromisoformat(days[i]) |
| if (cur - prev).days == 1: |
| current_run += 1 |
| if current_run > longest: |
| longest = current_run |
| else: |
| current_run = 1 |
|
|
| today = datetime.now().date() |
| last = datetime.fromisoformat(days[-1]).date() |
| if (today - last).days > 1: |
| current = 0 |
| else: |
| current = 1 |
| for i in range(len(days) - 2, -1, -1): |
| d = datetime.fromisoformat(days[i]).date() |
| if (last - d).days == current: |
| current += 1 |
| else: |
| break |
|
|
| return {"current": current, "longest": longest, "total_days": total_days} |
|
|
|
|
| def get_top_topics(db_path, limit=15): |
| conn = sqlite3.connect(db_path) |
| rows = conn.execute("SELECT text FROM entries").fetchall() |
| conn.close() |
|
|
| counter = Counter() |
| for (text,) in rows: |
| words = text.lower().split() |
| for w in words: |
| w = w.strip(".,!?;:\"'()[]{}") |
| if len(w) > 2 and w not in STOP_WORDS and w.isalpha(): |
| counter[w] += 1 |
|
|
| most_common = counter.most_common(limit) |
| return { |
| "topics": [w for w, _ in most_common], |
| "counts": [c for _, c in most_common], |
| } |
|
|
|
|
| def get_busiest_days(db_path): |
| conn = sqlite3.connect(db_path) |
| rows = conn.execute("SELECT ts FROM entries").fetchall() |
| conn.close() |
|
|
| dow = defaultdict(int) |
| hod = defaultdict(int) |
| for (ts,) in rows: |
| dt = datetime.fromtimestamp(ts) |
| dow[dt.strftime("%A")] += 1 |
| hod[dt.hour] += 1 |
|
|
| return { |
| "day_of_week": dict(sorted(dow.items(), key=lambda x: x[1], reverse=True)), |
| "hour_of_day": dict(sorted(hod.items())), |
| } |
|
|
|
|
| def get_entry_stats(db_path): |
| conn = sqlite3.connect(db_path) |
| row = conn.execute( |
| "SELECT COUNT(*), MIN(day), MAX(day), AVG(LENGTH(text) - LENGTH(REPLACE(text, ' ', '')) + 1) FROM entries" |
| ).fetchone() |
| conn.close() |
|
|
| total, first, last, avg = row |
| return { |
| "total_entries": total, |
| "first_entry": first or "", |
| "last_entry": last or "", |
| "avg_words": round(avg) if avg else 0, |
| } |
|
|