Skip to content
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
10 changes: 7 additions & 3 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ def calib_validation():
"""
if request.method == "POST":
return session_route.calib_results()
return Response(
"Invalid request method for route", status=405, mimetype="application/json"
)
return Response('Invalid request method for route', status=405, mimetype='application/json')

@app.route('/api/session/batch_predict', methods=['POST'])
def batch_predict():
if request.method == 'POST':
return session_route.batch_predict()
return Response('Invalid request method for route', status=405, mimetype='application/json')
20 changes: 20 additions & 0 deletions app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
blinker==1.9.0
click==8.1.8
Flask==3.1.0
flask-cors==5.0.1
itsdangerous==2.2.0
Jinja2==3.1.6
joblib==1.4.2
MarkupSafe==3.0.2
numpy==2.2.4
pandas==2.2.3
python-dateutil==2.9.0.post0
pytz==2025.2
scikit-learn==1.6.1
scipy==1.15.2
six==1.17.0
threadpoolctl==3.6.0
tzdata==2025.2
Werkzeug==3.1.3
gunicorn==23.0.0
requests==2.31.0
130 changes: 105 additions & 25 deletions app/routes/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@
import csv

from pathlib import Path
import os
import pandas as pd
import traceback
import re
<<<<<<< HEAD
import requests
=======
from flask import Flask, request, Response, send_file
>>>>>>> 42a70612727088340cf95589066fb593eb246472

# Local imports from app
from app.services.storage import save_file_locally
Expand Down Expand Up @@ -147,26 +155,13 @@


def calib_results():
"""
Generate calibration results.

This function generates calibration results based on the provided form data.
It saves the calibration points to a CSV file. Then, it uses the gaze_tracker module to predict the calibration results.

Returns:
Response: A JSON response containing the calibration results.

Raises:
IOError: If there is an error while writing to the CSV files.
"""
# Get form data from request
file_name = json.loads(request.form["file_name"])
fixed_points = json.loads(request.form["fixed_circle_iris_points"])
calib_points = json.loads(request.form["calib_circle_iris_points"])
screen_height = json.loads(request.form["screen_height"])
screen_width = json.loads(request.form["screen_width"])
k = json.loads(request.form["k"])
model = json.loads(request.form["model"])
from_ruxailab = json.loads(request.form['from_ruxailab'])
file_name = json.loads(request.form['file_name'])
fixed_points = json.loads(request.form['fixed_circle_iris_points'])
calib_points = json.loads(request.form['calib_circle_iris_points'])
screen_height = json.loads(request.form['screen_height'])
screen_width = json.loads(request.form['screen_width'])
k = json.loads(request.form['k'])

# Generate csv dataset of calibration points
os.makedirs(
Expand Down Expand Up @@ -219,15 +214,100 @@ def calib_results():
except IOError:
print("I/O error")

# data = gaze_tracker.train_to_validate_calib(calib_csv_file, predict_csv_file)
# Run prediction
data = gaze_tracker.predict(calib_csv_file, calib_csv_file, k)

if from_ruxailab:
try:
payload = {
"session_id": file_name,
"model": data,
"screen_height": screen_height,
"screen_width": screen_width,
"k": k
}

RUXAILAB_WEBHOOK_URL = "https://receivecalibration-ffptzpxikq-uc.a.run.app"

print("file_name:", file_name)

# Predict calibration results
data = gaze_tracker.predict(calib_csv_file, k, model_X=model, model_Y=model)
resp = requests.post(RUXAILAB_WEBHOOK_URL, json=payload)
print("Enviado para RuxaiLab:", resp.status_code, resp.text)
except Exception as e:
print("Erro ao enviar para RuxaiLab:", e)

# Return calibration results
return Response(json.dumps(data), status=200, mimetype="application/json")
return Response(json.dumps(data), status=200, mimetype='application/json')



def batch_predict():
try:
data = request.get_json()

iris_data = data['iris_tracking_data']
k = data.get('k', 3)
screen_height = data.get('screen_height')
screen_width = data.get('screen_width')

base_path = Path().absolute() / 'app/services/calib_validation/csv/data'
calib_csv_path = base_path / 'vcczxvzxcv_fixed_train_data.csv'
predict_csv_path = base_path / 'temp_batch_predict.csv'

print(f"Calib CSV Path: {calib_csv_path}")
print(f"Predict CSV Path: {predict_csv_path}")
print(f"Iris data sample (até 3): {iris_data[:3]}")

# Debug: colunas do CSV de calibração
df_calib = pd.read_csv(calib_csv_path)
print("Colunas do CSV de calibração:", df_calib.columns.tolist())

# Cria CSV temporário com dados de íris para predição
with open(predict_csv_path, 'w', newline='') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=[
'left_iris_x', 'left_iris_y', 'right_iris_x', 'right_iris_y'
])
writer.writeheader()
for item in iris_data:
writer.writerow({
'left_iris_x': item['left_iris_x'],
'left_iris_y': item['left_iris_y'],
'right_iris_x': item['right_iris_x'],
'right_iris_y': item['right_iris_y']
})

# Chama a predição com os dois CSVs de calibração
predictions = gaze_tracker.predict(
calib_csv_path,
calib_csv_path,
k
)

# Verifica se o retorno é lista, dicionário ou outro
if isinstance(predictions, list):
# Se for lista, adiciona timestamp e metadados a cada item
for i in range(len(predictions)):
predictions[i]['timestamp'] = iris_data[i].get('timestamp')
if screen_height is not None:
predictions[i]['screen_height'] = screen_height
if screen_width is not None:
predictions[i]['screen_width'] = screen_width
elif isinstance(predictions, dict):
# Se for dicionário, anexa metadados gerais (exemplo)
if screen_height is not None:
predictions['screen_height'] = screen_height
if screen_width is not None:
predictions['screen_width'] = screen_width
# Timestamp pode não fazer sentido em dicionário com estrutura complexa
else:
print("Retorno da predição tem tipo inesperado:", type(predictions))

return Response(json.dumps(predictions), status=200, mimetype='application/json')

except Exception as e:
print("Erro na batch_predict:", e)
traceback.print_exc()
return Response("Erro interno na predição", status=500)

# def session_results():
# session_id = request.args.__getitem__('id')

Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ scipy==1.15.2
six==1.17.0
threadpoolctl==3.6.0
tzdata==2025.2
Werkzeug==3.1.3
Werkzeug==3.1.3
gunicorn==23.0.0
requests==2.31.0
3 changes: 1 addition & 2 deletions wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@
import os
from app.main import app


if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
Loading