finished hw 4
This commit is contained in:
BIN
HW 4/__pycache__/hw4.cpython-314.pyc
Normal file
BIN
HW 4/__pycache__/hw4.cpython-314.pyc
Normal file
Binary file not shown.
251
HW 4/hw4.py
Normal file
251
HW 4/hw4.py
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
"""
|
||||||
|
HW 4: Open-Meteo Weather Utility
|
||||||
|
Benjamin Adovasio
|
||||||
|
API documentation: https://open-meteo.com/en/docs
|
||||||
|
|
||||||
|
Description:
|
||||||
|
This utility fetches current weather conditions from the Open-Meteo
|
||||||
|
Forecast API. You provide a latitude and longitude on the command line,
|
||||||
|
and the script prints a small weather report for that location.
|
||||||
|
|
||||||
|
How to run:
|
||||||
|
python3 hw4.py --lat 44.1004 --lon -70.2148
|
||||||
|
python3 hw4.py --lat 44.1004 --lon -70.2148 --temp-unit fahrenheit --wind-unit mph
|
||||||
|
python3 hw4.py --lat 44.1004 --lon -70.2148 --timezone America/New_York
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
|
|
||||||
|
BASE_URL = "https://api.open-meteo.com/v1/forecast"
|
||||||
|
CURRENT_FIELDS = [
|
||||||
|
"temperature_2m",
|
||||||
|
"relative_humidity_2m",
|
||||||
|
"apparent_temperature",
|
||||||
|
"is_day",
|
||||||
|
"precipitation",
|
||||||
|
"weather_code",
|
||||||
|
"cloud_cover",
|
||||||
|
"wind_speed_10m",
|
||||||
|
"wind_direction_10m",
|
||||||
|
]
|
||||||
|
|
||||||
|
#weather codes coppied from api docs
|
||||||
|
WEATHER_CODES = {
|
||||||
|
0: "Clear sky",
|
||||||
|
1: "Mainly clear",
|
||||||
|
2: "Partly cloudy",
|
||||||
|
3: "Overcast",
|
||||||
|
45: "Fog",
|
||||||
|
48: "Depositing rime fog",
|
||||||
|
51: "Light drizzle",
|
||||||
|
53: "Moderate drizzle",
|
||||||
|
55: "Dense drizzle",
|
||||||
|
56: "Light freezing drizzle",
|
||||||
|
57: "Dense freezing drizzle",
|
||||||
|
61: "Slight rain",
|
||||||
|
63: "Moderate rain",
|
||||||
|
65: "Heavy rain",
|
||||||
|
66: "Light freezing rain",
|
||||||
|
67: "Heavy freezing rain",
|
||||||
|
71: "Slight snow fall",
|
||||||
|
73: "Moderate snow fall",
|
||||||
|
75: "Heavy snow fall",
|
||||||
|
77: "Snow grains",
|
||||||
|
80: "Slight rain showers",
|
||||||
|
81: "Moderate rain showers",
|
||||||
|
82: "Violent rain showers",
|
||||||
|
85: "Slight snow showers",
|
||||||
|
86: "Heavy snow showers",
|
||||||
|
95: "Thunderstorm",
|
||||||
|
96: "Thunderstorm with slight hail",
|
||||||
|
99: "Thunderstorm with heavy hail",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def print_help() -> None:
|
||||||
|
"""Print a help message describing how to use the utility."""
|
||||||
|
print(
|
||||||
|
"""Open-Meteo Weather Utility
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 hw4.py --lat LATITUDE --lon LONGITUDE [options]
|
||||||
|
|
||||||
|
Required arguments:
|
||||||
|
--lat LATITUDE Latitude for the weather lookup (-90 to 90)
|
||||||
|
--lon LONGITUDE Longitude for the weather lookup (-180 to 180)
|
||||||
|
|
||||||
|
Optional arguments found via api docs:
|
||||||
|
--temp-unit UNIT celsius or fahrenheit (default: celsius)
|
||||||
|
--wind-unit UNIT kmh, ms, mph, or kn (default: kmh)
|
||||||
|
--timezone ZONE Time zone name or auto (default: auto)
|
||||||
|
--help Show this help message
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(argv: List[str]) -> Optional[Dict[str, str]]:
|
||||||
|
"""Parse command-line arguments using sys.argv-style input."""
|
||||||
|
if not argv or "--help" in argv:
|
||||||
|
print_help()
|
||||||
|
return None
|
||||||
|
|
||||||
|
option_map = {
|
||||||
|
"--lat": "latitude",
|
||||||
|
"--lon": "longitude",
|
||||||
|
"--temp-unit": "temperature_unit",
|
||||||
|
"--wind-unit": "wind_speed_unit",
|
||||||
|
"--timezone": "timezone",
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed = {
|
||||||
|
"temperature_unit": "celsius",
|
||||||
|
"wind_speed_unit": "kmh",
|
||||||
|
"timezone": "auto",
|
||||||
|
}
|
||||||
|
|
||||||
|
index = 0
|
||||||
|
while index < len(argv):
|
||||||
|
flag = argv[index]
|
||||||
|
|
||||||
|
if flag not in option_map:
|
||||||
|
raise ValueError(f"Unknown argument: {flag}")
|
||||||
|
|
||||||
|
if index + 1 >= len(argv):
|
||||||
|
raise ValueError(f"Missing value after {flag}")
|
||||||
|
|
||||||
|
parsed[option_map[flag]] = argv[index + 1]
|
||||||
|
index += 2
|
||||||
|
|
||||||
|
if "latitude" not in parsed or "longitude" not in parsed:
|
||||||
|
raise ValueError("You must provide both --lat and --lon.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
latitude = float(parsed["latitude"])
|
||||||
|
longitude = float(parsed["longitude"])
|
||||||
|
except ValueError as err:
|
||||||
|
raise ValueError("Latitude and longitude must be numbers.") from err
|
||||||
|
|
||||||
|
if not -90 <= latitude <= 90:
|
||||||
|
raise ValueError("Latitude must be between -90 and 90.")
|
||||||
|
|
||||||
|
if not -180 <= longitude <= 180:
|
||||||
|
raise ValueError("Longitude must be between -180 and 180.")
|
||||||
|
|
||||||
|
if parsed["temperature_unit"] not in {"celsius", "fahrenheit"}:
|
||||||
|
raise ValueError("Temperature unit must be celsius or fahrenheit.")
|
||||||
|
|
||||||
|
if parsed["wind_speed_unit"] not in {"kmh", "ms", "mph", "kn"}:
|
||||||
|
raise ValueError("Wind unit must be kmh, ms, mph, or kn.")
|
||||||
|
|
||||||
|
parsed["latitude"] = str(latitude)
|
||||||
|
parsed["longitude"] = str(longitude)
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def weather_description(code: int) -> str:
|
||||||
|
"""Return a readable weather description for an Open-Meteo WMO code."""
|
||||||
|
return WEATHER_CODES.get(code, "Unknown weather condition")
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_weather(query_options: Dict[str, str]) -> Dict:
|
||||||
|
"""Send the GET request to Open-Meteo and return the JSON response."""
|
||||||
|
try:
|
||||||
|
import requests
|
||||||
|
except ModuleNotFoundError as err:
|
||||||
|
raise RuntimeError(
|
||||||
|
"The requests library is not installed. Install it with: "
|
||||||
|
"python3 -m pip install requests"
|
||||||
|
) from err
|
||||||
|
|
||||||
|
# Open-Meteo parameters used in this utility:
|
||||||
|
# - latitude / longitude: required coordinates for the requested location.
|
||||||
|
# - current: tells the API which current-condition fields to include.
|
||||||
|
# - temperature_unit: lets the user choose Celsius or Fahrenheit.
|
||||||
|
# - wind_speed_unit: lets the user choose km/h, m/s, mph, or knots.
|
||||||
|
# - timezone: returns times in the requested zone, or auto-detects the zone.
|
||||||
|
params = {
|
||||||
|
"latitude": query_options["latitude"],
|
||||||
|
"longitude": query_options["longitude"],
|
||||||
|
"current": ",".join(CURRENT_FIELDS),
|
||||||
|
"temperature_unit": query_options["temperature_unit"],
|
||||||
|
"wind_speed_unit": query_options["wind_speed_unit"],
|
||||||
|
"timezone": query_options["timezone"],
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(BASE_URL, params=params, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
except requests.exceptions.RequestException as err:
|
||||||
|
raise ConnectionError(f"Request failed: {err}") from err
|
||||||
|
|
||||||
|
payload = response.json()
|
||||||
|
if payload.get("error"):
|
||||||
|
raise ValueError(payload.get("reason", "The API returned an unknown error."))
|
||||||
|
|
||||||
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
def display_weather_report(payload: Dict) -> None:
|
||||||
|
"""Print a readable weather report from the API response."""
|
||||||
|
current = payload["current"]
|
||||||
|
units = payload["current_units"]
|
||||||
|
code = current["weather_code"]
|
||||||
|
daylight = "Day" if current["is_day"] == 1 else "Night"
|
||||||
|
|
||||||
|
print("Open-Meteo Weather Report")
|
||||||
|
print(f"Coordinates: {payload['latitude']}, {payload['longitude']}")
|
||||||
|
print(f"Timezone: {payload['timezone']}")
|
||||||
|
print(f"Observed at: {current['time']}")
|
||||||
|
print(f"Condition: {weather_description(code)} (WMO code {code})")
|
||||||
|
print(f"Day/Night: {daylight}")
|
||||||
|
print(f"Temperature: {current['temperature_2m']} {units['temperature_2m']}")
|
||||||
|
print(
|
||||||
|
f"Feels like: {current['apparent_temperature']} "
|
||||||
|
f"{units['apparent_temperature']}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"Humidity: {current['relative_humidity_2m']} "
|
||||||
|
f"{units['relative_humidity_2m']}"
|
||||||
|
)
|
||||||
|
print(f"Precipitation: {current['precipitation']} {units['precipitation']}")
|
||||||
|
print(f"Cloud cover: {current['cloud_cover']} {units['cloud_cover']}")
|
||||||
|
print(
|
||||||
|
f"Wind: {current['wind_speed_10m']} {units['wind_speed_10m']} "
|
||||||
|
f"at {current['wind_direction_10m']} {units['wind_direction_10m']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
"""Coordinate argument parsing, API access, and report output."""
|
||||||
|
try:
|
||||||
|
query_options = parse_args(sys.argv[1:])
|
||||||
|
if query_options is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
payload = fetch_weather(query_options)
|
||||||
|
display_weather_report(payload)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
except ValueError as err:
|
||||||
|
print(f"Error: {err}")
|
||||||
|
print()
|
||||||
|
print_help()
|
||||||
|
return 1
|
||||||
|
|
||||||
|
except RuntimeError as err:
|
||||||
|
print(err)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
except ConnectionError as err:
|
||||||
|
print(err)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
except KeyError as err:
|
||||||
|
print(f"Unexpected API response. Missing field: {err}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
13
modify.sh
Normal file
13
modify.sh
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BASE_DIR="$HOME/dcs211_exam"
|
||||||
|
|
||||||
|
cd "$BASE_DIR"/data
|
||||||
|
|
||||||
|
mv notes.txt lab_notes.txt
|
||||||
|
|
||||||
|
mv "$BASE_DIR"/data/lab3.py "$BASE_DIR"/backup/lab3.py
|
||||||
|
|
||||||
|
cp "$BASE_DIR"/data/lab1.py "$BASE_DIR"/scripts/lab1.py
|
||||||
|
|
||||||
|
rm "$BASE_DIR"/data/lab2.py
|
||||||
22
setup_exam.sh
Normal file
22
setup_exam.sh
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BASE_DIR="$HOME/dcs211_exam"
|
||||||
|
|
||||||
|
mkdir "$BASE_DIR"/data
|
||||||
|
mkdir "$BASE_DIR"/scripts
|
||||||
|
mkdir "$BASE_DIR"/backup
|
||||||
|
|
||||||
|
cd "$BASE_DIR"/data
|
||||||
|
|
||||||
|
touch lab1.py
|
||||||
|
touch lab2.py
|
||||||
|
touch lab3.py
|
||||||
|
touch notes.txt
|
||||||
|
touch image1.png
|
||||||
|
touch image2.jpg
|
||||||
|
|
||||||
|
ls *.py
|
||||||
|
|
||||||
|
ls *.png
|
||||||
|
ls *.jpg
|
||||||
|
ls *.gif
|
||||||
Reference in New Issue
Block a user