From 642a8a69f108fb3faf02bde15b1b22b81503c040 Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Thu, 30 Nov 2023 02:38:40 -0500 Subject: [PATCH 01/12] Delete week5 directory --- week5/database.py | 74 ------------------------- week5/main.py | 38 ------------- week5/repository.py | 11 ---- week5/service.py | 48 ----------------- week5/weather.py | 129 -------------------------------------------- 5 files changed, 300 deletions(-) delete mode 100644 week5/database.py delete mode 100644 week5/main.py delete mode 100644 week5/repository.py delete mode 100644 week5/service.py delete mode 100644 week5/weather.py diff --git a/week5/database.py b/week5/database.py deleted file mode 100644 index 8f6b89e..0000000 --- a/week5/database.py +++ /dev/null @@ -1,74 +0,0 @@ -import sqlite3 -from datetime import datetime, timedelta -class WeatherDatabase: - - def __init__(self, db_file): - - self.conn = sqlite3.connect(db_file) - - self.cursor = self.conn.cursor() - - - self.cursor.excute(''' - CREATE TABLE IF NOT EXISTS weather( - id INTEGER PRIMARY KEY AUTOINCREMENT, - city TEXT, - temperature REAL, - timestamp TEXT - ) - ''') - - def reset_database(self): - - self.cursor.execute(''' - DELETE FROM weather - ''') - self.conn.commit() - print("Database deleted!") - - def insert_data(self, city, temperature, conditions): - - timestamp = datetime.now() - - self.cursor.execute(''' - SELECT * FROM weather - WHERE city = ? - ORDER BY timestamp DESC - LIMIT 1 - ''', (city, )) - - data = self.cursor.fetchone() - - # Check if data exists and if it is older than 24 hours before inserting - if data and datetime.strptime(data[4], "%Y-%m-%d %H:%M:%S") > timestamp - timedelta(hours=24): - return data - else: - self.cursor.execute(''' - INSERT INTO weather (city, temperature, conditions, timestamp) VALUES (?, ?, ?, ?) - ''', (city, temperature, conditions, timestamp.strftime("%Y-%m-%d %H:%M:%S"))) - self.conn.commit() - - def get_weather_data(self, weather_service, city): - - self.cursor.excute(''' - SELECT * FROM weather - WHERE city = ? - ''', (city,)) - data = self.cursor.fetchall() - - if data: - return data - else: - weather_data = weather_service.get_weather_data(city) - - self.cursor.excute(''' - INSERT INTO weather (city, temperature) - VALIES (?, ?) - ''', (city, str(weather_data))) - self.conn.commit() - - return weather_data - -def close_connect(self): - self.conn.close() - print("Database connection closed") diff --git a/week5/main.py b/week5/main.py deleted file mode 100644 index 628b6fb..0000000 --- a/week5/main.py +++ /dev/null @@ -1,38 +0,0 @@ -#I don't really have progress on main.py yet I don't know how to tie this structure of the program all together yet it's getting to be a lot of pieces to put together for a beginner like me. -#I will try though. - - -import argparse - -from database import WeatherDatabase -from service import WeatherService - -#Global constants -DB_FILE = "data/weather_database.db" -API_KEY = "todogetanAPIkey" - -def login(): - return "login" - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("city", help="City to get the weather data for") - parser.add_argument("--reset", action="store_true", help="Reset the database") - args = parser.parse_args() - - - #Create a database object - db = WeatherDatabase(DB_FILE) - service = WeatherService(API_KEY) - - #Reset the database if --reset is provided - if args.reset: - db.reset_database() - - # and print weather data for the provided city - city_weather_data = db.get_weather_data(service, args.city) - print(f"Weather data for {args.city}:", city_weather_data) - - if __name__ == "__main__": - main() - diff --git a/week5/repository.py b/week5/repository.py deleted file mode 100644 index bd9383e..0000000 --- a/week5/repository.py +++ /dev/null @@ -1,11 +0,0 @@ -class WeatherDataRepository: - def __init__(self, database): - self.database = database - - def get_weather_data(self, city): - return self.database.get_weather_data(city) - - def insert_weather_data(self, city, temperature, conditions): - self.database.insert_data(city, temperature, conditions) - - #this file could be like a interface to talk between the database and other classes diff --git a/week5/service.py b/week5/service.py deleted file mode 100644 index 152c948..0000000 --- a/week5/service.py +++ /dev/null @@ -1,48 +0,0 @@ -import geocoder -import requests - -class WeatherService: - - - def __init__(self, api_key): - self.api_key = api_key - - - def get_weather_data(self, city): - # Check if data is present in the repository - stored_data = self.data_repository.get_weather_data(city) - if stored_data: - return stored_data - - # If not, fetch data from the API - # ... (your existing API fetching logic) - # Get latitude and longitude for the cit - else: - g = geocoder.bing (city, key=self.api_key) - results = g.json - if results['status'] != 'OK': - print("Error fetching weather data!") - # Fetch weather data - url = "https://api.open-meteo.com/v1/forecast" - params = { - 'latitude': results['lat'], - 'longitude': results['lng'], - "hourly": "temperature_2m" - } - - response = requests.get(url=url, params=params) - if response.ok: - weather_data = response.json() - - else: - print("Error fetching weather data!") - weather_data = None - # Return weather data - # TODO: Get data for closer timestamp - data = weather_data["hourly"]["temperature_2m"] [0] - print("Weather data fetched successfully!") - - # Store the fetched data in the repository - self.data_repository.insert_weather_data(city, weather_data["temperature_2m"], weather_data["hourly"]) - - return data diff --git a/week5/weather.py b/week5/weather.py deleted file mode 100644 index 6d01f3a..0000000 --- a/week5/weather.py +++ /dev/null @@ -1,129 +0,0 @@ -import requests -import matplotlib.pyplot as plt -import random -from datetime import datetime, timedelta - - -class AbstractDataService: - def get_data(self, startDate: datetime, endDate: datetime): - raise NotImplementedError("Subclasses must implement this method") - -class MockedDataService(AbstractDataService): - def get_data(self, startDatetime: datetime, endDatetime: datetime): - # Mocked data retrieval logic - mocked_data = { - 'time': [], - 'temperature_2m': [] - } - - start_temperature = 5.0 - end_temperature = 20.0 - # Generate mocked time and temperature data - for i in range(72): # Assuming 72 data points based on the provided results - time = f'2023-11-26T{str(i).zfill(2)}:00' - temperature = round(random.uniform(start_temperature, end_temperature), 1) - - mocked_data['time'].append(time) - mocked_data['temperature_2m'].append(temperature) - - return mocked_data - - -class APICallingService(AbstractDataService): - def get_data(self, startDate: datetime, endDate: datetime): - print("placeholder test I'm kind of lost") - - def call_api(self, payload: dict, url: str): - #return {"api_key": "api_value", "payload": payload} - r = requests.get(url, params=payload) - json_data = r.json() - hourly_data = json_data.get("hourly", {}) - # Implement logic to call the external API with the provided payload - # Return the API response - #return {"api_key": "api_value"} - return hourly_data - - -class DataSourceHandler: - def __init__(self, data_service): - self.data_service = data_service - - def handle_data(self, startDate: datetime, endDate: datetime): - data = self.data_service.get_data(startDate, endDate) - # Implement handling logic - print("Handling data:") - print(data) - return data - - -class DataServiceFactory: - def create_data_service(self, service_type): - if service_type == "mocked": - return MockedDataService() - elif service_type == "api": - return APICallingService() - else: - raise ValueError("Invalid service type") - - -url = "https://api.open-meteo.com/v1/forecast" - -payload = {'latitude': 37.7723281, - 'longitude': -122.4530167, - 'hourly': 'temperature_2m'} - - - -# Example usage -factory = DataServiceFactory() - -# Create Mocked Data Service -mocked_service = factory.create_data_service("mocked") -print(f"Type of Mocked Service: {type(mocked_service)}") - -# Create API Data Service -api_service = factory.create_data_service("api") -print(f"Type of API Service: {type(api_service)}") -print(type(datetime), "---------------") -beginDate = datetime.now() - timedelta(days=7) -endDate = datetime.now() - -# Use DataSourceHandler to handle data from Mocked Service -mocked_data_handler = DataSourceHandler(mocked_service) -mocked_data_handler.handle_data(beginDate, endDate) - -# Use DataSourceHandler to handle data from API Service -api_data_handler = DataSourceHandler(api_service) -api_data_handler.handle_data(beginDate, endDate) - - - - -mocked_service = MockedDataService() -print(type(mocked_service)) -mocked_data = mocked_service.get_data(beginDate, endDate) - -print(mocked_data) -# print(type(payload)) - - -# r = requests.get(url, params=payload) - -#json_data = r.json() -#hourly_data = json_data.get("hourly", {}) -hourly_data = mocked_data - - -# print(hourly_data) - -fig, ax = plt.subplots() - - -ax.set_xlabel("Time (hourly)") - -ax.set_ylabel("temperature_2m") -ax.plot(hourly_data["time"], hourly_data["temperature_2m"]) - -plt.show() - -print("Hurray") From 5404abc4fe887b0608cf40b0e6c10de52b4799f1 Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Thu, 30 Nov 2023 02:40:50 -0500 Subject: [PATCH 02/12] Create weather.py --- week5/weather.py | 129 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 week5/weather.py diff --git a/week5/weather.py b/week5/weather.py new file mode 100644 index 0000000..6d01f3a --- /dev/null +++ b/week5/weather.py @@ -0,0 +1,129 @@ +import requests +import matplotlib.pyplot as plt +import random +from datetime import datetime, timedelta + + +class AbstractDataService: + def get_data(self, startDate: datetime, endDate: datetime): + raise NotImplementedError("Subclasses must implement this method") + +class MockedDataService(AbstractDataService): + def get_data(self, startDatetime: datetime, endDatetime: datetime): + # Mocked data retrieval logic + mocked_data = { + 'time': [], + 'temperature_2m': [] + } + + start_temperature = 5.0 + end_temperature = 20.0 + # Generate mocked time and temperature data + for i in range(72): # Assuming 72 data points based on the provided results + time = f'2023-11-26T{str(i).zfill(2)}:00' + temperature = round(random.uniform(start_temperature, end_temperature), 1) + + mocked_data['time'].append(time) + mocked_data['temperature_2m'].append(temperature) + + return mocked_data + + +class APICallingService(AbstractDataService): + def get_data(self, startDate: datetime, endDate: datetime): + print("placeholder test I'm kind of lost") + + def call_api(self, payload: dict, url: str): + #return {"api_key": "api_value", "payload": payload} + r = requests.get(url, params=payload) + json_data = r.json() + hourly_data = json_data.get("hourly", {}) + # Implement logic to call the external API with the provided payload + # Return the API response + #return {"api_key": "api_value"} + return hourly_data + + +class DataSourceHandler: + def __init__(self, data_service): + self.data_service = data_service + + def handle_data(self, startDate: datetime, endDate: datetime): + data = self.data_service.get_data(startDate, endDate) + # Implement handling logic + print("Handling data:") + print(data) + return data + + +class DataServiceFactory: + def create_data_service(self, service_type): + if service_type == "mocked": + return MockedDataService() + elif service_type == "api": + return APICallingService() + else: + raise ValueError("Invalid service type") + + +url = "https://api.open-meteo.com/v1/forecast" + +payload = {'latitude': 37.7723281, + 'longitude': -122.4530167, + 'hourly': 'temperature_2m'} + + + +# Example usage +factory = DataServiceFactory() + +# Create Mocked Data Service +mocked_service = factory.create_data_service("mocked") +print(f"Type of Mocked Service: {type(mocked_service)}") + +# Create API Data Service +api_service = factory.create_data_service("api") +print(f"Type of API Service: {type(api_service)}") +print(type(datetime), "---------------") +beginDate = datetime.now() - timedelta(days=7) +endDate = datetime.now() + +# Use DataSourceHandler to handle data from Mocked Service +mocked_data_handler = DataSourceHandler(mocked_service) +mocked_data_handler.handle_data(beginDate, endDate) + +# Use DataSourceHandler to handle data from API Service +api_data_handler = DataSourceHandler(api_service) +api_data_handler.handle_data(beginDate, endDate) + + + + +mocked_service = MockedDataService() +print(type(mocked_service)) +mocked_data = mocked_service.get_data(beginDate, endDate) + +print(mocked_data) +# print(type(payload)) + + +# r = requests.get(url, params=payload) + +#json_data = r.json() +#hourly_data = json_data.get("hourly", {}) +hourly_data = mocked_data + + +# print(hourly_data) + +fig, ax = plt.subplots() + + +ax.set_xlabel("Time (hourly)") + +ax.set_ylabel("temperature_2m") +ax.plot(hourly_data["time"], hourly_data["temperature_2m"]) + +plt.show() + +print("Hurray") From cb128fe1687dd927a49f686d43b6c9e56890a024 Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Thu, 30 Nov 2023 02:55:03 -0500 Subject: [PATCH 03/12] Create service.py --- week5/service.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 week5/service.py diff --git a/week5/service.py b/week5/service.py new file mode 100644 index 0000000..152c948 --- /dev/null +++ b/week5/service.py @@ -0,0 +1,48 @@ +import geocoder +import requests + +class WeatherService: + + + def __init__(self, api_key): + self.api_key = api_key + + + def get_weather_data(self, city): + # Check if data is present in the repository + stored_data = self.data_repository.get_weather_data(city) + if stored_data: + return stored_data + + # If not, fetch data from the API + # ... (your existing API fetching logic) + # Get latitude and longitude for the cit + else: + g = geocoder.bing (city, key=self.api_key) + results = g.json + if results['status'] != 'OK': + print("Error fetching weather data!") + # Fetch weather data + url = "https://api.open-meteo.com/v1/forecast" + params = { + 'latitude': results['lat'], + 'longitude': results['lng'], + "hourly": "temperature_2m" + } + + response = requests.get(url=url, params=params) + if response.ok: + weather_data = response.json() + + else: + print("Error fetching weather data!") + weather_data = None + # Return weather data + # TODO: Get data for closer timestamp + data = weather_data["hourly"]["temperature_2m"] [0] + print("Weather data fetched successfully!") + + # Store the fetched data in the repository + self.data_repository.insert_weather_data(city, weather_data["temperature_2m"], weather_data["hourly"]) + + return data From a88d45d1c9cfa4b577cfb317ae663933ac42ffc2 Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Thu, 30 Nov 2023 02:55:26 -0500 Subject: [PATCH 04/12] Create database.py --- week5/database.py | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 week5/database.py diff --git a/week5/database.py b/week5/database.py new file mode 100644 index 0000000..8f6b89e --- /dev/null +++ b/week5/database.py @@ -0,0 +1,74 @@ +import sqlite3 +from datetime import datetime, timedelta +class WeatherDatabase: + + def __init__(self, db_file): + + self.conn = sqlite3.connect(db_file) + + self.cursor = self.conn.cursor() + + + self.cursor.excute(''' + CREATE TABLE IF NOT EXISTS weather( + id INTEGER PRIMARY KEY AUTOINCREMENT, + city TEXT, + temperature REAL, + timestamp TEXT + ) + ''') + + def reset_database(self): + + self.cursor.execute(''' + DELETE FROM weather + ''') + self.conn.commit() + print("Database deleted!") + + def insert_data(self, city, temperature, conditions): + + timestamp = datetime.now() + + self.cursor.execute(''' + SELECT * FROM weather + WHERE city = ? + ORDER BY timestamp DESC + LIMIT 1 + ''', (city, )) + + data = self.cursor.fetchone() + + # Check if data exists and if it is older than 24 hours before inserting + if data and datetime.strptime(data[4], "%Y-%m-%d %H:%M:%S") > timestamp - timedelta(hours=24): + return data + else: + self.cursor.execute(''' + INSERT INTO weather (city, temperature, conditions, timestamp) VALUES (?, ?, ?, ?) + ''', (city, temperature, conditions, timestamp.strftime("%Y-%m-%d %H:%M:%S"))) + self.conn.commit() + + def get_weather_data(self, weather_service, city): + + self.cursor.excute(''' + SELECT * FROM weather + WHERE city = ? + ''', (city,)) + data = self.cursor.fetchall() + + if data: + return data + else: + weather_data = weather_service.get_weather_data(city) + + self.cursor.excute(''' + INSERT INTO weather (city, temperature) + VALIES (?, ?) + ''', (city, str(weather_data))) + self.conn.commit() + + return weather_data + +def close_connect(self): + self.conn.close() + print("Database connection closed") From 04c6d2674cb171785bddb7d036dd93262fbf1a6d Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Thu, 30 Nov 2023 02:59:24 -0500 Subject: [PATCH 05/12] Create repository.py --- week5/repository.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 week5/repository.py diff --git a/week5/repository.py b/week5/repository.py new file mode 100644 index 0000000..8530953 --- /dev/null +++ b/week5/repository.py @@ -0,0 +1,10 @@ +#my idea for this file is to make repostiroy a interface layer that works between the database and other classes but I don't know if this is necessary to decouple the design / classes. +class WeatherDataRepository: + def __init__(self, database): + self.database = database + + def get_weather_data(self, city): + return self.database.get_weather_data(city) + + def insert_weather_data(self, city, temperature, conditions): + self.database.insert_data(city, temperature, conditions) From 4490a1625310097a98d5fc72e30e1dbac9b031b9 Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Thu, 30 Nov 2023 03:01:01 -0500 Subject: [PATCH 06/12] Create main.py --- week5/main.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 week5/main.py diff --git a/week5/main.py b/week5/main.py new file mode 100644 index 0000000..cad22fb --- /dev/null +++ b/week5/main.py @@ -0,0 +1,37 @@ +#I haven't made much progress on main.py and I don't totally understand how to tie the overall design of my weather application together +# I have a lot of components and pieces but I am not sure how to make everything work while remaining loosely coupled etc + +import argparse + +from database import WeatherDatabase +from service import WeatherService + +#Global constants +DB_FILE = "data/weather_database.db" +API_KEY = "todogetanAPIkey" + +def login(): + return "login" + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("city", help="City to get the weather data for") + parser.add_argument("--reset", action="store_true", help="Reset the database") + args = parser.parse_args() + + + #Create a database object + db = WeatherDatabase(DB_FILE) + service = WeatherService(API_KEY) + + #Reset the database if --reset is provided + if args.reset: + db.reset_database() + + # and print weather data for the provided city + city_weather_data = db.get_weather_data(service, args.city) + print(f"Weather data for {args.city}:", city_weather_data) + + if __name__ == "__main__": + main() + From 056c65cdbe89add7cc356763c12c654d6f55fa4d Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Tue, 5 Dec 2023 03:20:38 -0500 Subject: [PATCH 07/12] Update weather.py more updates --- week5/weather.py | 111 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 96 insertions(+), 15 deletions(-) diff --git a/week5/weather.py b/week5/weather.py index 6d01f3a..31b738e 100644 --- a/week5/weather.py +++ b/week5/weather.py @@ -2,6 +2,39 @@ import matplotlib.pyplot as plt import random from datetime import datetime, timedelta +from weather_database import WeatherDatabase +from geopy.geocoders import Photon +Latitude = "25.594095" +Longitude = "85.137566" + + +geolocator = Photon(user_agent="geoapiExercises") +Latitude = "25.594095" +Longitude = "85.137566" + +location = geolocator.reverse(Latitude+","+Longitude) + +# Display +#print(location) +#print(type(location)) +print(type(location)) +print(location.raw.keys()) +print(location.raw.values()) +address = location.raw['properties'] + +#print(address) + + +city = address.get('city', '') +state = address.get('state', '') +country = address.get('country', '') +code = address.get('country_code') +zipcode = address.get('postcode') +print('City : ',city) +print('State : ',state) +print('Country : ',country) +print('Zip Code : ', zipcode) + class AbstractDataService: @@ -13,7 +46,8 @@ def get_data(self, startDatetime: datetime, endDatetime: datetime): # Mocked data retrieval logic mocked_data = { 'time': [], - 'temperature_2m': [] + 'temperature_2m': [], + 'city': [] } start_temperature = 5.0 @@ -25,6 +59,7 @@ def get_data(self, startDatetime: datetime, endDatetime: datetime): mocked_data['time'].append(time) mocked_data['temperature_2m'].append(temperature) + mocked_data['city'].append("test city") return mocked_data @@ -38,6 +73,33 @@ def call_api(self, payload: dict, url: str): r = requests.get(url, params=payload) json_data = r.json() hourly_data = json_data.get("hourly", {}) + # Append latitude and longitude to hourly_data + hourly_data['latitude'] = payload.get('latitude') + hourly_data['longitude'] = payload.get('longitude') + Longitude = str(payload.get('longitude')) + Latitude = str(payload.get('latitude')) + #Is this now tightly coupled because we're using the geolocator insdie the call_api, inside the serivce API? + #Should we put the geolocator inside a class and access it via an interface or something? + #Figure out the city from the payload longitude + latitude + geolocator = Photon(user_agent="geoapiExercises") + location = geolocator.reverse(Latitude+","+Longitude) + city = location.raw['properties']['city'] + hourly_data['city'] = city + + print("Fetching air quality data...") + air_quality_payload = { + 'lat': payload.get('latitude'), + 'lon': payload.get('longitude'), + 'appid': '3b94f08daa79eff0bb70f61127ac83ba' # Replace with your API key + } + r = requests.get("https://api.openweathermap.org/data/2.5/air_pollution", params=air_quality_payload) + json_air_data = r.json() + air_data = json_air_data.get("list", {}) + #print(json_air_data) + print(air_data) + + + #hourly_data['city'].append("test city") # Implement logic to call the external API with the provided payload # Return the API response #return {"api_key": "api_value"} @@ -78,32 +140,35 @@ def create_data_service(self, service_type): factory = DataServiceFactory() # Create Mocked Data Service -mocked_service = factory.create_data_service("mocked") -print(f"Type of Mocked Service: {type(mocked_service)}") +#mocked_service = factory.create_data_service("mocked") +#print(f"Type of Mocked Service: {type(mocked_service)}") # Create API Data Service api_service = factory.create_data_service("api") print(f"Type of API Service: {type(api_service)}") -print(type(datetime), "---------------") -beginDate = datetime.now() - timedelta(days=7) -endDate = datetime.now() +#print(type(datetime), "---------------") +#beginDate = datetime.now() - timedelta(days=7) +#endDate = datetime.now() # Use DataSourceHandler to handle data from Mocked Service -mocked_data_handler = DataSourceHandler(mocked_service) -mocked_data_handler.handle_data(beginDate, endDate) +#mocked_data_handler = DataSourceHandler(mocked_service) +#mocked_data_handler.handle_data(beginDate, endDate) # Use DataSourceHandler to handle data from API Service -api_data_handler = DataSourceHandler(api_service) -api_data_handler.handle_data(beginDate, endDate) +#api_data_handler = DataSourceHandler(api_service) +#api_data_handler.handle_data(beginDate, endDate) + +#mocked_service = MockedDataService() +#print(type(mocked_service)) +#mocked_data = mocked_service.get_data(beginDate, endDate) -mocked_service = MockedDataService() -print(type(mocked_service)) -mocked_data = mocked_service.get_data(beginDate, endDate) +#print(mocked_data) -print(mocked_data) +#api_service = APICallingService() +hourly_data = api_service.call_api(payload, url) # print(type(payload)) @@ -111,7 +176,22 @@ def create_data_service(self, service_type): #json_data = r.json() #hourly_data = json_data.get("hourly", {}) -hourly_data = mocked_data +#hourly_data = mocked_data +print(type(hourly_data["time"])) +print(type(hourly_data["temperature_2m"])) +print(type(hourly_data["city"])) +# View keys +print("Keys:", hourly_data.keys()) + +# View values +print("Values:", hourly_data.values()) +DB_File = "weather_db" +weather_db = WeatherDatabase(DB_File) +weather_db.close_connect() +#insert_data(self, city, temperature, time): +#weather_db.insert_data() +# View items +#print("Items:", hourly_data.items()) # print(hourly_data) @@ -127,3 +207,4 @@ def create_data_service(self, service_type): plt.show() print("Hurray") + From 46f8f45fc40c9707d57c09f81c522eabcf61631d Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Tue, 5 Dec 2023 03:21:16 -0500 Subject: [PATCH 08/12] Create weather_database.py new weather_database python file --- week5/weather_database.py | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 week5/weather_database.py diff --git a/week5/weather_database.py b/week5/weather_database.py new file mode 100644 index 0000000..87ac8c8 --- /dev/null +++ b/week5/weather_database.py @@ -0,0 +1,67 @@ +import sqlite3 +from datetime import datetime, timedelta + +class WeatherDatabase: + + def __init__(self, db_file): + + self.conn = sqlite3.connect(":memory:") + + self.cursor = self.conn.cursor() + + + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS weather( + id INTEGER PRIMARY KEY AUTOINCREMENT, + city TEXT, + temperature REAL, + time TEXT + ) + ''') + + def insert_data(self, city, temperature, time): + + timestamp = datetime.now() + + self.cursor.execute(''' + SELECT * FROM weather + WHERE city = ? + ORDER BY time DESC + LIMIT 1 + ''', (city, )) + + data = self.cursor.fetchone() + + # Check if data exists and if it is older than 24 hours before inserting + if data and datetime.strptime(data[4], "%Y-%m-%d %H:%M:%S") > timestamp - timedelta(hours=24): + return data + else: + self.cursor.execute(''' + INSERT INTO weather (city, temperature, time, timestamp) VALUES (?, ?, ?, ?) + ''', (city, temperature, time, timestamp.strftime("%Y-%m-%d %H:%M:%S"))) + self.conn.commit() + + def get_weather_data(self, weather_service, city): + + self.cursor.excute(''' + SELECT * FROM weather + WHERE city = ? + ''', (city,)) + data = self.cursor.fetchall() + + if data: + return data + else: + weather_data = weather_service.get_weather_data(city) + + self.cursor.excute(''' + INSERT INTO weather (city, temperature) + VALIES (?, ?) + ''', (city, str(weather_data))) + self.conn.commit() + + return weather_data + + def close_connect(self): + self.conn.close() + print("Database connection closed") From 76374661c7cf1bbda29d53bed3b2b620616272fa Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Tue, 5 Dec 2023 03:31:14 -0500 Subject: [PATCH 09/12] Update weather.py --- week5/weather.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week5/weather.py b/week5/weather.py index 31b738e..fafe9ee 100644 --- a/week5/weather.py +++ b/week5/weather.py @@ -90,7 +90,7 @@ def call_api(self, payload: dict, url: str): air_quality_payload = { 'lat': payload.get('latitude'), 'lon': payload.get('longitude'), - 'appid': '3b94f08daa79eff0bb70f61127ac83ba' # Replace with your API key + 'appid': '########################' # Replace with your API key } r = requests.get("https://api.openweathermap.org/data/2.5/air_pollution", params=air_quality_payload) json_air_data = r.json() From ed4e001810ac70cf691efc4e492e1b8831c2df09 Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Tue, 2 Jan 2024 08:05:45 -0500 Subject: [PATCH 10/12] Create weather.py --- week7/weather.py | 371 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 week7/weather.py diff --git a/week7/weather.py b/week7/weather.py new file mode 100644 index 0000000..9080063 --- /dev/null +++ b/week7/weather.py @@ -0,0 +1,371 @@ +import sys +import requests +import matplotlib.pyplot as plt +import random +from datetime import datetime, timedelta +from weather_database import WeatherDatabase +from geopy.geocoders import Photon +Latitude = "25.594095" +Longitude = "85.137566" + + +geolocator = Photon(user_agent="geoapiExercises") +Latitude = "25.594095" +Longitude = "85.137566" + +location = geolocator.reverse(Latitude+","+Longitude) + +# Display +#print(location) +#print(type(location)) +print(type(location)) +print(location.raw.keys()) +print(location.raw.values()) +address = location.raw['properties'] + +#print(address) + + +city = address.get('city', '') +state = address.get('state', '') +country = address.get('country', '') +code = address.get('country_code') +zipcode = address.get('postcode') +print('City : ',city) +print('State : ',state) +print('Country : ',country) +print('Zip Code : ', zipcode) + +class VisualizationHandler(): + def visualize_data(self, hourly_data: dict): + + fig, ax = plt.subplots() + + + ax.set_xlabel("Time (hourly)") + + ax.set_ylabel("temperature_2m") + print(hourly_data) + print(type(hourly_data)) + ax.plot(hourly_data["time"], hourly_data["temperature_2m"]) + + plt.show() + + def utilize_data(self, hourly_data: dict, weather_db: WeatherDatabase): + city = hourly_data.get('city', '') + temperature = hourly_data.get('temperature_2m', []) + time = hourly_data.get('time', []) + + # Store data in the database + for i in range(len(time)): + weather_db.insert_data(city, temperature[i], time[i]) + + # Additional data utilization logic can be added here + # For example, you can perform analysis on the data, generate reports, etc. + + print("Data utilization completed.") + + def analyze_data(self, weather_db: WeatherDatabase): + # Calculate average temperature for each city + cities = weather_db.get_all_cities() + average_temperatures = [] + + for city_info in location_lookups: + city = city_info["name"] + if city in cities: + data = weather_db.get_weather_data_for_city(city) + temperatures = [entry[2] for entry in data] # Assuming the temperature is at index 2 + average_temperature = sum(temperatures) / len(temperatures) + average_temperatures.append(average_temperature) + print(f"Average temperature for {city}: {average_temperature:.2f}°C") + else: + print(f"No data available for {city}") + + # Plotting the bar chart + self.plot_average_temperatures(location_lookups, average_temperatures) + + def plot_average_temperatures(self, cities, average_temperatures): + plt.figure(figsize=(10, 6)) + plt.bar(cities, average_temperatures, color='blue') + plt.xlabel('Cities') + plt.ylabel('Average Temperature (°C)') + plt.title('Average Temperature for Five Cities') + plt.show() + +class AbstractDataService: + def get_data(self, startDate: datetime, endDate: datetime): + raise NotImplementedError("Subclasses must implement this method") + + def utilize_data(self, hourly_data: dict, weather_db: WeatherDatabase): + raise NotImplementedError("Subclasses must implement this method") + +class MockedDataService(AbstractDataService): + def get_data(self, startDatetime: datetime, endDatetime: datetime): + # Mocked data retrieval logic + mocked_data = { + 'time': [], + 'temperature_2m': [], + 'city': [] + } + + start_temperature = 5.0 + end_temperature = 20.0 + # Generate mocked time and temperature data + for i in range(72): # Assuming 72 data points based on the provided results + time = f'2023-11-26T{str(i).zfill(2)}:00' + temperature = round(random.uniform(start_temperature, end_temperature), 1) + + mocked_data['time'].append(time) + mocked_data['temperature_2m'].append(temperature) + mocked_data['city'].append("test city") + + return mocked_data + + def utilize_data(self, hourly_data: dict, weather_db: WeatherDatabase): + print("we should do stuff here") + +class APICallingService(AbstractDataService): + def get_data(self, startDate: datetime, endDate: datetime, weather_db: WeatherDatabase): + # Check the database for existing data first + cached_data = weather_db.get_weather_data(self, city) + if cached_data: + print("Using cached data from the database.") + return cached_data + + # If data is not found in the database, make an API call + print("Making API call to fetch new data...") + api_data = self.call_api(payload, url) + + if api_data: + # Store the newly fetched data in the database + self.utilize_data(api_data, weather_db) + return api_data + else: + print("Error fetching data from API.") + return None + + def utilize_data(self, hourly_data: dict, weather_db: WeatherDatabase): + print("we should do stuff here") + + def call_api(self, payload: dict, url: str): + try: + r = requests.get(url, params=payload) + r.raise_for_status() # Raise an HTTPError for bad responses (4xx and 5xx status codes) + json_data = r.json() + except requests.exceptions.RequestException as e: + print(f"Error during API call: {e}") + return None + + hourly_data = json_data.get("hourly", {}) + + if hourly_data is None: + print("Error: No 'hourly' data in API response.") + return None + # Append latitude and longitude to hourly_data + hourly_data['latitude'] = payload.get('latitude') + hourly_data['longitude'] = payload.get('longitude') + Longitude = str(payload.get('longitude')) + Latitude = str(payload.get('latitude')) + #Is this now tightly coupled because we're using the geolocator insdie the call_api, inside the serivce API? + #Should we put the geolocator inside a class and access it via an interface or something? + #Figure out the city from the payload longitude + latitude + geolocator = Photon(user_agent="geoapiExercises") + location = geolocator.reverse(Latitude+","+Longitude) + city = location.raw['properties']['city'] + hourly_data['city'] = city + + print("Fetching air quality data...") + air_quality_payload = { + 'lat': payload.get('latitude'), + 'lon': payload.get('longitude'), + 'appid': 'ff8ef6c023a22aa043afef8f7e308d67' # Replace with your API key + } + r = requests.get("https://api.openweathermap.org/data/2.5/air_pollution", params=air_quality_payload) + json_air_data = r.json() + air_data = json_air_data.get("list", {}) + #print(json_air_data) + print(air_data) + + + #hourly_data['city'].append("test city") + # Implement logic to call the external API with the provided payload + # Return the API response + #return {"api_key": "api_value"} + return hourly_data + + + +class DataSourceHandler: + def __init__(self, data_service: AbstractDataService, visualization_handler: VisualizationHandler): + self.data_service = data_service + self.visualization_handler = visualization_handler + + def handle_data(self, startDate: datetime, endDate: datetime, weather_db: WeatherDatabase): + if(weather_db is None): + print("No weather database provided") + data = self.data_service.get_data(startDate, endDate) + else: + print("Weather database provided") + data = self.data_service.get_data(startDate, endDate, weather_db) + # Implement handling logic + print("Handling data:") + print(data) + print(startDate) + print(endDate) + # Use the provided VisualizationHandler + + self.visualization_handler.visualize_data(data) + + # Utilize data in the database + self.data_service.utilize_data(data, weather_db) + + +class DataServiceFactory: + def create_data_service(self, service_type: str): + if service_type == "mocked": + return MockedDataService() + elif service_type == "api": + return APICallingService() + else: + raise ValueError("Invalid service type") + + +url = "https://api.open-meteo.com/v1/forecast" + +location_lookups = [ + { + "name": "Detroit", + "longitude": 83.0458, + "latitude": 42.3314 + }, + { + "name": "Marquette", + "longitude": 87.3956, + "latitude": 46.5436 + }, + { + "name": "San Jose", + "longitude": -121.8863, + "latitude": 37.3382 + }, + { + "name": "Miami", + "longitude": -80.1918, + "latitude": 25.7617 + } + ] + +payload = {'latitude': 37.7723281, + 'longitude': -122.4530167, + 'hourly': 'temperature_2m'} + + + +# Example usage +factory = DataServiceFactory() + +# Create Mocked Data Service +#mocked_service = factory.create_data_service("mocked") +#print(f"Type of Mocked Service: {type(mocked_service)}")- +/*-- + +# Create API Data Service +api_service = factory.create_data_service("api") +print(f"Type of API Service: {type(api_service)}") +#print(type(datetime), "---------------") +#beginDate = datetime.now() - timedelta(days=7) +#endDate = datetime.now() + +# Use DataSourceHandler to handle data from Mocked Servic- +/*-- +#mocked_data_handler = DataSourceHandler(mocked_service) +#mocked_data_handler.handle_data(beginDate, endDate) + +# Use DataSourceHandler to handle data from API Service +#api_data_handler = DataSourceHandler(api_service) +#api_data_handler.handle_data(beginDate, endDate) + + + + +#mocked_service = MockedDataService() +#print(type(mocked_service)) +#mocked_data = mocked_service.get_data(beginDate, endDate- +/*-- + +#print(mocked_data) + +#api_service = APICallingService() +hourly_data = api_service.call_api(payload, url) +# print(type(payload)) + + +# r = requests.get(url, params=payload) + +#json_data = r.json() +#hourly_data = json_data.get("hourly", {}) +#hourly_data = mocked_data +print(type(hourly_data["time"])) +print(type(hourly_data["temperature_2m"])) +print(type(hourly_data["city"])) +# View keys +print("Keys:", hourly_data.keys()) + +# View values +print("Values:", hourly_data.values()) +DB_File = "weather_db" +weather_db = WeatherDatabase(DB_File) +#weather_db.close_connect() +#insert_data(self, city, temperature, time): +#weather_db.insert_data() +# View items +#print("Items:", hourly_data.items()) + + +# print(hourly_data) +print(type(hourly_data)) +vizHandler = VisualizationHandler() +vizHandler.visualize_data(hourly_data) + + +print("Hurray") + + + + + +# Modify the main part of the code to utilize the data +#if __name__ == "__main__": +usable_service = factory.create_data_service("api") +data_handler = DataSourceHandler(usable_service, Visualiz- +/*-- +dler()) + +# Define the database file +#DB_File = "weather_db" + +# Create an instance of the WeatherDatabase +#weather_db = WeatherDatabase(DB_File) + +# Open the database connection +#weather_db.open_connect() + +# Handle data and utilize it +beginDate = datetime.now() - timedelta(days=7) +endDate = datetime.now() +data_handler.handle_data(beginDate, endDate, weather_db) + + +# Use DataSourceHandler to handle data from API Service +api_data_handler = DataSourceHandler(api_service, Visuali- +/*-- +ndler()) +api_data_handler.handle_data(beginDate, endDate, weather_- +/*-- + +# Analyze and plot average temperatures for cities +vizHandler.analyze_data(weather_db, location_lookups) + +# Close the database connection +weather_db.close_connect() + From 48c2d48662ce3ce52a1acfdc4757429768a02ca9 Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Tue, 2 Jan 2024 08:06:08 -0500 Subject: [PATCH 11/12] Create weather_database.py --- week7/weather_database.py | 80 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 week7/weather_database.py diff --git a/week7/weather_database.py b/week7/weather_database.py new file mode 100644 index 0000000..35faee2 --- /dev/null +++ b/week7/weather_database.py @@ -0,0 +1,80 @@ +import sqlite3 +from datetime import datetime, timedelta + +class WeatherDatabase: + + def __init__(self, db_file): + + self.conn = sqlite3.connect(":memory:") + + self.cursor = self.conn.cursor() + + + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS weather( + id INTEGER PRIMARY KEY AUTOINCREMENT, + city TEXT, + temperature REAL, + time TEXT + ) + ''') + + def insert_data(self, city, temperature, time): + + timestamp = datetime.now() + + self.cursor.execute(''' + SELECT * FROM weather + WHERE city = ? + ORDER BY time DESC + LIMIT 1 + ''', (city, )) + + data = self.cursor.fetchone() + + # Check if data exists and if it is older than 24 hours before inserting + if data and datetime.strptime(data[4], "%Y-%m-%d %H:%M:%S") > timestamp - timedelta(hours=24): + return data + else: + self.cursor.execute(''' + INSERT INTO weather (city, temperature, time, timestamp) VALUES (?, ?, ?, ?) + ''', (city, temperature, time, timestamp.strftime("%Y-%m-%d %H:%M:%S"))) + self.conn.commit() + + def get_weather_data(self, city): + self.cursor.execute(''' + SELECT * FROM weather + WHERE city = ? + ORDER BY time DESC + LIMIT 1 + ''', (city,)) + + data = self.cursor.fetchone() + + if data: + return data[2] # Return the temperature from the cached data + else: + return None + + + def get_all_cities(self): + self.cursor.execute(''' + SELECT DISTINCT city FROM weather + ''') + cities = [entry[0] for entry in self.cursor.fetchall()] + return cities + + def get_weather_data_for_city(self, city): + self.cursor.execute(''' + SELECT * FROM weather + WHERE city = ? + ''', (city,)) + data = self.cursor.fetchall() + return data + + def open_connect(self): + print("Database connection opened") + + def close_connect(self): + self.conn.close() + print("Database connection closed") From 090c8d3ee0ff78f330063729c7490c0b01dcc0c0 Mon Sep 17 00:00:00 2001 From: Spiffy664 Date: Tue, 2 Jan 2024 21:08:27 -0500 Subject: [PATCH 12/12] Update README.md --- README.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e92f8d0..6429e62 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,73 @@ # oaf-psd-bootcamp -Initial repository for the Open Avenues: Professional Software Development Bootcamp +# Weather App + +## Overview + +The Weather App is designed to provide users with real-time weather and air quality information for a given location. This README.md file outlines the data utilization strategy, including the sources of data, API integration, and how the data is processed and presented in the application. + +## Data Sources + +The Weather App utilizes two primary data sources: + +1. **Weather Data:** + - Open-Meteo API: Used to fetch hourly weather forecasts based on geographical coordinates. + - Endpoint: `https://api.open-meteo.com/v1/forecast` + +2. **Air Quality Data:** + - OpenWeatherMap API: Used to retrieve air quality information for a specific location. + - Endpoint: `https://api.openweathermap.org/data/2.5/air_pollution` + +## API Key Requirements + +To use the Weather App, you need to obtain API keys for both data sources: + +- Open-Meteo API: No API key is required for the Open-Meteo API. +- OpenWeatherMap API: Sign up for a free account and obtain an API key from [OpenWeatherMap](https://openweathermap.org/api). + +## Project Structure + +The project is structured into several components: + +1. **APICallingService:** + - Manages the interaction with the Open-Meteo and OpenWeatherMap APIs. + - Fetches weather and air quality data. + +2. **DataSourceHandler:** + - Takes care of handling and combining data from multiple sources. + - Responsible for further data processing. + +3. **MockedDataService:** + - Provides mocked data for testing purposes. + +4. **WeatherDatabase:** + - Handles interactions with the weather database. + +5. **Main Script:** + - Combines data from different sources and visualizes it using Matplotlib. + +## Data Utilization Strategy + +1. **Weather Data:** + - The Weather App retrieves hourly weather forecasts using the Open-Meteo API. + - The data includes time, temperature at 2 meters, and the city. + +2. **Air Quality Data:** + - The OpenWeatherMap API is used to obtain air quality information. + - The air quality index (AQI) is extracted and combined with weather data. + +3. **Combining Data:** + - The `APICallingService` fetches data separately from weather and air quality APIs. + - The `DataSourceHandler` combines these datasets into a unified format. + +4. **Visualization:** + - Matplotlib is used for visualizing the combined data. + - The script generates a plot displaying the temperature trend and air quality index over time. + +## Usage + +1. **Obtain API Keys:** + - Sign up for OpenWeatherMap API and obtain the API key. + - Replace 'YOUR_OPENWEATHERMAP_API_KEY' in the code with your actual API key. + +2. **Run the App:** + - Execute the main script to fetch and visualize weather and air quality data.