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

MCS 275 Spring 2023 Homework 13 Solutions

  • Course Instructor: David Dumas
  • Contributors to this document: Johnny Joyce

Instructions:

  • Complete the problems below, which ask you to write Python scripts.

Deadline

This homework assignment must be submitted in Gradescope by Noon central time on Tuesday April 18, 2023.

Collaboration

Collaboration is prohibited, and you may only access resources (books, online, etc.) listed below.

Content

This homework is about HTML/CSS and Flask.

Resources you may consult

Most relevant:

Less likely to be relevant, but also allowed:

Point distribution

This homework assignment has 3 problems. The grading breakdown is:

Points Item
3 Autograder
5 Problem 2
5 Problem 3
2 Problem 4
15 Total

The part marked "autograder" reflects points assigned to your submission based on some simple automated checks for Python syntax, etc. The result of these checks is shown immediately after you submit.

What to do if you're stuck

Ask your instructor or TA a question by email, in office hours, or on discord.

Problem 2: Browser-based token

Modern application login systems tend to use a username, password, and a "second factor" such as a numeric code sent by SMS message or generated by an app or hardware device. In the case of an app or hardware device (called a token), the code is usually generated based on the current time and a small amount of secret data that is provided by the application when the account is created. Thus the application can predict what codes will be generated, but without knowledge of the secret data, the sequence looks random.

Here's a function that uses a secret string value called secret and the current time to generate a 6-character string that changes every 10 seconds.

In [24]:
import time

def time_and_secret_based_code(secret):
    """
    Return a 6-digit code (as a string) that combines
    a secret value and the current time.  This is a
    simplified algorithm not suited to actual use for
    authentication!
    """
    x = int(time.time()) // 10
    # Next few lines implement Fowler-Noll-Vo hash function
    # based on https://gist.github.com/amakukha/7854a3e910cb5866b53bf4b2af1af968
    h = 0x811c9dc5
    for c in str(secret) + str(x):
        h = ((ord(c)^h) * 0x01000193) & 0xFFFFFFFF
    s = ("{:06d}".format(h))[-6:]
    return s

Here's an example of it in action, showing different codes generated at 15-second intervals.

In [25]:
for _ in range(5):
    print(time_and_secret_based_code("MCS275"))
    time.sleep(15)
858813
194528
228289
450670
895432

Write a Flask application hwk13prob2.py that has a single route "/" where it displays the six-digit code returned by time_and_secret_based_code("MCS275") at the time the page is requested. The code should appear centered on the screen in large numerals.

Then, add the following tag inside the <head> of the HTML produced by this application to make it automatically reload every 15 seconds:

<meta http-equiv="refresh" content="15">

This means you'll see a new code every 15 seconds. This makes the Flask application a toy example of the kind of token you'd use in a two-factor authentication system.

Solution

In [ ]:
import flask
import time

app = flask.Flask("")

def time_and_secret_based_code(secret):
    """
    Return a 6-digit code (as a string) that combines
    a secret value and the current time.  This is a
    simplified algorithm not suited to actual use for
    authentication!
    """
    x = int(time.time()) // 10
    # Next few lines implement Fowler-Noll-Vo hash function
    # based on https://gist.github.com/amakukha/7854a3e910cb5866b53bf4b2af1af968
    h = 0x811c9dc5
    for c in str(secret) + str(x):
        h = ((ord(c)^h) * 0x01000193) & 0xFFFFFFFF
    s = ("{:06d}".format(h))[-6:]
    return s

@app.route("/")
def code():
    """HTML page with secret code. Refreshes every 15 seconds"""
    code = time_and_secret_based_code("MCS275")
    return """
            <!doctype html>
            <html> 
            <head>
                <title>Secret code</title>
                <meta http-equiv="refresh" content="15">
            </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(code)

app.run()

Problem 3: List by phase

Take the element info app that you developed in Worksheet 13 (or the one provided in the solutions) and modify it to add the following route:

  • /elements/phase/<ph>/ - Where <ph> is replaced by a word such as "solid", "liquid", "gas", or "artificial".

Visiting this route should show a HTML document with a bulleted list of the names of elements that have the specified phase at room temperature. For example

/elements/phase/liquid/

might produce a page that looks like this:

  • Bromine
  • Mercury

Then, make it so each element name in the bulleted list is a link to the page with information about that element. For example, I should be able to click on Bromine and see the page with information about element number 35 (which is Bromine).

Rename the application main script as hwk13prob3.py, and upload any templates or other files needed to run this application.

Solution

Most of the solution shown below is taken from the solution of Worksheet 13. The only new addition is the last function, element_lookup_phase.

In [3]:
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.route("/elements/phase/<ph>/")
def element_lookup_phase(ph):
    con = sqlite3.connect("elements.sqlite")
    query = con.execute("SELECT name, number FROM elements WHERE phase=?;", [ph])

    # Every part of the HTML that comes before the bullet points
    output = """
            <!doctype html>
            <html> 
            <head><title>Element info</title></head>
            <body style="font-family: Arial; padding: 2em; background:#D0D0D0;">
            <ul>
            """
    
    # Go through each element returned, turn it into an HTML bullet point, and put it in the output
    for row in query:
        name, number = row
        output += "<li><a href='/element/number/{}/'>{}</a></li>".format(number, name)
    
    # Close all remaining tags
    output += """
            </ul>
            </body>
            </html>
            """
    
    con.close()
    return output
    
app.run()

Problem 4: Project 4 check-in

Answer this problem in hwk13prob4.txt.

What is your project 4 topic, and how is your work going so far? Please answer very briefly, e.g. "I'm working on a login system for TrackFlow. I have started reading about how to make one." or "I don't know what I'll do, nor have I thought about it."

A nonempty response that appears genuine will get full credit.

If you have questions or concerns, you can mention them too to get feedback.

Revision history

  • 2023-04-13 Initial publication