Skip to content

Pitch

AthLink bridges the gap between your passion for sports and the challenge of finding like-minded athletes to play with in person. In existing apps, there are ways to connect and meet new people, but when it comes to finding other athletes to play with, it is hard to know if you will be compatible playing with another athlete without knowing their skill level and their goals for practice. This app will keep track of all of that information for you, and only suggest athletes that are compatible with what you are looking for in an athlete to play with. In addition, you can track your progress by inputting your workouts and setting weekly goals. Add pictures and videos to your profile to show other users your skills and highlights! Join us in revolutionizing the way athletes connect, compete, and build communities.

Functional Design

User

Purpose:

User authentication-- allows user to have an individualized identity

Principle:

After a user registers with a username, password, they can identify as that user by inputting the same username and password.

States:

Registered: set User
username,password: registered => one String

Actions:

  1. register(username:String, password:String,basic_data:set out user: User)
         user not in registered
         registered+=user
         u.username:=username
         u.password:=password

  2. authenticate(username:String,password:String, out u:User)
         user in registered
         user.username=username and user.password=password

Profile[Post,User,SkillScore]

Purpose:

holds all of the user’s stats, pictures, and background information

Principle:

After a user registers, they can add their estimated skill score, goals for playing, and background info (gender, sport, etc.) to their profile

States:

Current_posts: Profile=>set Post
profile_info: Profile=>set(Dict('gender':one String,sports: set String, skill: SkillScore, location: String,goal:String))

Actions:

updateInfo(profile_info,current_posts): update the user’s profile information or add a stat (with a picture) getInfo(profile_info): get the User’s information deleteProfile(current_posts): temporarily hide all posts on the profile from all other users revealProfile(current_posts): if profile was deleted, user can restore it

Alike[User,SkillScore]

Purpose:

shows you the users that match your preference

Principle:

when user inputs their preferences, this filters out the athletes that don’t match those references, so the user only sees athletes that match those preferences

States:

preferences: Alike => set(Dict('location_range': Number, 'gender':one String, 'goal':one String, 'sport':set String,'skill_range': (SkillScore,SkillScore)))
recommended_users: Alike => set User

Actions:

  1. updatePreferences(preferences): user can update their preferences to re-filter for other athletes
  2. filter(preferences,recommended_users): uses your preferences to filter all users and show only valid users (users that meet all preferences)
  3. showUsers(recommended_users): shows you all the users in the valid users set (all users that meet your preferences)

Post[Profile]

Purpose:

Share a stat, picture, or video to your profile/feed

Principle:

When a user finishes a match, they can post their stats and a picture of them playing

States:

Stat: Post => one Stat
Picture: Post => one Picture
comment: Post => set Comment
postDate: Post => one date

Actions:

  1. editPost(stat,picture,comment): Edit any information on the post
  2. deletePost(stat,picture): Delete the post; results in post being deleted on your profile and other people’s feeds
  3. addPost(Stat,Picture,postDate): Add a post; results in post being added to your profile and other people’s feeds (if they are in the Group of users that have visibility access)

Visibility[Post,User]

Purpose:

Make post visible to selected group of users

Principle:

When a user posts content, they can choose to post it to the public (viewable by any user), private (only author can view), or friend(viewable by all friends of the author) group

States:

status: Visibility=> {Private | Friends | Public}
Friends: Visibility => set User post: Visibility => one Post userToAdd: Visibility => one User userToRemove:Visibility => one User

Actions:

addFriend(Friends,userToAdd): add Friend to friend group removeFriend(Friends,userToRemove): remove Friend from friend group setAccess(post,friends,status) :set which users can see a given post

Connect[Profile]:

Purpose:

Enables athletes to talk with each other so they can set up a time to play.

Principle:

When a user wants to play or connect with another athlete, they send a request to play. If the request is accepted by the athlete, they can keep talking and are now friends.

States:

from,to: Connect => one User
user1Group: Connect => Profile.profile_info.Friends user2Group: Connect => Profile.profile_info.Friends

Actions:

sendRequest(from,to) //sends a request from one user to another to continue talking acceptRequest(user1Group,user2Group) //once accepted, both users will be added to the other user’s "friends" group denyRequest(from,to) //prevents users from further talking with each other

SkillScore[Post]:

Purpose:

categorize users based on their results

Principle:

If you beat a user with a higher skill score, this is considered positive for you but negative for them, so your skill score increases and theirs decreases.

States:

currentScore: SkillScore=> one Number
opponentScore:SkillScore=> one Number Score: SkillScore => (Post.Stat, otherScore) :Post.Stat is the match score, otherScore is the opponents skillScore
valid_scores: SkillScore => set Scores

Actions:

addScore(currentScore,scores,opponentScore,valid_scores): for every match that is played and for every piece of info that is inputted regarding user’s score, the user’s score will be re-evaluated and updated removeScore(currentScore,scores,valid_scores): removes the Stat

ExpiringResource[Post]

NOTE: got this idea from the tutorial (so pretty much reusing a concept)

Purpose:

set a "life-span" for scores

Operational Principle:

After user inputs a Stat (by posting it), that stat is given a life span of one year. After the life span is over, the stat will not be considered in the overall calculation of the users SkillScore.

States:

active: set Post.Stat
expiry: Post -> one Date

Actions:

allocate (r: Post, t: int) r.Stat not in active active += r r.expiry := // t secs after now deallocate (r: Post.Stat) r.Stat in active active -= r r.expiry := none renew (r: Post.Stat, t: int) r.Stat in active r.expiry := // t secs after now system expire (out r: Post.Stat) r.Stat in active r.expiry is before now active -= r r.expiry := none

Concept Syncs

app Athlink:  
    include User  
    include Profile  
    include Post
    include Visibility
    include SkillScore
    include ExpiringResource
    include Alike
    include Connect

    sync register(username:String,password:String,out user:User,profile_info)
        When User.register(username,password)
            Profile.updateInfo(profile_info,current_posts)

    sync accept(user1Group:set User, user2Group:set User,from,to:User)
        When Connect.accept(from,to,user1Group,user2Group)
            Visibility.addFriends(user1Group,to) && Visibility.addFriends(user2Group,from) //add users to each other's to Friends group 

    sync deleteContent(item,current_posts:set Post)
        When Post.deletePost(item)
	    Profile.updateInfo(profile_info,current_posts) //delete post from current_posts

    sync addContent(Stat,current_posts:set Post,valid_scores:set Scores,current_score: SkillScore)
        When Post.addPost(Stat,Picture)
            Profile.updateInfo(profile_info,current_posts)
	    Visibility.setAccess(author,friends,status)
	    SkillScore.addScore(currentScore,scores,opponentScore,valid_scores) //update SkillScore

    sync expire(post:one Post,current_scores,score)
        when ExpiringResource.expire(Post)
            SkillScore.removeScore(current_scores,score,valid_scores) 

    sync removeScore(score:SkillScore=> (Post.stat,Number),currentScore:SkillScore,valid_scores set Scores,profile_info,current_posts: set Post)
        when SkillScore.removeScore(currentScore,score,valid_scores)
            Profile.update_info(profile_info,current_posts) //update SkillScore on Profile

    sync preferences(profile_info,current_posts:set Post,preferences,recommended_users:set User)
        when Profile.update_info(profile_info,current_posts) //ex:if you change location, the set of users that we are filtering for is now different
            Alike.filter(preferences,recommended_users)

Dependency Diagram

Wireframes

Click on the link below to view the wireframes:

Figma Wireframes

Design Tradeoffs

  1. An Expiring SkillScore

(b). I had to decide whether a user's SkillScore should be based on all of their uploaded stats, no matter how long ago they were uploaded, or if I should set a time limit on a stat that is uploaded.
(c). I decided that a user's SkillScore should only be based on stats that were uploaded within the last year. This is because a user may have been good years ago, but if they stopped playing for a while, they are probably not still at that skill level. Likewise, a user may have improved a bunch, and their old stats are no longer reflective of the player they now are.

  1. Allowing Videos

(b). I had to decide whether a user should be able to upload videos when they post their stats, or whether it should just be pictures.
(c). While videos are a fun way to showcase highlights and good plays/points, I decided not to allow videos, because it could potentially misportray a user's skill level. The SkillScore is designed to accurately calculate the skill level of a player, but a player may look much better than they really are if they only upload videos of their highlights. The benefits of videos are they are a fun way for a user to show their skills, but in my opinion, the cons of misportraying skill level far outweighs this benefit.

  1. Info Portrayed

(b). I had to decide whether to portray all of the user's profile information (gender,SkillScore,sports,location,Goal) on their profile. (c). Since the profile is accessable by any user (even if not all the posts on the profile are accessable), I decided it wouldn't be safe for the location of the user to be displayed on the user's profile. I decided it was important to keep the rest of the information, because that information is important in considering how compatible two athletes may be.