Skip to content

Commit

Permalink
data can be added onto the database with a post endpoint.
Browse files Browse the repository at this point in the history
  • Loading branch information
shafwanur committed Sep 17, 2024
1 parent ea9ff40 commit f553fac
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 178 deletions.
107 changes: 0 additions & 107 deletions db-fixtures/fixture.json
Original file line number Diff line number Diff line change
@@ -1,107 +0,0 @@
{
"data": [
{
"_id": 2,
"name": "Bill Jane",
"location": "Deggendorf",
"weather_data": {
"coord": {
"lon": 12.9667,
"lat": 48.8333
},
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04n"
}
],
"base": "stations",
"main": {
"temp": 285.01,
"feels_like": 284.81,
"temp_min": 284.16,
"temp_max": 287.17,
"pressure": 1018,
"humidity": 98,
"sea_level": 1018,
"grnd_level": 965
},
"visibility": 10000,
"wind": {
"speed": 2.35,
"deg": 302,
"gust": 5.32
},
"clouds": {
"all": 100
},
"dt": 1726508289,
"sys": {
"type": 2,
"id": 2013220,
"country": "DE",
"sunrise": 1726461992,
"sunset": 1726507168
},
"timezone": 7200,
"id": 2938540,
"name": "Deggendorf",
"cod": 200
},
"last_updated": "2024-09-16"
},
{
"_id": 1,
"name": "John Doe",
"location": "London",
"weather_data": {
"coord": {
"lon": -0.1257,
"lat": 51.5085
},
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04d"
}
],
"base": "stations",
"main": {
"temp": 290.72,
"feels_like": 290.31,
"temp_min": 289.36,
"temp_max": 291.43,
"pressure": 1025,
"humidity": 68,
"sea_level": 1025,
"grnd_level": 1021
},
"visibility": 10000,
"wind": {
"speed": 4.63,
"deg": 260
},
"clouds": {
"all": 100
},
"dt": 1726422432,
"sys": {
"type": 2,
"id": 2075535,
"country": "GB",
"sunrise": 1726378557,
"sunset": 1726424130
},
"timezone": 3600,
"id": 2643743,
"name": "London",
"cod": 200
},
"last_updated": "2024-09-15"
}
]
}
159 changes: 88 additions & 71 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,59 @@
from pymongo import MongoClient
from fastapi import FastAPI, HTTPException, Request, Depends
from datetime import datetime
from bson import ObjectId

app = FastAPI()
EPS = 1e-4

def get_db_connection():
client = MongoClient(os.getenv("MONGO_URL"))
db = client.zelara_db
return db

def get_bearer_token(request: Request):
authorization: str = request.headers.get("Authorization")
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code = 401, detail = "Authorization header missing or invalid")

token = authorization.split("Bearer ")[1]
if not token:
raise HTTPException(status_code = 401, detail = "Bearer token missing")

return token

@app.get("/")
def read_root():
return {"message": "Welcome to the Zelara Weather API!"}

@app.get("/api/data") # appends whatever exists in db-fixtures/fixture.json into the database, returns all entries in the database
@app.get("/api/data") # returns all entries in the database
def read_data():
db = get_db_connection()
collection = db["mycollection"]
rows = list(collection.find({}))

if not rows:
raise HTTPException(status_code = 404, detail = "No data found")
raise HTTPException(status_code = 404, detail = "Database is empty")

return {"data": rows}
for row in rows:
row["_id"] = str(row["_id"])

return {
"count": len(rows),
"data": rows
}

@app.get("/weather") # get weather of one specific city
def get_weather_data(city: str, api_key: str = Depends(get_bearer_token)):
url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}"
@app.delete("/")
def delete_all_entries(): # deletes all entries in the database. should NOT be misused.
db = get_db_connection()
collection = db["mycollection"]
result = collection.delete_many({})
return {
"status_code": 200,
"message": f"Deleted {result.deleted_count} documents."
}

def get_bearer_token(request: Request): # extracts bearer token from header created whilst making the API call
authorization: str = request.headers.get("Authorization")
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code = 401, detail = "Authorization header missing or invalid")

token = authorization.split("Bearer ")[1]
if not token:
raise HTTPException(status_code = 401, detail = "Bearer token missing")

return token

def get_response(url): # a function to handle default behavior when making API calls to openweather
try:
response = requests.get(url, timeout = 10)
response.raise_for_status()
Expand All @@ -49,68 +65,69 @@ def get_weather_data(city: str, api_key: str = Depends(get_bearer_token)):
raise HTTPException(status_code = response.status_code, detail = str(http_err))
except requests.exceptions.RequestException as e:
raise HTTPException(status_code = 500, detail = str(e))

def get_geolocation(city: str, api_key: str):
def get_geolocation(city: str, api_key: str): # getting latitude and longitude values from a city name
lat, lon = -1, -1
url = f"http://api.openweathermap.org/geo/1.0/direct?q={city}&limit=1&appid={api_key}"
try:
response = requests.get(url, timeout = 10)
response.raise_for_status()
geo_data = response.json()
if not geo_data:
raise HTTPException(status_code=404, detail="City not found")
lat = geo_data[0]["lat"]
lon = geo_data[0]["lon"]
except requests.exceptions.HTTPError as http_err:
raise HTTPException(status_code=response.status_code, detail=str(http_err))
except requests.exceptions.RequestException as e:
raise HTTPException(status_code = 500, detail = str(e))

geo_data = get_response(url)
lat = geo_data[0]["lat"]
lon = geo_data[0]["lon"]
return (lat, lon)

@app.get("/air_pollution") # get air quality data of a specific city. latitude and longitude values have to be provided
def get_air_pollution_index(city: str, api_key: str = Depends(get_bearer_token)):
lat, lon = get_geolocation(city, api_key)
# Fetch air pollution data
url = f"http://api.openweathermap.org/data/2.5/air_pollution/forecast?lat={lat}&lon={lon}&appid={api_key}"
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as http_err:
raise HTTPException(status_code = response.status_code, detail = str(http_err))
except requests.exceptions.RequestException as e:
raise HTTPException(status_code = 500, detail = str(e))

@app.get("/update") # update a user's location and/or update their weather if more than 24 hours have passed since the last change
def update(_id: int, location: str, api_key: str = Depends(get_bearer_token)):
db = get_db_connection()
collection = db["mycollection"]
current_datetime = datetime.now().strftime('%Y-%m-%d')
@app.get("/find/city") # find if a city already exists in the database
def find_city(name: str, lat = None, lon = None, api_key: str = Depends(get_bearer_token)):
if lat == None and lon == None:
lat, lon = get_geolocation(name, api_key)

rows = collection.find({"_id": _id})
for row in rows:
if location != row["location"] or abs((datetime.strptime(current_datetime, '%Y-%m-%d') - datetime.strptime(row["last_updated"], '%Y-%m-%d'))).days >= 1:
weather_data = get_weather_data(location, api_key)
collection.update_one(row, {"$set": {"location": location, "weather_data": weather_data, "last_updated": current_datetime}})
return {"success": "Updated successfully!"}

return {"data": "0 updates done successfully"}

@app.get("/bulk_refresh") # bulk refresh everything, egal ob es vor 24 Stunden gemacht wurde oder ned
def bulk_refresh(api_key: str = Depends(get_bearer_token)):
db = get_db_connection()
collection = db["mycollection"]
rows = list(collection.find({}))
current_datetime = datetime.now().strftime('%Y-%m-%d')
for row in rows:
print(row)
weather_data = get_weather_data(row["location"], api_key)
collection.update_one(row, {"$set": {"weather_data": weather_data, "last_updated": current_datetime}})

rows = list(collection.find({}))
geolocation_object = row["weather_data"]["coord"]
if abs(geolocation_object["lat"] - lat) < EPS and abs(geolocation_object["lon"] - lon) < EPS:
return {
"status_code": 200,
"id": str(row["_id"])
}

return {
"status_code": 404,
"message": "City doesn't exist in the database."
}

if not rows:
raise HTTPException(status_code = 404, detail = "No data found")
@app.get("/find/id") # find id of an existing city in the database
def find_id(id: str):
db = get_db_connection()
collection = db["mycollection"]
row = collection.find_one({"_id": ObjectId(id)})
if row:
row["_id"] = str(row["_id"])
return {
"status_code": 200,
"data": row
}

return {
"status_code": 404,
"detail": f"_id {id} does not exist in the database"
}

@app.post("/add") # add a new city to the database.
def add_city(city: str, api_key: str = Depends(get_bearer_token)):
lat, lon = get_geolocation(city, api_key)
if find_city(city, lat, lon)["status_code"] == 200:
raise HTTPException(status_code = 404, detail = "City already exists in the database.")

return {"data": rows}
db = get_db_connection()
collection = db["mycollection"]
url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={api_key}"
weather_data = get_response(url)
data = {
"weather_data": weather_data,
"last_updated": datetime.now().strftime("%d.%m.%Y")
}
result = collection.insert_one(data)
return {
"status_code": "200",
"message": f"Successfully added object with id {result.inserted_id}"
}

0 comments on commit f553fac

Please sign in to comment.