{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# MCS 275 Spring 2022 Worksheet 13 Solutions\n", "\n", "* Course instructor: David Dumas\n", "* Solutions prepared by: Johnny Joyce" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Topics\n", "\n", "\n", "This worksheet focuses on **HTML**, **CSS**, and the basics of the Python web framework **Flask**. (We'll continue working on Flask in the upcoming week, with more Flask-related exercises coming in Worksheet 14.)\n", "\n", "## Resources\n", "\n", "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.\n", "\n", "\n", "* [Lecture 33 - HTML and CSS](http://dumas.io/teaching/2022/spring/mcs275/slides/lecture33.html)\n", "* [Lecture 34 - Planning our web app](http://dumas.io/teaching/2022/spring/mcs275/slides/lecture34.html)\n", "* [Lecture 35 - Flask](http://dumas.io/teaching/2021/spring/mcs275/slides/lecture35.html)\n", "* [w3schools HTML tutorial](https://www.w3schools.com/html/)\n", "* [w3schools CSS tutorial](https://www.w3schools.com/css/)\n", "* [Flask tutorial](https://flask.palletsprojects.com/en/1.1.x/tutorial/)\n", "* [JSFiddle](https://jsfiddle.net/) - place to quickly test HTML+CSS without leaving your browser\n", "* Course sample code:\n", " * [html examples](https://github.com/daviddumas/mcs275spring2022/tree/main/samplecode/html)\n", " * [flask examples](https://github.com/daviddumas/mcs275spring2022/tree/main/samplecode/flask-apps)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. DIY MCS 275 home page\n", "\n", "In MCS 275, all of the following materials are distributed as URLs that link to public web pages:\n", "\n", "* Lecture slides, which have URLs like\n", "```\n", "https://www.dumas.io/teaching/2022/spring/mcs275/slides/lecture35.html\n", "```\n", "* Worksheets, which have URLs like\n", "```\n", "https://www.dumas.io/teaching/2022/spring/mcs275/nbview/worksheets/worksheet13.html\n", "```\n", "* Worksheet solutions, which have URLs like\n", "```\n", "https://www.dumas.io/teaching/2022/spring/mcs275/nbview/worksheets/worksheet13soln.html\n", "```\n", "* Homework assignments, which have URLs like\n", "```\n", "https://www.dumas.io/teaching/2022/spring/mcs275/nbview/homework/homework11.html\n", "```\n", "* Homework solutions, which have URLs like\n", "```\n", "https://www.dumas.io/teaching/2022/spring/mcs275/nbview/homework/homework11soln.html\n", "```\n", "* Projects, which have URLs like\n", "```\n", "https://www.dumas.io/teaching/2022/spring/mcs275/nbview/projects/project3.html\n", "```\n", "\n", "Write a Python script `mcs275spring2022.py` that generates an HTML file (writing it to `mcs275spring2022.html`) that contains a header, sections for each class of course material, and lists of the individual documents, as shown below. (The page will be quite long, so the image below shows its contents in two columns instead of one very tall image. However, the actual page should have just one column of content.) Each list item should be a link to the actual course document.\n", "\n", "**Note: For this program, you're not using Flask. You're making a program that opens and writes to a text file. This is testing your HTML knowledge and your string handling skills.**\n", "\n", "![Unstyled MCS 275 link list](images/ws13-unstyled-mcs275-home.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Solution" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "html = \"\"\"\n", " \n", " \n", " \n", " MCS 275 Spring 2022\n", " \n", " \n", "

MCS 275 Spring 2022

\n", "
\n", "

Lectures

\n", " \n", "
\n", "
\n", "

Worksheets

\n", " \n", "
\n", "
\n", "

Worksheet Solutions

\n", " \n", "
\n", "
\n", "

Homework

\n", " \n", "
\n", "
\n", "

Homework Solutions

\n", " \n", "
\n", "
\n", "

Projects

\n", " \n", "
\n", " \n", " \n", " \"\"\"\n", " \n", "with open(\"mcs275spring2022.html\",\"w\",encoding=\"UTF-8\") as fp:\n", " fp.write(html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Styling the link list\n", "Make the output HTML refer to a stylesheet `courselinks.css` and then create that stylesheet. Add directives so that the body of the document requests the typeface \"Helvetica\", and add other rules to make the rendered `mcs275spring2022.html` look as much as possible like the one below.\n", "\n", "![Styled MCS 275 link list](images/ws13-styled-mcs275-home.png)\n", "\n", "*Note: The actual typeface in this image isn't Helvetica; my browser substituted a similar typeface because it couldn't find the one the CSS specified.*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Solution\n", "\n", "To have the HTML file refer to `courselinks.css`, we add this to the head:\n", "\n", "* ``" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Inside `courselinks.css`:**\n", "\n", "`body {\n", " font-family: \"Helvetica\";\n", " color: white;\n", " background: #303030;\n", "}\n", "a {\n", " color: #C0FFC0;\n", "}\n", "h1, h2 {\n", " background: #505050;\n", "}\n", "ul {\n", " color: #C0FFC0;\n", " background: #603030;\n", "}\n", "li {\n", " background: #303060;\n", "}\n", "`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Base converter\n", "\n", "### A Flask feature\n", "\n", "First, let's discuss a feature of Flask that didn't come up in Lecture 35, but will be useful in this problem.\n", "\n", "Recall that if `app` is a `flask.Flask` object, then the decorator\n", "\n", "```\n", "@app.route(\"/orthogonal/sauntering/balloon/\")\n", "def f():\n", " return \"...HTML HERE...\"\n", "```\n", "will arrange that the function `f` gets called whenever the document `/orthogonal/sauntering/balloon` is requested from the application (e.g. by you opening `http://localhost:8000/orthogonal/sauntering/balloon/` in a web browser).\n", "\n", "This is great for fixed URLs, but sometimes you want many similar URLs to all be handled by a single function. Typically, the function would look at certain parts of the URL to decide what to do. For example, in an application that has pages with information about planets, you could define separate functions for each of\n", "```\n", "/orbital_period/venus/\n", "/orbital_period/earth/\n", "/orbital_period/mars/\n", "```\n", "but it would be nicer to have a single function that can return a page about the orbital period for any of the planets. Flask supports this with a syntax like\n", "\n", "```\n", "@app.route(\"/orbital_period//\") # part inside < and > brackets is a placeholder\n", "def get_period(planet): # the string appearing there gets passed as an argument\n", " return \"...HTML HERE...\"\n", "```\n", "so that a request for `/orbital_period/earth/` will call `get_period(\"earth\")` while a request for `/orbit_period/mars/` will call `get_period(\"mars\")`. Note that the parameters of the function need to match the names given inside the `<...>` appearing in the route description (e.g. `` in the route description means we need a parameter called `planet`).\n", "\n", "### The task\n", "\n", "Write a Flask application `baseconvert.py` that can convert between decimal, binary, and hexadecimal, based on a request encoded as a URL such as\n", "```\n", "/decimal/120/to/binary/\n", "/binary/1001/to/hexadecimal/\n", "/hexadecimal/f00f/to/decimal/\n", "```\n", "with the general syntax being \n", "```\n", "/INBASE/N/to/OUTBASE/\n", "```\n", "where `INBASE` and `OUTBASE` are each one of `decimal`, `binary`, or `hexadecimal`, and where `N` is a sequence of digits representing an integer in the number system `INBASE`.\n", "\n", "The application should only have a single route which decides what to do based on parts of the URL.\n", "\n", "Thus, for example, a request for `/decimal/9/to/binary/` might generate this HTML code as a response:\n", "```\n", "\n", "\n", "\n", "Decimal 9 is binary 1001\n", "\n", "\n", "

9 = 0b1001

\n", "\n", "\n", "```\n", "and for `/hexadecimal/1f/to/decimal/`, the HTML your application sends as the response might be:\n", "```\n", "\n", "\n", "\n", "Hexadecimal 1f is decimal 31\n", "\n", "\n", "

0x1f = 31

\n", "\n", "\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Solution" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " * Serving Flask app \"__main__\" (lazy loading)\n", " * Environment: production\n", "\u001b[31m WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n", "\u001b[2m Use a production WSGI server instead.\u001b[0m\n", " * Debug mode: off\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", "127.0.0.1 - - [14/Apr/2022 14:48:36] \"GET / HTTP/1.1\" 404 -\n", "127.0.0.1 - - [14/Apr/2022 14:49:00] \"GET /binary/1010101010/to/decimal HTTP/1.1\" 308 -\n", "127.0.0.1 - - [14/Apr/2022 14:49:00] \"GET /binary/1010101010/to/decimal/ HTTP/1.1\" 200 -\n" ] } ], "source": [ "from flask import Flask\n", "\n", "app = Flask(__name__)\n", "\n", "\n", "def make_title(inbase, n, outbase, converted):\n", " '''Helper function for `convert`. Returns HTML code of title'''\n", " if outbase == \"binary\" or outbase == \"hexadecimal\": # If applicable, remove prefixes \"0b\" or \"0x\"\n", " converted = str(converted)[2:] \n", " return \"{} {} is {} {}\".format(inbase, n, outbase, converted)\n", " \n", " \n", "def make_body(inbase, n, outbase, converted):\n", " '''Helper function for `convert`. Returns HTML code of body'''\n", " prefix = {\"decimal\": \"\", \"binary\": \"0b\", \"hexadecimal\": \"0x\"}\n", " n = prefix[inbase] + n # If applicable, put prefix \"0b\" or \"0x\" in front of the number given by user\n", " return \"

{} = {}

\".format(n, converted)\n", "\n", "\n", "@app.route(\"///to//\") # Each entry inside < > will become an argument for convert()\n", "def convert(inbase, n, outbase):\n", " '''Converts number `n` from base `inbase` to base `outbase`. Returns as HTML code.'''\n", " \n", " # First, interpret n as a decimal number (regardless of outbase)\n", " base_dict = {\"decimal\": 10, \"binary\": 2, \"hexadecimal\": 16}\n", " as_decimal = int(n, base_dict[inbase])\n", " \n", " # Now convert decimal to required outbase\n", " if outbase == \"decimal\":\n", " converted = as_decimal\n", " elif outbase == \"binary\":\n", " converted = bin(as_decimal)\n", " elif outbase == \"hexadecimal\":\n", " converted = hex(as_decimal)\n", " \n", " # Call helper functions. Assemble and return the HTML code\n", " title = make_title(inbase, n, outbase, converted)\n", " body = make_body(inbase, n, outbase, converted)\n", " html = '''\n", " \n", " \n", " {} \n", " {} \n", " \n", " '''.format(title, body)\n", " return html\n", "\n", "app.run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" } }, "nbformat": 4, "nbformat_minor": 4 }