README.me update + minor changes
This commit is contained in:
		
							
								
								
									
										17
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								README.md
									
									
									
									
									
								
							@@ -1,4 +1,5 @@
 | 
			
		||||
# mpwo
 | 
			
		||||
**A simple self-hosted workout/activity tracker.**  
 | 
			
		||||
 | 
			
		||||
[](https://python.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://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:  
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											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):
 | 
			
		||||
    """
 | 
			
		||||
    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''):
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -146,6 +146,10 @@ input, textarea {
 | 
			
		||||
  margin-right: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dashboard {
 | 
			
		||||
  margin-top: 30px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sport-img {
 | 
			
		||||
  max-width: 35px;
 | 
			
		||||
  max-height: 35px;
 | 
			
		||||
 
 | 
			
		||||
@@ -28,10 +28,6 @@ export default function ActivityCard (props) {
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
          <div className="col">
 | 
			
		||||
            <p>
 | 
			
		||||
              <i className="fa fa-calendar" aria-hidden="true" />{' '}
 | 
			
		||||
              Start at {activity.activity_date}
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
              <i className="fa fa-clock-o" aria-hidden="true" />{' '}
 | 
			
		||||
              Duration: {activity.duration}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,12 +33,11 @@ class DashBoard extends React.Component {
 | 
			
		||||
        <Helmet>
 | 
			
		||||
          <title>mpwo - Dashboard</title>
 | 
			
		||||
        </Helmet>
 | 
			
		||||
        <h1 className="page-title">Dashboard</h1>
 | 
			
		||||
        {message ? (
 | 
			
		||||
          <code>{message}</code>
 | 
			
		||||
        ) : (
 | 
			
		||||
          (activities.length > 0 && sports.length > 0) ? (
 | 
			
		||||
            <div className="container">
 | 
			
		||||
            <div className="container dashboard">
 | 
			
		||||
              <div className="row">
 | 
			
		||||
                <div className="col-md-4">
 | 
			
		||||
                  <Records records={records} sports={sports} />
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user