generated from mwc/lab_weather
i made it look better
This commit is contained in:
@@ -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
28
weather/forcast.txt
Normal 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'}]
|
||||||
@@ -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}")
|
||||||
@@ -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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user