README.me update + minor changes
This commit is contained in:
		
							
								
								
									
										17
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,4 +1,5 @@ | |||||||
| # mpwo | # mpwo | ||||||
|  | **A simple self-hosted workout/activity tracker.**   | ||||||
|  |  | ||||||
| [](https://python.org) | [](https://python.org) | ||||||
| [](http://flask.pocoo.org/)  | [](http://flask.pocoo.org/)  | ||||||
| @@ -7,8 +8,22 @@ | |||||||
| [](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> | [](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> | ||||||
| [](https://travis-ci.org/SamR1/mpwo) | [](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)   | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| Notes:   | Notes:   | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								docs/images/mpwo_screenshot-01.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/images/mpwo_screenshot-01.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 406 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docs/images/mpwo_screenshot-02.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/images/mpwo_screenshot-02.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 436 KiB | 
| @@ -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''): | ||||||
|   | |||||||
| @@ -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() | ||||||
| }) | }) | ||||||
|   | |||||||
| @@ -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() | ||||||
| }) | }) | ||||||
|   | |||||||
| @@ -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; | ||||||
|   | |||||||
| @@ -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} | ||||||
|   | |||||||
| @@ -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} /> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user