From 2ba0ad501e770cce2ae4a0e1e1cf2c83608f1ca0 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Wed, 15 Jul 2020 14:23:40 -0500 Subject: [PATCH] initial --- .gitignore | 3 ++ config.py | 2 + goalctl | 82 +++++++++++++++++++++++++++++++ goals/100-days-of-code.json | 9 ++++ goals/code-a-lot.json | 9 ++++ server.py | 96 +++++++++++++++++++++++++++++++++++++ tweeter | 4 ++ utils.py | 12 +++++ 8 files changed, 217 insertions(+) create mode 100644 .gitignore create mode 100644 config.py create mode 100755 goalctl create mode 100644 goals/100-days-of-code.json create mode 100644 goals/code-a-lot.json create mode 100644 server.py create mode 100755 tweeter create mode 100644 utils.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b1f638 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +*.pyc +/data.db diff --git a/config.py b/config.py new file mode 100644 index 0000000..84c81be --- /dev/null +++ b/config.py @@ -0,0 +1,2 @@ +start_of_week = "monday" +timeout = 15 * 60 diff --git a/goalctl b/goalctl new file mode 100755 index 0000000..6d3cd82 --- /dev/null +++ b/goalctl @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +# kinds of goals: +# - overall +# "i want to code at least {time} per {interval}" +# - in a project +# - in a language +# - in an editor + +from datetime import timedelta, date, datetime, time +import sys +import json +import sqlite3 +from config import timeout +from utils import window + +conn = sqlite3.connect("data.db") + +if len(sys.argv) < 2: + print("provide a file", file=sys.stderr) + sys.exit(1) + +filename = sys.argv[1] +with open(filename) as f: + data = json.load(f) + +goal_type = data["type"] +if goal_type == "overall": + # in seconds + goal_min = int(data["goal_min"]) + + # figure out what timestamps to pull from + if data["interval"] == "day": + # except! + def convert_weekday(s): + s = s.lower() + return dict( + sunday=0, + monday=1, + tuesday=2, + wednesday=3, + thursday=4, + friday=5, + saturday=6, + ).get(s) + excepts = list(map(convert_weekday, data["except"])) + + if len(sys.argv) > 2: + # if a day is given, print that day + d = datetime.strptime(sys.argv[2], "%Y-%m-%d").date() + start_time = datetime.combine(d, time(hour=0, minute=0, second=0)) + end_time = datetime.combine(d, time(hour=23, minute=59, second=59)) + else: + # find the current day! + today = date.today() + start_time = datetime.combine(today, time(hour=0, minute=0, second=0)) + end_time = datetime.now() + print(f"from {start_time} to {end_time}") + start_stamp = datetime.timestamp(start_time) + end_stamp = datetime.timestamp(end_time) + + # get all the entries + c = conn.cursor() + curs = c.execute(""" + select time from heartbeats where time > ? and time < ? order by time; + """, [start_stamp, end_stamp]) + + coded_secs = 0 + for (first, second) in window(curs, 2): + print(datetime.fromtimestamp(first[0])) + diff = second[0] - first[0] + if diff < timeout: + coded_secs += diff + print(coded_secs) + + elif data["interval"] == "week": + # start of the week + today = date.today() + last_monday = today - timedelta(days=today.weekday()) + print(last_monday) + +# vim: set ft=python: + diff --git a/goals/100-days-of-code.json b/goals/100-days-of-code.json new file mode 100644 index 0000000..54c3a22 --- /dev/null +++ b/goals/100-days-of-code.json @@ -0,0 +1,9 @@ +{ + "type": "overall", + "goal_min": 3600, + "interval": "day", + "except": [ + "saturday", + "sunday" + ] +} diff --git a/goals/code-a-lot.json b/goals/code-a-lot.json new file mode 100644 index 0000000..e8cf559 --- /dev/null +++ b/goals/code-a-lot.json @@ -0,0 +1,9 @@ +{ + "type": "overall", + "goal_min": 60, + "interval": "week", + "except": [ + "saturday", + "sunday" + ] +} diff --git a/server.py b/server.py new file mode 100644 index 0000000..b8aea0d --- /dev/null +++ b/server.py @@ -0,0 +1,96 @@ +from http.server import BaseHTTPRequestHandler +import socketserver +import sqlite3 +from collections import namedtuple +import json + + +PORT = 5800 +conn = sqlite3.connect("data.db") + +c = conn.cursor() +# c.execute("""drop table if exists "heartbeats" """) +c.execute(""" + create table if not exists "heartbeats" ( + id int primary key, + entity text, + type text, + category text, + time real, + project text, + branch text, + language text, + dependencies text, + lines int, + lineno int, + cursorpos int, + is_write boolean, + user_agent text + ); +""") +conn.commit() + + +class Handler(BaseHTTPRequestHandler): + def do_GET(self): + pass + + def do_POST(self): + if self.path == "/api/v1/heartbeats.bulk": + content_len = int(self.headers.get("Content-Length")) + # todo: cap this or potential vuln? + body = self.rfile.read(content_len) + + def object_hook(d): + return namedtuple( + "heartbeat", + d.keys() + )(*d.values()) + + data = json.loads(body, object_hook=object_hook) + print(data) + + c = conn.cursor() + for heartbeat in data: + c.execute( + """ + insert into "heartbeats" + (entity, type, category, time, project, branch, + language, dependencies, lines, lineno, cursorpos, + is_write, user_agent) + values + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + [ + heartbeat.entity, + heartbeat.type, + heartbeat.category, + heartbeat.time, + heartbeat.project, + heartbeat.branch, + heartbeat.language, + ",".join(heartbeat.dependencies), + heartbeat.lines, + heartbeat.lineno, + heartbeat.cursorpos, + heartbeat.is_write, + heartbeat.user_agent, + ], + ) + conn.commit() + + +class Server(socketserver.TCPServer): + allow_reuse_address = True + + def __init__(self): + super().__init__(("", PORT), Handler) + + +server = Server() +try: + server.serve_forever() +except KeyboardInterrupt: + print("shutting down") + server.shutdown() + conn.close() diff --git a/tweeter b/tweeter new file mode 100755 index 0000000..eac5514 --- /dev/null +++ b/tweeter @@ -0,0 +1,4 @@ +#!/bin/bash + + +# vim: set ft=bash : diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..bdba4ce --- /dev/null +++ b/utils.py @@ -0,0 +1,12 @@ +from itertools import islice + +def window(seq, n=2): + "Returns a sliding window (of width n) over data from the iterable" + " s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... " + it = iter(seq) + result = tuple(islice(it, n)) + if len(result) == n: + yield result + for elem in it: + result = result[1:] + (elem,) + yield result