Compare commits

..

14 Commits

Author SHA1 Message Date
Thomas Klaehn
e84ab19187 gpx_parser: Adapt dates evaluation to new pandas version 2022-02-16 09:37:43 +01:00
Thomas Klaehn
7f7c65e3fa VSCode: Add development container
Signed-off-by: Thomas Klaehn <thomas.klaehn@perinet.io>
2022-02-16 09:36:43 +01:00
Thomas Klaehn
7a7d6dedf3 Add exception catching 2020-10-12 17:53:16 +02:00
Thomas Klaehn
320bab0188 Add sub folders as input sources 2020-08-11 11:58:51 +02:00
Thomas Klaehn
5d88a026cd Fix: Corrected number of last 14 days 2020-01-14 13:14:31 +01:00
Thomas Klaehn
9761814f6f Fix: remove install_requires because it's unclear 2020-01-08 12:46:19 +01:00
Thomas Klaehn
d65eeaf0fd Fix expected distance chart month end 2020-01-08 11:31:10 +01:00
Thomas Klaehn
ae4cfa3a6d VScode: add environment for debugging 2019-11-22 11:39:20 +01:00
Thomas Klaehn
8a0318b656 Fix expected distance chart labelling. 2019-08-15 13:54:01 +02:00
Thomas Klaehn
e1c68a55b3 Add expected year distance chart. 2019-08-15 13:00:44 +02:00
Thomas Klaehn
2cd36b5e52 Refactor setup. 2019-08-14 12:48:37 +02:00
Thomas Klaehn
ddbb5f159f Refactor accumulated distance creation. 2019-08-14 11:37:57 +02:00
Thomas Klaehn
0cf59eb7f1 Add Debug launch config for vs code. 2019-08-08 14:26:36 +02:00
Thomas Klaehn
38f496dd8d Accumulated Distance: Change from monthly to daily 2019-08-08 14:25:19 +02:00
6 changed files with 173 additions and 55 deletions

View File

@@ -0,0 +1,30 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.163.1/containers/cpp
{
"name": "bicycle-statistics",
"image": "bic-dev:latest",
// --network=host is necessary, since default docker network does not transport mdns|dns-sd, since they route through docker0
// "runArgs": [ "--network=host", "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.defaultProfile.linux.shell":"/bin/sh"
},
// Add the IDs of extensions you want installed when the container is created.
// "extensions": [
// "ms-vscode.cpptools"
// ],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "gcc -v",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
// "remoteUser": "vscode",
// "postStartCommand" : "sudo rm /var/run/avahi-daemon/pid; sudo avahi-daemon --no-drop-root --no-chroot -D || true",
}

19
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "bicycle-statistics",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/bicycle_statistics/__main__.py",
"console": "integratedTerminal",
"args": [
"/home/tkl/Nextcloud/Bicycle",
"${workspaceFolder}"
],
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
}
]
}

View File

@@ -13,8 +13,8 @@ LOG_FORMAT = "%(asctime)s %(levelname)s %(message)s"
UPDATE_INTERVAL = 60
logging.basicConfig(format=LOG_FORMAT, level=log_level, filename=LOG_FILE)
# logging.basicConfig(format=LOG_FORMAT, level=log_level)
# logging.basicConfig(format=LOG_FORMAT, level=log_level, filename=LOG_FILE)
logging.basicConfig(format=LOG_FORMAT, level=log_level)
log = logging.getLogger('bicycle-statistics')
def parse_args():

View File

@@ -13,6 +13,9 @@ 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']
@@ -54,9 +57,8 @@ def plot_line_chart(values, ticklabels, title, xlabel, ylabel, filename, xtick_r
Args:
values (dict): key: line name
value (list): line values
ticklabels (list): Names for the tick labels (must be same length as value list).
ticklabels (list): Names for the tick labels.
title (str): Title of the chart.
'''
fig = plt.figure()
ax1 = fig.add_subplot(111)
@@ -66,27 +68,49 @@ def plot_line_chart(values, ticklabels, title, xlabel, ylabel, filename, xtick_r
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 len(ticklabels) == len(values[key]):
plt.plot(ticklabels, values[key], label=key)
if key == 'blind':
plt.plot(values[key], linestyle='None')
else:
short_ticklabels = list()
for i in range(0, len(values[key])):
short_ticklabels.append(ticklabels[i])
plt.plot(short_ticklabels, values[key], label=key)
x_base = numpy.arange(len(ticklabels))
plt.xticks(x_base, ticklabels, rotation=xtick_rotation)
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
@@ -100,9 +124,10 @@ class Gpx2Html(object):
self.update()
def update(self):
infiles = glob.glob(os.path.join(self.infolder, '*.gpx'))
for filename in infiles:
self.tracks.add(filename)
for root, _, files in os.walk(self.infolder):
for name in files:
if name.endswith(".gpx"):
self.tracks.add(os.path.join(root, name))
self.logger.info("Begin update of png's/html...")
distances = list()
@@ -121,29 +146,61 @@ class Gpx2Html(object):
'Average Speed', 'Month', 'km/h',
os.path.join(self.outfolder, 'avg_spd.png'))
now = datetime.datetime.now()
# Accumulated distance:
accumulated_distances = dict()
for year in distances_dict.keys():
accumulated_distance = list()
accumulated_distance.append(0)
for i in range(0, len(distances_dict[year])):
accumulated_distance.append(accumulated_distance[i] + distances_dict[year][i])
accumulated_distances[year] = 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
current_year = datetime.datetime.today().year
current_month = datetime.datetime.today().month
current_year_distance = list()
self.logger.info("Current month: %s", current_month)
for i in range(0, current_month + 1):
current_year_distance.append(accumulated_distances[current_year][i])
accumulated_distances[current_year] = current_year_distance
plot_line_chart(accumulated_distances, [""] + MONTH_LABELS,
"accumulated distance", 'Month', 'km',
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=14)
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()
@@ -246,6 +303,7 @@ class Gpx2Html(object):
handle.write('<p>\n')
handle.write('<IMG SRC="{}" ALT="Distance">\n'.format('distance.png'))
handle.write('<IMG SRC="{}" ALT="Distance">\n'.format('acc_dist.png'))
handle.write('<IMG SRC="{}" ALT="Distance">\n'.format('exp_dist.png'))
handle.write('<IMG SRC="{}" ALT="Distance">\n'.format('avg_spd.png'))
handle.write('<IMG SRC="{}" ALT="Distance">\n'.format('last_14_days.png'))
handle.write('</p>\n')

View File

@@ -61,6 +61,7 @@ class Tracks(object):
if filename not in self.__files:
self.logger.info("Adding file %s.", filename)
with open(filename, 'r') as f:
try:
self.__files.append(filename)
gpx = gpxpy.parse(f)
for raw in gpx.tracks:
@@ -81,6 +82,9 @@ class Tracks(object):
self.__avg_speed[trk_year] = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0, 12: 0}
self.__avg_speed[trk_year][trk_month] = self.__distance[trk_year][trk_month] / (self.__duration[trk_year][trk_month] / 3600)
self.logger.info("Adding done.")
except Exception as exception:
# TODO: Add logging mechanism.
pass
def years(self):
ret = None
@@ -110,6 +114,6 @@ class Tracks(object):
tracks = list()
dates = pd.date_range(start_date.date(), end_date.date())
for track in self.__tracks:
if track.start_time.date() in dates:
if track.start_time.date() in dates.date:
tracks.append(track)
return tracks

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env python
from distutils.core import setup
import sys
import os
import shutil
import stat
from setuptools import setup
NAME = 'bicycle-statistics'
VERSION = '0.2.0'
@@ -12,11 +12,18 @@ AUTHOR = 'Thomas Klaehn'
EMAIL = 'tkl@blackfinn.de'
PACKAGES = ['bicycle_statistics', 'gpx_parser', 'gpx2html']
SCRIPTS = ['example-gpx-parser', 'bicycle-stat']
# REQUIRES = ['geopy', 'gpxpy', 'matplotlib', 'numpy', 'pandas']
DAEMON_START_SCRIPT = os.path.join("/lib/systemd/system", "bicycle-stat.service")
SERVICEDIR = "/lib/systemd/system"
DAEMON_START_SCRIPT = os.path.join(SERVICEDIR, "bicycle-stat.service")
if sys.argv[1] == 'install':
os.makedirs(SERVICEDIR)
shutil.copyfile("bicycle-stat.service", DAEMON_START_SCRIPT)
os.chmod(DAEMON_START_SCRIPT, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
setup(name=NAME, version=VERSION, author=AUTHOR, author_email=EMAIL, packages=PACKAGES, scripts=SCRIPTS)
# setup(name=NAME, version=VERSION, author=AUTHOR, author_email=EMAIL,
# packages=PACKAGES, scripts=SCRIPTS, install_requires=REQUIRES)
setup(name=NAME, version=VERSION, author=AUTHOR, author_email=EMAIL,
packages=PACKAGES, scripts=SCRIPTS)