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