#!/usr/bin/env python3
# habitus - A statistics and trends calculator for Harsh
# Author: Simon Volpert <simon@simonvolpert.com>
# Project page: gemini://simonvolpert.com/habitus/
# This program is free software, released under the MIT license. See the LICENSE file for more information
import datetime
import pathlib
import sys
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
args = sys.argv[1:]
if args == ['--help']:
print('Usage: {} [TRACK]...'.format(pathlib.Path(sys.argv[0]).name))
print('Print statistics regarding your harsh log file')
print('\nOptional arguments:')
print(' TRACK Specific tracks to limit analysis to')
sys.exit()
today = datetime.date.today()
logfile = pathlib.Path.home() / '.config/harsh/log'
journal = logfile.read_text().split('\n')
journal.sort(reverse=True)
def new_track():
return {
'days': [],
'weeks': [],
'months': [],
'quarters': [],
'years': [],
'weekdays': [0, 0, 0, 0, 0, 0, 0],
'now': datetime.date.today(),
'total': 0,
'max_streak': 0,
'max_break': 0
}
def calculate_trend(track, period, number):
if len(data[track][period]) < number + number:
return '?'
period1 = sum(data[track][period][0:number])
period2 = sum(data[track][period][number:number+number])
if period2 == period1:
return '= (0%)'
elif period2 == 0:
return '+++ (+1000.0%)'
ratio = period1 / period2
value = ' ({:+.1f}%)'.format(ratio * 100 - 100)
if ratio > 1.5:
return '+++' + value
elif ratio > 1.25:
return '++' + value
elif ratio > 1.1:
return '+' + value
elif ratio >= 0.9:
return '=' + value
elif ratio >= 0.75:
return '-' + value
elif ratio >= 0.5:
return '--' + value
else:
return '---' + value
data = {}
_weekday = None
for entry in journal:
if entry == '':
continue
elif entry.startswith('#'):
continue
date, track, flag = entry.split(' : ')
# Seed weekday tracking
if _weekday is None:
_weekday = datetime.date.fromisoformat(date).weekday()
# Limit to tracks we're explicitly interested in
if args != [] and track not in args:
continue
if track not in data:
data[track] = new_track()
track = data[track]
then = datetime.date.fromisoformat(date)
# Fill skipped records
for i in range((track['now'] - then).days - 1):
track['days'].append(False)
track['days'].append(flag == 'y')
track['now'] = then
# Count missing todays as breaks
for track in data:
if data[track]['now'] < today:
data[track]['days'].insert(0, False)
# Tally streaks and breaks
for track in data.values():
_streak = 0
_break = 0
_week = 7
this_week = 0
_month = 30
this_month = 0
_quarter = 91
this_quarter = 0
_year = 365
this_year = 0
for day in track['days']:
if day:
track['total'] += 1
_streak += 1
_break = 0
if _streak > track['max_streak']:
track['max_streak'] = _streak
this_week += 1
this_month += 1
this_quarter += 1
this_year += 1
track['weekdays'][_weekday] += 1
else:
_streak = 0
_break += 1
if _break > track['max_break']:
track['max_break'] = _break
_week -= 1
_month -= 1
_quarter -= 1
_year -= 1
if _week == 0:
track['weeks'].append(this_week)
this_week = 0
_week = 7
if _month == 0:
track['months'].append(this_month)
this_month = 0
_month = 30
if _quarter == 0:
track['quarters'].append(this_quarter)
this_quarter = 0
_quarter = 91
if _year == 0:
track['years'].append(this_year)
this_year = 0
_year = 365
_weekday -= 1
if _weekday == -1:
_weekday = 6
# Flush remaining days in each period
if _week < 7:
track['weeks'].append(this_week)
if _month < 30:
track['months'].append(this_month)
if _quarter < 91:
track['quarters'].append(this_quarter)
if _year < 365:
track['years'].append(this_year)
# Output the results
for track in data:
print(track)
print(' Total:', data[track]['total'])
print(' Longest streak:', data[track]['max_streak'])
print(' Longest break:', data[track]['max_break'])
print(' Weekly mean: {:.1f}'.format(sum(data[track]['weeks']) / len(data[track]['weeks'])))
print(' Monthly mean: {:.1f}'.format(sum(data[track]['months']) / len(data[track]['months'])))
print(' Quarterly mean: {:.1f}'.format(sum(data[track]['quarters']) / len(data[track]['quarters'])))
print(' Yearly mean: {:.1f}'.format(sum(data[track]['years']) / len(data[track]['years'])))
print(' 1-week trend:', calculate_trend(track, 'weeks', 1))
print(' 2-week trend:', calculate_trend(track, 'weeks', 2))
print(' 1-month trend:', calculate_trend(track, 'months', 1))
print(' 3-month trend:', calculate_trend(track, 'months', 3))
print(' 6-month trend:', calculate_trend(track, 'months', 6))
print(' 1-year trend:', calculate_trend(track, 'years', 1))
print(' Weekday breakdown:')
for day in range(7):
print(' {}: {} ({:.1f}%)'.format(weekdays[day], data[track]["weekdays"][day], data[track]['weekdays'][day] / sum(data[track]['weekdays']) * 100))