Social Media Client Project
This project is similar to the message board project, except the API is more complex and you will need multiple screens to implement all of the required features. The web API for this project supports both posting messages and sending private messages to other users.
This project is to be completed individually, and is due by the end of the day on Monday, May 3.
Code Organization
You will need to split your code up into multiple files that address different concerns.
Server Interface
As in the message board project, the code which uses the API must not be directly coupled to any Kivy code.
Screens
For this project you will need different Kivy screens for viewing public posts, viewing private conversations, composing posts, etc.
Window Size
Since this app is intended to be written as a mobile application, use a window size that reflects the aspect ratio of modern smartphones. Most smartphone screens are twice as tall as they are wide, or have an aspect ratio that is close to 2 to 1. You can change the window size by first importing Config
like so:
from kivy.config import Config
Then before calling the run()
method of your App
class you can set the width and height. Using a width of 512 and a height of 1024 works pretty well for a prototype:
if __name__ == '__main__':
Config.set('graphics', 'width', '512')
Config.set('graphics', 'height', '1024')
YourApp().run()
Error Handling
You must use exceptions to handle errors in this project.
There are a number of things that can go wrong when you make a request to the server. The parameters might be wrong, the server might be down, etc. If there is a connection issue, the requests
call will raise an exception whose base class is requests.exceptions.RequestException
. In this case, re-raise an exception of your own. If there is an issue with the way you used the API, the response code will be something other than 200. If that is the case, raise your own exception as well. In the Kivy portions of your code, every time you make a call to one of the server interface methods, put it in a try
block and if an exception is raised, report it with a popup.
For example, if your module containing code that interacts with the server is called server_interface.py
and you create your own exception type called ServerInterfaceException
, you might do something like this:
try:
posts = server_interface.get_posts()
except server_interface.ServerInterfaceException as e:
popup_error(str(e))
Remember you can make your own exception class like so:
class ServerInterfaceException(Exception):
pass
API Specification
Below are details about the server’s API.
Overview
The server supports authenticated users who can make public posts, send private messages to other users, and upvote posts. Since many operations require authentication, your app will need to allow users to enter their credentials (username and token). You can simultaneously check to see that the entered username exists and get that user’s ID by using the API.
Every user has a unique user ID number, a username, and an authentication token which serves as a password. For simplicity, the username can be changed but the token cannot.
Posts can be top level posts, or a reply to another post. Every post has a unique ID. When fetching posts, the JSON representing a post will also contain the user ID and username of the poster, the time the post was posted, the body of the post, the ID of the parent post (which is -1 if the post is not a reply), and the number of upvotes the post has received.
Any alphanumeric string of 16 characters or less within the post that begins with #
is a tag. You can optionally fetch only posts with a certain tag.
While anyone can fetch all public posts, fetching private messages requires a token, and users can only see private messages that they are involved in. Messages are fetched by conversation, so two user IDs are required to fetch messages.
The base URL for all requests is http://nsommer.wooster.edu/social
Requests that do not use the API correctly will result in a response with a status code of 400 that contains an error message within a JSON object like so:
{
"message": "User authentication failed"
}
If a request results in a response with a status code of 400, use the provided message as the error message in your exception.
We have made GET and POST requests before, and now this API introduces DELETE and PATCH requests. As you might guess, DELETE is used when you want to delete something. PATCH is used when you want to modify something. The requests
module has delete()
and patch()
functions that work like get()
and post()
.
The parameters for requests must be passed using the data
parameter of the appropriate requests
function. For example, to change a user’s username you must provide the user’s ID, their token, and the new username, which you can do like so:
requests.patch(url, data={'uid': uid, 'token': token, 'username': new_username})
Each operation below has the HTTP request type and what you need to append to the base URL to form the request URL.
A Note on Security
For simplicity, there are a number of things about the server for this project that do not follow good security practices. In particular:
- Requests are made using HTTP rather than HTTPS, which means that data exchanged with the server is unencrypted and could be intercepted.
- The first user created has an ID of 1, and each subsequent user has an ID that is one greater than the last. It is better practice to use randomized user IDs.
- Authentication is done with tokens that are relatively short. This is to make client development easier, normally tokens are much longer.
- The token that is created when a user is created is the only authentication method available, and cannot be changed.
Create a user
POST /users
Parameters
username
- Alphanumeric username between 3 and 16 characters long
Response
username
- Name of the user
uid
- ID of the user
token
- String used to authenticate the user
Change a username
PATCH /users
Parameters
uid
- ID of the user whose name will be changed
token
- Authentication token of the user
username
- New username for the user
Response
username
- New name of the user
uid
- ID of the user
token
- String used to authenticate the user
Get a user
This allows you to look up a user by ID or name.
GET /users
Parameters
Provide only one of the following:
uid
- ID of the user
username
- Username of the user
Response
uid
- ID of the user
username
- Username of the user
Create a public post
POST /posts
Parameters
uid
- Integer user ID of the poster
token
- Ihe user’s authentication token
parentid
- If this post is a reply to another post, this is the ID of the parent post. Omit this parameter or set it to -1 if this is not a reply.
content
- The content of the post, between 1 and 1000 characters in length. Any alphanumeric strings of at most 16 characters within the content that begin with a #
are considered tags.
Response
postid
- numeric ID of the new post
Edit a public post
PATCH /posts
Parameters
uid
- ID of the user that owns the post
token
- Authentication token of the user that owns the post
postid
- ID of the post to edit
content
- The new body of the post
Response
postid
- ID of the edited post
Delete a public post
DELETE /posts
Parameters
uid
- ID of the user that owns the post
token
- Authentication token of the user that owns the post
postid
- ID of the post to edit
Response
postid
- ID of the edited post
Get public posts
GET /posts
Parameters
limit
- Maximum number of posts to fetch. Optional, defaults to 50.
uid
- Only fetch posts from the user with this ID. Optional, posts from all users are returned if omitted.
tag
- Only fetch posts that have this tag. Optional, posts with any or no tags will be returned if omitted.
Response
- A list of post objects which have the following attributes:
postid
- ID of the post
uid
- ID of the user who created the post
username
- Name of the user who created the post
time
- The time the post was created
content
- The body of the post
parentid
- ID of the post this is a reply to, -1 if it is not a reply
upvotes
- Number of times this post has been upvoted
Create a message
POST /messages
Parameters
senderid
- ID of the sender
recipientid
- ID of the recipient
token
- The authorization token of the sender
content
- The body of the message, between 1 and 1000 characters in length
Response
messageid
- The ID of the sent message
Get messages
This fetches messages within a conversation between two users.
GET /messages
Parameters
uid
- The ID of the user making the request
otherid
- The ID of the other user in the conversation
token
- The authorization token of the user making the request
limit
- Maximum number of posts to fetch. Optional, defaults to 50.
Response
- A list of message objects which have the following attributes:
senderid
- ID of the sender
sender
- Username of the sender
recipientid
- ID of the recipient
recipient
- Username of the recipient
time
- The time the message was sent
content
- The body of the message
Get conversations
GET /conversations
Parameters
uid
- ID of the user making the request
token
- Authorization token of the user making the request
Response
- A list of user objects representing the users with which the requesting user has had conversations. Each user object has these properties:
uid
- ID of the user
username
- Username of the user
Upvote a post
POST /upvotes
Parameters
uid
- ID of the user making the request
token
- Authorization token of the user making the request
postid
- ID of the post to upvote
Response
postid
- ID of the upvoted post
Submission
The project is due by the end of the day on Monday, May 3.
Push your project to git-keeper.
For full credit your app must:
- Adhere to the requirements above
- Support all of the operations in the API.
- Be well documented
- Adhere to PEP 8
- Use exceptions to handle errors
Social Media Client Project
This project is similar to the message board project, except the API is more complex and you will need multiple screens to implement all of the required features. The web API for this project supports both posting messages and sending private messages to other users.
This project is to be completed individually, and is due by the end of the day on Monday, May 3.
Code Organization
You will need to split your code up into multiple files that address different concerns.
Server Interface
As in the message board project, the code which uses the API must not be directly coupled to any Kivy code.
Screens
For this project you will need different Kivy screens for viewing public posts, viewing private conversations, composing posts, etc.
Window Size
Since this app is intended to be written as a mobile application, use a window size that reflects the aspect ratio of modern smartphones. Most smartphone screens are twice as tall as they are wide, or have an aspect ratio that is close to 2 to 1. You can change the window size by first importing
Config
like so:Then before calling the
run()
method of yourApp
class you can set the width and height. Using a width of 512 and a height of 1024 works pretty well for a prototype:Error Handling
You must use exceptions to handle errors in this project.
There are a number of things that can go wrong when you make a request to the server. The parameters might be wrong, the server might be down, etc. If there is a connection issue, the
requests
call will raise an exception whose base class isrequests.exceptions.RequestException
. In this case, re-raise an exception of your own. If there is an issue with the way you used the API, the response code will be something other than 200. If that is the case, raise your own exception as well. In the Kivy portions of your code, every time you make a call to one of the server interface methods, put it in atry
block and if an exception is raised, report it with a popup.For example, if your module containing code that interacts with the server is called
server_interface.py
and you create your own exception type calledServerInterfaceException
, you might do something like this:Remember you can make your own exception class like so:
API Specification
Below are details about the server’s API.
Overview
The server supports authenticated users who can make public posts, send private messages to other users, and upvote posts. Since many operations require authentication, your app will need to allow users to enter their credentials (username and token). You can simultaneously check to see that the entered username exists and get that user’s ID by using the API.
Every user has a unique user ID number, a username, and an authentication token which serves as a password. For simplicity, the username can be changed but the token cannot.
Posts can be top level posts, or a reply to another post. Every post has a unique ID. When fetching posts, the JSON representing a post will also contain the user ID and username of the poster, the time the post was posted, the body of the post, the ID of the parent post (which is -1 if the post is not a reply), and the number of upvotes the post has received.
Any alphanumeric string of 16 characters or less within the post that begins with
#
is a tag. You can optionally fetch only posts with a certain tag.While anyone can fetch all public posts, fetching private messages requires a token, and users can only see private messages that they are involved in. Messages are fetched by conversation, so two user IDs are required to fetch messages.
The base URL for all requests is
http://nsommer.wooster.edu/social
Requests that do not use the API correctly will result in a response with a status code of 400 that contains an error message within a JSON object like so:
If a request results in a response with a status code of 400, use the provided message as the error message in your exception.
We have made GET and POST requests before, and now this API introduces DELETE and PATCH requests. As you might guess, DELETE is used when you want to delete something. PATCH is used when you want to modify something. The
requests
module hasdelete()
andpatch()
functions that work likeget()
andpost()
.The parameters for requests must be passed using the
data
parameter of the appropriaterequests
function. For example, to change a user’s username you must provide the user’s ID, their token, and the new username, which you can do like so:Each operation below has the HTTP request type and what you need to append to the base URL to form the request URL.
A Note on Security
For simplicity, there are a number of things about the server for this project that do not follow good security practices. In particular:
Create a user
Parameters
username
- Alphanumeric username between 3 and 16 characters longResponse
username
- Name of the useruid
- ID of the usertoken
- String used to authenticate the userChange a username
Parameters
uid
- ID of the user whose name will be changedtoken
- Authentication token of the userusername
- New username for the userResponse
username
- New name of the useruid
- ID of the usertoken
- String used to authenticate the userGet a user
This allows you to look up a user by ID or name.
Parameters
Provide only one of the following:
uid
- ID of the userusername
- Username of the userResponse
uid
- ID of the userusername
- Username of the userCreate a public post
Parameters
uid
- Integer user ID of the postertoken
- Ihe user’s authentication tokenparentid
- If this post is a reply to another post, this is the ID of the parent post. Omit this parameter or set it to -1 if this is not a reply.content
- The content of the post, between 1 and 1000 characters in length. Any alphanumeric strings of at most 16 characters within the content that begin with a#
are considered tags.Response
postid
- numeric ID of the new postEdit a public post
Parameters
uid
- ID of the user that owns the posttoken
- Authentication token of the user that owns the postpostid
- ID of the post to editcontent
- The new body of the postResponse
postid
- ID of the edited postDelete a public post
Parameters
uid
- ID of the user that owns the posttoken
- Authentication token of the user that owns the postpostid
- ID of the post to editResponse
postid
- ID of the edited postGet public posts
Parameters
limit
- Maximum number of posts to fetch. Optional, defaults to 50.uid
- Only fetch posts from the user with this ID. Optional, posts from all users are returned if omitted.tag
- Only fetch posts that have this tag. Optional, posts with any or no tags will be returned if omitted.Response
postid
- ID of the postuid
- ID of the user who created the postusername
- Name of the user who created the posttime
- The time the post was createdcontent
- The body of the postparentid
- ID of the post this is a reply to, -1 if it is not a replyupvotes
- Number of times this post has been upvotedCreate a message
Parameters
senderid
- ID of the senderrecipientid
- ID of the recipienttoken
- The authorization token of the sendercontent
- The body of the message, between 1 and 1000 characters in lengthResponse
messageid
- The ID of the sent messageGet messages
This fetches messages within a conversation between two users.
Parameters
uid
- The ID of the user making the requestotherid
- The ID of the other user in the conversationtoken
- The authorization token of the user making the requestlimit
- Maximum number of posts to fetch. Optional, defaults to 50.Response
senderid
- ID of the sendersender
- Username of the senderrecipientid
- ID of the recipientrecipient
- Username of the recipienttime
- The time the message was sentcontent
- The body of the messageGet conversations
Parameters
uid
- ID of the user making the requesttoken
- Authorization token of the user making the requestResponse
uid
- ID of the userusername
- Username of the userUpvote a post
Parameters
uid
- ID of the user making the requesttoken
- Authorization token of the user making the requestpostid
- ID of the post to upvoteResponse
postid
- ID of the upvoted postSubmission
The project is due by the end of the day on Monday, May 3.
Push your project to git-keeper.
For full credit your app must: