Table of Contents
If you find typos or problems with the lab instructions, please let us know.
Ok, a web page that prints “Hello World” is not really very compelling. Let’s do something a bit more interesting.
In part 2 of the SPIS webapps tutorial, we’ll learn about how to:
Perform a calculation based on a parameter that’s in the URL
Start building a larger multi-page web application
Part 2 is individual; each of you should do it separately, though you are allowed (in fact encouraged) to help one another if/when you are stuck.
Consider the following function that converts Fahrenheit to Celsius. Here it is:
def ftoc(ftemp):
return (ftemp - 32.0) * (5.0 / 9.0)
To check your understanding, verify for yourself that ftoc(212) returns 100 (the boiling point of water) and ftoc(32) returns 0.
Let’s add this to our main.py file, so that we have the following (new code shown in bold):
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
def ftoc(ftemp):
return (ftemp - 32.0) * (5.0 / 9.0)
if __name__ == "__main__":
app.run(host='0.0.0.0')
Now, you can test your function by doing this:
Click on the "Terminal" tab above the lower-right portion of your screen. Now that you're at the command line type ipython and press Enter/Return to start the IPython interpreter, which is a predecessor to Jupyter/Colab notebooks.
Type from main import * and press Enter/Return
This loads the functions from main.py: you can now use the ftoc function.
In the IPython session in the Terminal window, type various function calls such as ftoc(212.0) and ftoc(32.0) in the Python Console
If your ftoc function works and you are satisfied, enter exit() to leave IPython.
You are good to go to the next step where we connect this function to the web app.
To make this code work with the web app, we need to add a few more lines of code. The new code is shown in bold below.
First, we’ll just do it and see what it does, then we’ll unpack each line of code and explain its purpose below.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
def ftoc(ftemp):
return (ftemp - 32.0) * (5.0 / 9.0)
@app.route('/ftoc/<ftemp_str>')
def convert_ftoc(ftemp_str):
ftemp = 0.0
try:
ftemp = float(ftemp_str)
ctemp = ftoc(ftemp)
return "In Fahrenheit: " + ftemp_str + " In Celsius: " + str(ctemp)
except ValueError:
return "Sorry. Could not convert " + ftemp_str + " to a number"
if __name__ == "__main__":
app.run(host='0.0.0.0')
After doing that, try running your new code again by clicking the Play button at the top. Once again your site will open in a new tab.
You should now be able to put in URLs such as the ones below, where the last part is a Fahrenheit temperature you want to convert to Celsius:
Note: Make sure to replace the first part of these URLs (https://YOUR_URL) with your own URL that you copied and put into a new tab.
For example if your Codespace is accssible at the URL https://upgraded-spoon-jq7j76576rj3jp47.github.dev/, then the URL will be https://upgraded-spoon-jq7j76576rj3jp47-5000.app.github.dev/
Try it and see what you get. Also try some URLs where the last part is not valid, e.g.
If you’ve gotten this far, you’ve gotten a great start.
Check in with your partner to make sure that both of you have succeeded up to this point.
One of the keys to understanding how Flask works is to focus first on these lines of code in main.py
@app.route('/')
@app.route('/ftoc/<ftemp_str>')
The parts of our code that start with the @ sign are called decorators. In this case, they come right before a function definition, and they tell Python to do something special with the function definition that follows.
In this case, the @app.route(path) decorator indicates that URLs that end in path should be routed to the function that follows.
In the path parameter of the app.route decorator, anything in angle brackets, such as <ftemp_str> stands for a value that is passed into the function as a parameter. By default, these are always of type str (string), just like when you call the built-in input() function, and therefore have to be converted if they are going to be used as something else (e.g. float, int, etc.).
Now try some things on your own.
Now, let's test your knowledge of what you've seen so far by seeing if you can extend this web app on your own. (This is still individual work, but you may consult with your pair partner if you need support.) If you run into any problems, (e.g. the dreaded "Internal Server Error" see the Troubleshooting section below
Add a function that converts Celsius to Fahrenheit, i.e. def ctof(ctemp): that just like the ftoc function, is a “pure” function, i.e. it “doesn’t know its part of a web app”, and computes an answer only based on its parameters.
Then, add a function with a decorator @app.route('/ctof/<ctemp_str>') that will convert the ctemp_str into a number, pass it into the function, and produce output. You can use the convert_ftoc function as an example to build on.
Add a pure function that converts miles to kilometers, i.e. def miles_to_km(miles):
Then, add a function with a decorator @app.route('/mtokm/<miles_str>') that will convert the miles_str into a number, pass it into the function, and produce output.
If that works, then see if you can each write your own function (each pair partner)— any Python function of your choosing—and then add it to the web app. It could be a conversion, but it could be also anything that we can compute in Python.
Remember that the parameters to your @app.route decorator are always strings (type str) by default, so you may need to convert between types. Flask actually does allow you to specify a number to some extent. If you write, say, @app.route('/mtokm/<int:miles>') then the variable miles in miles_to_km will have type int. But Flask will only do the routing if you have an actual integer in the URL: if you include a decimal or a string then you'll get a 404 Not Found error and Flask will not call the miles_to_km function. You could also write @app.route('/mtokm/<float:miles>') but then Flask won't match an integer, so this method is less flexible.
Note that you can use a route such as @app.route('some-route/<a>/<b>/<c>') if you wanted to have a function that took three inputs instead of just one.
The function that handles that would look something like this. Here we are just echoing the values to the web page, but it would be nice to do something more interesting, such as computing the total length, finding the longest or shortest of the three, etc.
Note: For your new changes to be rendered, you must close your server by clicking in the Terminal window and pressing Ctrl-C or Cmd-C and then rerun it (by pressing the Play button again).
Here's another example of a custom function. Note that yours shouldn't be an exact copy of this one. Be creative and try to come up with something else. (You may use functions you already studied elsewhere during SPIS though; the idea is to learn how to use a web app to provide into to the function, and display the result.)
The pure function. It takes three strings a, b, c as parameters, puts them in a list, sorts the list, then returns it.
The handler for the web request. Note that since the parameters a, b, and c are assumed to be strings, we don't have to do any conversion. We only have to convert the resulting list to a string (via str(result)) so that we can return it
An example call to the function:
If you get the Internal Server Error, like this, don't panic! What to do is explained below.
This means something went wrong in your app, and typically if you look in the Console window, you'll see what. For example, suppose we have this function, which has an error in it. (Can you spot the error? Spoiler alert: VS Code has underlined it with a wavy red line)
When we run this, we get Internal Server Error. But we also get this output in the Terminal window. It's a bit of a scavenger hunt, but if you read slowly and carefully, eventually you find the helpful part. We zoom in on this in the image below.
Fix the bug, and the Internal Server Error goes away!
In general, this is how you'll find and fix errors in your web app.
The important thing I want you to take away is that, at this point, we have a way to:
Take anything you can do in Python with a function
Convert that into a web app that anyone in the world can use to run your function
That's pretty cool. You can take your Python code, and expose it to a global audience.
Now, granted, there's still long ways to go between this, and professional looking web apps:
The user interface isn't great: we'd like to be able to prompt users for input with nice forms, instead of asking them to type numbers into a URL
The output isn't great either: so far, it's just plain looking text.
We haven't talked about security yet: if you wanted to have only some people have access to your app, and not others, or if different users should have different roles (e.g. mentees, mentors, instructors). That is still to come.
We also haven't talked about data storage yet: if you want to upload some information into your web app and have it remember that so you can come back to it later? That's still very TBD.
But, the important thing is that you have a way to take your Python programming, and share it with the world. We hope that gives you some motivation to keep going!
Now, let's open the README.md file in your repo in the Codespace tab.
Add links for the running web apps that you've created. Include a link to the root URL such as
https://upgraded-spoon-jq7j76576rj3jp47-5000.app.github.dev/
In addition, include a description of the custom function you wrote in Step 4, and the URL used to access it.
For example, supposed you wrote a function that determines which string is longer between two strings, def longer_string(string1, string2), and you implemented that using a web address /longer_string/string1/string2, you'd write something like in your README.md file. The link should be clickable when you save the README, and should execute your function.
My custom function is named longer_string and it is available at the web address:
https://upgraded-spoon-jq7j76576rj3jp47-5000.app.github.dev/longer/short/much_much_longer
Also include links to tests of the ftoc, ctof, and mtokm functions, such as these:
ftoc test: https://upgraded-spoon-jq7j76576rj3jp47-5000.app.github.dev/ftoc/212
ctof test: https://upgraded-spoon-jq7j76576rj3jp47-5000.app.github.dev/ctof/20
mtokm test: https://upgraded-spoon-jq7j76576rj3jp47-5000.app.github.dev/mtokm/143
Each of the links should do the appropriate conversion when you access them.
This step is important, because it's how the mentors can get access to your work to give you feedback.
That means whenever you're done with coding for Part 2, leave the webserver running. It will continue in the background. When you come back, the Terminal window should still be showing the output of the web server (meaning that you could stop it with Ctrl-C).
Follow the instructions Step 4 of Part 1 of this lab to commit and push your changes to GitHub:
In the left sidebar, click on the Source Control icon (it looks like three circles connected by a few lines).
Under "Changes," you should see a list of the files you've worked on so far. Click on the "+" in each row for each file you want to add to your commit (right now this should just be main.py).
Each commit must contain a message, so type a message such as "Finished lab 08 part 2" in the Message box. Then click Commit to finish creating the commit.
The blue button you just clicked should now read "Sync Changes 1" where 1 is the number of commits in your local clone that have not yet been pushed to GitHub. Click the button again to push.
We've been creating branches to keep track of your milestones. Create a branch called part2 as you did with part1 so that the mentors can see that state of your code right now (in case you later change anything). Here are the instructions again:
In the Source Control sidebar, click the "..." in the upper right to open a menu. Click "Branch" -> "Create Branch ...".
In the box that appears enter "part2" as your branch name.
Click the blue "Publish Branch" button in the Source Control sidebar.
There may be a pop-up in the lower right corner of a screen that asks if you want to create a pull request. Click the button to dismiss the pop-up -- you don't need this.
Now we want to switch back to working on the main branch. Click the "..." again, and then select "Checkout to..." from the menu. Another selection should appear at the top of the screen. Choose "main" (not "origin/main") to switch back to working on the main branch.
Our next steps will be to learn a bit about:
HTML and CSS, the languages we use to make our web pages look like “real web pages” instead of just plain text.
Both of those are for our next lesson, but we can at least get a head start if you’ve finished this material early.
So, at the moment, the values we are returning from our functions are plain text that shows up in the browser. That’s fine for getting started, but eventually we’d like something that looks like a “real web page”. For that, we’ll need to learn a little bit about HTML and CSS.
If you have not worked with HTML/CSS before
Get started on learning HTML and CSS now using one of the best free resources on the web, the site: w3schools
We suggest you do the following:
Visit the HTML tutorial and read/skim over the first seven lessons
After learning some HTML, learn a bit of CSS by reading over the first four lessons of the CSS tutorial
After each lesson check in with your pair partner to make sure both of you are understanding the content.
Side note: the Python tutorial is also "not bad" as a quick reference / review.
If you and your partner want to learn more about HTML/CSS (or Python) you can spend more time on the website.
Up until now, we've been working individually to make sure that each member of the pair or trio has the basic mechanics down.
In the next part of the lab, there is about to be a deeper dive into webapps content.
Begin working with your partner together for the rest of the lab and before continuing, make sure that both of you have a solid understanding of the beginning content of this lab. After both of you feel that you have a good understanding of the beginning content of the lab, continue through the rest of the lab, making sure both of you have a good understanding of the material.
Then, move on to Part 3: Templates