README.me update + minor changes

This commit is contained in:
Sam 2018-05-30 17:48:58 +02:00
parent 7d410956b5
commit 1c206de590
9 changed files with 25 additions and 19 deletions

View File

@ -1,4 +1,5 @@
# mpwo # mpwo
**A simple self-hosted workout/activity tracker.**
[![Python Version](https://img.shields.io/badge/python-3.6-brightgreen.svg)](https://python.org) [![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/) [![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)<sup><sup>1</sup></sup> [![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)<sup><sup>1</sup></sup>
[![Build Status](https://travis-ci.org/SamR1/mpwo.svg?branch=master)](https://travis-ci.org/SamR1/mpwo) [![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: Notes:

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

View File

@ -262,6 +262,10 @@ def generate_map(map_filepath, map_data):
def get_map_hash(map_filepath): 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() md5 = hashlib.md5()
with open(map_filepath, 'rb') as f: with open(map_filepath, 'rb') as f:
for chunk in iter(lambda: f.read(128 * md5.block_size), b''): for chunk in iter(lambda: f.read(128 * md5.block_size), b''):

View File

@ -35,7 +35,6 @@ test('should throw an error if the user is not registered', async t => {
// assert user registration failed // assert user registration failed
await t await t
.expect(Selector('H1').withText('Login').exists).ok() .expect(Selector('H1').withText('Login').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Invalid credentials.').exists).ok() 'Invalid credentials.').exists).ok()
}) })
@ -62,7 +61,6 @@ test('should allow a user to login', async t => {
// assert user is redirected to '/' // assert user is redirected to '/'
await t await t
.expect(Selector('H1').withText('Login').exists).notOk() .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 // assert user registration failed
await t await t
.expect(Selector('H1').withText('Login').exists).ok() .expect(Selector('H1').withText('Login').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Invalid credentials.').exists).ok() 'Invalid credentials.').exists).ok()
}) })
@ -95,7 +92,6 @@ test('should throw an error if the password is invalid', async t => {
// assert user registration failed // assert user registration failed
await t await t
.expect(Selector('H1').withText('Login').exists).ok() .expect(Selector('H1').withText('Login').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Invalid credentials.').exists).ok() 'Invalid credentials.').exists).ok()
}) })

View File

@ -37,7 +37,6 @@ test('should allow a user to register', async t => {
// assert user is redirected to '/' // assert user is redirected to '/'
await t await t
.expect(Selector('H1').withText('Dashboard').exists).ok()
.expect(Selector('H1').withText('Register').exists).notOk() .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 // assert user registration failed
await t await t
.expect(Selector('H1').withText('Register').exists).ok() .expect(Selector('H1').withText('Register').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Sorry. That user already exists.').exists).ok() '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 // assert user registration failed
await t await t
.expect(Selector('H1').withText('Register').exists).ok() .expect(Selector('H1').withText('Register').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Sorry. That user already exists.').exists).ok() '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 // assert user registration failed
await t await t
.expect(Selector('H1').withText('Register').exists).ok() .expect(Selector('H1').withText('Register').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Username: 3 to 12 characters required.').exists).ok() '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 // assert user registration failed
await t await t
.expect(Selector('H1').withText('Register').exists).ok() .expect(Selector('H1').withText('Register').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Username: 3 to 12 characters required.').exists).ok() '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 // assert user registration failed
await t await t
.expect(Selector('H1').withText('Register').exists).ok() .expect(Selector('H1').withText('Register').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Valid email must be provided.').exists).ok() '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 // assert user registration failed
await t await t
.expect(Selector('H1').withText('Register').exists).ok() .expect(Selector('H1').withText('Register').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Password and password confirmation don\'t match.').exists).ok() '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 // assert user registration failed
await t await t
.expect(Selector('H1').withText('Register').exists).ok() .expect(Selector('H1').withText('Register').exists).ok()
.expect(Selector('H1').withText('Dashboard').exists).notOk()
.expect(Selector('code').withText( .expect(Selector('code').withText(
'Password: 8 characters required.').exists).ok() 'Password: 8 characters required.').exists).ok()
}) })

View File

@ -146,6 +146,10 @@ input, textarea {
margin-right: 5px; margin-right: 5px;
} }
.dashboard {
margin-top: 30px;
}
.sport-img { .sport-img {
max-width: 35px; max-width: 35px;
max-height: 35px; max-height: 35px;

View File

@ -28,10 +28,6 @@ export default function ActivityCard (props) {
</div> </div>
)} )}
<div className="col"> <div className="col">
<p>
<i className="fa fa-calendar" aria-hidden="true" />{' '}
Start at {activity.activity_date}
</p>
<p> <p>
<i className="fa fa-clock-o" aria-hidden="true" />{' '} <i className="fa fa-clock-o" aria-hidden="true" />{' '}
Duration: {activity.duration} Duration: {activity.duration}

View File

@ -33,12 +33,11 @@ class DashBoard extends React.Component {
<Helmet> <Helmet>
<title>mpwo - Dashboard</title> <title>mpwo - Dashboard</title>
</Helmet> </Helmet>
<h1 className="page-title">Dashboard</h1>
{message ? ( {message ? (
<code>{message}</code> <code>{message}</code>
) : ( ) : (
(activities.length > 0 && sports.length > 0) ? ( (activities.length > 0 && sports.length > 0) ? (
<div className="container"> <div className="container dashboard">
<div className="row"> <div className="row">
<div className="col-md-4"> <div className="col-md-4">
<Records records={records} sports={sports} /> <Records records={records} sports={sports} />