Created
March 18, 2023 01:50
-
-
Save WxBDM/82869afd9eeccbe00e9b8d023453dc0c to your computer and use it in GitHub Desktop.
Make a request from the National Weather Service API. This uses a flask server to make the request and returns a few weather parameters for a given station (currently KJFK). This also comes coupled together with a front-end.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Weather Data</title> | |
<link rel="stylesheet" href="static/style.css"> | |
<script src="static/weather.js"></script> | |
</head> | |
<body> | |
<h1>Current Weather for <span class="station"></span></h1> | |
<p>Temperature: <span class="temperature"></span></p> | |
<p>Wind Speed: <span class="wind-speed"></span></p> | |
<p>Pressure: <span class="pressure"></span></p> | |
<h2>Weather Data History</h2> | |
<table class="weather-data-history"> | |
<thead> | |
<tr> | |
<th>Date</th> | |
<th>Temperature</th> | |
<th>Wind Speed</th> | |
<th>Pressure</th> | |
</tr> | |
</thead> | |
<tbody> | |
</tbody> | |
</table> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import threading | |
from flask import Flask, jsonify, make_response | |
import logging | |
import aiohttp | |
import asyncio | |
from datetime import datetime | |
import logging.handlers | |
logging.basicConfig(level=logging.INFO) | |
# == IMPORTANT== | |
# The css, javascript, and html file must be put into | |
# the static/ directory. Your structure should look like: | |
# folder/ | |
# ├── server.py | |
# └── static/ | |
# ├── index.html | |
# ├── style.css | |
# └── weather.js | |
# =============== | |
# === modify the following, as they're required by the api, otherwise a 403 forbidden error will happen. | |
application_name = "app_name" | |
your_email = "your_email" | |
station = 'KJFK' | |
# ==== | |
API_ENDPOINT = f'https://api.weather.gov/stations/{station}/observations/latest' | |
app = Flask(__name__) | |
weather_data = {} | |
async def get_weather_data(): | |
global weather_data | |
headers = {'User-Agent': application_name + ", " + your_email} | |
async with aiohttp.ClientSession() as session: | |
try: | |
async with session.get(API_ENDPOINT, headers=headers) as response: | |
response.raise_for_status() | |
data = await response.json() | |
temperature = data['properties']['temperature']['value'] | |
wind_speed = data['properties']['windSpeed']['value'] | |
pressure = data['properties']['barometricPressure']['value'] | |
# etc. - extract any other weather data you need | |
# Update the weather data | |
weather_data.update({ | |
'temperature': temperature, | |
'wind_speed': wind_speed, | |
'pressure' : pressure, | |
'station' : station | |
}) | |
# Log the weather data | |
logging.info(f"{datetime.now()}: Temperature: {temperature}°C, Wind Speed: {wind_speed} mph") | |
except aiohttp.ClientError as err: | |
logging.error(f"{datetime.now()}: Error occurred during API request: {err}") | |
async def main(): | |
while True: | |
await get_weather_data() | |
await asyncio.sleep(60) # wait for 60 seconds | |
def start_loop(loop): | |
asyncio.set_event_loop(loop) | |
loop.create_task(main()) | |
try: | |
loop.run_forever() | |
finally: | |
loop.close() | |
def configure_logging(): | |
logger = logging.getLogger(__name__) | |
logger.setLevel(logging.INFO) | |
handler = logging.handlers.RotatingFileHandler('logger.log', maxBytes=100000, backupCount=1) | |
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') | |
handler.setFormatter(formatter) | |
logger.addHandler(handler) | |
@app.route('/') | |
def index(): | |
return app.send_static_file('index.html') | |
@app.route('/api/weather', methods=['GET']) | |
def get_latest_weather(): | |
return jsonify(weather_data) | |
if __name__ == '__main__': | |
configure_logging() | |
logging.info("Starting the server.") | |
loop = asyncio.new_event_loop() | |
t = threading.Thread(target=start_loop, args=(loop,)) | |
t.start() | |
app.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-family: Arial, sans-serif; | |
margin: 1; | |
} | |
.container { | |
max-width: 800px; | |
margin: 0 auto; | |
padding: 20px; | |
} | |
h1 { | |
text-align: center; | |
font-size: 2rem; | |
margin-bottom: 20px; | |
} | |
.weather-data { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 20px; | |
} | |
.weather-data .weather-label { | |
font-weight: bold; | |
} | |
.weather-data .weather-value { | |
font-size: 1.5rem; | |
} | |
.weather-data-history { | |
width: 100%; | |
border-collapse: collapse; | |
margin-bottom: 20px; | |
} | |
.weather-data-history th { | |
font-weight: bold; | |
padding: 10px; | |
text-align: left; | |
border-bottom: 2px solid #ccc; | |
} | |
.weather-data-history td { | |
padding: 10px; | |
border-bottom: 1px solid #ccc; | |
} | |
.temperature { | |
color: #ff8c00; | |
} | |
.wind-speed { | |
color: #0000ff; | |
} | |
.pressure { | |
color: #008000; | |
} | |
.station { | |
font-size: 1.5rem; | |
text-align: center; | |
margin-bottom: 20px; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const WEATHER_DATA_URL = '/api/weather'; | |
// Function to fetch the latest weather data and update the display | |
async function updateWeatherData() { | |
const response = await fetch(WEATHER_DATA_URL); | |
const weatherData = await response.json(); | |
// Update the current weather display | |
const currentTempElem = document.querySelector('.temperature'); | |
currentTempElem.textContent = `${weatherData.temperature} °F`; | |
const currentWindElem = document.querySelector('.wind-speed'); | |
currentWindElem.textContent = `${weatherData.wind_speed} mph`; | |
const currentPressureElem = document.querySelector('.pressure'); | |
currentPressureElem.textContent = `${weatherData.pressure} inHg`; | |
// Update the station name | |
const stationElem = document.querySelector('.station'); | |
stationElem.textContent = weatherData.station; | |
// Update the weather data history table | |
const historyTableBody = document.querySelector('.weather-data-history tbody'); | |
const newRow = historyTableBody.insertRow(0); | |
const dateCell = newRow.insertCell(0); | |
const tempCell = newRow.insertCell(1); | |
const windCell = newRow.insertCell(2); | |
const pressureCell = newRow.insertCell(3); | |
const date = new Date(); | |
dateCell.textContent = `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; | |
tempCell.textContent = `${weatherData.temperature} °F`; | |
windCell.textContent = `${weatherData.wind_speed} mph`; | |
pressureCell.textContent = `${weatherData.pressure} inHg`; | |
} | |
// Function to update the weather data every minute | |
async function updateWeatherDataLoop() { | |
await updateWeatherData(); | |
setTimeout(updateWeatherDataLoop, 60000); // update every 60 seconds | |
} | |
// Call the updateWeatherDataLoop function to start updating the weather data | |
updateWeatherDataLoop(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment