192 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python
 | 
						|
 | 
						|
import argparse
 | 
						|
import datetime
 | 
						|
import sys
 | 
						|
import gpx_parser
 | 
						|
import matplotlib
 | 
						|
matplotlib.use('Agg')
 | 
						|
import matplotlib.pyplot as plt
 | 
						|
import numpy
 | 
						|
import os
 | 
						|
import pandas as pd
 | 
						|
import collections
 | 
						|
 | 
						|
MONTH_LABELS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
 | 
						|
 | 
						|
def parse_args():
 | 
						|
    '''Shell argument parser.'''
 | 
						|
    parser = argparse.ArgumentParser()
 | 
						|
    parser.add_argument('infolder', help='Specify the in folder.')
 | 
						|
    parser.add_argument('outfolder', help='Specify the out folder.')
 | 
						|
    return parser.parse_args()
 | 
						|
 | 
						|
 | 
						|
def plot_bar_chart(labels, ticklabels, values, title, xlabel, ylabel, filename, xtick_rotation=0):
 | 
						|
    fig = plt.figure()
 | 
						|
    ax1 = fig.add_subplot(111)
 | 
						|
    ax1.grid(zorder=0)
 | 
						|
    ax1.spines["top"].set_visible(False)
 | 
						|
    ax1.spines["bottom"].set_visible(False)
 | 
						|
    ax1.spines["left"].set_visible(False)
 | 
						|
    ax1.spines["right"].set_visible(False)
 | 
						|
 | 
						|
    plt.title(title)
 | 
						|
    plt.xlabel(xlabel)
 | 
						|
    plt.ylabel(ylabel)
 | 
						|
 | 
						|
    width = 1.0 / len(values) - 0.03
 | 
						|
    x_base = numpy.arange(len(ticklabels))
 | 
						|
    x_pos = list()
 | 
						|
 | 
						|
    for i in range(len(values)):
 | 
						|
        x_pos.append([x + (width / 2) + i * width for x in range(len(x_base))])
 | 
						|
        plt.bar(x_pos[i], values[i], width=width, label=labels[i], zorder=2)
 | 
						|
 | 
						|
    plt.xticks(x_base, ticklabels, rotation=xtick_rotation)
 | 
						|
 | 
						|
    # Tweak spacing to prevent clipping of tick-labels
 | 
						|
    plt.subplots_adjust(bottom=0.2)
 | 
						|
 | 
						|
    plt.legend()
 | 
						|
    plt.savefig(filename)
 | 
						|
 | 
						|
 | 
						|
