initial
This commit is contained in:
commit
2ba0ad501e
8 changed files with 217 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
/data.db
|
2
config.py
Normal file
2
config.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
start_of_week = "monday"
|
||||||
|
timeout = 15 * 60
|
82
goalctl
Executable file
82
goalctl
Executable file
|
@ -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:
|
||||||
|
|
9
goals/100-days-of-code.json
Normal file
9
goals/100-days-of-code.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"type": "overall",
|
||||||
|
"goal_min": 3600,
|
||||||
|
"interval": "day",
|
||||||
|
"except": [
|
||||||
|
"saturday",
|
||||||
|
"sunday"
|
||||||
|
]
|
||||||
|
}
|
9
goals/code-a-lot.json
Normal file
9
goals/code-a-lot.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"type": "overall",
|
||||||
|
"goal_min": 60,
|
||||||
|
"interval": "week",
|
||||||
|
"except": [
|
||||||
|
"saturday",
|
||||||
|
"sunday"
|
||||||
|
]
|
||||||
|
}
|
96
server.py
Normal file
96
server.py
Normal file
|
@ -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()
|
4
tweeter
Executable file
4
tweeter
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
# vim: set ft=bash :
|
12
utils.py
Normal file
12
utils.py
Normal file
|
@ -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
|
Loading…
Reference in a new issue