Simon Volpert habitus / master habitus
master

Tree @master (Download .tar.gz)

habitus @masterraw · history · blame

#!/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(date):
	return {
		'days': [],
		'weeks': [],
		'months': [],
		'quarters': [],
		'years': [],
		'weekdays': [0, 0, 0, 0, 0, 0, 0],
		'now': datetime.date.today(),
		'latest_date': datetime.date.fromisoformat(date),
		'total': 0,
		'max_streak': 0,
		'max_break': 0,
		'current_streak_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(date)
	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]['latest_date'] < 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)
	# Calculate current streek/break
	for day in track['days']:
		if day != track['days'][0]:
			break
		track['current_streak_break'] += 1

# Output the results
for track in data:
	print(track)
	print('    Total:', data[track]['total'])
	_streak = '    Current %s:' % ('streak' if data[track]['days'][0] else 'break')
	print(_streak, data[track]['current_streak_break'])
	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))