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

MCS 275 Spring 2023 Worksheet 13 Example Solutions

  • Course instructor: David Dumas
  • Contributors to this document: Johnny Joyce, Kylash Viswanathan

Topics

This worksheet focuses on the basics of HTML/CSS and the Python web framework Flask. (We'll continue working on Flask in the upcoming week, with more Flask-related exercises coming in Worksheet 14.)

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.

1. Big centered 275

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.

<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.

Now, modify it in the following ways.

A. External stylesheet

The HTML above has inline CSS (that is, it is in a <style> tag). Convert it to use an external stylesheet called ws13.css. Confirm it still works.

B. Content and style changes

Change the content of the <div> from onions to the number 275. Modify the CSS so that the page has a dark blue background and a pale yellow-white text color, with a sans serif font.

In the HTML, apply a second class to the <div> called giant, and modify the CSS so that class has a font size of 64px.

Also change the title of the page to Big centered 275.

Solution

Here's what the CSS file may look like:

/* 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;
}

body{
    background-color: darkblue;
    color: #FFFBA3; /*     https://htmlcolorcodes.com/     */
    font-family: Arial;
}

.giant{
    font-size: 64px;
}

And here's the corresponding HTML file:

<html>
    <head>
        <title>Big centered 275</title>
        <link rel="stylesheet" href="q1-275.css">
    </head>
    <body>
        <div class="center-screen giant">
            275
        </div>
    </body>
</html>

2. 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

For simplicity, the solutions in the cells below do not use templates.

To see a version of part A that does use templates, see this link: (Google drive link).

This contains a zip file with the required files arranged as needed (including a .py file to run the flask application, a .HTML file for the template, and a corresponding .css file used by the HTML page). Solutions for questions other than part A using templates are not shown, but the non-template versions can be modified analogously.

In [ ]:
import flask
import random

app = flask.Flask("Utilities")

@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()

3. Elements info app

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.

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

In [ ]:
import flask
import sqlite3

app = flask.Flask("Elements")

@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 """
            <!doctype html>
            <html> 
            <head><title>Element info</title></head>
            <body style="font-family: Arial; padding: 2em; background:#D0D0D0;">
            <h1>{}</h1>
            <h2>{}</h2>
            <p>{}</p>
            </body>
            </html>
            """.format(symbol, name, 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()

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.