Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleaned Backend #51

Merged
merged 10 commits into from
Jun 5, 2024
Merged
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
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# Usage

## To start the Flask Backend:
- cd into /birdmig/backend/app
- enter ```uvicorn base:app --reload``` in your terminal
- ```cd``` into ```/birdmig/backend/app```
- Run the backend
- To run the backend for showcase and documentation purposes, enter ```fastapi dev base.py``` in your terminal.
- Access docs by navigating to ```localhost:8000/docs``` on your web browser
- To run the backend for debugging purposes, run ```uvicorn base:app --reload``` in your terminal.
- Note that this only runs the backend. It can not be navigated to on the web browser, nor will documentation be available.

## To start the React Frontend:

Expand Down
2 changes: 0 additions & 2 deletions backend/.flaskenv

This file was deleted.

6 changes: 1 addition & 5 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,8 @@ COPY app/data/ /code/data/

COPY app/models/ /code/models/

# Set environment variables
ENV FLASK_APP=app/base.py
ENV FLASK_RUN_HOST=0.0.0.0

# Expose the port the app runs on
EXPOSE 8000

# Command to run the application
CMD ["flask", "run"]
CMD ["fastapi", "dev", "base.py"]
8 changes: 0 additions & 8 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
# Data format:
- ```info```: string
- ```sdmData```: x-y coordinate pair.
- ```timeSeriesData```: array of time-series values for climate variables
- ```precipiation```
- ```climate```
- ```temperature```

# Running Tests:
Write all tests for the backend in the ```tests``` directory.

Expand Down
98 changes: 11 additions & 87 deletions backend/app/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import uvicorn
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, JSONResponse
Expand All @@ -9,7 +8,6 @@

import os

#from .config import AppConfig
import pandas as pd
import numpy as np
from shapely.geometry import LineString
Expand All @@ -19,9 +17,6 @@
from PIL import Image
from io import BytesIO
import base64
# DEBUGGING
import sys
from time import sleep

app = FastAPI()
frontend_origin = "http://localhost:3000"
Expand Down Expand Up @@ -184,33 +179,26 @@ def get_precipitation_data(selectedYear: int):
return monthly_avg.to_dict(orient='records')


'''app.get('/bird-data/{bird_name}')
def get_bird_data(bird_name):
if bird_name in bird_data:
return bird_data'''


class PredictionInputs(BaseModel):
bird: str
year: int
emissions: str

@app.put('/prediction')
async def predict(prediction_input: PredictionInputs):
async def get_predictions(prediction_input: PredictionInputs):
selected_bird = prediction_input.bird
selected_year = str(prediction_input.year)
emission_Type = prediction_input.emissions

# For image data:
output_path = f"../../model/outputs/png-images/{birdsModelDirs[selected_bird]}/{emission_Type}/{selected_year}.png"
dataImg = Image.open(output_path)
buffer = BytesIO()
dataImg.save(buffer, format="png")

# Artificial delay to simulate model prediction time
#sleep(2.5)
# FastAPI requests can handle asynchronous predictions.
# In practice, this would be waiting for a machine learning model to generate
# predictions. In this version, waiting wasn't necessary, so it returns immediately.

#If we'll need to encapsulate a file, use this:
return {
"prediction": base64.b64encode(buffer.getvalue()).decode(),
"resFormat": dataImg.format
Expand All @@ -230,8 +218,8 @@ def get_bird_info(bird_name):
raise HTTPException(
status_code= 404,
detail= f"data for bird {bird_name} does not exist.")


@app.get('/json/{filename}')
def send_json(filename):
climate_file_loc = os.path.join('climate_data/json_data', filename)
Expand All @@ -244,6 +232,7 @@ def send_json(filename):

@app.get('/get_trajectory_data')
def get_trajectory_data(bird: str, birdID: str):

filename = f'./data/{bird}.csv'
try:
df = pd.read_csv(filename)
Expand All @@ -253,71 +242,29 @@ def get_trajectory_data(bird: str, birdID: str):
if bird_data.empty:
raise HTTPException(
status_code= 404,
details= 'No trajectory data found for given bird ID')
detail= 'No trajectory data found for given bird ID')

# Convert data to dictionary format
trajectory_data = bird_data[['LATITUDE', 'LONGITUDE', 'TIMESTAMP']].to_dict(orient='records')
return trajectory_data
except FileNotFoundError:
raise HTTPException(
status_code= 404,
details= f'CSV file for {bird} not found')
detail= f'CSV file for {filename} not found')


@app.get('/get_bird_ids')
def get_bird_ids(bird: str):
filename = f'./data/{bird}.csv'

try:
df = pd.read_csv(filename)
bird_ids = df['ID'].unique().tolist()
return bird_ids
except FileNotFoundError:
raise HTTPException(
status_code=404,
detail= f'CSV file for {bird} not found')


@app.get('/get_general_migration')
def get_general_migration(selected_bird: str):
filename = f'./data/{selected_bird}.csv'

try:
df = pd.read_csv(filename, low_memory=False)

# Convert TIMESTAMP column to datetime
df['TIMESTAMP'] = pd.to_datetime(df['TIMESTAMP'])

# Group by ID and get the start and end months for each group
grouped = df.groupby('ID')
print(grouped)
simplified_polylines = []
for _, group in grouped:
# Sort by timestamp
group = group.sort_values(by='TIMESTAMP')

# Get coordinates
coordinates = list(zip(group['LATITUDE'], group['LONGITUDE']))

# Apply Douglas-Peucker algorithm for simplification
simplified_line = simplify_line(coordinates)

# Calculate direction for simplified line
start_point = simplified_line[0]
end_point = simplified_line[-1]
delta_lat = end_point[0] - start_point[0]
delta_lon = end_point[1] - start_point[1]
direction = np.arctan2(delta_lat, delta_lon) * (180 / np.pi)

# Append simplified line with direction to simplified_polylines
simplified_polylines.append({
'coordinates': simplified_line,
'direction': direction
})

return {'segmented_polylines': simplified_polylines}

except Exception as e:
raise HTTPException(status_code= 400, detail=str(e))
detail= f'CSV file for {filename} not found')


def simplify_line(coordinates, tolerance=0.1):
Expand Down Expand Up @@ -377,26 +324,3 @@ def get_SDM_data(prediction_input: PredictionInputs):

# Send converted data to frontend
return JSONResponse(content=converted_data)

'''@api.route('/get_SDM_data')
@cross_origin(supports_credentials=True)
def get_SDM_data():
tiff_file_path = f"../model/outputs/tiff-images/{birdsModelDirs[session['bird']]}/{session['emissions']}/{session['year']}/probability_1.0.tif"
try:
# Open the image using Pillow
img = Image.open(tiff_file_path)
dataset = rasterio.open(tiff_file_path)
print(dataset.bounds)
# Extract data from the image
img_array = np.array(img)
response_data = {'tiff_data': img_array.tolist()}
# Send the TIFF data as JSON response
return jsonify(response_data)

except Exception as e:
return f'An error occurred: {e}'
'''

if __name__ == '__main__':
api.run(debug=True)
socket_io.run(api, debug=True, port=5000)
10 changes: 0 additions & 10 deletions backend/app/config.py

This file was deleted.

16 changes: 7 additions & 9 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
fastapi

python-dotenv
jinja2
pandas
Shapely

pytest
pydantic
requests
pytest-cov
flask_testing
pandas
numpy
shapely
rasterio
pillow
pytest
Loading
Loading