feat: Expand ActivityEntry model and enhance activity log handling
- Added new fields to the ActivityEntry model for improved tracking: hr_min, pace_min_per_km, cadence, avg_power, elevation_gain, temperature_celsius, humidity_percent, avg_hr_percent, and kcal_per_km. - Updated the create_activity function to accommodate the new fields in the activity log. - Modified session metrics handling to ensure accurate data retrieval and merging based on the updated schema.
This commit is contained in:
parent
3296dfca28
commit
766b64cd64
|
|
@ -391,8 +391,8 @@ def replace_activity_session_metrics(
|
||||||
(activity_log_id, spec["training_parameter_id"], vn, vi, vt, vb),
|
(activity_log_id, spec["training_parameter_id"], vn, vi, vt, vb),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Übergang: Spalten in activity_log sind maßgeblich — EAV für alle source_field-Parameter angleichen
|
# Kein sync_column_backed nach PUT /metrics: der Request ist maßgeblich für EAV. Ein Spalten-Sync würde
|
||||||
sync_column_backed_session_metrics(cur, profile_id, activity_log_id)
|
# Werte aus nicht mitgeschriebenen activity_log-Spalten wieder verwerfen.
|
||||||
|
|
||||||
return fetch_activity_session_metrics(cur, activity_log_id)
|
return fetch_activity_session_metrics(cur, activity_log_id)
|
||||||
|
|
||||||
|
|
@ -408,10 +408,40 @@ def get_activity_session_logical_unit(cur, profile_id: str, activity_log_id: str
|
||||||
cur, header.get("training_category"), header.get("training_type_id")
|
cur, header.get("training_category"), header.get("training_type_id")
|
||||||
)
|
)
|
||||||
metrics = fetch_activity_session_metrics(cur, activity_log_id)
|
metrics = fetch_activity_session_metrics(cur, activity_log_id)
|
||||||
|
by_key = {m["key"]: m for m in metrics}
|
||||||
|
merged_metrics: List[Dict[str, Any]] = list(metrics)
|
||||||
|
for s in schema:
|
||||||
|
k = s["key"]
|
||||||
|
if k in by_key:
|
||||||
|
continue
|
||||||
|
sf = s.get("source_field")
|
||||||
|
if not sf or (isinstance(sf, str) and not str(sf).strip()):
|
||||||
|
continue
|
||||||
|
col = str(sf).strip()
|
||||||
|
if col not in header:
|
||||||
|
continue
|
||||||
|
raw = header.get(col)
|
||||||
|
if raw is None:
|
||||||
|
continue
|
||||||
|
dt = s["data_type"]
|
||||||
|
try:
|
||||||
|
val = _coerce_raw_value_for_parameter(dt, raw)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
continue
|
||||||
|
merged_metrics.append(
|
||||||
|
{
|
||||||
|
"training_parameter_id": s["training_parameter_id"],
|
||||||
|
"key": k,
|
||||||
|
"data_type": dt,
|
||||||
|
"unit": s.get("unit"),
|
||||||
|
"value": val,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
merged_metrics.sort(key=lambda x: x["key"])
|
||||||
return {
|
return {
|
||||||
"header": header,
|
"header": header,
|
||||||
"schema": schema,
|
"schema": schema,
|
||||||
"metrics": metrics,
|
"metrics": merged_metrics,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,17 @@ class ActivityEntry(BaseModel):
|
||||||
kcal_resting: Optional[float] = None
|
kcal_resting: Optional[float] = None
|
||||||
hr_avg: Optional[float] = None
|
hr_avg: Optional[float] = None
|
||||||
hr_max: Optional[float] = None
|
hr_max: Optional[float] = None
|
||||||
|
hr_min: Optional[int] = None # DB-Spalte hr_min (Parameter min_hr)
|
||||||
distance_km: Optional[float] = None
|
distance_km: Optional[float] = None
|
||||||
rpe: Optional[int] = None
|
rpe: Optional[int] = None
|
||||||
|
pace_min_per_km: Optional[float] = None
|
||||||
|
cadence: Optional[int] = None
|
||||||
|
avg_power: Optional[int] = None
|
||||||
|
elevation_gain: Optional[int] = None
|
||||||
|
temperature_celsius: Optional[float] = None
|
||||||
|
humidity_percent: Optional[int] = None
|
||||||
|
avg_hr_percent: Optional[float] = None
|
||||||
|
kcal_per_km: Optional[float] = None
|
||||||
source: Optional[str] = 'manual'
|
source: Optional[str] = 'manual'
|
||||||
notes: Optional[str] = None
|
notes: Optional[str] = None
|
||||||
training_type_id: Optional[int] = None # v9d: Training type categorization
|
training_type_id: Optional[int] = None # v9d: Training type categorization
|
||||||
|
|
|
||||||
|
|
@ -102,9 +102,10 @@ def create_activity(e: ActivityEntry, x_profile_id: Optional[str]=Header(default
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""INSERT INTO activity_log
|
"""INSERT INTO activity_log
|
||||||
(id,profile_id,date,start_time,end_time,activity_type,duration_min,kcal_active,kcal_resting,
|
(id,profile_id,date,start_time,end_time,activity_type,duration_min,kcal_active,kcal_resting,
|
||||||
hr_avg,hr_max,distance_km,rpe,source,notes,
|
hr_avg,hr_max,hr_min,distance_km,pace_min_per_km,cadence,avg_power,elevation_gain,
|
||||||
|
temperature_celsius,humidity_percent,avg_hr_percent,kcal_per_km,rpe,source,notes,
|
||||||
training_type_id,training_category,training_subcategory,created)
|
training_type_id,training_category,training_subcategory,created)
|
||||||
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,CURRENT_TIMESTAMP)""",
|
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,CURRENT_TIMESTAMP)""",
|
||||||
(
|
(
|
||||||
eid,
|
eid,
|
||||||
pid,
|
pid,
|
||||||
|
|
@ -117,7 +118,16 @@ def create_activity(e: ActivityEntry, x_profile_id: Optional[str]=Header(default
|
||||||
d["kcal_resting"],
|
d["kcal_resting"],
|
||||||
d["hr_avg"],
|
d["hr_avg"],
|
||||||
d["hr_max"],
|
d["hr_max"],
|
||||||
|
d.get("hr_min"),
|
||||||
d["distance_km"],
|
d["distance_km"],
|
||||||
|
d.get("pace_min_per_km"),
|
||||||
|
d.get("cadence"),
|
||||||
|
d.get("avg_power"),
|
||||||
|
d.get("elevation_gain"),
|
||||||
|
d.get("temperature_celsius"),
|
||||||
|
d.get("humidity_percent"),
|
||||||
|
d.get("avg_hr_percent"),
|
||||||
|
d.get("kcal_per_km"),
|
||||||
d["rpe"],
|
d["rpe"],
|
||||||
d["source"],
|
d["source"],
|
||||||
d["notes"],
|
d["notes"],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef, startTransition } from 'react'
|
||||||
import { Upload, Pencil, Trash2, Check, X, CheckCircle } from 'lucide-react'
|
import { Upload, Pencil, Trash2, Check, X, CheckCircle } from 'lucide-react'
|
||||||
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } from 'recharts'
|
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } from 'recharts'
|
||||||
import { api } from '../utils/api'
|
import { api } from '../utils/api'
|
||||||
|
|
@ -26,7 +26,16 @@ const ACTIVITY_LOG_PAYLOAD_KEYS = new Set([
|
||||||
'kcal_resting',
|
'kcal_resting',
|
||||||
'hr_avg',
|
'hr_avg',
|
||||||
'hr_max',
|
'hr_max',
|
||||||
|
'hr_min',
|
||||||
'distance_km',
|
'distance_km',
|
||||||
|
'pace_min_per_km',
|
||||||
|
'cadence',
|
||||||
|
'avg_power',
|
||||||
|
'elevation_gain',
|
||||||
|
'temperature_celsius',
|
||||||
|
'humidity_percent',
|
||||||
|
'avg_hr_percent',
|
||||||
|
'kcal_per_km',
|
||||||
'rpe',
|
'rpe',
|
||||||
'source',
|
'source',
|
||||||
'notes',
|
'notes',
|
||||||
|
|
@ -412,7 +421,9 @@ export default function ActivityPage() {
|
||||||
}
|
}
|
||||||
setEditing(null)
|
setEditing(null)
|
||||||
setSessionDetail(null)
|
setSessionDetail(null)
|
||||||
await load()
|
startTransition(() => {
|
||||||
|
void load()
|
||||||
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message || 'Speichern fehlgeschlagen')
|
setError(err.message || 'Speichern fehlgeschlagen')
|
||||||
setTimeout(() => setError(null), 6000)
|
setTimeout(() => setError(null), 6000)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user