Habits v0.03
So some further improvements to Habits, with the third release!
- Now the program runs on an infinite loop, so once you finish viewing statistics or inputting data, it waits for you to decide on the next command.
- I’ve also updated the input mechanism so you just have to enter a number based on the choice of what you want to do instead of having to type out “input”, “stats”, or “chart”.
- I also show the activity names when asking the user to type the activity name to view statistics or charts, and this will be further updated in a future release to just use a number in the same method as above.
- When asking for date for input, there is a choice to choose today’s date without having to input the full date, or to manually choose a date by typing it out in the YYYY-MM-DD format.

import csv
import datetime
import matplotlib.pyplot as plt
def track_activity(activity, date, sets):
with open('activity_tracker.csv', 'a', newline='') as csvfile:
activity_writer = csv.writer(csvfile, delimiter=',')
for s in sets:
activity_writer.writerow([activity, date, s])
def get_activity_stats(activity):
total_sets = 0
max_sets = 0
sets_by_date = {}
with open('activity_tracker.csv', 'r') as csvfile:
activity_reader = csv.reader(csvfile, delimiter=',')
num_sets = 0
dates = []
sets = []
for row in activity_reader:
if row[0] == activity:
total_sets += int(row[2])
max_sets = max(max_sets, int(row[2]))
num_sets += 1
if row[1] not in sets_by_date:
sets_by_date[row[1]] = 0
sets_by_date[row[1]] += int(row[2])
if num_sets > 0:
avg_sets = total_sets / num_sets
else:
avg_sets = 0
print(f'Total {activity}: {total_sets}')
print(f'Total sets: {num_sets}')
print(f'Average {activity} per set: {avg_sets:.2f}')
print(f'Max {activity} per set: {max_sets}')
return (sets_by_date.keys(), sets_by_date.values())
def show_bar_chart(activity, dates, sets):
plt.bar(dates, sets)
plt.xlabel('Date')
plt.ylabel(activity)
plt.title(f'{activity} per Day')
plt.show()
def get_date():
# ask user for option
print("1. Input data for today")
print("2. Choose another day")
option = input("Enter an option: ")
# input data for today
if option == "1":
return datetime.date.today()
# choose another day
elif option == "2":
# ask user for date
return input("Enter a date (yyyy-mm-dd format): ")
else:
print("Invalid option")
return get_date()
def get_menu_option():
# print menu
print("1. Input new data")
print("2. View statistics")
print("3. View chart")
print("4. Quit")
# ask user for option
option = input("Enter an option: ")
return option
def main():
option = get_menu_option()
# execute chosen option
while option != "4":
if option == "1":
activity = input('Enter activity name: ')
date = get_date()
sets = []
while True:
num_sets = input(f'Enter number of {activity} sets (or "done" to finish): ')
if num_sets.lower() == 'done':
break
sets.append(int(num_sets))
track_activity(activity, date, sets)
elif option == "2":
# list out all the activities
with open('activity_tracker.csv', 'r') as csvfile:
activity_reader = csv.reader(csvfile, delimiter=',')
activities = []
for row in activity_reader:
if row[0] not in activities:
activities.append(row[0])
# allow the user to choose an activity
activity = input(f'Enter activity name ({", ".join(activities)}): ')
get_activity_stats(activity)
elif option == "3":
# list out all the activities
with open('activity_tracker.csv', 'r') as csvfile:
activity_reader = csv.reader(csvfile, delimiter=',')
activities = []
for row in activity_reader:
if row[0] not in activities:
activities.append(row[0])
# allow the user to choose an activity
activity = input(f'Enter activity name ({", ".join(activities)}): ')
dates, sets = get_activity_stats(activity)
show_bar_chart(activity, dates, sets)
else:
print("Invalid option")
option = get_menu_option()
if __name__ == "__main__":
main()
This is already quite a decent functioning mini-application, and it is starting to give me significant ideas on how a more fully fledged system could work in the future.