Server-Side Scripting/Internet Data/Python (FastAPI)

routers/lesson8.py

edit
# This program reads JSON data from Wikidata with countries
# and Celsius temperatures. It displays the data in Celsius
# and Fahrenheit sorted in decending order by temperature.
#
# References:
#   https://www.mathsisfun.com/temperature-conversion.html
#   https://en.wikibooks.org/wiki/Python_Programming
#   https://www.wikidata.org/wiki/Wikidata:SPARQL_query_service

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

import json
import urllib.parse
import urllib.request

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

URL = "https://query.wikidata.org/sparql"
QUERY = """
SELECT DISTINCT ?Country ?MaximumTemperature WHERE {
  ?countryItem wdt:P31 wd:Q6256;
    p:P6591 ?maximumTemperatureRecord.
  ?maximumTemperatureRecord psv:P6591 ?maximumTemperatureValue.
  ?maximumTemperatureValue wikibase:quantityAmount ?maximumTemperatureQuantity;
    wikibase:quantityUnit ?temperatureUnit.
  {
    ?countryItem rdfs:label ?Country.
    FILTER((LANG(?Country)) = "en")
  }
  {
    ?temperatureUnit wdt:P5061 ?unitSymbol.
    FILTER((LANG(?unitSymbol)) = "en")
    FILTER(CONTAINS(?unitSymbol, "C"))
  }
  BIND(CONCAT(STR(?maximumTemperatureQuantity), " ", ?unitSymbol) AS ?MaximumTemperature)
}
ORDER BY (?Country)
"""

@router.get("/", response_class=HTMLResponse)
async def get_lesson8(request: Request):
    try:
        result = await get_data(URL, QUERY)
    except Exception as exception:
        result = exception

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

async def get_data(url, query):
    try:
        data = get_raw_data(url, query)
        records = get_records(data)
        records.sort(key=lambda x:x["celsius"], reverse=True)
        result = format_table(records)
        return result
    except Exception as exception:
        return str(exception)
        
def get_raw_data(url, query):
    url += "?query=" + urllib.parse.quote(query)
    url += "&format=json"
    data = urllib.request.urlopen(url).read().decode()
    data = json.loads(data)
    return data

def get_records(data):
    records = []
    for dictionary in data["results"]["bindings"]:
        record = get_record(dictionary)
        records.append(record)
    records.sort(key=lambda x:x["celsius"], reverse=True)
    return records 

def get_record(dictionary):
    country = dictionary["Country"]["value"]
    celsius = dictionary["MaximumTemperature"]["value"]

    index = celsius.find(" °C")
    if index < 0:
        raise ValueError("Invalid data format")

    try:
        celsius = float(celsius[0:index])
        fahrenheit = celsius * 9 / 5 + 32
    except:
        raise ValueError("Invalid temperature data")

    record = {}
    record["country"] = country
    record["celsius"] = celsius
    record["fahrenheit"] = fahrenheit
    return record

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

    for record in records:
        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/lesson8.html

edit
<!DOCTYPE html>
<html lang="en">

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

<body>
    <h1>Temperature Conversion</h1>
    <h2>Wikidata</h2>
    {{table|safe}}
</body>

</html>

Try It

edit

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

See Also

edit