Jump to content

Server-Side Scripting/Associative Arrays/Python (FastAPI)

From Wikiversity

routers/lesson7.py

[edit | edit source]
# This program reads a user-selected text file of countries
# and Celsius temperatures. It displays the data in Celsius
# and Fahrenheit sorted in descending order by temperature.
#
# File format:
# Country,MaximumTemperature
# Bulgaria,45.2 °C
# Canada,45 °C
#
# References:
#   https://www.mathsisfun.com/temperature-conversion.html
#   https://fastapi.tiangolo.com/tutorial/request-files/

from fastapi import APIRouter, Request, File, UploadFile
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

router = APIRouter(prefix="/lesson7")
templates = Jinja2Templates(directory="templates")

@router.get("/", response_class=HTMLResponse)
async def get_lesson7(request: Request):
    return templates.TemplateResponse(
        "lesson7.html", 
        {   
            "request": request,
            "table": ""
        }
    )

@router.post("/", response_class=HTMLResponse)
async def post_lesson7(request: Request, file: UploadFile = File(...)):
    try:
        result = await process_file(file)
    except Exception as exception:
        result = exception

    return templates.TemplateResponse(
        "lesson7.html", 
        {   
            "request": request,
            "table": result
        }
    )

async def process_file(file):
    if not file:
        return "No file selected"

    text = await file.read()
    text = text.decode()
    lines = text.strip().split("\n")
    if "country," in lines[0].lower():
        lines.pop(0) # Remove heading line

    table = []
    for line in lines:
        try:
            array = process_line(line)
            table.append(array)
        except ValueError as exception:
            return str(exception)

    table.sort(key=lambda x:x["celsius"], reverse=True)

    result = f"<h2>{file.filename}</h2>"
    result += format_table(table)
    return result

def process_line(line):
    record = {}
    array = line.split(",")
    if len(array) != 2:
        raise ValueError("Invalid file format")

    record["country"] = array[0]
    celsius = array[1]
    index = celsius.find(" °C")
    if index < 0:
        raise ValueError("Invalid file format")

    try:
        celsius = float(celsius[0:index])
    except:
        raise ValueError("Invalid temperature data")

    record["celsius"] = celsius
    fahrenheit = celsius * 9 / 5 + 32
    record["fahrenheit"] = fahrenheit
    return record

def format_table(table):
    result = "<table><tr><th>Country</th>"
    result += "<th>Celsius</th>"
    result += "<th>Fahrenheit</th></tr>"

    for record in table:
        result += f"<tr><td>{record['country']}</td>" 
        result += f"<td>{record['celsius']:.1f}° C</td>"
        result += f"<td>{record['fahrenheit']:.1f}° F</td></tr>"

    result += "</table>"
    return result

templates/lesson7.html

[edit | edit source]
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Lesson 7</title>
    <link rel="stylesheet" href="{{url_for('static', path='/styles.css')}}">
    <style>
        p {
            min-height: 1em;
        }
    </style>
</head>

<body>
    <h1>Temperature Conversion</h1>
    <p>Select a file of Celsius temperatures for conversion:</p>
    <form method="POST" enctype="multipart/form-data">
        <input type="file" id="file" name="file">
        <input type="submit">
    </form>
    {{table|safe}}
</body>

</html>

Try It

[edit | edit source]

See Server-Side Scripting/Routes and Templates/Python (FastAPI) to create a test environment.

See Also

[edit | edit source]