A document from MCS 275 Spring 2024, instructor Emily Dumas. You can also get the notebook file.

MCS 275 Spring 2024 Worksheet 12 Solutions

  • Course instructor: Emily Dumas,
  • Contributers to this document: Johnny Joyce, Kylash Viswanathan, Patrick Ward

Topics

This worksheet focuses on Flask web applications.

Resources

These things might be helpful while working on the problems. Remember that for worksheets, we don't strictly limit what resources you can consult, so these are only suggestions.

Prep by trying this HTML example: Big centered onions

Here is the code for an HTML document that, when loaded in a browser, just displays the word "onions" in the center of the browser window.

<!doctype html>
<html>
    <head>
        <title>No title</title>
        <style>
            /* Adapted from https://stackoverflow.com/questions/982054/
               A class that places the object in the center of the
               browser window.  */
            .center-screen {
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
                text-align: center;
                min-height: 100vh;
            }
        </style>
    </head>
    <body>
      <div class="center-screen">
          onions
      </div>
    </body>
</html>

Put this in an HTML file, save it, and confirm you can load it in a browser. This will serve as a starting point for some templates you'll have in the Flask applications you write for this worksheet.

1. Single page utilities

This problem asks you to write a Flask application, but a relatively simple one that can probably be done without HTML templates. You can use templates if you want, but it's also OK to just write functions that return strings and embed the HTML in those strings.

Make a flask application called spu.py (single page utilities) that has the following routes, all of which produce a page with a single word or number centered in the browser window, styled as in problem 1.

To be clear, we're talking about a single Flask application, and each part of the problem asks you to add another route to it.

A. /coin/

When you visit localhost:5000/coinflip/ (or the corresponding URL with a different port number that Flask selects), you should see either the word HEADS or TAILS centered on the screen in large letters. The word is selected at random, with each having a 50% probability.

B. /fib/<n>/

When you visit localhost:5000/fib/13/ (or the corresponding URL with a different port number that Flask selects), you should see the number 233 centered on the screen in large numerals. More generally, if you replace 13 in the URL with another positive integer $n$, the page should display the $n^{\mathrm{th}}$ Fibonacci number $F_n$ in the same way.

C. /switch/<x>/

When you visit localhost:5000/switch/1/ (or the corresponding URL with a different port number that Flask selects), you see the word ON in the center of the screen in big black letters against a white background. The word ON is actually a link, and if you click it, it takes you to /switch/0/. That page shows the word OFF in white text on a black background, with OFF being a link to /switch/1/.

Thus, this part of the app behaves like a light switch. Clicking toggles it on or off.

Solution

In [ ]:
import flask
import random

app = flask.Flask(__name__)

@app.route("/coin/")
def coinflip():
    """Randomly displays "HEADS" or "TAILS" with 50/50 odds"""
    word = random.choice(["HEADS", "TAILS"])
    return """
            <!doctype html>
            <html> 
            <head><title>Coin flip</title></head>
            <body style="display:flex; flex-direction:column; justify-content:center; align-items:center; text-align:center; min-height:100vh;">
            <p style="font-size:64px;">{}</p>
            </body>
            </html>
            """.format(word)

def fib(n):
    """Recursive function for n-th Fibonacci number"""
    if n == 0 or n == 1:
        return n
    else:
        return fib(n-1) + fib(n-2)

@app.route("/fib/<n>/")
def fib_webpage(n):
    """Display n-th Fibonacci number in big numbers in center of screen"""
    n = int(n)
    return """
            <!doctype html>
            <html> 
            <head><title>Fibonacci</title></head>
            <body style="display:flex; flex-direction:column; justify-content:center; align-items:center; text-align:center; min-height:100vh;">
            <p style="font-size:64px;">{}</p>
            </body>
            </html>
            """.format(fib(n))

@app.route("/switch/<x>/")
def switch(x):
    """Webpage to simulate a light switch"""
    
    if int(x) == 1:
        text = "ON"
        background_color = "white"
        foreground_color = "black"
        link_destination = 0
        
    else:
        text = "OFF"
        background_color = "black"
        foreground_color = "white"
        link_destination = 1
        
    return """
            <!doctype html>
            <html> 
            <head><title>Switch goes wheeeee</title></head>
            <body style="background-color:{}; display:flex; flex-direction:column; justify-content:center; align-items:center; text-align:center; min-height:100vh;">
            <p>
            <a style="color:{}; font-size:64px;" href=/switch/{}/>{}</a>
            </p>
            </body>
            </html>
            """.format(background_color, foreground_color, link_destination, text)


