Merge pull request 'feat: add relaxed arm circumference measurement and update related features' (#91) from develop into main
Reviewed-on: #91
This commit is contained in:
commit
5fbaa7cc8b
|
|
@ -223,6 +223,11 @@ def calculate_arm_28d_delta(profile_id: str) -> Optional[float]:
|
|||
return _calculate_circumference_delta(profile_id, 'c_arm', 28)
|
||||
|
||||
|
||||
def calculate_arm_relaxed_28d_delta(profile_id: str) -> Optional[float]:
|
||||
"""28-day relaxed arm circumference change (cm)."""
|
||||
return _calculate_circumference_delta(profile_id, 'c_arm_relaxed', 28)
|
||||
|
||||
|
||||
def calculate_thigh_28d_delta(profile_id: str) -> Optional[float]:
|
||||
"""Calculate 28-day thigh circumference change (cm)"""
|
||||
delta = _calculate_circumference_delta(profile_id, 'c_thigh', 28)
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ __all__ = [
|
|||
'calculate_hip_28d_delta',
|
||||
'calculate_chest_28d_delta',
|
||||
'calculate_arm_28d_delta',
|
||||
'calculate_arm_relaxed_28d_delta',
|
||||
'calculate_thigh_28d_delta',
|
||||
'calculate_waist_hip_ratio',
|
||||
'calculate_recomposition_quadrant',
|
||||
|
|
|
|||
|
|
@ -370,7 +370,8 @@ def get_circumference_summary_data(
|
|||
('c_hip', 'Hüfte'),
|
||||
('c_thigh', 'Oberschenkel'),
|
||||
('c_calf', 'Wade'),
|
||||
('c_arm', 'Arm')
|
||||
('c_arm', 'Oberarm kontrahiert'),
|
||||
('c_arm_relaxed', 'Oberarm'),
|
||||
]
|
||||
|
||||
measurements = []
|
||||
|
|
@ -401,7 +402,7 @@ def get_circumference_summary_data(
|
|||
})
|
||||
|
||||
# Calculate confidence based on how many points we have
|
||||
confidence = calculate_confidence(len(measurements), 8, "general")
|
||||
confidence = calculate_confidence(len(measurements), 9, "general")
|
||||
|
||||
if not measurements:
|
||||
return {
|
||||
|
|
@ -640,10 +641,15 @@ def calculate_chest_28d_delta(profile_id: str) -> Optional[float]:
|
|||
|
||||
|
||||
def calculate_arm_28d_delta(profile_id: str) -> Optional[float]:
|
||||
"""Calculate 28-day arm circumference change (cm)"""
|
||||
"""28-Tage-Delta Oberarm kontrahiert (c_arm), cm."""
|
||||
return _calculate_circumference_delta(profile_id, 'c_arm', 28)
|
||||
|
||||
|
||||
def calculate_arm_relaxed_28d_delta(profile_id: str) -> Optional[float]:
|
||||
"""28-Tage-Delta Oberarm entspannt (c_arm_relaxed), cm."""
|
||||
return _calculate_circumference_delta(profile_id, 'c_arm_relaxed', 28)
|
||||
|
||||
|
||||
def calculate_thigh_28d_delta(profile_id: str) -> Optional[float]:
|
||||
"""Calculate 28-day thigh circumference change (cm)"""
|
||||
delta = _calculate_circumference_delta(profile_id, 'c_thigh', 28)
|
||||
|
|
|
|||
5
backend/migrations/059_circumference_c_arm_relaxed.sql
Normal file
5
backend/migrations/059_circumference_c_arm_relaxed.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
-- Zusätzlicher Umfang: Oberarm entspannt (c_arm = historisch / Oberarm kontrahiert)
|
||||
ALTER TABLE circumference_log ADD COLUMN IF NOT EXISTS c_arm_relaxed NUMERIC(5,2);
|
||||
|
||||
COMMENT ON COLUMN circumference_log.c_arm IS 'Oberarmumfang kontrahiert/angespannt (bestehende Daten)';
|
||||
COMMENT ON COLUMN circumference_log.c_arm_relaxed IS 'Oberarmumfang entspannt';
|
||||
|
|
@ -50,6 +50,7 @@ class CircumferenceEntry(BaseModel):
|
|||
c_thigh: Optional[float] = None
|
||||
c_calf: Optional[float] = None
|
||||
c_arm: Optional[float] = None
|
||||
c_arm_relaxed: Optional[float] = None
|
||||
notes: Optional[str] = None
|
||||
photo_id: Optional[str] = None
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@ Body Composition (5):
|
|||
- waist_hip_ratio
|
||||
- recomposition_quadrant
|
||||
|
||||
Circumference Deltas (5):
|
||||
Circumference Deltas (6):
|
||||
- waist_28d_delta
|
||||
- arm_28d_delta
|
||||
- arm_28d_delta (Oberarm kontrahiert, c_arm)
|
||||
- arm_relaxed_28d_delta (Oberarm entspannt, c_arm_relaxed)
|
||||
- chest_28d_delta
|
||||
- hip_28d_delta
|
||||
- thigh_28d_delta
|
||||
|
|
@ -1033,14 +1034,14 @@ def register_body_metrics():
|
|||
|
||||
arm_28d_delta_metadata = PlaceholderMetadata(
|
||||
key="arm_28d_delta",
|
||||
description="Armumfang Änderung 28d (cm)",
|
||||
description="Oberarm kontrahiert (c_arm): Umfangs-Änderung 28d (cm)",
|
||||
resolver_function="_safe_float('arm_28d_delta', decimals=1)",
|
||||
data_layer_function="calculate_arm_28d_delta",
|
||||
semantic_contract=(
|
||||
"Liefert die Veränderung des Armumfangs in Zentimetern über 28 Tage. "
|
||||
"Positive Werte bedeuten Zunahme, negative Werte Reduktion."
|
||||
"Veränderung des kontrahierten/angespannten Oberarmumfangs (Spalte c_arm) in cm über 28 Tage. "
|
||||
"Entspricht historischen Einträgen „Arm“ vor Einführung des zweiten Messpunkts."
|
||||
),
|
||||
business_meaning="Ergänzender Umfangsindikator für detaillierte Körperentwicklungsanalysen",
|
||||
business_meaning="Arm-Umfang unter Anspannung (z. B. leicht gebeugter Arm, Bizeps leicht aktiv)",
|
||||
unit="cm",
|
||||
example_output="+0.6",
|
||||
**circumference_delta_common
|
||||
|
|
@ -1054,6 +1055,30 @@ def register_body_metrics():
|
|||
arm_28d_delta_metadata.set_evidence("example_output", EvidenceType.DRAFT_DERIVED)
|
||||
register_placeholder(arm_28d_delta_metadata)
|
||||
|
||||
# ── arm_relaxed_28d_delta ────────────────────────────────────────────────
|
||||
|
||||
arm_relaxed_28d_delta_metadata = PlaceholderMetadata(
|
||||
key="arm_relaxed_28d_delta",
|
||||
description="Oberarm entspannt (c_arm_relaxed): Umfangs-Änderung 28d (cm)",
|
||||
resolver_function="_safe_float('arm_relaxed_28d_delta', decimals=1)",
|
||||
data_layer_function="calculate_arm_relaxed_28d_delta",
|
||||
semantic_contract=(
|
||||
"Veränderung des entspannten Oberarmumfangs (Spalte c_arm_relaxed) in cm über 28 Tage."
|
||||
),
|
||||
business_meaning="Arm-Umfang bei locker hängendem Arm ohne zusätzliche Muskelanspannung",
|
||||
unit="cm",
|
||||
example_output="+0.3",
|
||||
**circumference_delta_common
|
||||
)
|
||||
arm_relaxed_28d_delta_metadata.evidence.update(circ_delta_evidence)
|
||||
arm_relaxed_28d_delta_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
|
||||
arm_relaxed_28d_delta_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
|
||||
arm_relaxed_28d_delta_metadata.set_evidence("semantic_contract", EvidenceType.DRAFT_DERIVED)
|
||||
arm_relaxed_28d_delta_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
|
||||
arm_relaxed_28d_delta_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
|
||||
arm_relaxed_28d_delta_metadata.set_evidence("example_output", EvidenceType.DRAFT_DERIVED)
|
||||
register_placeholder(arm_relaxed_28d_delta_metadata)
|
||||
|
||||
# ── chest_28d_delta ──────────────────────────────────────────────────────
|
||||
|
||||
chest_28d_delta_metadata = PlaceholderMetadata(
|
||||
|
|
|
|||
|
|
@ -749,7 +749,8 @@ _SAFE_FLOAT_NONE_REASON: Dict[str, str] = {
|
|||
"waist_28d_delta": "Taillen-Delta 28 Tage nicht berechenbar (zwei auswertbare Messungen nötig)",
|
||||
"hip_28d_delta": "Hüft-Delta 28 Tage nicht berechenbar",
|
||||
"chest_28d_delta": "Brust-Delta 28 Tage nicht berechenbar",
|
||||
"arm_28d_delta": "Arm-Delta 28 Tage nicht berechenbar",
|
||||
"arm_28d_delta": "Oberarm kontrahiert: Delta 28 Tage nicht berechenbar",
|
||||
"arm_relaxed_28d_delta": "Oberarm entspannt: Delta 28 Tage nicht berechenbar",
|
||||
"thigh_28d_delta": "Oberschenkel-Delta 28 Tage nicht berechenbar",
|
||||
"waist_hip_ratio": "Taille-Hüfte-Verhältnis nicht berechenbar",
|
||||
"energy_balance_7d": (
|
||||
|
|
@ -913,6 +914,7 @@ def _safe_float(func_name: str, profile_id: str, decimals: int = 1) -> str:
|
|||
'hip_28d_delta': body_metrics.calculate_hip_28d_delta,
|
||||
'chest_28d_delta': body_metrics.calculate_chest_28d_delta,
|
||||
'arm_28d_delta': body_metrics.calculate_arm_28d_delta,
|
||||
'arm_relaxed_28d_delta': body_metrics.calculate_arm_relaxed_28d_delta,
|
||||
'thigh_28d_delta': body_metrics.calculate_thigh_28d_delta,
|
||||
'waist_hip_ratio': body_metrics.calculate_waist_hip_ratio,
|
||||
'energy_balance_7d': nutrition_metrics.calculate_energy_balance_7d,
|
||||
|
|
@ -1556,6 +1558,7 @@ PLACEHOLDER_MAP: Dict[str, Callable[[str], str]] = {
|
|||
'{{hip_28d_delta}}': lambda pid: _safe_float('hip_28d_delta', pid),
|
||||
'{{chest_28d_delta}}': lambda pid: _safe_float('chest_28d_delta', pid),
|
||||
'{{arm_28d_delta}}': lambda pid: _safe_float('arm_28d_delta', pid),
|
||||
'{{arm_relaxed_28d_delta}}': lambda pid: _safe_float('arm_relaxed_28d_delta', pid),
|
||||
'{{thigh_28d_delta}}': lambda pid: _safe_float('thigh_28d_delta', pid),
|
||||
'{{waist_hip_ratio}}': lambda pid: _safe_float('waist_hip_ratio', pid, decimals=3),
|
||||
'{{recomposition_quadrant}}': lambda pid: _safe_str('recomposition_quadrant', pid),
|
||||
|
|
@ -1785,7 +1788,7 @@ def get_available_placeholders(categories: Optional[List[str]] = None) -> Dict[s
|
|||
'{{weight_7d_median}}', '{{weight_28d_slope}}', '{{weight_90d_slope}}',
|
||||
'{{fm_28d_change}}', '{{lbm_28d_change}}',
|
||||
'{{waist_28d_delta}}', '{{hip_28d_delta}}', '{{chest_28d_delta}}',
|
||||
'{{arm_28d_delta}}', '{{thigh_28d_delta}}',
|
||||
'{{arm_28d_delta}}', '{{arm_relaxed_28d_delta}}', '{{thigh_28d_delta}}',
|
||||
'{{waist_hip_ratio}}', '{{recomposition_quadrant}}',
|
||||
'{{body_progress_score}}',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -67,10 +67,10 @@ def upsert_circ(e: CircumferenceEntry, x_profile_id: Optional[str]=Header(defaul
|
|||
# INSERT new entry
|
||||
eid = str(uuid.uuid4())
|
||||
cur.execute("""INSERT INTO circumference_log
|
||||
(id,profile_id,date,c_neck,c_chest,c_waist,c_belly,c_hip,c_thigh,c_calf,c_arm,notes,photo_id,created)
|
||||
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,CURRENT_TIMESTAMP)""",
|
||||
(id,profile_id,date,c_neck,c_chest,c_waist,c_belly,c_hip,c_thigh,c_calf,c_arm,c_arm_relaxed,notes,photo_id,created)
|
||||
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,CURRENT_TIMESTAMP)""",
|
||||
(eid,pid,d['date'],d['c_neck'],d['c_chest'],d['c_waist'],d['c_belly'],
|
||||
d['c_hip'],d['c_thigh'],d['c_calf'],d['c_arm'],d['notes'],d['photo_id']))
|
||||
d['c_hip'],d['c_thigh'],d['c_calf'],d['c_arm'],d.get('c_arm_relaxed'),d['notes'],d['photo_id']))
|
||||
|
||||
# Phase 2: Increment usage counter (only for new entries)
|
||||
increment_feature_usage(pid, 'circumference_entries')
|
||||
|
|
|
|||
|
|
@ -312,14 +312,38 @@ Datumsformat: YYYY-MM-DD
|
|||
cur.execute("SELECT id, date, weight, note, source, created FROM weight_log WHERE profile_id=%s ORDER BY date", (pid,))
|
||||
write_csv(zf, "weight.csv", [r2d(r) for r in cur.fetchall()], ['id','date','weight','note','source','created'])
|
||||
|
||||
cur.execute("SELECT id, date, c_waist, c_hip, c_chest, c_neck, c_arm, c_thigh, c_calf, notes, created FROM circumference_log WHERE profile_id=%s ORDER BY date", (pid,))
|
||||
cur.execute(
|
||||
"SELECT id, date, c_waist, c_hip, c_chest, c_neck, c_arm, c_arm_relaxed, c_thigh, c_calf, notes, created FROM circumference_log WHERE profile_id=%s ORDER BY date",
|
||||
(pid,),
|
||||
)
|
||||
rows = [r2d(r) for r in cur.fetchall()]
|
||||
for r in rows:
|
||||
r['waist'] = r.pop('c_waist', None); r['hip'] = r.pop('c_hip', None)
|
||||
r['chest'] = r.pop('c_chest', None); r['neck'] = r.pop('c_neck', None)
|
||||
r['upper_arm'] = r.pop('c_arm', None); r['thigh'] = r.pop('c_thigh', None)
|
||||
r['upper_arm_contracted'] = r.pop('c_arm', None)
|
||||
r['upper_arm_relaxed'] = r.pop('c_arm_relaxed', None)
|
||||
r['thigh'] = r.pop('c_thigh', None)
|
||||
r['calf'] = r.pop('c_calf', None); r['forearm'] = None; r['note'] = r.pop('notes', None)
|
||||
write_csv(zf, "circumferences.csv", rows, ['id','date','waist','hip','chest','neck','upper_arm','thigh','calf','forearm','note','created'])
|
||||
write_csv(
|
||||
zf,
|
||||
"circumferences.csv",
|
||||
rows,
|
||||
[
|
||||
'id',
|
||||
'date',
|
||||
'waist',
|
||||
'hip',
|
||||
'chest',
|
||||
'neck',
|
||||
'upper_arm_contracted',
|
||||
'upper_arm_relaxed',
|
||||
'thigh',
|
||||
'calf',
|
||||
'forearm',
|
||||
'note',
|
||||
'created',
|
||||
],
|
||||
)
|
||||
|
||||
cur.execute("SELECT id, date, sf_chest, sf_abdomen, sf_thigh, sf_triceps, sf_subscap, sf_suprailiac, sf_axilla, sf_method, body_fat_pct, notes, created FROM caliper_log WHERE profile_id=%s ORDER BY date", (pid,))
|
||||
rows = [r2d(r) for r in cur.fetchall()]
|
||||
|
|
|
|||
|
|
@ -112,12 +112,17 @@ async def import_zip(
|
|||
csv_data = zf.read('data/circumferences.csv').decode('utf-8-sig')
|
||||
reader = csv.DictReader(io.StringIO(csv_data), delimiter=';')
|
||||
for row in reader:
|
||||
_ua_contr = (
|
||||
row.get('upper_arm_contracted')
|
||||
or row.get('upper_arm')
|
||||
)
|
||||
_ua_rel = row.get('upper_arm_relaxed')
|
||||
cur.execute("""
|
||||
INSERT INTO circumference_log (
|
||||
profile_id, date, c_waist, c_hip, c_chest, c_neck,
|
||||
c_arm, c_thigh, c_calf, notes, created
|
||||
c_arm, c_arm_relaxed, c_thigh, c_calf, notes, created
|
||||
)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
ON CONFLICT (profile_id, date) DO NOTHING
|
||||
""", (
|
||||
pid,
|
||||
|
|
@ -126,7 +131,8 @@ async def import_zip(
|
|||
float(row['hip']) if row.get('hip') else None,
|
||||
float(row['chest']) if row.get('chest') else None,
|
||||
float(row['neck']) if row.get('neck') else None,
|
||||
float(row['upper_arm']) if row.get('upper_arm') else None,
|
||||
float(_ua_contr) if _ua_contr not in (None, '') else None,
|
||||
float(_ua_rel) if _ua_rel not in (None, '') else None,
|
||||
float(row['thigh']) if row.get('thigh') else None,
|
||||
float(row['calf']) if row.get('calf') else None,
|
||||
row.get('note', ''),
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ CREATE TABLE IF NOT EXISTS circumference_log (
|
|||
c_thigh NUMERIC(5,2),
|
||||
c_calf NUMERIC(5,2),
|
||||
c_arm NUMERIC(5,2),
|
||||
c_arm_relaxed NUMERIC(5,2),
|
||||
notes TEXT,
|
||||
photo_id UUID,
|
||||
created TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import { CIRCUMFERENCE_POINTS } from '../utils/guideData'
|
|||
import UsageBadge from '../components/UsageBadge'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const FIELDS = ['c_neck','c_chest','c_waist','c_belly','c_hip','c_thigh','c_calf','c_arm']
|
||||
const LABELS = {c_neck:'Hals',c_chest:'Brust',c_waist:'Taille',c_belly:'Bauch',c_hip:'Hüfte',c_thigh:'Oberschenkel',c_calf:'Wade',c_arm:'Oberarm'}
|
||||
const FIELDS = ['c_neck','c_chest','c_waist','c_belly','c_hip','c_thigh','c_calf','c_arm','c_arm_relaxed']
|
||||
const LABELS = {c_neck:'Hals',c_chest:'Brust',c_waist:'Taille',c_belly:'Bauch',c_hip:'Hüfte',c_thigh:'Oberschenkel',c_calf:'Wade',c_arm:'Oberarm kontrahiert',c_arm_relaxed:'Oberarm'}
|
||||
|
||||
function empty() { return {date:dayjs().format('YYYY-MM-DD'), c_neck:'',c_chest:'',c_waist:'',c_belly:'',c_hip:'',c_thigh:'',c_calf:'',c_arm:'',notes:'',photo_id:''} }
|
||||
function empty() { return {date:dayjs().format('YYYY-MM-DD'), c_neck:'',c_chest:'',c_waist:'',c_belly:'',c_hip:'',c_thigh:'',c_calf:'',c_arm:'',c_arm_relaxed:'',notes:'',photo_id:''} }
|
||||
|
||||
export default function CircumScreen() {
|
||||
const [entries, setEntries] = useState([])
|
||||
|
|
|
|||
|
|
@ -387,7 +387,7 @@ export default function MeasureWizard() {
|
|||
<div>
|
||||
<div style={{fontSize:16,fontWeight:600}}>Umfänge messen</div>
|
||||
<div style={{fontSize:12,color:'var(--text3)',marginTop:2}}>
|
||||
8 Messpunkte · Hals, Brust, Taille, Bauch, Hüfte, Oberschenkel, Wade, Oberarm
|
||||
9 Messpunkte · inkl. Oberarm kontrahiert + Oberarm entspannt
|
||||
</div>
|
||||
</div>
|
||||
<ChevronRight size={20} style={{marginLeft:'auto',color:'var(--text3)'}}/>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export default function NewMeasurement() {
|
|||
date: dayjs().format('YYYY-MM-DD'),
|
||||
weight: null,
|
||||
c_neck:null, c_chest:null, c_waist:null, c_belly:null,
|
||||
c_hip:null, c_thigh:null, c_calf:null, c_arm:null,
|
||||
c_hip:null, c_thigh:null, c_calf:null, c_arm:null, c_arm_relaxed:null,
|
||||
sf_method:'jackson3',
|
||||
sf_chest:null, sf_axilla:null, sf_triceps:null, sf_subscap:null,
|
||||
sf_suprailiac:null, sf_abdomen:null, sf_thigh:null,
|
||||
|
|
@ -132,7 +132,7 @@ export default function NewMeasurement() {
|
|||
|
||||
{/* Umfänge */}
|
||||
<Section title="Umfänge" open={openSections.circum} onToggle={()=>toggle('circum')}
|
||||
hint="Hals, Brust, Taille, Bauch, Hüfte, Oberschenkel, Wade, Oberarm">
|
||||
hint="Hals, Brust, Taille, Bauch, Hüfte, Oberschenkel, Wade, Oberarm (kontrahiert + entspannt)">
|
||||
{CIRCUMFERENCE_POINTS.map(p => (
|
||||
<NumInput key={p.id} label={p.label}
|
||||
guideText={p.where.length > 50 ? p.where.substring(0,48)+'…' : p.where}
|
||||
|
|
|
|||
|
|
@ -228,11 +228,16 @@ export const CIRCUMFERENCE_GUIDE = [
|
|||
posture:'Aufrecht, Gewicht gleichmäßig verteilt',
|
||||
how:'Waagerecht, Muskel entspannt',
|
||||
tip:'Morgens messen – abends schwellen Beine durch Wassereinlagerungen an' },
|
||||
{ id:'c_arm', name:'Oberarm', color:'#1D9E75',
|
||||
{ id:'c_arm', name:'Oberarm kontrahiert', color:'#1D9E75',
|
||||
where:'Dickste Stelle, Mitte zwischen Schultergelenk und Ellenbogen',
|
||||
posture:'Arm locker hängen lassen und entspannen',
|
||||
posture:'Ellenbogen leicht gebeugt, Bizeps leicht angespannt',
|
||||
how:'Waagerecht, senkrecht zur Längsachse',
|
||||
tip:'Immer denselben Arm (rechts); auch angespannt messen und notieren' },
|
||||
tip:'Bestehende Messreihen = kontrahiert' },
|
||||
{ id:'c_arm_relaxed', name:'Oberarm', color:'#2E7D57',
|
||||
where:'Gleiche Stelle wie kontrahiert',
|
||||
posture:'Arm locker hängen, ohne Anspannung',
|
||||
how:'Waagerecht',
|
||||
tip:'Entspannte Messung für Vergleich zur Kontraktionsmessung' },
|
||||
]
|
||||
|
||||
export const CALIPER_GUIDE = {
|
||||
|
|
|
|||
|
|
@ -50,11 +50,18 @@ export const CIRCUMFERENCE_POINTS = [
|
|||
tip: 'Morgens messen – gegen Abend schwellen Beine durch Wassereinlagerungen an'
|
||||
},
|
||||
{
|
||||
id: 'c_arm', label: 'Oberarm', color: '#1D9E75',
|
||||
id: 'c_arm', label: 'Oberarm kontrahiert', color: '#1D9E75',
|
||||
where: 'An der dicksten Stelle des Oberarms – Mitte zwischen Schultergelenk und Ellenbogen',
|
||||
posture: 'Arm locker hängen lassen und entspannen',
|
||||
how: 'Waagerecht anlegen, senkrecht zur Längsachse des Arms',
|
||||
tip: 'Immer denselben Arm messen (meist rechts) – beide Werte notieren (entspannt & angespannt)'
|
||||
posture: 'Ellenbogen leicht gebeugt, Bizeps leicht anspannen (wie leichter „Kraft-Griff“)',
|
||||
how: 'Waagerecht anlegen, Maßband ohne Luftspalt, nicht einschneidend',
|
||||
tip: 'Historische Einträge beziehen sich auf diesen Wert (angespannter Arm)'
|
||||
},
|
||||
{
|
||||
id: 'c_arm_relaxed', label: 'Oberarm', color: '#2E7D57',
|
||||
where: 'Gleiche Stelle wie beim kontrahierten Messpunkt – dickste Stelle, Mitte Oberarm',
|
||||
posture: 'Arm locker und gerade hängen lassen, Schulter entspannt, kein Spannen',
|
||||
how: 'Waagerecht anlegen; vorher tief einatmen und normal ausatmen, dann messen',
|
||||
tip: 'Typischer „Alltags“-Umfang ohne extra Muskelanspannung'
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user