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:

Create a user

POST /users

Parameters

Response

Change a username

PATCH /users

Parameters

Response

Get a user

This allows you to look up a user by ID or name.

GET /users

Parameters

Provide only one of the following:

Response

Create a public post

POST /posts

Parameters

Response

Edit a public post

PATCH /posts

Parameters

Response

Delete a public post

DELETE /posts

Parameters

Response

Get public posts

GET /posts

Parameters

Response

Create a message

POST /messages

Parameters

Response

Get messages

This fetches messages within a conversation between two users.

GET /messages

Parameters

Response

Get conversations

GET /conversations

Parameters

Response

Upvote a post

POST /upvotes

Parameters

Response

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: