Skip to content

Flask server and UI

sberkun edited this page Jun 3, 2021 · 9 revisions

Running the server

python3 server.py
python3 Shepherd.py
python3 YDL.py

Shepherd and YDL aren't strictly necessary if you just want to test html/css/js changes. The Flask stuff is run by server.py.

What is Flask?

Great question! Flask is a Python web framework; basically a server with some spicy bits. We mostly use it to serve html pages from a Python server, and have those pages communicate with our Python code using Flask-SocketIO. Here are the official docs:

Our Flask server, server.py, will serve pages from the shepherd/templates folder; these templates may pull in resources from the shepherd/static folder.

Adding New Pages to the UI

First, make sure to add the new page to the UI_PAGES dictionary in Utils.py. Otherwise server.py won't know that the page exists.

If you want to extend the base template (comes with a navbar and logging for websocket messages), start with this:

{% extends 'base.html' %}
{% block head_content %}
<title>Example Title</title>
<style>
  /* you can delete this element, or add page-specific style rules*/
</style>
<script>
  // this function is called when the page is loaded, from base.html
  // socket: the main socketio websocket
  // send: a function that sends a header through the socket, and also logs it
  function main_js_content(socket, send) {
    //add socket listeners and javascript stuff here
  }
</script>
{% endblock %}
{% block html_content %}
<!-- add html content here -->
{% endblock %}

Note that if you want to include this page in the navbar, you should add it to the navbar in base.html.

If you do not want to extend the base template, but you still want to send messages to Shepherd, make sure to send messages with the following format:

socket.emit("ui-to-server", getCookie("password"), shepherd_header, json_string)

where json_string is optional.

Communication Between Shepherd and Javascript

In Shepherd.py, messages typically should be sent with ydl_send to the UI target. For example:

ydl_send(YDL_TARGETS.UI, UI_HEADER.CHEESE, {"cheese_type": "mozzarella"})

This will be forwarded through YDL to server.py, which will forward it through a websocket to all currently open UI pages. It can then be read by javascript code like:

socket.on('cheese', function (data) { // note that 'cheese' has to match UI_HEADER.CHEESE
  data = JSON.parse(data);
  let cheese_type = data.cheese_type;
  ...
});

In the other direction, javascript code should typically use the send() function provided by base.html. For example:

send('pizza', JSON.stringify({"toppings": "extra cheese"}));

This will be forwarded through a websocket to server.py, which will forward it through YDL to Shepherd.py. Note that the header "pizza" must be included in Utils.py as SHEPHERD_HEADER.PIZZA or something similar. Then, if SHEPHERD_HEADER.PIZZA is included in the function mappings, it can be handled like:

def receive_pizza(args):
    toppings = args["toppings"]
    ...

Summary:

Here are the steps that need to be done to add a message from Shepherd -> frontend:

  • add the header to Utils.py, under UI_HEADER
  • add socket.on listener to frontend
  • add ydl_send call to Shepherd.py

Here are the steps that need to be done to add a message from frontend -> Shepherd:

  • add the header to Utils.py, under SHEPHERD_HEADER
  • add the header to the relevant mapping(s) in Shepherd.py (i.e. EVERYWHERE_FUNCTIONS dictionary)
  • add listening function to Shepherd.py
  • add send call to frontend.

More details on password protection

The password protection exists so that "sensitive" pages (like the match controls) can be served from the same server as pages that students are free to access.

The current implementation is that the password stored in a cookie with the name "password"; attempting to access a sensitive page without the cookie set will cause server.py to serve password.html instead. server.py should also reject any websocket messages that don't include the correct password.

The password should never be written in plain text anywhere in the Shepherd repo; pass it by word-of-mouth, or by Slack. If you want to change the password, change the hash string in server.py.