app.run()

2. Elements info app

This problem asks you to develop a Flask application, adding additional features in each part. Put your work in a file called elefact.py, which should be in its own folder (ideally called elefact). You will probably want to create subfolders elefact/templates and elefact/static to hold templates and static files, too.

Here's a SQLite database with information about the first 112 chemical elements:

It has columns:

  • number - the atomic number
  • name - the element name
  • symbol - one- or two-letter symbol for the element
  • periodnum - the number of the period in the periodic table that contains this element
  • groupnum - the number of the group in the periodic table that contains this element
  • phase - whether this element is a solid, liquid, or gas at 25C and 1 atmosphere of pressure
  • category - metal, metalloid, noble gas, etc.
  • interesting_fact - NULL for most, but in some cases contains a sentence with an interesting fact about the element.

Note: The column phase may also contain the value "artificial" for some artificially-produced radioactive elements which are produced in such small quantities that characterization of their physical properties is not possible. But in a few cases, such as Plutonium, this value is incorrectly applied to artificial elements where such characterization has been done (e.g. Plutonium is a solid at room temperature). If we work with the elements database again, I'll try to correct the ones for which the phase info is incorrect.

A. Basic lookup by number

Make a Flask application that uses HTML templates and this database to generate a page with information about any element on demand.

For example, the endpoint /element/number/4/ should produce a page looking something like this (note the presence of an interesting fact):

Be

Beryllium

The element with atomic number 4. This Alkaline Earth Metal is a solid at standard temperature and pressure. A brittle, toxic metal when pure, it is a component of gemstones such as emerald and aquamarine.

And the endpoint /element/number/61/ should produce a page looking something like this (note the lack of phase information, and the lack of an interesting fact):

Pm

Promethium

The element with atomic number 61. This Lanthanide is an artificially produced element whose phase at standard temperature and pressure is not known.

B. Lookup by symbol

Add a feature to the application so it also generates the same sort of page at endpoints that specify an element's symbol such as /element/symbol/Ag/.

Solution

Here is the html template expected in templates/element_info_page:

```html

Element info

Here is the main script:

In [ ]:
import flask
import sqlite3

app = flask.Flask(__name__)

@app.route("/element/number/<n>/")
def element_lookup(n):
    """Webpage with information about element with atomic number `n`."""
    con = sqlite3.connect("elements.sqlite")
    row = con.execute("SELECT symbol, name, interesting_fact FROM elements WHERE number=?;", [int(n)])
    symbol, name, fact = row.fetchone()
    
    if fact is None:
        # If a fact is missing, we need to piece one together using other information.
        row = con.execute("SELECT number, category, phase FROM elements WHERE number=?;", [int(n)])
        number, category, phase = row.fetchone()
        fact = "The element with atomic number {}. This {} is ".format(number, category)
        if phase == "artificial":
            fact += "an artificially produced element whose phase at standard temperature and pressure is not known."
        else:
            fact += "a {} at standard temperature and pressure.".format(phase)
            
    con.close()
    
    return flask.render_template("element_info_page.html", symbol = symbol, name = name, fact = fact)

@app.route("/element/symbol/<symb>/")
def element_lookup_symbol(symb):
    """Lookup by symbol. Finds the corresponding number then call `element_lookup`."""
    con = sqlite3.connect("elements.sqlite")
    row = con.execute("SELECT number FROM elements WHERE symbol=?;", [symb])
    n = row.fetchone()[0]
    con.close()
    return element_lookup(n)

app.run(debug = True)

4. Extras (no solutions will be given)

If you finish the material above, add additional features to the elements Flask app:

Phase-dependent styling

Give the element info pages a different background color (always light in color, but maybe green, purple, yellow, or gray) depending on the phase of the element at room temperature (solid, liquid, gas, or artificial/unknown).

Add links to the elements pages that take you to the next and previous element (by atomic number). Hydrogen has no previous element, and Copernicium has no next element in this dataset, so handle those possibilities appropriately.

Optional photo

When asked for an element page, say for element 17, have the application check to see whether a file 117.jpg exists in the static/ subdirectory. If it does, then have that image included on the page. Add a couple of images of chemical element samples in this way, by finding, downloading, and renaming public-domain images from the web.

Revision history

  • 2024-04-01 Initial release