Course: CSCI 1250

Lesson: Decision Structures

Description: GitHub

import os
from typing import Callable, Optional, TypeVar
 
INVALID_UNIT_ERR = ValueError("Invalid unit. Must be 'C', 'F', or 'K'.")
 
# python generics my beloved
T = TypeVar("T")
 
def prompt(
    msg: str,
    transform: Optional[Callable[[str], T]] = None,
    err_msg="Invalid response, please try again."
) -> T:
    transform_fn = (lambda x: x) if transform is None else transform
 
    while True:
        response = input(msg).strip()
 
        try:
            return transform_fn(response)
        except Exception:
            os.system("cls" if os.name == "nt" else "clear")
            print(err_msg)
 
 
def parse_unit(unit: str) -> str:
    if unit.lower() in ["c", "f", "k"]:
        return unit.lower()
    else:
        raise INVALID_UNIT_ERR
 
 
def calculate_temperatures(temperature: float, unit: str) -> dict[str, float]:
    match unit:
        case "c":
            celsius = temperature
        case "f":
            celsius = (temperature - 32) * 5/9
        case "k":
            celsius = temperature - 273.15
        case _:
            raise INVALID_UNIT_ERR
 
    return {
        "celsius": celsius,
        "fahrenheit": celsius * 9/5 + 32,
        "kelvin": celsius + 273.15,
    }
 
 
def temperature_table(temperatures: dict[str, float]) -> str:
    headers = ["Unit", "Temperature"]
    rows = [
        (unit.title(), f"{temp:.2f}") for unit, temp in temperatures.items()
    ]
 
    max_unit_w = max(len(headers[0]), max(len(row[0]) for row in rows))
    max_temp_w = max(len(headers[1]), max(len(row[1]) for row in rows))
 
    table = f"╭{"─" * (max_unit_w + 2)}┮{"─" * (max_temp_w + 2)}â•Ū\n"
    table += f"│ {headers[0]:<{max_unit_w}} │ {headers[1]:>{max_temp_w}} │\n"
    table += f"├{"─" * (max_unit_w + 2)}┾{"─" * (max_temp_w + 2)}â”Ī\n"
 
    for unit, temp in rows:
        table += f"│ {unit:<{max_unit_w}} │ {temp:>{max_temp_w}} │\n"
 
    table += f"╰{"─" * (max_unit_w + 2)}â”ī{"─" * (max_temp_w + 2)}â•Ŋ"
 
    return table
 
 
def main() -> None:
    temperature = prompt("Enter a temperature: ", float)
    unit = prompt("Enter the unit of the temperature (C/F/K): ", parse_unit)
    temperatures = calculate_temperatures(temperature, unit)
 
    print(f"Based on {temperature:.2f}°{unit.upper()}:")
    print(temperature_table(temperatures))
 
 
if __name__ == "__main__":
    main()