def main():
 | 
						|
    args = parse_args()
 | 
						|
    tracks = gpx_parser.Tracks(args.infolder)
 | 
						|
    years_distance = list()
 | 
						|
    years_avg_spd = list()
 | 
						|
    years = list()
 | 
						|
    for year in range(2017, datetime.datetime.now().year + 1):
 | 
						|
        month_avg_spd = {1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0}
 | 
						|
        month_distance = {1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0}
 | 
						|
        month_duration = {1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0}
 | 
						|
        for month in range(1, 13):
 | 
						|
            if month == 12:
 | 
						|
                max_day = (datetime.date(year + 1, 1, 1) - datetime.timedelta(days=1)).day
 | 
						|
            else:
 | 
						|
                max_day = (datetime.date(year, month + 1, 1) - datetime.timedelta(days=1)).day
 | 
						|
            for day in range(1, max_day + 1):
 | 
						|
                date_tracks = tracks.get(year, month, day)
 | 
						|
                for track in date_tracks:
 | 
						|
                    month_distance[month] += (track.distance / 1000)    # km
 | 
						|
                    month_duration[month] += track.duration.total_seconds() / 3600  # h
 | 
						|
        for i in range(1, 13):
 | 
						|
            if month_duration[i] > 0:
 | 
						|
                month_avg_spd[i] = month_distance[i] / month_duration[i]
 | 
						|
        years_distance.append(month_distance.values())
 | 
						|
        years_avg_spd.append(month_avg_spd.values())
 | 
						|
        years.append(str(year))
 | 
						|
 | 
						|
    out_folder = os.path.abspath(args.outfolder)
 | 
						|
 | 
						|
    dst_file_name = 'distance.png'
 | 
						|
    plot_bar_chart(years, MONTH_LABELS, years_distance, 'Distance', 'Month', 'km', os.path.join(out_folder, dst_file_name))
 | 
						|
 | 
						|
    avg_file_name = 'avg_spd.png'
 | 
						|
    plot_bar_chart(years, MONTH_LABELS, years_avg_spd, 'Average Speed', 'Month', 'km/h', os.path.join(out_folder, avg_file_name))
 | 
						|
 | 
						|
    # last n days
 | 
						|
    n = 14
 | 
						|
    end_date = datetime.date.today()
 | 
						|
    start_date = end_date - datetime.timedelta(days=n)
 | 
						|
    dates = pd.date_range(start_date, end_date)
 | 
						|
 | 
						|
    date_distance = dict()
 | 
						|
    date_duration = dict()
 | 
						|
    date_avg_spd = dict()
 | 
						|
 | 
						|
    for date in dates:
 | 
						|
        date_str = "{0:04d}-{1:02d}-{2:02d}".format(date.year, date.month, date.day)
 | 
						|
        date_tracks = tracks.get(date.year, date.month, date.day)
 | 
						|
        for track in date_tracks:
 | 
						|
            try:
 | 
						|
                current_dist = date_distance[date_str]
 | 
						|
                current_duration = date_duration[date_str]
 | 
						|
            except KeyError:
 | 
						|
                current_dist = 0
 | 
						|
                current_duration = 0
 | 
						|
            current_dist += track.distance / 1000
 | 
						|
            date_distance.update({date_str:current_dist})
 | 
						|
            current_duration += track.duration.total_seconds() / 3600
 | 
						|
            date_duration.update({date_str:current_duration})
 | 
						|
        # check for empty dates
 | 
						|
        try:
 | 
						|
            current_dist = date_distance[date_str]
 | 
						|
            current_duration = date_duration[date_str]
 | 
						|
        except KeyError:
 | 
						|
            date_distance.update({date_str:0})
 | 
						|
            date_duration.update({date_str:0})
 | 
						|
 | 
						|
    date_duration = collections.OrderedDict(sorted(date_duration.items()))
 | 
						|
 | 
						|
    for key, value in date_duration.items():
 | 
						|
        if value == 0:
 | 
						|
            date_avg_spd.update({key:0})
 | 
						|
        else:
 | 
						|
            avg_spd = date_distance[key] / value
 | 
						|
            date_avg_spd.update({key:avg_spd})
 | 
						|
 | 
						|
    date_avg_spd = collections.OrderedDict(sorted(date_avg_spd.items()))
 | 
						|
    date_distance = collections.OrderedDict(sorted(date_distance.items()))
 | 
						|
 | 
						|
    dst_n_file_name = "distance_last_{}_days.png".format(n)
 | 
						|
    plot_bar_chart(["Distance", "Average speed"],
 | 
						|
                   date_distance.keys(),
 | 
						|
                   [date_distance.values(), date_avg_spd.values()],
 | 
						|
                   'Last {} days'.format(n),
 | 
						|
                   'Date',
 | 
						|
                   'km, km/h',
 | 
						|
                   os.path.join(out_folder, dst_n_file_name),
 | 
						|
                   90)
 | 
						|
 | 
						|
    html_file = os.path.join(out_folder, 'index.html')
 | 
						|
    with open(html_file, 'w') as handle:
 | 
						|
        handle.write('<!DOCTYPE html>\n')
 | 
						|
        handle.write('<html>\n')
 | 
						|
        handle.write('<head>\n')
 | 
						|
        handle.write('<style>\n')
 | 
						|
        handle.write('table {\n')
 | 
						|
        handle.write('    border-collapse: separate;\n')
 | 
						|
        handle.write('    border-spacing: 20px 0;\n')
 | 
						|
        handle.write('}\n')
 | 
						|
        handle.write('th {\n')
 | 
						|
        handle.write('    text-align: left;\n')
 | 
						|
        handle.write('}\n')
 | 
						|
        handle.write('</style>\n')
 | 
						|
        handle.write('<title> Bicycle </title>\n')
 | 
						|
        handle.write('</head>\n')
 | 
						|
        handle.write('<body>\n')
 | 
						|
        handle.write('<center>\n')
 | 
						|
        handle.write('<h1> Bicycle </h1>\n')
 | 
						|
        handle.write('<p>\n')
 | 
						|
 | 
						|
        handle.write('<table>\n')
 | 
						|
        handle.write('<tr>\n')
 | 
						|
        for year in years:
 | 
						|
            handle.write('<th>{}</th>\n'.format(year))
 | 
						|
        handle.write('</tr>\n')
 | 
						|
 | 
						|
        handle.write('<tr>\n')
 | 
						|
        for i in range(len(years_distance)):
 | 
						|
            handle.write('<td>{} km</td>\n'.format(round(sum(years_distance[i]), 1)))
 | 
						|
        handle.write('</tr>\n')
 | 
						|
        handle.write('</table>\n')
 | 
						|
 | 
						|
        handle.write('</p>\n')
 | 
						|
 | 
						|
        handle.write('<p>\n')
 | 
						|
        handle.write('<IMG SRC="{}" ALT="Distance">\n'.format(dst_file_name))
 | 
						|
        handle.write('<IMG SRC="{}" ALT="Distance">\n'.format(avg_file_name))
 | 
						|
        handle.write('<IMG SRC="{}" ALT="Distance">\n'.format(dst_n_file_name))
 | 
						|
        handle.write('</p>\n')
 | 
						|
 | 
						|
        handle.write('</body>\n')
 | 
						|
        handle.write('<center>\n')
 | 
						|
        handle.write('</html>\n')
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    sys.exit(main())
 |