Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 2 additions & 3 deletions week5/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#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.

#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

Expand Down
3 changes: 1 addition & 2 deletions week5/repository.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#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
Expand All @@ -7,5 +8,3 @@ def get_weather_data(self, 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
111 changes: 96 additions & 15 deletions week5/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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

Expand All @@ -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': '########################' # 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"}
Expand Down Expand Up @@ -78,40 +140,58 @@ 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))


# r = requests.get(url, params=payload)

#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)
Expand All @@ -127,3 +207,4 @@ def create_data_service(self, service_type):
plt.show()

print("Hurray")

67 changes: 67 additions & 0 deletions week5/weather_database.py
Original file line number Diff line number Diff line change
@@ -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")
Loading