---
title: Lab Book - Web Apps
tags: COMP1010-23T0
slideOptions:
transition: slide
---
<style>
.reveal {
font-size: 20px;
}
.reveal div.para {
text-align: left;
}
.reveal ul {
display: block;
}
.reveal ol {
display: block;
}
img[alt=drawing] { width: 200px; }
</style>
# COMP1010 Lab Book: Web Apps
## Assumed Knowledge
Before starting this lab book, it is assumed that you have a solid understanding of COMP1010 Topic 3 (Python and Programming Fundamentals).
## Instructions
Unless otherwise specified, the following instructions apply to the whole of this lab book.
* All questions must be implemented using Python in Flask. It is recommended that you use PyHTML, but not essential.
* You may **not** use `render_template`.
* Every file submitted must include your name and student number in a comment at the top of your file above your `import` statements.
## Tasks to Not Forget
| Week Due | Task |
|---|---|
| 2a/2b | Before submitting your proposal in week 3, get your topic approved by your tutor. I recommend filling in the Problem Statement, Minimal Viable Product and Nice to Haves, and giving that to your tutor to read over. (0 marks)|
| 4a | Project progress check-in. (1 mark) |
| 5a | Project progress check-in. (1 mark) |
## Simple Websites (Q1 and Q2)
**Topics covered:**
* 5.0 - Web Apps Overview
* 5.1 - HTML
* 5.2 - PyHTML
* 5.3 - Flask
### 1. Text Server (1 mark)
**Setting up:**
1. Create your file `text_server.py`.
2. At the top of the file (above your `import` statements), in a comment, include your name and student number.
**Task:**
Write a Flask web server that analyses text. On the main page have a textbox and a submit button. If the submit button is pressed, on the next page show an analysis of whatever was in the text box. This analysis should include the number of characters (including spaces and punctuation), the number of characters (excluding punctuation), and the number of words.
**Note**:
* All responses the server gives should be in HTML. It is recommended that you use this as an opportunity to familiarise yourself with HTML and PyHTML.
* You may use the web servers implemented in lectures as a guide to structuring your code.
**Step By Step:**
This section guides you through the implementation of this question for any students who would like it.
<details>
<summary>
<b>1. Set up your Flask app.</b>
</summary>
- [ ] Here is the generic structure of the code taken from [our lectures](https://hackmd.io/@sim/HkLvcwUVK#/3). Copy it into your `text_server.py` file, below your name.
```{python}
from flask import Flask, request
from pyhtml import html # TODO add imports
app = Flask(__name__)
@app.route('/', methods=["GET", "POST"])
def homepage():
if request.method == 'POST':
# TODO you received form data, handle it
# (usually produce HTML)
else:
# TODO you didn't receive form data
# (usually produce HTML)
response = html(
# TODO put your pyhtml code here
)
return response
if __name__ == "__main__":
app.run(debug=True)
```
There is also a similar structure, with more elements explained [here](https://hackmd.io/@sim/HkLvcwUVK#/10). Make sure you ask your tutor or on the forums if there's any part you don't understand.
</details>
<details>
<summary>
<b>2. Create a function for each route. Update the routes, function names and HTTP methods for each copied function.</b>
</summary>
- [ ] Since we need 2 web pages in our application, copy and paste the homepage function. Don't forget to include the route line above the function.
```{python}
@app.route('/', methods=["GET", "POST"])
def homepage():
if request.method == 'POST':
# TODO you received form data, handle it
# (usually produce HTML)
else:
# TODO you didn't receive form data
# (usually produce HTML)
response = html(
# TODO put your pyhtml code here
)
return response
```
**Note:** You may decide to have different functionality under the same route. For this walk-through, we are having one web page per route.
- [ ] Change the name of the first function from `homepage` to `main_page` as this is what it's referred to in the question.
```{python}
@app.route('/', methods=["GET", "POST"])
def homepage():
```
becomes:
```{python}
@app.route('/', methods=["GET", "POST"])
def main_page():
```
- [ ] Give your other function an appropriate route name and function name.
```{python}
@app.route('/', methods=["GET", "POST"])
def homepage():
```
becomes something like (you can choose your own route name and function name):
```{python}
@app.route('/display_analysis', methods=["GET", "POST"])
def display_analysis():
```
- [ ] Because our `display_analysis` page is only loaded in response to a form submission, update the HTTP methods allowed for it to only contain `"POST"`.
```{python}
@app.route('/display_analysis', methods=["POST"])
def display_analysis():
```
</details>
<details>
<summary>
<b>3. For each route, write code to produce the HTML.</b>
</summary>
For our `main_page`, we need:
* A form which, when submitted goes to `"/display_analysis"` route, and contains:
* A [text area](https://www.w3schools.com/tags/tag_textarea.asp) for the user to enter text.
* A submit button for the user to indicate when they are finished typing.
You may also decide to include:
* A heading to describe what the application is about.
* Instructions for the user.
* A label on the text field.
* A title for the window.
Here is some sample code which produces the HTML for the bare essentials for the `/` route:
```{python}
response = html(
form(action="/display_analysis")(
textarea(name="text", rows="4", cols="50"),
input_(type="submit")
)
)
```
- [ ] Update your `main_page` function so that when we navigate to the `/` route, it displays the necessary HTML in the browser. When the `Submit` button is clicked, it should navigate to the `/display_analysis` route (which currently displays a blank page).
For our `display_analysis` page we need:
* A paragraph to display the results of the analysis.
You may also decide to include:
* A heading.
* A title for the window.
* The text which the user entered so that if they don't get the results they expected, they can see what they entered to try understand why.
* A button for them to go back to the `main_page` to do it again.
Here is some sample code which produces the HTML for the bare essentials for the `/display_analysis` route:
```{python}
response = html(
p(analysis)
)
```
</details>
<details>
<summary>
<b>4. Implement additional functionality.</b>
</summary>
In this case, when we load the `/display_analysis` route, before we construct the HTML to display, we need to:
- [ ] Get the text out of the form which was submitted. Put it into a variable.
- [ ] Write a function (suggested name: `analyse_text`) which returns an analysis of the text. (Technically this could be done in the function directly, but it's better programming practice to separate out parts of the code with different purpose into their own functions.) This analysis should include the number of characters (including spaces and punctuation), the number of characters (excluding punctuation), and the number of words.
```{python}
app = Flask(__name__)
# <-- insert your analysis function here
@app.route('/', methods=["GET", "POST"])
def main_page():
```
- [ ] Call the function on the submitted text and get the results into a variable called `analysis`. (You can call this variable anything suitable, I mention `analysis` here because that is the variable name I programmed into the PyHTML function call above.)
</details>
<details>
<summary>
<b>5. Test your application works.</b>
</summary>
- [ ] Run your application with a couple different inputs to check it produces the expected output for each lot of input.
</details>
### 2. Password Evaluator (1 mark)
**Setting up:**
1. Create your file `password_evaluator.py`.
2. At the top of the file (above your `import` statements), in a comment, include your name and student number.
**Task:**<br>
Write a Flask application which has the following (minimal) functionality:
* Prompts the user to enter a password.
* Gives the user feedback about how strong their password is.
* Gives the user feedback about how they can improve the strength of their password.
| Strength | Meaning |
|---|---|
|Strong|The password is at least 8 characters long, and has both upper and lower case characters.|
|Weak|The password is at least 8 characters long, or has both upper and lower case characters.|
|Very Weak|The password is neither at least 8 characters long, nor has both upper and lower case characters.|
There are no marks for writing the function - this is just good practice. The mark is for the web app.
---
## More Complex Websites (Q3a and Q3b)
**Assumed knowledge:**
* 5.0 - Web Apps Overview
* 5.1 - HTML
* 5.2 - PyHTML
* 5.3 - Flask
**Topics covered:**
* 5.4 - Cookies
* 3.8 - Serialization
### 3a. Restaurant Finder Part A (2 marks)
**Setting up:**
1. Create your file `restaurant_finder.py`.
2. At the top of the file (above your `import` statements), in a comment, include your name and student number.
**Background:**
At the time of writing this lab book, there is a question in topic 3.7.1 which can be found in the scaffold for topic 3 Python and Programming Fundamentals. Ideally, we will write a solution to this question during a revision lecture in week 6. If not, refer to the scaffold question, and start by writing a solution to that question.
**Task:**
Using the code in [3.7.1 Restaurant Finder](https://colab.research.google.com/github/sim-mautner/cs1010-22t1/blob/master/lectures/03-python-and-programming-fundamentals/07-revision.ipynb), write a Flask application which has the following (minimal) functionality:
* Allows the user to add restaurants.
* Allows the user to add preferences.
* Displays a list of suitable restaurants.
* Use cookies to maintain the list of restaurants and preferences.
| Another way of exaplaining what to do: |
|---|
|<ol><li>Write a Flask application (and use PyHTML in it) to make an application which allows a user to add restaurants and preferences to a list.</li><li>Copy-paste the `suitable_restaurants(restaurants, preferences)` function in without editing it. Add elements to your Flask application so that the user can run this function on the restaurants and preferences they've entered.</li><li>Add the functionality to store the restaurants and preferences the user has entered, as cookies so that they don't have to re-enter the information each time they use the app.</li><li>(for part B) Update the application so that the restaurants are stored in a file in json format so that all users can add to a growing list of restaurants being checked.</li>
</ol>|
### 3b. Restaurant Finder Part B (0.5 marks)
**Task:**
Update your Restaurant Finder application to:
* store and retrieve the existing restaurants in a json file on the server.
* (continue to use cookies to maintain the list of preferences).
## External Data Sources (Q4)
For this question ~~you may~~ complete ~~Option 1 or~~ Option 2. ~~Either one (not both) is required to gain the marks for Q4.~~
**Assumed knowledge:**
* 5.0 - Web Apps Overview
* 5.1 - HTML
* 5.2 - PyHTML
* 5.3 - Flask
**Topics covered:**
* 5.5 - External Data Sources
<details>
<summary>now not available</summary>
### 4. (Option 1) COVID Updates (1.5 marks)
The [Covid-19-API](https://github.com/M-Media-Group/Covid-19-API) can be used to get the latest update on the current COVID stats such as number of cases and vaccination rate.
**Setting up:**
1. Create your file `covid.py`.
2. At the top of the file (above your import statements), in a comment, include your name and student number.
**Task:**
Using this API and the `requests` library, write a program that constantly prompts the user for input to get the information they want.
It should have the following output format:
1. Prints out: "What country would you like to search for? "
2. Waits for the user to input a country. (You can assume only valid contry names will be entered.)
3. Prints out: "What stats would you like to know? [cases|vaccines] "
4. Waits for the user to enter either "cases" or "vaccines". (You don't need to worry about users entering anything other than these 2.)
5. If the input was cases prints out: "There are [number] confirmed cases" (replace [number] with the number of confirmed cases in the input [country]).
6. Otherwise prints out:
* "As of [date]",
* "[percent] first dose",
* "[percent] second dose"
each on a separate line, refer to the example output below for the format of [date] and [percent]. (Percentages must have 2 decimal places. [documentation here](https://www.w3schools.com/python/ref_func_round.asp))
7. Last go back to step 1 and repeat this process until the user kills the program at any point. (The user can manually terminate any program at any point by using the comand ctrl-C.)
**Example:**
```
What country would you like to search for? India
What stats would you like to know? [cases|vaccines] cases
There are 34189774 confirmed cases
What country would you like to search for? Australia
What info would you like to know? [cases|vaccines] vaccines
As of 2021/10/26
63.68% first dose
76.87% second dose
```
</details>
### 4. (Option 2) Meals (1.5 marks)
The [Meal DB API](https://www.themealdb.com/api.php) can be used to find recipes.
**Setting up:**
1. Create your file `meals.py`.
2. At the top of the file (above your import statements), in a comment, include your name and student number.
**Task:**
Using this API and the `requests` library, complete the following tasks.
1. Write a function `get_categories()` which returns a complete list of categories of recipes available. (This must not be hard-coded, it must access the API each time it's called to get an up-to-date list of categories.)
2. Write a function `get_recipe_names_by_category(category_name)` which takes in the name of a category and returns a list of names of recipes which appear in this category.
3. Put your functions into the code below (see the TODO labels on lines 9 and 12) and run the web application. (You should be able to understand everything this code is doing by reading through it. If you don't understand any part of it, make sure you ask your tutor or lecturer.)
```python=
# Written by Sim Mautner on 10/7/22 for the purpose of COMP1010 labwork.
import requests
from flask import Flask, request
from pyhtml import html, body, select, option, form, input_, li, ul, h1, p, label
app = Flask(__name__)
def get_categories():
return [] # TODO make this your function
def get_recipe_names_by_category(category):
return [] # TODO make this your function
def list_to_dropdown_pyhtml(list_name, my_list):
pyhtml_list = []
for item in my_list:
pyhtml_list.append(option(item))
return select(name=list_name)(pyhtml_list)
def list_to_pyhtml(my_list):
items_in_pyhtml = []
for item in my_list:
items_in_pyhtml.append(li(item))
return ul(items_in_pyhtml)
@app.route('/recipes', methods=['POST'])
def recipes():
my_html = html(
body(
h1("Suggested Recipes"),
p(f"Selected category: {request.form['food_categories']}"),
list_to_pyhtml(get_recipe_names_by_category(request.form['food_categories'])),
form(action='/')(
input_(type='submit', value='Back')
)
)
)
return str(my_html)
@app.route('/', methods=['GET', 'POST'])
def homepage():
my_html = html(
body(
h1("Recipe Suggestions"),
form(action='/recipes')(
label("Select a category: "),
list_to_dropdown_pyhtml("food_categories", get_categories()),
input_(type="submit")
)
)
)
return str(my_html)
if __name__=="__main__":
app.run(debug=True)
```
## CSS (Q5)
**Assumed knowledge:**
* 5.0 - Web Apps Overview
* 5.1 - HTML
* 5.2 - PyHTML
* 5.3 - Flask
**Topics covered:**
* 5.6 - CSS
### Q5 (1.5 marks)
**Task:**
Write a CSS file for your Restaurant Finder application from week 7. Include in your CSS file a comment describing the decisions you made and how these improve the usability of your application. (For these marks you need to show your tutor that it works with your `restaurant_finder.py`.)
If and only if you did not do enough of the Restaurant Finder task, you may do this task for **both** Q1 and Q2 of this lab book instead.
---