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

MCS 275 Spring 2023 Worksheet 14 Solutions

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

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.

1. Get OrderNova or TrackFlow running locally

The Flask application written in lecture was called OrderNova in the 12pm section, and TrackFlow in the 1pm section. The applications are quite similar, and use a SQLite database to manage a queue of work orders that can be created, assigned to workers, and marked as completed.

To prepare for the next problem, download a copy of the Flask application for your section and get it running on your computer.

A. Download

The application has a number of files, so you can do it in either of these ways:

List of files needed to run TrackFlow

  • trackflow.py - Main program
  • createdb.py - Utility to reset database
  • timefmt.py - Module for time formatting
  • static/ - Subdirectory for static files
  • static/trackflow.css - Main stylesheet
  • templates/ - Subdirectory for templates
  • templates/tf-showorder.html - Template for displaying work order status
  • templates/tf-workerview.html - Template for displaying worker view
  • templates/tf-neworder.html - Template for new work order form

List of files needed to run OrderNova

  • ordernova.py - Main program
  • createdb.py - Utility to reset database
  • timefmt.py - Module for time formatting
  • static/ - Subdirectory for static files
  • static/ordernova.css - Main stylesheet
  • templates/ - Subdirectory for templates
  • templates/on-showorder.html - Template for displaying work order status
  • templates/on-workerview.html - Template for displaying worker view
  • templates/on-neworder.html - Template for new work order form

B. Test it out

Open a terminal and cd to the location of the main .py script (ordernova.py or trackflow.py). Run that script with Python, e.g. using a command

python3 ordernova.py

or similarly with your interpreter name and possibly replacing ordernova with trackflow.

You should see output similar to this:

The database 'ordernova.sqlite' was not found.  Creating it.
Making sure the DB contains the necessary tables...Done
Populating DB with sample data, since it was empty...Done
 * Serving Flask app 'OrderNova'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit

Notice the port number 5000 on the second-to-last line. It might not be 5000 for you, but you need to know it.

Now, open a browser and visit http://localhost:5000/, replacing 5000 with the port number as needed.

You should see a simple white home page with links to some of the application endpoints. Try them out. Create a work order. Take it on as an assignment. Complete it.

2. New features

Modify the work order tracking application to add these new features. Test them to make sure they work.

A. Workers list

This application doesn't have a table to store workers, but you can generate a list of workers that have appeared as the assigned worker for some task in the table (whether completed or not).

Make a route /workerlist/ that generates a page styled similarly to the work order view page, but which gives a bulleted list of all the workers currently in the database. Each worker's name should only appear once. The worker's username should be a link to their worker view page.

So for example you might see a list like this:

Workers

  • ddumas
  • gwashington

And the first item would link to /worker/ddumas/, and the next one to /worker/gwashington/

B. On this day

Here's a function that can take a day described by its year, month, and day numbers and return two time stamps ts0,ts1 so that a timestamp x lies within that day if and only if ts0 <= x < ts1.

In [7]:
import datetime

def timestamp_range_for_day(year,month,day):
    """
    Return the timestamps when a calendar day
    begins and ends.
    """
    ts0 = datetime.datetime(year,month,day).timestamp()
    ts1 = (datetime.datetime(year,month,day)+datetime.timedelta(days=1)).timestamp()
    return ts0,ts1

Use this to add the following feature to the work order tracking application: Visiting a URL of the form /reports/day/2023/04/15/ will show a page listing the work orders created on that day, assigned on that day, and completed on that day. These should be in three separate sections, and modeled after the worker view template.

Solution

It would be quite difficult to place all of the solutions in cells in this notebook. Instead, the following links can be used to download full versions of the apps that include all of the changes we need to make:

The changes are as follows:

  • ordernova.py (respectively trackflow.py) was renamed to ordernova-soln.py (resp. trackflow-soln.py). Functions named workerlist() and orders_by_day() were added, which account for the new routes.

  • A new template on-workerlist.html (respectively tf-workerlist.html) was created for part A

  • A new template on-tasksbyday.html (respectively tf-tasksbyday.html) was created for part B

  • The function timestamp_range_for_day() that was given in part B was added to timefmt.py

3. Custom sorts and max/min

Use anonymous functions (lambda) with Python's sorted(), max(), and min() functions to answer these questions.

A.

Generate the first 20 powers of 7 (meaning $7^1$ to $7^{20}$) and sort them according to how many different decimal digits they use. That is, 343 would appear near the start of the list since it uses only two distinct digits, while 1628413597910449 would appear near the end, as it uses all 10 digits.

Solution:

In [1]:
sorted([7**x for x in range(1,21)], key = lambda x: len(set(str(x))))
Out[1]:
[7,
 49,
 343,
 2401,
 16807,
 117649,
 823543,
 40353607,
 282475249,
 79792266297612001,
 5764801,
 1977326743,
 13841287201,
 96889010407,
 33232930569601,
 11398895185373143,
 678223072849,
 4747561509943,
 232630513987207,
 1628413597910449]

B.

Here's a text file with 10000 English words, one per line:

Let's say the endscore of a word is the number of times its last letter appears in the word. For example, "plasma" has an endscore of 2 because the last letter, a, appears twice. And "associates" has an endscope of 3 because it contains 3 copies of the last letter, s.

Using custom sorting and lambda, find 20 words with the highest endscores in this list.

Solution

We can use the following cell to open the list of words:

In [1]:
words = [x.strip() for x in open("wordlist.10000.txt","rt").readlines()]

Then the following lambda function counts the number of occurrences of the final letter: lambda w: w.count(w[-1])).

This results in the following answer:

In [2]:
sorted(words, key = lambda w: w.count(w[-1]))[-20:]
Out[2]:
['evanescence',
 'everywhere',
 'excellence',
 'experience',
 'independence',
 'interference',
 'massachusetts',
 'mississippi',
 'possess',
 'preference',
 'reference',
 'refrigerator',
 'representative',
 'sessions',
 'submissions',
 'sunglasses',
 'tennessee',
 'volleyball',
 'assessments',
 'documentcreatetextnode']

C.

Use the same word list as the last problem. Suppose we say the variety of a word is the ratio of the number of distinct letters to the length of the word. For example, "cameras" has 6 distinct letters and has a length of 7, so its variety is $\approx 0.85714$.

Among words with at least four letters, find one that has the lowest variety.

Solution

To find the variety of a word w, we can use len(set(w))/len(w). So the answer is as follows:

In [3]:
sorted([ w for w in words if len(w)>=4], key = lambda w: len(set(w))/len(w))[:10]
Out[3]:
['mississippi',
 'barbara',
 'tennessee',
 'engineering',
 'anna',
 'annotation',
 'assess',
 'assessed',
 'banana',
 'boob']

To verify that this formula works, let's find the position of the word "cameras" in the list:

In [4]:
idx = words.index("cameras")
print(idx)
1300

Then the cell below shows us that the variety is approximately 0.85714, as expected

In [6]:
print("The word {} has variety {}".format(words[idx], len(set(words[idx]))/len(words[idx])))
The word cameras has variety 0.8571428571428571