Skip to content

PascalSun/sportpartner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SportPartner

MEAN stack project

Introduction

Author

  1. Group 29
  2. Team members: QIANG SUN(21804416) and XUE YU (20657462)

Brief

If you want to play basketball or football, you will need some people to team up;
If you want to start go to gym or learn to swim, you will need a guide;
If you want to run everyday and you cannot insist on, you will need a partner support each other;

So people can just register on our website about what they want and what they can do;
we will match them up as a list( also can be shown on the map) based on the information they provide.

Deploy

Heroku

  1. Create a Heroku account: https://www.heroku.com/
  2. Connect it with Github
  3. Deploy it with this repo (you can fork this one first)
  4. Wait for the outcome
  5. Demo: https://sportspartner.herokuapp.com/

Local Machine

  1. install nodejs and npm :https://nodejs.org/en/download/package-manager/
  2. clone the repo git clone https://github.com/PascalSun/sportpartner
  3. cd ./sportpartner
  4. npm install
  5. npm start
  6. open your web broswer: localhost://3000

Ubuntu setup

  1. sudo apt-get update
    install nvm curl https://raw.githubusercontent.com/creationix/nvm/v0.16.1/install.sh | sh
  2. install nodejs: nvm install 6.10.2
  3. install npm: sudo apt-get install npm
  4. install git: sudo apt-get install git
  5. git clone https://github.com/PascalSun/sportpartner
  6. cd ./sportpartner
  7. npm install
  8. npm start
  9. want to keep it running, use something like forever

Matches

Show Matches Demo

  1. login link: https://sportspartner.herokuapp.com/login
  2. login with {username:admin@admin.com,password:admin1}
  3. see match link: https://sportspartner.herokuapp.com/match?map=1

About the match algorithm

There are five variables here to match:

  • sex{male,female};
  • age{0~99};
  • sports{basketball,football...};
  • sport skill degree{0,10};
  • address

To match, the user need to setup profile and preference first.
Then it will show the partners who have exactly the sex and sports kind the user want as a list.
And the order of the partners will be decided by difference degree, which combined by location distance, age difference, and skill difference:
diff = skilldiff+agediff/5+(location distance)x200
The formula can be adjusted via further research.

Development Process

Architecture Used

Use MEAN stack obviously

  1. cloud mongodb from mLab as database
  2. Nodejs and Express framework
  3. Angular and REST api used to allow user change the way they search the matches, and give a result immediately and frequently
  4. Use MVC
    • Model for database interface
    • Controller to do the function work
    • Routes deal with router to controllers
  5. jade as our view engine

Problems and Solutions

  1. Login functions: use passport.js to fullfill the feature, quite hard when you start to do things
  2. How to deal with address:
    • find the exact address of the user input
    • how to calculate the address distance
    • geocode it into [lat,lng] and decode it to address Solutions:
    • Choose to use google map api to autocomplete the user's address from client side
      and check it whether legal or not from server side
    • use mongodb 2dsphere index and $near which can calculate and sort by distance between address (google distance matrix can also be a option, but will not as fast as this one)
    • to use the mongodb distance feature, address need to be stored in format [lng,lat].
      so we use google map api again to geocode address and decode address
  3. Show matches via google map We use google map maker to deal with the diff json generated by server side
  4. We have the feature to leave a message, and we want to send an email to the receiver to notice him about who and what message are left to him.
    So we use nodemailer library together with mailgun(mail server provider) to send email
  5. Above are the main problems we faced and choices we have made, some other bugs and issues we have made are solved during the development process

Test

The whole development is basically Test Driven Development. However, as totoally new learners to mean stack, we don't have the experience about how to write a test case before we even haven't known exactly what the features will like. So during the process, every time we finish a function, we just test it by hand, to make sure every feature and function works, and every branch is covered.
And after each feature finished, we will test the whole feature.

And in the end, we do the unit and user test.

  • Unit Test: Use path coverage Strategy, and try to cover 100% branches and statement

    • In fact, when I start to do the test, I start to realize that it is not a good choice which we just use the REST API structure with the match list function.
      It will be much easier to test, if we use that.
    • Also, the dependency between different files makes the test much harder.
    • At the same time, when we find a bug, It will take long time to fix and the process is quite struggling
    • to test, just run: npm test, and the test result will show on coverage folder
  • User Test: Black Box Test, which without knowing about the code backend.
    I asked my girl friend and my friends to test all the features, and gave some feedback They indeed gave some advice:

    • the user should have a username, not the same as the email
    • the user should have a head portrait, for the partners to know each other
    • when the net speed is very low, sometime show error
    • can not be used on a small screen phone, because can not find login button. (which have been fixed)
    • why not just show the address, but need to click to see the address
    • in firefox49.9, can not click the edit preference button

    Also we use selenium IDE tool to generate some script files with different scenarios, to make sure every time we modify something, the whole system works properly.

