Assignment 3: Convergent Design
Assignment Instructions
These are the 6.1040 assignment instructions for this assignment. It goes into greater depth about the expectations, requirements, and deliverables for this assignment. It also includes a rubric detailing how this assignment will be graded.
Community Carpool
A communal carpool coordination social network connecting users that are traveling to the same activity, using location data to help plan the most efficient and convenient routes.
Application Overview
Audience
Community Carpool is designed for community members who participate in the same organized activities. Geared specifically toward parents of children who participate in after-school (or extracurricular) activities, Community Carpool simplifies the carpool coordination/organization process, scheduling, and route-planning.
Parents of children who participate in the same after-school activities would use this application to organize carpools to that activity.
Example Activities:
- Sports Teams
- Theater Lessons
- Cultural/Religious Classes
- After-School Programs
Pitch
Community Carpool is intended to help parents coordinate and set up carpools for their children that participate in the same after-school activities.
As described in greater detail in the Assignment 2: Divergent Design webpage, Community Carpool has many features and functional elements that enable users to use the application in the most intuitive and beneficial way. The key functionality includes the ability to set up Activity
and Carpool
groups of Users
for members/participants of the same activity. This enables Users
to more easily coordinate carpool groups using a User
-defined "Nearest Neighbor" algorithm, perhaps setting up carpools based on how great of a detour a certain User
is from another User's
route, or how many additional miles must be driven to pick up this person (as well as other methods of computing this value). Overall, the goal is to simplify, organize, and better coordinate the Carpool scheduling process for parents.
Community Carpool can be used to set up, coordinate, and monitor frequent/periodic carpools, serving as a "hub" to organize, plan, and route recurring carpools to the same destination. In addition to taking users' home location and destination data into account, communication between carpoolers can be facilitated using this application.
Dependency Diagram
Concepts
Location
Purpose
A physical address/location associated with another entity (User
, Activity
, etc.)
Principle
Locations
represent physical addresses on Earth that are associated with a User
or an Activity
. They must be valid addresses.
State
locations: set Location
address: string
homeAddresses: set Users
destinations: set Activity
locations: set Location
address: string
homeAddresses: set Users
destinations: set Activity
Actions
verifyRealAddress(address: str) -> Location
## Check public maps APIs to see whether address exists
assert address is real
setLocation(address: str) -> Location
location = verifyRealAddress(address: str)
location.address = address
locations += location
verifyRealAddress(address: str) -> Location
## Check public maps APIs to see whether address exists
assert address is real
setLocation(address: str) -> Location
location = verifyRealAddress(address: str)
location.address = address
locations += location
Map [Location]
Purpose
Display the Locations
on a map.
Principle
Maps
would display the Locations
of Users
, Activities
, or other entities that have a Location
associated with them on a visual graphic, making it easy for Users
to visualize where things are geographically.
State
locations: set Location
map: set Entities
locations: set Location
map: set Entities
Actions
displayLocationOnMap(map: Map, location: Location) -> Map
map += location
displayUserOnMap(map: Map, user: User) -> Map
assert user.address != None
map += User.address
removeFromMap(map: Map, location: Location)
assert location in map
map -= location
displayLocationOnMap(map: Map, location: Location) -> Map
map += location
displayUserOnMap(map: Map, user: User) -> Map
assert user.address != None
map += User.address
removeFromMap(map: Map, location: Location)
assert location in map
map -= location
User
Purpose
Allow users to create a public-facing user profile so that they can use tha app.
Principle
Users are represented by user profiles, which enables users to register for Activity Groups
, set their addresses, and serves as a one-stop-shop for information about that user.
State
registeredUsers: set User
username: string, password: string -> register(u: string, p: string) -> User
name: string
address: string -> setLocation(address) -> Location
registeredUsers: set User
username: string, password: string -> register(u: string, p: string) -> User
name: string
address: string -> setLocation(address) -> Location
Actions
register(username: str, password: str) -> user: User
assert username not in registeredUsers
user = User()
user.username = username
user.password = password
user.name = None
user.address = None
registeredUsers[username] = user
authenticate(username: str, password: str) -> user: User
assert username in registeredUsers
registeredUsers[username].password == password
addName(user: User, name: str)
user.name = name
renameUser(user: User, name: str)
user.name = name
addHomeAddress(user: User, address: Location) -> Location
location: Location = setLocation(address)
user.address = location
location.homeAddresses += user
updateHomeAddress(user: User, location: Location)
user.address = location
deleteHomeAddress(user: User)
user.address = None
register(username: str, password: str) -> user: User
assert username not in registeredUsers
user = User()
user.username = username
user.password = password
user.name = None
user.address = None
registeredUsers[username] = user
authenticate(username: str, password: str) -> user: User
assert username in registeredUsers
registeredUsers[username].password == password
addName(user: User, name: str)
user.name = name
renameUser(user: User, name: str)
user.name = name
addHomeAddress(user: User, address: Location) -> Location
location: Location = setLocation(address)
user.address = location
location.homeAddresses += user
updateHomeAddress(user: User, location: Location)
user.address = location
deleteHomeAddress(user: User)
user.address = None
Session [User]
Purpose
Authenticate a User
for a period of time and set them as the active User
Principle
When a Session
starts, a user's credentials and data is saved and they are "logged in" for the duration of the Session
, which ultimately expires when a User
"logs out". Sessions
store data associated with that user for the period that the Session
is active.
State
activeSessions: set Session
user: activeSessions -> User
activeSessions: set Session
user: activeSessions -> User
Actions
startSession(user: User) -> session: Session
assert user not in activeSessions
activeSessions += session
session.user = user
getUser(session: Session) -> user: User
assert session in activeSessions
user = session.user
end(session: Session)
assert session in activeSessions
activeSessions -= session
session.user = None
startSession(user: User) -> session: Session
assert user not in activeSessions
activeSessions += session
session.user = user
getUser(session: Session) -> user: User
assert session in activeSessions
user = session.user
end(session: Session)
assert session in activeSessions
activeSessions -= session
session.user = None
Activity [User]
Purpose
A subset of Users
in a private group, where data is only accessible to those in the group.
Principle
Activities
are subsets of Users
who all share a commonality (participating in the same activity). Private messaging, information, and data can be accessed by Users
who have been approved to join the Activity
and is only accessible to Activity
members.
State
managers: set User
members: set User
activityGroups: set Activity
destination: one Location
managers: set User
members: set User
activityGroups: set Activity
destination: one Location
Actions
createActivity(manager: User, destination: Location, name: str) -> Activity
assert name not in activityGroups
activity = Activity()
activity.name = name
activity.managers += manager
activity.destination = destination
activityGroups += activity
modifyDestination(activity: Activity, manager: User, destination: Location)
assert manager in activity.managers
activity.destination = destination
setActivityPassword(activity: Activity, user: User, code: str)
assert user in activity.managers
assert activity.password == None
activity.password = code
modifyActivityPassword(activity: Activity, user: User, og_pass: str, new_pass: str)
assert user in activity.managers
assert activity.password == og_pass
activity.password = new_pass
addMember(activity: Activity, user: User, password: str)
assert user not in activity.members
assert user not in activity.managers
assert activity.password == password
activity.members += user
addManager(activity: Activity, manager: User, user: User)
assert manager in activity.managers
assert user not in activity.managers
activity.members -= user
activity.managers += user
removeMember(activity: Activity, manager: User, user: User)
assert manager in activity.managers
assert user in activity.members
assert user not in activity.managers
activity.members -= user
removeManager(activity: Activity, manager: User)
assert manager in activity.managers
assert user not in activity.members
activity.managers -= manager
createActivity(manager: User, destination: Location, name: str) -> Activity
assert name not in activityGroups
activity = Activity()
activity.name = name
activity.managers += manager
activity.destination = destination
activityGroups += activity
modifyDestination(activity: Activity, manager: User, destination: Location)
assert manager in activity.managers
activity.destination = destination
setActivityPassword(activity: Activity, user: User, code: str)
assert user in activity.managers
assert activity.password == None
activity.password = code
modifyActivityPassword(activity: Activity, user: User, og_pass: str, new_pass: str)
assert user in activity.managers
assert activity.password == og_pass
activity.password = new_pass
addMember(activity: Activity, user: User, password: str)
assert user not in activity.members
assert user not in activity.managers
assert activity.password == password
activity.members += user
addManager(activity: Activity, manager: User, user: User)
assert manager in activity.managers
assert user not in activity.managers
activity.members -= user
activity.managers += user
removeMember(activity: Activity, manager: User, user: User)
assert manager in activity.managers
assert user in activity.members
assert user not in activity.managers
activity.members -= user
removeManager(activity: Activity, manager: User)
assert manager in activity.managers
assert user not in activity.members
activity.managers -= manager
Carpool [User, Activity]
Purpose
A subset of Users
in the same ActivityGroup
that will commute together.
Principle
Carpools
are subsets of Users
in the same Activity
who will commute to the Activity
together. Private messaging, information, and data can be accessed by Users
who have been approved to join the Carpool
and is only accessible to Carpool
members.
State
driver: one User
members: set User
capacity: Integer
driver: one User
members: set User
capacity: Integer
Actions
createCarpoolGroup(driver: User, activity: Activity, capacity: int, name: str) -> Carpool
assert name not in activity.carpools
carpool = Carpool()
carpool.name = name
carpool.driver = user
carpool.capacity = capacity
carpool.destination = activity.destination
activity.carpools += carpool
addMember(carpool: Carpool, user: User)
assert user not in carpool.members
assert user != carpool.driver
assert carpool.capacity < len(carpool.members)
carpool.members += user
changeDriver(carpool: Carpool, og_driver: User, new_driver: User)
assert og_driver == carpool.driver
assert new_driver in carpool.members
carpool.members -= new_driver
carpool.members += og_driver
carpool.driver = new_driver
removeMember(carpool: Carpool, user: User)
assert user in carpool.members
assert user != carpool.driver
carpool.members -= user
createCarpoolGroup(driver: User, activity: Activity, capacity: int, name: str) -> Carpool
assert name not in activity.carpools
carpool = Carpool()
carpool.name = name
carpool.driver = user
carpool.capacity = capacity
carpool.destination = activity.destination
activity.carpools += carpool
addMember(carpool: Carpool, user: User)
assert user not in carpool.members
assert user != carpool.driver
assert carpool.capacity < len(carpool.members)
carpool.members += user
changeDriver(carpool: Carpool, og_driver: User, new_driver: User)
assert og_driver == carpool.driver
assert new_driver in carpool.members
carpool.members -= new_driver
carpool.members += og_driver
carpool.driver = new_driver
removeMember(carpool: Carpool, user: User)
assert user in carpool.members
assert user != carpool.driver
carpool.members -= user
Post [User]
Purpose
Enables Users
to share information with each other.
Principle
Users
can create content and upload it to Carpool Community as a Post
, allowing other Users
to interact with this content, Comment
on it, and react to it. Posts
are a semi-public form of communication, where Users
choose which subset of Users
can view the Post
.
State
posts: set Post
content: Post -> one Content
user: Post -> one User
posts: set Post
content: Post -> one Content
user: Post -> one User
Actions
createPost(user: User, content: Content) -> Post
post = Post()
post.creator = user
post.content = content
posts += post
modifyPost(user: user, post: Post, content: Content)
assert post in posts
assert post.creator == user
post.content = content
deletePost(user: user, post: Post)
assert post in posts
assert post.creator == user
posts -= post
createPost(user: User, content: Content) -> Post
post = Post()
post.creator = user
post.content = content
posts += post
modifyPost(user: user, post: Post, content: Content)
assert post in posts
assert post.creator == user
post.content = content
deletePost(user: user, post: Post)
assert post in posts
assert post.creator == user
posts -= post
Comment [User, Post]
Purpose
Enables Users
to comment on already existing Posts
.
Principle
Users
can comment on Posts
and share their thoughts with other Users
on Community Carpool. These Comments
would be public to everyone that can view the Post
they are associated with, and unlike Posts
, Comments
cannot exist without a "parent-Post
".
State
comments: Post -> set Comments
content: Comment -> one Content
user: Comment -> one User
comments: Post -> set Comments
content: Comment -> one Content
user: Comment -> one User
Actions
createComment(user: User, post: Post, content: Content) -> Comment
assert post in posts
comment = Comment()
comment.creator = user
comment.content = content
comment.post = post
post.comments += comment
comments += comment
modifyComment(user: user, comment: comment, content: Content)
assert comment in comments
assert comment.creator == user
comment.content = content
deleteComment(user: user, comment: Comment)
assert comment in comments
assert comment.creator == user or comment.post.creator == user
comment.post.comments -= comment
comments -= comment
createComment(user: User, post: Post, content: Content) -> Comment
assert post in posts
comment = Comment()
comment.creator = user
comment.content = content
comment.post = post
post.comments += comment
comments += comment
modifyComment(user: user, comment: comment, content: Content)
assert comment in comments
assert comment.creator == user
comment.content = content
deleteComment(user: user, comment: Comment)
assert comment in comments
assert comment.creator == user or comment.post.creator == user
comment.post.comments -= comment
comments -= comment
Reaction [User, Entity]
Clarification on Entity
Entity
is being used as a generalized term for a Concept
. In this case, an Entity
can be a Post
, Comment
, or a Message
.
Purpose
Enables Users
to React
to an Entity
(another Concept), demonstrating support for/against the Entity
publicly.
Principle
Users
can React
to an Entity
, which will appear publicly to all parties that can view the original Entity
. This acts as a sort of feedback mechanism for others to gauge Users
responses to something.
State
reactionTotals: Entity -> set Reaction -> one integer
reactedUsers: Entity -> set User -> one Reaction
reactions: set Reaction
reactionTotals: Entity -> set Reaction -> one integer
reactedUsers: Entity -> set User -> one Reaction
reactions: set Reaction
Actions
react(user: User, entity: Entity, r: Reaction)
assert r in reactions
assert entity.reactedUsers.users != user
entity.reactionTotals[r] += 1
entity.reactedUsers[user] = r
modifyReaction(user: user, entity: Entity, r: Reaction)
assert r in reactions
assert user in entity.reactedUsers
entity.reactionTotals[entity.reactedUsers[user]] -= 1
entity.reactionTotals[r] += 1
entity.reactedUsers[user] = r
deleteReaction(user: user, entity: Entity)
assert user in entity.reactedUsers
entity.reactionTotals[entity.reactedUsers[user]] -= 1
entity.reactedUsers -= user
react(user: User, entity: Entity, r: Reaction)
assert r in reactions
assert entity.reactedUsers.users != user
entity.reactionTotals[r] += 1
entity.reactedUsers[user] = r
modifyReaction(user: user, entity: Entity, r: Reaction)
assert r in reactions
assert user in entity.reactedUsers
entity.reactionTotals[entity.reactedUsers[user]] -= 1
entity.reactionTotals[r] += 1
entity.reactedUsers[user] = r
deleteReaction(user: user, entity: Entity)
assert user in entity.reactedUsers
entity.reactionTotals[entity.reactedUsers[user]] -= 1
entity.reactedUsers -= user
Message [User]
Purpose
Messages
allow Users
to directly communicate with each other privately.
Principle
Messages
serve as a private form of communication between two (or more) Users
who may or may not share a connection through Activities
or Carpools
. It is a general, private form of communication.
State
messages: set Message
messages: set Message
Actions
sendMessage(sender: User, recipient: User, content: Content)
assert sender in registeredUsers
assert recipient in registered Users
assert sender != recipient
message = Message()
message.content = content
message.time = now()
sender.messages[recipient][sent] += message
recipient.messages[sender][recieved] += message
sendMessage(sender: User, recipient: User, content: Content)
assert sender in registeredUsers
assert recipient in registered Users
assert sender != recipient
message = Message()
message.content = content
message.time = now()
sender.messages[recipient][sent] += message
recipient.messages[sender][recieved] += message
Synchronizations
Update Maps
When User
Modifies Their Address (Location
)
If a User
modifies their home address, this update should be synchronized/propagated across all of the entities that reference the User
's address. This includes any Maps
, Activities
, and Carpools
that the User
might be involved with.
sync User.updateHomeAddress(user: User, address: Location)
og_loc = user.address
for map in user.maps:
map.removeFromMap(map, og_loc)
map.displayLocationOnMap(map, address)
user.address = address
sync User.updateHomeAddress(user: User, address: Location)
og_loc = user.address
for map in user.maps:
map.removeFromMap(map, og_loc)
map.displayLocationOnMap(map, address)
user.address = address
Remove User's
Location
When They Leave an Activity
If a User
decides to leave an Activity
, in addition to removing that User
from the Activity
, their Location
should be removed from any references to that Location
within the Activity
. Notifications should also be sent to any Carpools
that the User
might have been a designated driver for. Additionally, update Carpool
capacities to reflect the newly available space that this User
no longer occupies.
sync Activity.memberLeaves(activity: Activity, user: User)
userCarpool = activity.carpools[user]
if user == userCarpool.driver:
notifyCarpoolMembers()
Carpool.removeMember(userCarpool, user)
else if user in userCarpool.members:
Carpool.removeMember(userCarpool, user)
Activity.removeMember(activity, user)
sync Activity.memberLeaves(activity: Activity, user: User)
userCarpool = activity.carpools[user]
if user == userCarpool.driver:
notifyCarpoolMembers()
Carpool.removeMember(userCarpool, user)
else if user in userCarpool.members:
Carpool.removeMember(userCarpool, user)
Activity.removeMember(activity, user)
Delete Comments
When a Post
is Deleted
If the author of a Post
decides to delete that Post
, all Comment
entities should also be deleted, as they are no longer affiliated with any parent-Post
. Due to the lack of any affiliation between Comments
and a Post
, there is no need to continue storing these Comments
in the app's database (after a set period of time of course).
sync Post.deletePost(user: user, post: Post)
assert post in posts
assert post.creator == user
for comment in post.comments:
Comment.deleteComment(user, comment)
posts -= post
sync Post.deletePost(user: user, post: Post)
assert post in posts
assert post.creator == user
for comment in post.comments:
Comment.deleteComment(user, comment)
posts -= post
Wireframe
My wireframe can be viewed on Figma.
Design Tradeoffs
User
Administration Handling
Within an Activity
or Carpool
, I had to decide whether I represent Users
as administrators and members, or whether I should track this separately. While there are certainly levels of authority within Community Carpool, I had to choose whether to add two additional Concepts to represent these authoritative levels, and how to handle sharing these different authoritative levels between Carpools
and Activities
. Ultimately I decided to track these different levels of authority using sets of activity.managers
and activity.members
, checking which of these sets a User
belongs to.
Options
For each level of authoritity, a new Concept
could be defined to represent this object. It would require assigning Users
to either be Managers
or Members
, but this adds a lot of complexity (particularly when these concepts can be basically reused for Carpools
and Activities
).
Publicity of User's
Location
Data
As part of the Community Carpool carpool coordination process Users'
home addresses are viewable to all members of an Activity
that a User
is a member of. Assuming that people trust the other Users
that they participate in these Activities
with, this doesn't pose an inherent danger to a person (which is why I designed the application this way), but it could still be considered a little bit of a breach of privacy, as this rather sensitive location data can be viewed by others. The rationale was that this is not too different from a Phone Book, but I realize that these databases are not great for User
privacy either.
Options
Rather than displaying Users'
actual addresses to all of the Members of an Activity
, a small radial circle that encloses a User's
address could have been used, and then when a Carpool
is configured, the actual address of the person would be shared with the members of that Carpool
.
While this is a viable option that does not reduce too much functionality, it makes coordinating carpools a bit less convenient, as people would not see exactly where another User
may live.
Rotational Carpool Driver Handling
One feature I thought about implementing was the ability for Carpool drivers to rotate periodically, but I found that this would be a rather niche feature that would be difficult to implement without adding a lot of complexity to the design. Additionally, it is easy to replicate this behavior by simply creating two Carpool
groups, each with the same Users
in it but designating Users
to be different drivers.
Options
By making Carpool.driver
a set, it would be possible to implement an automatic "rotation" for the Carpool.driver
, setting a different driver depending on the day of the week or the specific week. However, this adds a lot of complexity to the design and the functionality could be replicated by simply creating two Carpool
groups with the same Users
but different drivers.