Compare commits

...
8 Commits
14 changed files with 47 additions and 2224 deletions
+4
View File
@@ -22,12 +22,16 @@ from flask_htmx import HTMX
import minify_html
import os
from dotenv import load_dotenv
from flask_compress import Compress
# Load environment variables from .env file in non-production environments
if os.environ.get('FLASK_ENV') != 'production':
load_dotenv()
app = Flask(__name__)
app.config['COMPRESS_REGISTER'] = True
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 31536000 # 1 year
Compress(app)
app.config.from_pyfile('config.py')
app.secret_key = os.environ.get('SECRET_KEY', '2a661781919643cb8a5a8bc57642d99f')
jinja_partials.register_extensions(app)
+16 -13
View File
@@ -1,10 +1,11 @@
import os
import psycopg2
from psycopg2 import pool
from psycopg2.extras import RealDictCursor
from datetime import datetime
from dateutil.relativedelta import relativedelta
from urllib.parse import urlparse
from flask import g
from flask import g, current_app
from features.exercises import Exercises
from features.people_graphs import PeopleGraphs
from features.person_overview import PersonOverview
@@ -16,6 +17,8 @@ from utils import get_exercise_graph_model
class DataBase():
_pool = None
def __init__(self, app=None):
self.stats = Stats(self.execute)
self.exercises = Exercises(self.execute)
@@ -25,29 +28,29 @@ class DataBase():
self.schema = Schema(self.execute)
self.activityRequest = Activity(self.execute)
db_url = urlparse(os.environ['DATABASE_URL'])
# if db_url is null then throw error
if not db_url:
if not os.environ.get('DATABASE_URL'):
raise Exception("No DATABASE_URL environment variable set")
def getDB(self):
db = getattr(g, 'database', None)
if db is None:
if DataBase._pool is None:
db_url = urlparse(os.environ['DATABASE_URL'])
g.database = psycopg2.connect(
DataBase._pool = pool.ThreadedConnectionPool(
1, 20, # minconn, maxconn
database=db_url.path[1:],
user=db_url.username,
password=db_url.password,
host=db_url.hostname,
port=db_url.port
)
db = g.database
return db
def close_connection(exception):
db = getattr(g, 'database', None)
def getDB(self):
if 'database' not in g:
g.database = self._pool.getconn()
return g.database
def close_connection(self, exception=None):
db = g.pop('database', None)
if db is not None:
db.close()
self._pool.putconn(db)
def execute(self, query, args=(), one=False, commit=False):
conn = self.getDB()
+2 -1
View File
@@ -17,4 +17,5 @@ Flask-Bcrypt==1.0.1
email-validator==2.2.0
requests==2.26.0
polars>=0.20.0
pyarrow>=14.0.0
pyarrow>=14.0.0
Flask-Compress==1.13
-63
View File
File diff suppressed because one or more lines are too long
-12
View File
File diff suppressed because one or more lines are too long
-2029
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-18
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -12,7 +12,7 @@
<link href="/static/css/style.css" rel="stylesheet">
<script src="/static/js/htmx.min.js" defer></script>
<script src="/static/js/hyperscript.min.js"></script>
<script src="/static/js/hyperscript.min.js" defer></script>
</head>
<body>
+6 -2
View File
@@ -130,8 +130,12 @@
<div class="overflow-x-auto rounded-lg">
<div class="align-middle inline-block min-w-full">
<div class="shadow overflow-hidden sm:rounded-lg">
<div class="w-full mt-2 pb-2 aspect-video">
{{ render_partial('partials/sparkline.html', **exercise.graph) }}
<div class="w-full mt-2 pb-2 aspect-video"
hx-get="{{ url_for('get_exercise_progress_for_user', person_id=person.id, exercise_id=exercise.id, min_date=min_date, max_date=max_date) }}"
hx-trigger="intersect once" hx-swap="outerHTML">
<div class="flex items-center justify-center h-full bg-gray-50 rounded-lg">
<div class="text-sm text-gray-400 animate-pulse font-medium">Loading graph...</div>
</div>
</div>
<table class="min-w-full divide-y divide-gray-200">
+7 -1
View File
@@ -105,7 +105,13 @@
<div class="mt-4 mb-4 w-full grid grid-cols-1 2xl:grid-cols-2 gap-4">
{% for graph in exercise_progress_graphs %}
{{ render_partial('partials/sparkline.html', **graph.progress_graph) }}
<div hx-get="{{ url_for('get_exercise_progress_for_user', person_id=person_id, exercise_id=graph.exercise_id, min_date=min_date, max_date=max_date) }}"
hx-trigger="intersect once" hx-swap="outerHTML">
<div class="flex items-center justify-center h-48 bg-gray-50 rounded-lg">
<div class="text-sm text-gray-400 animate-pulse font-medium">Loading {{ graph.exercise_name }}...
</div>
</div>
</div>
{% endfor %}
</div>
+11 -7
View File
@@ -48,18 +48,22 @@ def get_exercise_graph_model(title, estimated_1rm, repetitions, weight, start_da
best_fit_points = []
try:
if len(relative_positions) > 1: # Ensure there are enough points for polyfit
# Fit a polynomial of the given degree
coeffs = np.polyfit(relative_positions, estimated_1rm_scaled, degree)
# Filter out NaNs if any (though scaled values shouldn't have them if ranges are correct)
mask = ~np.isnan(estimated_1rm_scaled)
x_fit = relative_positions[mask]
y_fit = estimated_1rm_scaled[mask]
# Ensure we have enough unique X positions for the given degree
if len(np.unique(x_fit)) > degree:
coeffs = np.polyfit(x_fit, y_fit, degree)
poly_fit = np.poly1d(coeffs)
y_best_fit = poly_fit(relative_positions)
best_fit_points = list(zip(y_best_fit.tolist(), relative_positions.tolist()))
else:
raise ValueError("Not enough data points for polyfit")
except (np.linalg.LinAlgError, ValueError) as e:
# Handle cases where polyfit fails
best_fit_points = []
except (np.linalg.LinAlgError, ValueError, TypeError) as e:
# Handle cases where polyfit fails or input is invalid
best_fit_points = []
m, b = 0, 0
# Prepare data for plots
repetitions_data = {