Reference

Development Logs

Functions

  • Basic Views
  • Set up mongodb
    • local machine
    • mLab (add in the end, with some test data)
  • Register Functions
    • Register/Login with passport.js
    • Validate Password
      • client side
      • server side
        • the same
        • not empty
    • Validate email format
      • not empty
      • already token
    • Change Password
      • view, client side check
      • server side change the password
    • After register, redirect to login page and keep the username and password info
    • error handle
      • login: how to customize the Unauthorized white page
      • register
  • Edit profile
    • view,models,controller,route framework
    • autocomplete address via google api
    • transform address to coordinate, via client side
    • validate it and store info to database
    • Decode coordinate to address from server side
    • User stories
      • view profile
      • edit profile
      • when the user login the first time, then redirect to edit page directly
  • Edit prefernece
    • models,view,controller,router
    • edit and view preference
  • Communication:
    • view basic profile of other user
    • models stored visitor and hosts
    • list who view you and who you have viewed
    • Leave Message
    • See who are contacting with you
    • See who you have contacted
    • Add email notification: Use mailgun altogether with nodemailer to send email to receiver about content and sender when some leave message
  • Show matches
    • Location Based Match
      • Match all people, around, and show on the map
      • Match people meet prefer, show as a list, not included the one not meet requirements
      • On the map, the people meet the preference will be labeled with order, and those who not meet requirements will be labeled as O
      • use rest API, and Angular, not need to change prefer data, show the results
    • Graphic Match Visualization

Pages

  • Instructions about how to launching on a cloud platform
  • user data collection
  • display match
  • explain match algorithm
  • describe what architecture used, choices made, difficulities faced
  • About test / validation Strategy and results
  • Introduction and references

Todo Next

  • the number value problem
  • the match problem
  • add some data in the mlab
  • List all the info as about page of the web

Some Notes

  • basic login function vs customize error login

    // Post to login
    // router.post('/login', passport.authenticate('local', { failureFlash: 'Invalid username or password.' }), function(req,res) {
    //   console.log(res);
    //   res.render('index',{username:req.body.username,user:req.user});
    // });
    
    // Post to login: need to deal with error information
    router.post('/login', function(req, res, next) {
      passport.authenticate('local', function(err, user, info) {
        if (err) { console.log('1');res.render('login',{message:"Username or Password Wrong"});  }
        if (!user) { console.log('2');res.render('login',{message:info.message}); }
        req.logIn(user, function(err) {
            if (err) {
            res.render('login',{message:'Username and Password not match'});
            }
            res.render('index',{username:req.body.username,user:user});
        });
      })(req, res,next);
    
    });
    
    • the logIn function is very important to keep alive
  • Sometimes, we need to define the passport Strategy by ourself

    // passport.use(new LocalStrategy(
    //   function(username, password, done) {
    //     /* see done being invoked with different paramters
    //        according to different situations */
    //     Account.findOne({ username: username }, function (err, user) {
    //       if (err) { console.log('wrong'); return done(err); }
    //       if (!user) { console.log('now user'); return done(null, false); }
    //       // if (!user.verifyPassword(password)) {  console.log('now xx'); return done(null, false); }
    //       return done(null, user);
    //     });
    //   }
    // ));
    
  • Javascript function won't wait, they are not the same pace

function geocodeAddress() {

  var geocoder = new google.maps.Geocoder();
  var address = document.getElementById('address').value;
  console.log(address);

  geocoder.geocode({'address': address}, function(results, status) {
    console.log(status)

    if (status === 'OK') {
      console.log(results[0].geometry.location.lat());
      console.log(results[0].geometry.location.lng());
      // document.getElementById('addresslat').setAttribute('value',results[0].geometry.location.lat());
      // document.getElementById('addresslng').value = results[0].geometry.location.lng();

      document.getElementById('addresslat').value = results[0].geometry.location.lat();
      document.getElementById('addresslng').value = results[0].geometry.location.lng();
      console.log(document.profile.addresslat.value);
      if(document.profile.addresslat.value!==''){
        document.profile.submit();
        console.log('here');
        return true;
      }
      else{
        return false;
      }
    } else {
      alert('Please ensure your address valid!');
      return false;
    }
  });
  alert('Are you sure to submit?')
  return false; ///  there must be a return false here
  • The mongoose query callback is really disaster, need to find some solutions.