diff --git a/Pipfile b/Pipfile index 4f913fa..b86d932 100644 --- a/Pipfile +++ b/Pipfile @@ -4,9 +4,11 @@ verify_ssl = true name = "pypi" [packages] -numpy = "*" +geopy = "*" matplotlib = "*" -requests = "2.32" +numpy = "*" +pytz = "*" +requests = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index eeec9aa..88e0dc9 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e9b6a65b58f567612bd972fe4d418a525f8ce074120f1b49939a2a5e73f07ac9" + "sha256": "126729a6f98a1153aadb9df0008587107239cbb495b6d44f658c16a275f11a0a" }, "pipfile-spec": 6, "requires": { @@ -18,11 +18,11 @@ "default": { "certifi": { "hashes": [ - "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", - "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b" + "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079", + "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39" ], "markers": "python_version >= '3.7'", - "version": "==2025.6.15" + "version": "==2025.7.9" }, "charset-normalizer": { "hashes": [ @@ -241,6 +241,22 @@ "markers": "python_version >= '3.9'", "version": "==4.58.5" }, + "geographiclib": { + "hashes": [ + "sha256:6b7225248e45ff7edcee32becc4e0a1504c606ac5ee163a5656d482e0cd38734", + "sha256:f7f41c85dc3e1c2d3d935ec86660dc3b2c848c83e17f9a9e51ba9d5146a15859" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0" + }, + "geopy": { + "hashes": [ + "sha256:50283d8e7ad07d89be5cb027338c6365a32044df3ae2556ad3f52f4840b3d0d1", + "sha256:ae8b4bc5c1131820f4d75fce9d4aaaca0c85189b3aa5d64c3dcaf5e3b7b882a7" + ], + "index": "pypi", + "version": "==2.4.1" + }, "idna": { "hashes": [ "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", @@ -565,9 +581,17 @@ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==2.9.0.post0" }, + "pytz": { + "hashes": [ + "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", + "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00" + ], + "index": "pypi", + "version": "==2025.2" + }, "requests": { "hashes": [ "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", @@ -581,7 +605,7 @@ "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==1.17.0" }, "urllib3": { diff --git a/data/build_weather.py b/data/build_weather.py index 1c56f9c..b1882f3 100644 --- a/data/build_weather.py +++ b/data/build_weather.py @@ -1,75 +1,85 @@ +import pytz import requests import datetime +from geopy.geocoders import Nominatim -class Weather: - # Example usage (requires OpenWeatherMap API key) - # Replace 'YOUR_API_KEY' with your actual key from https://openweathermap.org/ - def get_weather(self, latitude: float, longitude: float, date_str: str, hour: int) -> dict: - """ - Fetches weather data for the specified location and time. +def get_timezone(latitude: float, longitude: float): + geolocator = Nominatim() + location = geolocator.reverse(f"{latitude}, {longitude}") + + if location: + for tag in location.raw['address']: + if 'timezone' in tag: + tz_name = tag.split('=')[1] + return pytz.timezone(tz_name) + +def get_weather(latitude: float, longitude: float, date_str: str, hour: int) -> dict: + """ + Fetches weather data for the specified location and time. + + Args: + latitude (float): Latitude of the location in degrees. + longitude (float): Longitude of the location in degrees. + date_str (str): Date in YYYYMMDD format. + hour (int): Hour (0-23) when you want to know the weather. + + Returns: + dict: Dictionary containing temperature, condition, and sunrise/sunset times. + """ + # Convert date string components for API request + date = f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:8]}" + + stats_to_get = [ + "temperature_2m", + "relative_humidity_2m", + "dew_point_2m", + "apparent_temperature", + "pressure_msl", + "surface_pressure", + "precipitation", + "rain", + "snowfall", + "cloud_cover", + "cloud_cover_low", + "cloud_cover_mid", + "cloud_cover_high", + "shortwave_radiation", + "direct_radiation", + "direct_normal_irradiance", + "diffuse_radiation", + "global_tilted_irradiance", + "sunshine_duration", + "wind_speed_10m", + "wind_speed_100m", + "wind_direction_10m", + "wind_direction_100m", + "wind_gusts_10m", + "et0_fao_evapotranspiration", + "weather_code", + "snow_depth", + "vapour_pressure_deficit", + "soil_temperature_0_to_7cm", + "soil_temperature_7_to_28cm", + "soil_temperature_28_to_100cm", + "soil_temperature_100_to_255cm", + "soil_moisture_0_to_7cm", + "soil_moisture_7_to_28cm", + "soil_moisture_28_to_100cm", + "soil_moisture_100_to_255cm", + ] + + stats_to_get = ','.join(stats_to_get) + timezone = get_timezone(latitude, longitude) + + try: + api_url = f"https://archive-api.open-meteo.com/v1/archive?latitude={latitude}&longitude={longitude}&timezone={timezone}&start_date={date}&end_date={date}&hourly={stats_to_get}" + response = requests.get(api_url) + data = response.json() - Args: - latitude (float): Latitude of the location in degrees. - longitude (float): Longitude of the location in degrees. - date_str (str): Date in YYYYMMDD format. - hour (int): Hour (0-23) when you want to know the weather. - - Returns: - dict: Dictionary containing temperature, condition, and sunrise/sunset times. - """ - # Convert date string components for API request - date = f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:8]}" + if response.status_code != 200: + return {"error": "Failed to fetch weather", "details": data} - stats_to_get = [ - "temperature_2m", - "relative_humidity_2m", - "dew_point_2m", - "apparent_temperature", - "pressure_msl", - "surface_pressure", - "precipitation", - "rain", - "snowfall", - "cloud_cover", - "cloud_cover_low", - "cloud_cover_mid", - "cloud_cover_high", - "shortwave_radiation", - "direct_radiation", - "direct_normal_irradiance", - "diffuse_radiation", - "global_tilted_irradiance", - "sunshine_duration", - "wind_speed_10m", - "wind_speed_100m", - "wind_direction_10m", - "wind_direction_100m", - "wind_gusts_10m", - "et0_fao_evapotranspiration", - "weather_code", - "snow_depth", - "vapour_pressure_deficit", - "soil_temperature_0_to_7cm", - "soil_temperature_7_to_28cm", - "soil_temperature_28_to_100cm", - "soil_temperature_100_to_255cm", - "soil_moisture_0_to_7cm", - "soil_moisture_7_to_28cm", - "soil_moisture_28_to_100cm", - "soil_moisture_100_to_255cm", - ] - - stats_to_get = ','.join(stats_to_get) - - try: - api_url = f"https://archive-api.open-meteo.com/v1/archive?latitude={latitude}&longitude={longitude}&start_date={date}&end_date={date}&hourly={stats_to_get}" - response = requests.get(api_url) - data = response.json() - - if response.status_code != 200: - return {"error": "Failed to fetch weather", "details": data} - - return data - - except Exception as e: - return {"error": str(e), "details": f"No weather data available for {latitude}, {longitude} on {date_str}"} + return data + + except Exception as e: + return {"error": str(e), "details": f"No weather data available for {latitude}, {longitude} on {date_str}"} diff --git a/data/db_connect.py b/data/db_connect.py index ecdd8a9..43e6c3b 100644 --- a/data/db_connect.py +++ b/data/db_connect.py @@ -13,17 +13,26 @@ class Database: cursor.executescript(sql_script_string) self.db.commit() - def select(self, index): + def select(self, query, values): # Query the database for the specified index cursor = self.db.cursor() - query = "SELECT name, address FROM people WHERE id = ?" - cursor.execute(query, (index,)) + cursor.execute(query, values) result = cursor.fetchone() if result: return result else: return None + def selectall(self, query, values): + # Query the database for the specified index + cursor = self.db.cursor() + cursor.execute(query, values) + result = cursor.fetchall() + if result: + return result + else: + return None + def insert(self, query, values): # Insert new entry into the database cursor = self.db.cursor() diff --git a/data/sql/build_db.sql b/data/sql/build_db.sql index 96ea95b..cec0119 100644 --- a/data/sql/build_db.sql +++ b/data/sql/build_db.sql @@ -138,15 +138,20 @@ CREATE TABLE IF NOT EXISTS team_game ( CREATE TABLE IF NOT EXISTS weather ( game_id INTEGER NOT NULL, - temperature SMALLINT, - wind_speed FLOAT, - air_pressure FLOAT, + temperature FLOAT, humidity SMALLINT UNSIGNED, - uv_index FLOAT, - air_quality TINYINT UNSIGNED, - percipitation_type CHAR(10), - percipitation_amount FLOAT, - sky_condition CHAR(20), + dew_point FLOAT, + apparent_temperature FLOAT, + air_pressure FLOAT, + wind_speed FLOAT, + precipitation FLOAT, + rain FLOAT, + snowfall FLOAT, + cloud_cover SMALLINT UNSIGNED, + wind_speed FLOAT, + wind_direction SMALLINT UNSIGNED, + wind_gusts SMALLINT UNSIGNED, + sun_rise TIME, sun_set TIME, moon_phase TINYINT UNSIGNED, diff --git a/data/stats_importer.py b/data/stats_importer.py index 4f4e653..eec998f 100644 --- a/data/stats_importer.py +++ b/data/stats_importer.py @@ -2,6 +2,7 @@ import os import csv import shutil from data.db_connect import Database +from data.build_weather import get_weather class Importer: def __init__(self, database: Database): @@ -71,6 +72,19 @@ class Importer: ) """ + game_data = [ + game_stats["date"], game_stats["num-of-game"], game_stats["day-of-week"], + game_stats["length-in-outs"], game_stats["day-night"], game_stats["completion-info"], + game_stats["forfeit"], game_stats["protest"], game_stats["park-id"], + game_stats["attendance"], game_stats["length-in-min"], game_stats["home-plate-ump-id"], + game_stats["home-plate-ump-name"], game_stats["1b-plate-ump-id"], game_stats["1b-plate-ump-name"], + game_stats["2b-plate-ump-id"], game_stats["2b-plate-ump-name"], game_stats["3b-plate-ump-id"], + game_stats["3b-plate-ump-name"], game_stats["lf-plate-ump-id"], game_stats["lf-plate-ump-name"], + game_stats["rf-plate-ump-id"], game_stats["rf-plate-ump-name"], + ] + + game_id = self.database.insert(insert_game, game_data) + insert_team_game = """ INSERT INTO team_game ( @@ -124,19 +138,6 @@ class Importer: ) """ - game_data = [ - game_stats["date"], game_stats["num-of-game"], game_stats["day-of-week"], - game_stats["length-in-outs"], game_stats["day-night"], game_stats["completion-info"], - game_stats["forfeit"], game_stats["protest"], game_stats["park-id"], - game_stats["attendance"], game_stats["length-in-min"], game_stats["home-plate-ump-id"], - game_stats["home-plate-ump-name"], game_stats["1b-plate-ump-id"], game_stats["1b-plate-ump-name"], - game_stats["2b-plate-ump-id"], game_stats["2b-plate-ump-name"], game_stats["3b-plate-ump-id"], - game_stats["3b-plate-ump-name"], game_stats["lf-plate-ump-id"], game_stats["lf-plate-ump-name"], - game_stats["rf-plate-ump-id"], game_stats["rf-plate-ump-name"], - ] - - game_id = self.database.insert(insert_game, game_data) - visiting_team_data = [ game_id, game_stats["visiting-team"], game_stats["visiting-game-num"], game_stats["visiting-score"], game_stats["visiting-line-scores"], game_stats["visiting-at-bats"], @@ -189,3 +190,35 @@ class Importer: self.database.insert(insert_team_game, visiting_team_data) self.database.insert(insert_team_game, home_team_data) + + park_data = self.database.select("SELECT latitude, longitude FROM parks WHERE park_id = ?", (game_stats["park-id"],)) + + hour = 15 if game_stats["day-night"] == "D" else 19 + historic_weather = get_weather(park_data[0], park_data[1], game_stats["date"], hour) + historic_weather = historic_weather["hourly"] + + insert_into_weather = """ + INSERT INTO weather + ( + game_id, temperature, humidity, + dew_point, apparent_temperature, air_pressure, + wind_speed, precipitation, rain, + snowfall, cloud_cover, wind_speed, + wind_direction, wind_gusts, sun_rise, + sin_set, moon_phase + ) + VALUES + ( + ?, ?, ?, + ?, ?, ?, + ?, ?, ?, + ?, ?, ?, + ?, ?, ?, + ?, ? + ) + """ + + weather_data = [ + game_id, historic_weather["temperature_2m"][hour], historic_weather["relative_humidity_2m"][hour], + + ] diff --git a/main.py b/main.py index bf201d7..84776f4 100644 --- a/main.py +++ b/main.py @@ -2,7 +2,7 @@ import numpy as np # helps with the math import matplotlib.pyplot as plt # to plot error during training from data.db_connect import Database from data.stats_importer import Importer -from data.build_weather import Weather +from data.build_weather import get_weather # input data inputs = np.array([[0, 0, 1, 0], @@ -59,23 +59,26 @@ class NeuralNetwork: return prediction if __name__ == '__main__': - build_db_path = "./data/sql/build_db.sql" - fill_parks_path = "./data/sql/prefill_parks.sql" - fill_teams_path = "./data/sql/prefill_teams.sql" + #build_db_path = "./data/sql/build_db.sql" + #fill_parks_path = "./data/sql/prefill_parks.sql" + #fill_teams_path = "./data/sql/prefill_teams.sql" db_file = "./database/baseball.db" db_conn = Database(db_file) - db_conn.run_sql_file(build_db_path) - db_conn.run_sql_file(fill_parks_path) - db_conn.run_sql_file(fill_teams_path) + #db_conn.run_sql_file(build_db_path) + #db_conn.run_sql_file(fill_parks_path) + #db_conn.run_sql_file(fill_teams_path) - imp = Importer(db_conn) - imp.parse_all_data("./data/stats/to_import", "./data/stats/imported/") + #imp = Importer(db_conn) + #imp.parse_all_data("./data/stats/to_import", "./data/stats/imported/") #we = Weather() #print(we.get_weather(39.26733000, -76.79831000, "20250706", 12)) + park_data = db_conn.select("SELECT latitude, longitude FROM parks WHERE park_id = ? OR park_id = ?", ("ATL03","KAN06",)) + print(park_data[0]) + else: # create neural network NN = NeuralNetwork(inputs, outputs)