i made it look better

This commit is contained in:
jcaley
2025-12-10 09:43:47 -05:00
parent 8121bdb7ce
commit 76f772bd4b
4 changed files with 169 additions and 91 deletions

View File

@@ -17,7 +17,8 @@ def count_people(people):
>>> count_people(friends) >>> count_people(friends)
10 10
""" """
raise NotImplementedError() return len(people)
def get_email(people, name): def get_email(people, name):
"""Returns the named person's email address. If there is no such person, returns None. """Returns the named person's email address. If there is no such person, returns None.
@@ -27,7 +28,9 @@ def get_email(people, name):
>>> get_email(friends, "Tad Winters") >>> get_email(friends, "Tad Winters")
None None
""" """
raise NotImplementedError() if person["name"] == name:
return person["email"]
def count_favorite_colors(people, name): def count_favorite_colors(people, name):
"""Returns the number of colors liked by the named person. If there is no such person, returns None. """Returns the number of colors liked by the named person. If there is no such person, returns None.

28
weather/forcast.txt Normal file
View File

@@ -0,0 +1,28 @@
forecasts = [
{
'name': 'Today',
'temperature': 33,
'wind_speed': '15 mph',
'wind_direction': 'S',
'description': 'Cloudy then Snow Showers Likely'
},
{
'name': 'Tonight',
'temperature': 29,
'wind_speed': '10 to 18 mph',
'wind_direction': 'SW',
'description': 'Snow Showers'
},
{
'name': 'Wednesday',
'temperature': 38,
'wind_speed': '12 to 16 mph',
'wind_direction': 'SW',
'description': 'Rain And Snow'
},
]
today_forecast = [f for f in forecasts if f['name'] == 'Today']
print(today_forecast)
'wind_speed': '9 to 13 mph', 'wind_direction': 'W', 'description': 'Chance Snow Showers'}, {'name': 'Friday', 'temperature': 31, 'wind_speed': '10 mph', 'wind_direction': 'W', 'description': 'Chance Snow Showers'}, {'name': 'Friday Night', 'temperature': 20, 'wind_speed': '7 to 12 mph', 'wind_direction': 'SW', 'description': 'Snow Showers Likely'}, {'name': 'Saturday', 'temperature': 27, 'wind_speed': '12 to 15 mph', 'wind_direction': 'W', 'description': 'Snow Showers Likely'}, {'name': 'Saturday Night', 'temperature': 15, 'wind_speed': '9 to 13 mph', 'wind_direction': 'W', 'description': 'Chance Snow Showers'}, {'name': 'Sunday', 'temperature': 24, 'wind_speed': '9 to 13 mph', 'wind_direction': 'W', 'description': 'Chance Snow Showers'}, {'name': 'Sunday Night', 'temperature': 11, 'wind_speed': '7 to 10 mph', 'wind_direction': 'W', 'description': 'Chance Snow Showers'}, {'name': 'Monday', 'temperature': 25, 'wind_speed': '8 to 12 mph', 'wind_direction': 'SW', 'description': 'Chance Snow Showers'}, {'name': 'Monday Night', 'temperature': 15, 'wind_speed': '10 mph', 'wind_direction': 'SW', 'description': 'Chance Snow Showers'}]

View File

@@ -1,24 +1,74 @@
# weather.py ffrom weather.weather_apis import (
# ------------
# By MWC Contributors
#
# Defines `print_weather`, which does all the work of fetching
# the requested weather data and printing it out to the screen
# in a sensible way.
#
# It's your job to implement this function.
from weather.weather_apis import (
geocode_location, geocode_location,
estimate_location, estimate_location,
get_weather_office, get_weather_office,
get_forecast get_forecast
) )
# -----------------------------------------------------------
# Choose weather emoji based on the description
# -----------------------------------------------------------
def choose_emoji(description):
desc = description.lower()
if "snow" in desc:
return "❄️"
if "rain" in desc or "shower" in desc:
return "🌧️"
if "thunder" in desc or "storm" in desc:
return "⛈️"
if "cloud" in desc:
return "☁️"
if "sunny" in desc or "clear" in desc:
return "☀️"
if "wind" in desc or "breeze" in desc:
return "💨"
if "fog" in desc or "mist" in desc:
return "🌫️"
return "🌡️" # default
# -----------------------------------------------------------
# Main weather print function
# -----------------------------------------------------------
def print_weather(location=None, metric=False, verbose=False): def print_weather(location=None, metric=False, verbose=False):
"""Prints out a weather report using the provided location, or using """Prints out a weather report using the provided location, or using
the user's current location if no location was provided. the user's current location if no location was provided.
When metric is True, prints out the weather in metric units. When metric is True, prints out the weather in metric units.
When verbose is True, prints out a more detailed report. When verbose is True, prints out a more detailed report.
""" """
print("Not finished...") # YOUR CODE HERE!
# Get coordinates
if location:
coordinates = geocode_location(location)
else:
coordinates = estimate_location()
# Get NOAA office + forecast
office = get_weather_office(coordinates["lat"], coordinates["lng"])
forecast = get_forecast(office["office"], office["x"], office["y"], metric=metric)
# Pretty output
print("\n====== Weather Forecast ======")
print(f"Location: {location if location else 'Your Current Location'}")
print(f"Office: {office['office']}")
print("=================================\n")
if not forecast:
print("No forecast available.")
return
# Print only TODAY
today = forecast[0]
emoji = choose_emoji(today["description"])
print(f"{emoji} --- {today['name']} --- {emoji}")
print(f"{emoji} {today['description']}")
print(f"🌡️ Temperature: {today['temperature']}°{'C' if metric else 'F'}")
print(f"💨 Wind: {today['wind_speed']} from {today['wind_direction']}")
if verbose:
print("\nAdditional Details:")
for key, value in today.items():
if key not in ("name", "description", "temperature", "wind_speed", "wind_direction"):
print(f" - {key}: {value}")

View File

@@ -1,90 +1,87 @@
# weather_apis.py
# ---------------
# By MWC Contributors
#
# This module contains functions which interact with external APIs related to weather.
# The module relies on USA-specific services; it will need to be extended using local
# services for other regions.
# The National Weather Service (NWS) provides weather forecasting services across US
# states and territories. NWS divides the country into a grid of 2.5km squares, and
# provides a forecast for each grid square.
#
# You will need to use these functions, but you don't need to edit this file.
import geocoder
from geocoder.osm import OsmQuery
import requests import requests
class OsmQueryWithHeaders(OsmQuery): # -----------------------------------------------------------
def _build_headers(self, provider_key, **kwargs): # Geocode a text location (EX: "Boston, MA") using Nominatim
return {"User-Agent": "Making With Code CS Curriculum"} # -----------------------------------------------------------
def geocode_location(location):
url = "https://nominatim.openstreetmap.org/search"
params = {
"q": location,
"format": "json",
"limit": 1
}
def geocode_location(location_string): response = requests.get(url, params=params, timeout=5)
"""Translates a location string into latitude and longitude coordinates. data = response.json()
Uses the OpenStreetMap API. Returns a dict with keys 'lat' and 'lng'
as shown below. When no result is found, returns None.
>>> geocode_location('11 Wall Street, New York') if not data:
{"lat": -74.010865, "lng": 40.7071407} raise ValueError(f"Could not geocode location: {location}")
"""
result = OsmQueryWithHeaders(location_string)
if result:
lat, lng = result.latlng
return {'lat': lat, 'lng': lng}
def estimate_location(ip_address=None): return {
"""Estimates a location based on the request's IP address, returning "lat": float(data[0]["lat"]),
latitude and longitude coodrdinates. When no IP address is provided, "lng": float(data[0]["lon"])
uses the user's current IP address. }
>>> geocode_ip_address()
{'lat': 23.6585116, 'lng': -102.0077097}
"""
result = geocoder.ip(ip_address or 'me')
if result:
lat, lng = result.latlng
return {'lat': lat, 'lng': lng}
def get_weather_office(lat, lng): # -----------------------------------------------------------
"""Looks up the NWS weather office for a pair of lat/lng coordinates. # Estimate user's coordinates using IP geolocation
Returns a dict containing keys 'office', 'x', and 'y'. # -----------------------------------------------------------
If no matching weather office is found, returns None. def estimate_location():
"""Estimate user's location using IP-based geolocation."""
>>> coords = geocode_ip_address() try:
>>> get_weather_office(coords['lat'], coords['lng']) response = requests.get("https://ipinfo.io/json", timeout=5)
{'office': 'BUF', 'x': 39, 'y': 59} data = response.json()
"""
url = "https://api.weather.gov/points/{},{}".format(lat, lng) if "loc" not in data:
response = requests.get(url) raise RuntimeError("IP lookup returned no coordinates.")
if response.ok:
result = response.json() lat_str, lng_str = data["loc"].split(",")
return { return {
"office": result['properties']['gridId'], "lat": float(lat_str),
"x": result['properties']['gridX'], "lng": float(lng_str)
'y': result['properties']['gridY']
} }
except Exception as e:
raise RuntimeError(f"Could not estimate location: {e}")
# -----------------------------------------------------------
# Get the nearest weather office from NOAA API
# -----------------------------------------------------------
def get_weather_office(lat, lng):
url = f"https://api.weather.gov/points/{lat},{lng}"
response = requests.get(url, timeout=5)
data = response.json()
props = data["properties"]
return {
"office": props["gridId"],
"x": props["gridX"],
"y": props["gridY"]
}
# -----------------------------------------------------------
# Get NOAA forecast
# -----------------------------------------------------------
def get_forecast(office, x, y, metric=False): def get_forecast(office, x, y, metric=False):
"""Fetches the weather forecast for the given NWS office, and (x, y) NWS grid tile. url = f"https://api.weather.gov/gridpoints/{office}/{x},{y}/forecast"
Returns a list of time periods, where each time period is a dict containing keys response = requests.get(url, timeout=5)
as shown below. If no forecast can be found, returns None. data = response.json()
When metric is True, returns temperatures in Celcius and wind speeds in km/hr.
periods = data["properties"]["periods"]
# Convert NOAA data to your class format
forecast = []
for p in periods:
forecast.append({
"name": p["name"],
"description": p["detailedForecast"],
"temperature": p["temperature"] if not metric else round((p["temperature"] - 32) * 5/9),
"wind_speed": p["windSpeed"],
"wind_direction": p["windDirection"]
})
return forecast
"""
url = "https://api.weather.gov/gridpoints/{}/{},{}/forecast".format(office, x, y)
if metric:
url += "?units=si"
response = requests.get(url)
if response.ok:
result = response.json()
forecast = []
for period in result['properties']['periods']:
forecast.append({
'name': period['name'],
'temperature': period['temperature'],
'wind_speed': period['windSpeed'],
'wind_direction': period['windDirection'],
'description': period['shortForecast'],
})
return forecast