#!/usr/bin/env python import argparse import datetime import glob 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 from gpx_parser import Tracks import pytz import calendar MONTH_LABELS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'] 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) plt.close('all') def plot_line_chart(values, ticklabels, title, xlabel, ylabel, filename, xtick_rotation=0): '''Plot a line chart. Args: values (dict): key: line name value (list): line values ticklabels (list): Names for the tick labels. title (str): Title of the chart. ''' 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) step = len(values[min(values.keys())]) / len(ticklabels) ax1.set_xticks(numpy.arange(1, len(ticklabels) * step, step)) ax1.set_xticklabels(ticklabels, rotation=xtick_rotation) plt.title(title) plt.xlabel(xlabel) plt.ylabel(ylabel) for key in values.keys(): if key == 'blind': plt.plot(values[key], linestyle='None') else: plt.plot(values[key], label=key) plt.legend() plt.savefig(filename) plt.close('all') def generate_date_list(start_date, end_date): r = (end_date + datetime.timedelta(days=1) - start_date).days return[start_date + datetime.timedelta(days=i) for i in range(r)] def get_distance_by_date(tracks, date): '''Return accumulated distances [km] by date. Args: tracks (list(Track)): List of tracks to be evaluated. date (datetine.datetime): Date to be evaluated. Return (float): Accumulated distance [km] for date. ''' distance = 0 for track in tracks: if track.start_time.year == date.year and \ track.start_time.month == date.month and \ track.start_time.day == date.day: distance += track.distance if distance > 0: distance /= 1000 return distance class Gpx2Html(object): def __init__(self, infolder, outfolder, logger): self.logger = logger self.infolder = infolder self.outfolder = os.path.abspath(outfolder) if not os.path.exists(self.outfolder): os.makedirs(self.outfolder) self.tracks = Tracks(logger) self.update() def update(self): infiles = glob.glob(os.path.join(self.infolder, '*.gpx')) for filename in infiles: self.tracks.add(filename) self.logger.info("Begin update of png's/html...") distances = list() avg_speeds = list() distances_dict = dict() for year in self.tracks.years(): distances.append(self.tracks.distances(year)) distances_dict[year] = self.tracks.distances(year) avg_speeds.append(self.tracks.avg_speeds(year)) plot_bar_chart(self.tracks.years(), MONTH_LABELS, distances, 'Distance', 'Month', 'km', os.path.join(self.outfolder, 'distance.png')) plot_bar_chart(self.tracks.years(), MONTH_LABELS, avg_speeds, 'Average Speed', 'Month', 'km/h', os.path.join(self.outfolder, 'avg_spd.png')) now = datetime.datetime.now() # Accumulated distance: accumulated_distance = dict() for year in self.tracks.years(): acc_year_dist = list() start_date = datetime.datetime(year, 1, 1) end_date = datetime.datetime(year, 12, 31) if year == now.year: end_date = now tracks = self.tracks.tracks(start_date, end_date) for date in generate_date_list(start_date, end_date): date_distance = get_distance_by_date(tracks, date) try: acc_year_dist.append(date_distance + acc_year_dist[-1]) except IndexError: acc_year_dist.append(date_distance) accumulated_distance[year] = acc_year_dist plot_line_chart(accumulated_distance, MONTH_LABELS, "Accumulated Distance", 'Month', 'km/year', os.path.join(self.outfolder, 'acc_dist.png')) # Expected year distance: start_date =datetime.datetime(now.year, 1, 1) end_date = now tracks = self.tracks.tracks(start_date, end_date) acc_year_dist = list() exp_year_dist = list() blind_line = list() for date in generate_date_list(start_date, end_date): date_distance = get_distance_by_date(tracks, date) blind_line.append(0) try: acc_year_dist.append(date_distance + acc_year_dist[-1]) except IndexError: acc_year_dist.append(date_distance) expexted_distance = acc_year_dist[-1] / date.timetuple().tm_yday * 365 exp_year_dist.append(expexted_distance) today = datetime.date.today() last_month_day = today.replace(day=calendar.monthrange(today.year, today.month)[1]) diff = last_month_day - today for i in range(diff.days): blind_line.append(0) xyz = dict() xyz['driven'] = acc_year_dist xyz['expected @ year end'] = exp_year_dist xyz['blind'] = blind_line plot_line_chart(xyz, MONTH_LABELS[:now.month], "Distance", 'Month', 'km/year', os.path.join(self.outfolder, 'exp_dist.png')) end_date = datetime.datetime.today() start_date = end_date - datetime.timedelta(days=13) # last 14 days last_n_tracks = self.tracks.tracks(start_date, end_date) last_n_distances = dict() last_n_durations = dict() dates = pd.date_range(start_date.date(), end_date.date()) for date in dates: for track in last_n_tracks: if date.date() == track.start_time.date(): get = 0 try: get = last_n_distances[date.date()] except KeyError: pass if get == 0: last_n_distances[date.date()] = track.distance / 1000 else: last_n_distances[date.date()] += track.distance / 1000 try: get = last_n_durations[date.date()] except KeyError: pass if get == 0: last_n_durations[date.date()] = track.duration.total_seconds() else: last_n_durations[date.date()] += track.duration.total_seconds() else: try: get = last_n_distances[date.date()] except KeyError: last_n_distances[date.date()] = 0 try: get = last_n_durations[date.date()] except KeyError: last_n_durations[date.date()] = 0 last_n_dist = list() last_n_dur = list() last_n_avg = list() last_n_dates = list() for date in dates: try: last_n_dist.append(last_n_distances[date.date()]) except KeyError: last_n_dist.append(0) try: last_n_dur.append(last_n_durations[date.date()]) except KeyError: last_n_dur.append(0) date_str = "{0:04d}-{1:02d}-{2:02d}".format(date.year, date.month, date.day) last_n_dates.append(date_str) try: if last_n_durations[date.date()] == 0: last_n_avg.append(0) else: last_n_avg.append(last_n_distances[date.date()] / (last_n_durations[date.date()] / 3600)) except KeyError: last_n_avg.append(0) plot_bar_chart(["Distance", "Average speed"], last_n_dates, [last_n_dist, last_n_avg], 'Last 14 days', 'Date', 'km, km/h', os.path.join(self.outfolder, 'last_14_days.png'), 90) self.__write_html_file() self.logger.info("End update of png's/html...") def __write_html_file(self): with open(os.path.join(self.outfolder, 'index.html'), 'w') as handle: handle.write('\n') handle.write('\n') handle.write('\n') handle.write('\n') handle.write(' Bicycle \n') handle.write('\n') handle.write('\n') handle.write('
\n') handle.write('

Bicycle

\n') handle.write('

\n') handle.write('\n') handle.write('\n') for year in self.tracks.years(): handle.write('\n'.format(year)) handle.write('\n') handle.write('\n') for year in self.tracks.years(): handle.write('\n'.format(round(sum(self.tracks.distances(year)), 1))) handle.write('\n') handle.write('
{}
{} km
\n') handle.write('

\n') handle.write('

\n') handle.write('Distance\n'.format('distance.png')) handle.write('Distance\n'.format('acc_dist.png')) handle.write('Distance\n'.format('exp_dist.png')) handle.write('Distance\n'.format('avg_spd.png')) handle.write('Distance\n'.format('last_14_days.png')) handle.write('

\n') handle.write('\n') handle.write('
\n') handle.write('\n')