Skip to content

Instantly share code, notes, and snippets.

@jackbow
Last active August 25, 2021 22:49
Show Gist options
  • Save jackbow/1a06f474a6c48aa3c1d3468cb25f9774 to your computer and use it in GitHub Desktop.
Save jackbow/1a06f474a6c48aa3c1d3468cb25f9774 to your computer and use it in GitHub Desktop.
Simple CLI Time Tracker

Simple CLI Timetracking Script

Installation:

  1. If not using conda, rewrite work.sh to work with your python manager
  2. Put in your own wage, expected hours/week, etc in time.py
  3. echo source work.sh >> .bashrc

Usage:

  1. work
    • Logs time as long as process is running and shows a progress bar (I usually keep a lil terminal open just for timetracking on a separate desktop with slack & my email open)
    • Stop process with ctrl-c which safely exits program and gives you some stats
    • Saves worked hours into ~/timesheet.csv by default.
  2. work submit
    • Retrieves past two weeks of hours and displays it per day for submitting.
  3. If anything goes wrong, just edit the csv file.
import pandas as pd
from tqdm import trange
import os
import sys
import time
import math
import signal
from datetime import datetime as dt, timedelta
# magic numbers
GOAL_WEEK = 25
GOAL_DAYS = 5
GOAL_HOURS = GOAL_WEEK / GOAL_DAYS
WAGE = 22.4
TIMESHEET_CSV = '~/timesheet.csv'
# helpers
def hours_to_seconds(hours):
return math.ceil(hours * 60 * 60)
def seconds_to_hours(seconds):
return round((seconds / 60 / 60) + 0.0005, 3)
def today_exists():
return timesheet.date[timesheet.shape[0]-1] == str(dt.date(dt.now()))
def pay_period_hours():
# resets on even weeks (submit on odd weeks)
if WEEK_NUM % 2 == 0:
return round(timesheet[timesheet.week == WEEK_NUM].hours.sum() + 0.0005, 3)
return round(timesheet[timesheet.week >= WEEK_NUM - 1].hours.sum() + 0.0005, 3)
def pay_week_hours():
return round(timesheet[timesheet.week == WEEK_NUM].hours.sum() + 0.0005, 3)
def calc_pay(hrs):
return round(hrs * WAGE, 2)
def print_days_completed():
print(f'\t{round(timesheet[timesheet.week == WEEK_NUM].hours.sum()/GOAL_HOURS, 1)} / {GOAL_DAYS} days')
# save on ctrl-c
def signal_handler(sig, frame):
today_hours = round(seconds_to_hours(math.ceil(time.time() - start_time)) +
today_prev_hours + 0.0005, 3)
timesheet.at[timesheet.shape[0]-1, 'hours'] = today_hours
timesheet.to_csv(TIMESHEET_CSV, index=False)
if (today_hours != pd.read_csv(TIMESHEET_CSV).hours[timesheet.shape[0]-1]):
print('file write error')
print(f'\n\t{today_hours} hours, ${calc_pay(today_hours)}')
print(f"\t{round(pay_period_hours(),1)} / {GOAL_HOURS * GOAL_DAYS} hrs, ${calc_pay(pay_week_hours())} / ${calc_pay(GOAL_HOURS * GOAL_DAYS)}")
print_days_completed()
sys.exit(0)
# load data
timesheet = pd.read_csv(TIMESHEET_CSV)
WEEK_NUM = dt.date(dt.now()).isocalendar()[1]
# hour submission output
if (len(sys.argv) > 1 and sys.argv[1] == 'submit'):
if WEEK_NUM % 2 == 0:
print(timesheet[timesheet.week == WEEK_NUM])
else:
print(timesheet[timesheet.week >= WEEK_NUM-1])
print(f"{pay_period_hours()} hrs, ${calc_pay(pay_period_hours())}")
sys.exit(0)
# time tracking initialization
start_time = time.time()
if not today_exists():
timesheet = pd.concat([timesheet, pd.DataFrame(
{'date': [dt.date(dt.now())], 'hours': [0], 'week': [WEEK_NUM]})], ignore_index=True)
today_prev_hours = timesheet.hours[timesheet.shape[0]-1]
signal.signal(signal.SIGINT, signal_handler)
# time tracking output
print_days_completed()
if today_prev_hours < GOAL_HOURS:
print(f'\t{dt.now().strftime("%I:%M %p")} --> {(dt.now() + timedelta(hours=GOAL_HOURS - today_prev_hours)).strftime("%I:%M %p")}')
for seconds in trange(0, hours_to_seconds(GOAL_HOURS), initial=hours_to_seconds(today_prev_hours)):
time.sleep(1)
if (seconds >= hours_to_seconds(GOAL_HOURS)):
break
os.system(f"osascript -e 'display alert \"{GOAL_HOURS} Hours Worked!\" message \"Congrats\"'")
while True:
for seconds in trange(0, hours_to_seconds(1)):
time.sleep(1)
work() {
conda activate base
python ~/time.py $1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment