diff --git a/README.md b/README.md index f5af3a98..3ccb0320 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # mpwo +**A simple self-hosted workout/activity tracker.** [![Python Version](https://img.shields.io/badge/python-3.6-brightgreen.svg)](https://python.org) [![Flask Version](https://img.shields.io/badge/flask-1.0-brightgreen.svg)](http://flask.pocoo.org/) @@ -7,8 +8,22 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/45d64b31e37e4890a239b8298e66a011)](https://www.codacy.com/app/SamR1/mpwo?utm_source=github.com&utm_medium=referral&utm_content=SamR1/mpwo&utm_campaign=Badge_Coverage)1 [![Build Status](https://travis-ci.org/SamR1/mpwo.svg?branch=master)](https://travis-ci.org/SamR1/mpwo) -Self hosted workout/activity tracker written with Flask and React (_work in progress_). +--- +This application allows you to track your sports activity from gpx files and keep your data on your own server. +Several mobile apps can store workout data locally and export it into a gpx file. +Examples (for Android): +* [Runner Up](https://github.com/jonasoreland/runnerup) (GPL v3) +* [ForRunners](https://github.com/brvier/ForRunners) (GPL v3) +* [AlpineQuest](https://www.alpinequest.net/) (Proprietary) +... + +**Still under development (not ready for production).** +(see [Wiki](https://github.com/SamR1/mpwo/wiki) for more informations) + +![mpwo screenshot](docs/images/mpwo_screenshot-01.png) + +![mpwo screenshot](docs/images/mpwo_screenshot-02.png) --- Notes: diff --git a/docs/images/mpwo_screenshot-01.png b/docs/images/mpwo_screenshot-01.png new file mode 100644 index 00000000..0edc6230 Binary files /dev/null and b/docs/images/mpwo_screenshot-01.png differ diff --git a/docs/images/mpwo_screenshot-02.png b/docs/images/mpwo_screenshot-02.png new file mode 100644 index 00000000..fd62f556 Binary files /dev/null and b/docs/images/mpwo_screenshot-02.png differ diff --git a/mpwo_api/mpwo_api/activities/utils.py b/mpwo_api/mpwo_api/activities/utils.py index 18b22eae..e44fdd1a 100644 --- a/mpwo_api/mpwo_api/activities/utils.py +++ b/mpwo_api/mpwo_api/activities/utils.py @@ -262,6 +262,10 @@ def generate_map(map_filepath, map_data): def get_map_hash(map_filepath): + """ + md5 hash is used as id instead of activity id, to retrieve map image + (maps are sensitive data) + """ md5 = hashlib.md5() with open(map_filepath, 'rb') as f: for chunk in iter(lambda: f.read(128 * md5.block_size), b''): diff --git a/mpwo_client/e2e/login.test.js b/mpwo_client/e2e/login.test.js index fbccca61..065b2d2b 100644 --- a/mpwo_client/e2e/login.test.js +++ b/mpwo_client/e2e/login.test.js @@ -35,7 +35,6 @@ test('should throw an error if the user is not registered', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Login').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Invalid credentials.').exists).ok() }) @@ -62,7 +61,6 @@ test('should allow a user to login', async t => { // assert user is redirected to '/' await t .expect(Selector('H1').withText('Login').exists).notOk() - .expect(Selector('H1').withText('Dashboard').exists).ok() }) @@ -78,7 +76,6 @@ test('should throw an error if the email is invalid', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Login').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Invalid credentials.').exists).ok() }) @@ -95,7 +92,6 @@ test('should throw an error if the password is invalid', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Login').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Invalid credentials.').exists).ok() }) diff --git a/mpwo_client/e2e/register.test.js b/mpwo_client/e2e/register.test.js index 03652fd5..306211c3 100644 --- a/mpwo_client/e2e/register.test.js +++ b/mpwo_client/e2e/register.test.js @@ -37,7 +37,6 @@ test('should allow a user to register', async t => { // assert user is redirected to '/' await t - .expect(Selector('H1').withText('Dashboard').exists).ok() .expect(Selector('H1').withText('Register').exists).notOk() }) @@ -55,7 +54,6 @@ test('should throw an error if the username is taken', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Register').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Sorry. That user already exists.').exists).ok() }) @@ -76,7 +74,6 @@ test('should throw an error if the email is taken', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Register').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Sorry. That user already exists.').exists).ok() }) @@ -97,7 +94,6 @@ test('should throw an error if the username is too short', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Register').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Username: 3 to 12 characters required.').exists).ok() }) @@ -118,7 +114,6 @@ test('should throw an error if the user is too long', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Register').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Username: 3 to 12 characters required.').exists).ok() }) @@ -139,7 +134,6 @@ test('should throw an error if the email is invalid', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Register').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Valid email must be provided.').exists).ok() }) @@ -158,7 +152,6 @@ test('should throw an error if passwords don\'t match', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Register').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Password and password confirmation don\'t match.').exists).ok() }) @@ -179,7 +172,6 @@ test('should throw an error if the password is too short', async t => { // assert user registration failed await t .expect(Selector('H1').withText('Register').exists).ok() - .expect(Selector('H1').withText('Dashboard').exists).notOk() .expect(Selector('code').withText( 'Password: 8 characters required.').exists).ok() }) diff --git a/mpwo_client/src/components/App.css b/mpwo_client/src/components/App.css index 7015e898..66f812eb 100644 --- a/mpwo_client/src/components/App.css +++ b/mpwo_client/src/components/App.css @@ -146,6 +146,10 @@ input, textarea { margin-right: 5px; } +.dashboard { + margin-top: 30px; +} + .sport-img { max-width: 35px; max-height: 35px; diff --git a/mpwo_client/src/components/Dashboard/ActivityCard.jsx b/mpwo_client/src/components/Dashboard/ActivityCard.jsx index 2e3ab17a..90ba94be 100644 --- a/mpwo_client/src/components/Dashboard/ActivityCard.jsx +++ b/mpwo_client/src/components/Dashboard/ActivityCard.jsx @@ -28,10 +28,6 @@ export default function ActivityCard (props) { )}
- {' '} - Start at {activity.activity_date} -
{' '}
Duration: {activity.duration}
diff --git a/mpwo_client/src/components/Dashboard/index.jsx b/mpwo_client/src/components/Dashboard/index.jsx
index 2be49f7f..ebcac3d0 100644
--- a/mpwo_client/src/components/Dashboard/index.jsx
+++ b/mpwo_client/src/components/Dashboard/index.jsx
@@ -33,12 +33,11 @@ class DashBoard extends React.Component {
{message}
) : (
(activities.length > 0 && sports.length > 0) ? (
-