This homework assignment must be submitted in Gradescope by Noon central time on Tuesday April 25, 2023.
This is the last homework assignment in MCS 275. After this, the only graded work remaining is Project 4.
Collaboration is prohibited, and you may only access resources (books, online, etc.) listed below.
This homework is about Flask application development.
Most relevant:
Less likely to be relevant, but also allowed:
This homework assignment has 2 problems. The grading breakdown is:
Points | Item |
---|---|
3 | Autograder |
6 | Problem 2 |
6 | Problem 3 |
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.
Ask your instructor or TA a question by email, in office hours, or on discord.
This problem concerns a Flask application that collects and stores bug reports (e.g. describing problems users have with a piece of software or technology) in a SQLite database. In this application, a bug report has the following data associated with it:
id
- A primary key assigned by the databasecreated_ts
- The timestamp (time.time()
return value) when the bug report was createdfullname
- The full name of the person who created the reportdescription
- A description of the problemseverity
- One of the words low
, medium
, high
, or critical
.As provided, the application is incomplete.
Start by getting the incomplete application:
bugreport.py
- Main programstatic/
- Subdirectory for static filesstatic/main.css
- Main stylesheettemplates/
- Subdirectory for templatestemplates/report_list.html
- Template for page listing all bug reportsAs provided, the application has:
/
- A front page/create/
- A form for a creating new bug report/list/
- A page listing all bug reports/create/done
- A page meant to be shown after a bug report has been added to the databaseHowever, it is missing the body of the function for
/create/submit
- The URL that receives form submissions and adds a new bug report to the DB (adding a suitable value for created_ts
as well)Write the body of the corresponding function create_report_submission
in bugreport.py
, and submit the modified bugreport.py
as part of your homework. (Don't submit the rest of the files in this application.)
Note: You might need to try the existing application out and read through the source code in some detail to get all the information you need to complete this problem.
# The update to bugreport.py is given below
@app.route("/create/submit", methods=["GET", "POST"]) # GET-only is default
def create_report_submission():
"Create a bug report based on information submitted in the form"
fullname = flask.request.values.get("fullname")
description = flask.request.values.get("description")
severity = flask.request.values.get("severity")
con = sqlite3.connect(DB_FILE)
res = con.execute(
"""
INSERT INTO reports (created_ts, fullname, description, severity)
VALUES (?,?,?,?);
""",
(time.time(), fullname, description, severity),
)
con.commit()
con.close()
return flask.redirect("/create/done")
Imagine a Flask application that, when you visit URL /question/1/
asks you a question with a numerical answer, such as
Take the smallest power of 7 that contains all 10 decimal digits (0,1, ... 9). Form an integer out of the rightmost five digits in that number (when written in decimal).
In this case, the answer is 10449. That means you're supposed to visit /question/10449/
next. That page may ask you another question, telling you where to go next, and so on, until at some point you open the page for the answer to the previous question and see
Winner! You captured the flag.
However, this application only lets one person "win" in this way. That is, once someone has visited the final page, it is marked as "captured" and any subsequent visit to any of the question pages or the final page will show
(The CTF game is over. Someone else has captured the flag.)
Also, at any time, visiting a page /question/<int:n>/
where n
is not one of the supported questions/answers will show
Oops. That is not a correct answer. (Maybe use the browser's back button and try again?)
You could store the information needed for this application in a database table structured as follows:
n | question | is_winner | captured |
---|---|---|---|
1 | Take the smallest power of 7 that contains... | 0 | |
10449 | What's the smallest prime greater than 37? | 0 | |
41 | What is 11 raised to the 4th power? | 0 | |
14641 | 1 | 0 |
(This means 14641 is the winning answer, and it hasn't been visited yet. Note that n=1
is always the entry point of the game, and the winning answer never has a question associated to it.)
Write a Flask application that implements this idea, using a SQLite database with a single table as above. You can pre-populate it with the data shown above, or make your own game, but the program should initialize the database when it is run.
Your application doesn't need to have any styling (CSS) at all. Use of HTML templates is also optional.
Note that this application has just one route, but that function has a lot in it (i.e. several DB queries and a significant amount of program logic). It needs to:
captured
in the row where is_winner
equals 1)n
import flask
import sqlite3
import os
DB_FILE = "ctf.sqlite"
app = flask.Flask("Capture The Flag")
@app.route("/question/<int:n>/")
def question(n):
"""Capture the flag game"""
# A string representing a simple HTML page that displays a given message when `.format is used`
html_str = """<!doctype html>
<html>
<head><title>Capture The Flag</title></head>
<body><h1>{}</h1></body>
</html>
"""
con = sqlite3.connect(DB_FILE)
res = con.execute("SELECT captured FROM ctf WHERE is_winner = 1;")
if int(res.fetchone()[0]): # If won
con.close()
return html_str.format("(The CTF game is over. Someone else has captured the flag.)")
res = con.execute("SELECT question, is_winner FROM ctf WHERE n = ?;", (n,))
row = res.fetchone()
if row is None: # If wrong answer
con.close()
return html_str.format("Oops. That is not a correct answer. (Maybe use the browser's back button and try again?)")
if int(row[1]): # If winner
con.execute("UPDATE ctf SET captured=1 WHERE is_winner=1")
con.commit()
con.close()
return html_str.format("Winner! You captured the flag")
con.close()
return html_str.format(row[0])
# Make sure database exists
add_sample_data = False
if not os.path.exists(DB_FILE):
print("The database '{}' was not found. Creating it.".format(DB_FILE))
add_sample_data = True
con = sqlite3.connect(DB_FILE)
print("Making sure the DB contains the necessary tables...", end="")
con.execute("""
CREATE TABLE IF NOT EXISTS ctf (
n INTEGER PRIMARY KEY,
question TEXT,
is_winner INT,
captured INT);""")
print("Done")
if add_sample_data:
print("Populating DB with sample data, since it was empty...", end="")
con.execute("INSERT INTO ctf (n,question,is_winner) VALUES (?,?,?)", (1,"Take the smallest power of 7 that contains all 10 decimal digits (0,1, ... 9). Form an integer out of the rightmost five digits in that number (when written in decimal).", 0))
con.execute("INSERT INTO ctf (n,question,is_winner) VALUES (?,?,?)", (10449,"What's the smallest prime greater than 37?", 0))
con.execute("INSERT INTO ctf (n,question,is_winner) VALUES (?,?,?)", (41,"What is 11 raised to the 4th power?", 0))
con.execute("INSERT INTO ctf (n,question,is_winner,captured) VALUES (?,?,?,?)", (14641, "", 1, 0))
print("Done")
con.commit()
con.close()
# Run the application
app.run() # This will never return. You lose all control over the program.