kinemotion 0.34.0__py3-none-any.whl → 0.35.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kinemotion might be problematic. Click here for more details.

@@ -7,114 +7,26 @@ Provides severity levels (ERROR, WARNING, INFO) for different categories
7
7
  of metric issues.
8
8
  """
9
9
 
10
- from dataclasses import dataclass, field
11
- from enum import Enum
10
+ from dataclasses import dataclass
12
11
 
13
- from kinemotion.core.dropjump_validation_bounds import (
14
- AthleteProfile,
12
+ from kinemotion.core.validation import (
13
+ MetricsValidator,
14
+ ValidationResult,
15
+ )
16
+ from kinemotion.dropjump.validation_bounds import (
15
17
  DropJumpBounds,
16
18
  estimate_athlete_profile,
17
19
  )
18
20
 
19
21
 
20
- class ValidationSeverity(Enum):
21
- """Severity level for validation issues."""
22
-
23
- ERROR = "ERROR" # Metrics invalid, likely data corruption
24
- WARNING = "WARNING" # Metrics valid but unusual, needs review
25
- INFO = "INFO" # Normal variation, informational only
26
-
27
-
28
22
  @dataclass
29
- class ValidationIssue:
30
- """Single validation issue."""
23
+ class DropJumpValidationResult(ValidationResult):
24
+ """Drop jump-specific validation result."""
31
25
 
32
- severity: ValidationSeverity
33
- metric: str
34
- message: str
35
- value: float | None = None
36
- bounds: tuple[float, float] | None = None
37
-
38
-
39
- @dataclass
40
- class ValidationResult:
41
- """Complete validation result for drop jump metrics."""
42
-
43
- issues: list[ValidationIssue] = field(default_factory=list)
44
- status: str = "PASS" # "PASS", "PASS_WITH_WARNINGS", "FAIL"
45
- athlete_profile: AthleteProfile | None = None
46
26
  rsi: float | None = None
47
27
  contact_flight_ratio: float | None = None
48
28
  height_kinematic_trajectory_consistency: float | None = None # % error
49
29
 
50
- def add_error(
51
- self,
52
- metric: str,
53
- message: str,
54
- value: float | None = None,
55
- bounds: tuple[float, float] | None = None,
56
- ) -> None:
57
- """Add error-level issue."""
58
- self.issues.append(
59
- ValidationIssue(
60
- severity=ValidationSeverity.ERROR,
61
- metric=metric,
62
- message=message,
63
- value=value,
64
- bounds=bounds,
65
- )
66
- )
67
-
68
- def add_warning(
69
- self,
70
- metric: str,
71
- message: str,
72
- value: float | None = None,
73
- bounds: tuple[float, float] | None = None,
74
- ) -> None:
75
- """Add warning-level issue."""
76
- self.issues.append(
77
- ValidationIssue(
78
- severity=ValidationSeverity.WARNING,
79
- metric=metric,
80
- message=message,
81
- value=value,
82
- bounds=bounds,
83
- )
84
- )
85
-
86
- def add_info(
87
- self,
88
- metric: str,
89
- message: str,
90
- value: float | None = None,
91
- ) -> None:
92
- """Add info-level issue."""
93
- self.issues.append(
94
- ValidationIssue(
95
- severity=ValidationSeverity.INFO,
96
- metric=metric,
97
- message=message,
98
- value=value,
99
- )
100
- )
101
-
102
- def finalize_status(self) -> None:
103
- """Determine final pass/fail status based on issues."""
104
- has_errors = any(
105
- issue.severity == ValidationSeverity.ERROR for issue in self.issues
106
- )
107
- has_warnings = any(
108
- issue.severity == ValidationSeverity.WARNING for issue in self.issues
109
- )
110
-
111
- if has_errors:
112
- self.status = "FAIL"
113
- elif has_warnings:
114
- self.status = "PASS_WITH_WARNINGS"
115
- else:
116
- self.status = "PASS"
117
-
118
30
  def to_dict(self) -> dict:
119
31
  """Convert validation result to JSON-serializable dictionary.
120
32
 
@@ -144,28 +56,19 @@ class ValidationResult:
144
56
  }
145
57
 
146
58
 
147
- class DropJumpMetricsValidator:
59
+ class DropJumpMetricsValidator(MetricsValidator):
148
60
  """Comprehensive drop jump metrics validator."""
149
61
 
150
- def __init__(self, assumed_profile: AthleteProfile | None = None):
151
- """Initialize validator.
152
-
153
- Args:
154
- assumed_profile: If provided, validate against this specific profile.
155
- Otherwise, estimate from metrics.
156
- """
157
- self.assumed_profile = assumed_profile
158
-
159
- def validate(self, metrics: dict) -> ValidationResult:
62
+ def validate(self, metrics: dict) -> DropJumpValidationResult:
160
63
  """Validate drop jump metrics comprehensively.
161
64
 
162
65
  Args:
163
66
  metrics: Dictionary with drop jump metric values
164
67
 
165
68
  Returns:
166
- ValidationResult with all issues and status
69
+ DropJumpValidationResult with all issues and status
167
70
  """
168
- result = ValidationResult()
71
+ result = DropJumpValidationResult()
169
72
 
170
73
  # Estimate athlete profile if not provided
171
74
  if self.assumed_profile:
@@ -208,7 +111,7 @@ class DropJumpMetricsValidator:
208
111
  return result
209
112
 
210
113
  def _check_contact_time(
211
- self, contact_time_ms: float, result: ValidationResult
114
+ self, contact_time_ms: float, result: DropJumpValidationResult
212
115
  ) -> None:
213
116
  """Validate contact time."""
214
117
  contact_time_s = contact_time_ms / 1000.0
@@ -233,7 +136,7 @@ class DropJumpMetricsValidator:
233
136
  )
234
137
 
235
138
  def _check_flight_time(
236
- self, flight_time_ms: float, result: ValidationResult
139
+ self, flight_time_ms: float, result: DropJumpValidationResult
237
140
  ) -> None:
238
141
  """Validate flight time."""
239
142
  flight_time_s = flight_time_ms / 1000.0
@@ -257,7 +160,7 @@ class DropJumpMetricsValidator:
257
160
  )
258
161
 
259
162
  def _check_jump_height(
260
- self, jump_height_m: float, result: ValidationResult
163
+ self, jump_height_m: float, result: DropJumpValidationResult
261
164
  ) -> None:
262
165
  """Validate jump height."""
263
166
  bounds = DropJumpBounds.JUMP_HEIGHT
@@ -280,7 +183,10 @@ class DropJumpMetricsValidator:
280
183
  )
281
184
 
282
185
  def _check_rsi(
283
- self, contact_time_ms: float, flight_time_ms: float, result: ValidationResult
186
+ self,
187
+ contact_time_ms: float,
188
+ flight_time_ms: float,
189
+ result: DropJumpValidationResult,
284
190
  ) -> None:
285
191
  """Validate RSI and cross-check consistency."""
286
192
  contact_time_s = contact_time_ms / 1000.0
@@ -313,7 +219,7 @@ class DropJumpMetricsValidator:
313
219
  self,
314
220
  jump_height_kinematic_m: float,
315
221
  jump_height_trajectory_m: float,
316
- result: ValidationResult,
222
+ result: DropJumpValidationResult,
317
223
  ) -> None:
318
224
  """Validate consistency between kinematic and trajectory-based heights.
319
225
 
@@ -15,64 +15,7 @@ References:
15
15
  - Covens et al. (2019): Drop jump kinetics across athletes
16
16
  """
17
17
 
18
- from dataclasses import dataclass
19
- from enum import Enum
20
-
21
-
22
- class AthleteProfile(Enum):
23
- """Athlete performance categories for metric bounds."""
24
-
25
- ELDERLY = "elderly" # 70+, deconditioned
26
- UNTRAINED = "untrained" # Sedentary, no training
27
- RECREATIONAL = "recreational" # Fitness class, moderate activity
28
- TRAINED = "trained" # Regular athlete, 3-5 years training
29
- ELITE = "elite" # Competitive athlete, college/professional level
30
-
31
-
32
- @dataclass
33
- class MetricBounds:
34
- """Physiological bounds for a single metric.
35
-
36
- Attributes:
37
- absolute_min: Absolute minimum value (error threshold)
38
- practical_min: Practical minimum for weakest athletes
39
- recreational_min: Minimum for recreational athletes
40
- recreational_max: Maximum for recreational athletes
41
- elite_min: Minimum for elite athletes
42
- elite_max: Maximum for elite athletes
43
- absolute_max: Absolute maximum value (error threshold)
44
- unit: Unit of measurement (e.g., "s", "m", "ratio")
45
- """
46
-
47
- absolute_min: float
48
- practical_min: float
49
- recreational_min: float
50
- recreational_max: float
51
- elite_min: float
52
- elite_max: float
53
- absolute_max: float
54
- unit: str
55
-
56
- def contains(self, value: float, profile: AthleteProfile) -> bool:
57
- """Check if value is within bounds for athlete profile."""
58
- if profile == AthleteProfile.ELDERLY:
59
- return self.practical_min <= value <= self.recreational_max
60
- elif profile == AthleteProfile.UNTRAINED:
61
- return self.practical_min <= value <= self.recreational_max
62
- elif profile == AthleteProfile.RECREATIONAL:
63
- return self.recreational_min <= value <= self.recreational_max
64
- elif profile == AthleteProfile.TRAINED:
65
- # Trained athletes: midpoint between recreational and elite
66
- trained_min = (self.recreational_min + self.elite_min) / 2
67
- trained_max = (self.recreational_max + self.elite_max) / 2
68
- return trained_min <= value <= trained_max
69
- elif profile == AthleteProfile.ELITE:
70
- return self.elite_min <= value <= self.elite_max
71
- return False
72
-
73
- def is_physically_possible(self, value: float) -> bool:
74
- """Check if value is within absolute physiological limits."""
75
- return self.absolute_min <= value <= self.absolute_max
18
+ from kinemotion.core.validation import AthleteProfile, MetricBounds
76
19
 
77
20
 
78
21
  class DropJumpBounds:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kinemotion
3
- Version: 0.34.0
3
+ Version: 0.35.1
4
4
  Summary: Video-based kinematic analysis for athletic performance
5
5
  Project-URL: Homepage, https://github.com/feniix/kinemotion
6
6
  Project-URL: Repository, https://github.com/feniix/kinemotion
@@ -0,0 +1,37 @@
1
+ kinemotion/__init__.py,sha256=sxdDOekOrIgjxm842gy-6zfq7OWmGl9ShJtXCm4JI7c,723
2
+ kinemotion/api.py,sha256=oZB4Xk8KLqwOEYJCZnmEKPn_mAyQacw4s4QAhwTPH8I,39479
3
+ kinemotion/cli.py,sha256=cqYV_7URH0JUDy1VQ_EDLv63FmNO4Ns20m6s1XAjiP4,464
4
+ kinemotion/cmj/__init__.py,sha256=Ynv0-Oco4I3Y1Ubj25m3h9h2XFqeNwpAewXmAYOmwfU,127
5
+ kinemotion/cmj/analysis.py,sha256=OfNTMLPwZIRYbX-Yd8jgZ-7pqnHRz7L2bWAHVYFsQ60,18955
6
+ kinemotion/cmj/cli.py,sha256=Mj2h9It1jVjAauvtCxfLWTRijj7zbYhxZuebhw2Zz6w,10828
7
+ kinemotion/cmj/debug_overlay.py,sha256=fXmWoHhqMLGo4vTtB6Ezs3yLUDOLw63zLIgU2gFlJQU,15892
8
+ kinemotion/cmj/joint_angles.py,sha256=HmheIEiKcQz39cRezk4h-htorOhGNPsqKIR9RsAEKts,9960
9
+ kinemotion/cmj/kinematics.py,sha256=qRBe87NkX-7HQTQ8RoF-EpvfcffgP5vycJJRrxpHboc,10307
10
+ kinemotion/cmj/metrics_validator.py,sha256=p_hgg0Q0UAiWGNXKSW4E9kPvyEgPNAujaxrk2XUmsBY,28619
11
+ kinemotion/cmj/validation_bounds.py,sha256=yRhmpUzGJs0QYyL1o3mOkgUSTe4XTO2MONMITTjCv3c,11778
12
+ kinemotion/core/__init__.py,sha256=HsqolRa60cW3vrG8F9Lvr9WvWcs5hCmsTzSgo7imi-4,1278
13
+ kinemotion/core/auto_tuning.py,sha256=wtCUMOhBChVJNXfEeku3GCMW4qED6MF-O_mv2sPTiVQ,11324
14
+ kinemotion/core/cli_utils.py,sha256=zbnifPhD-OYofJioeYfJtshuWcl8OAEWtqCGVF4ctAI,7966
15
+ kinemotion/core/debug_overlay_utils.py,sha256=TyUb5okv5qw8oeaX3jsUO_kpwf1NnaHEAOTm-8LwTno,4587
16
+ kinemotion/core/experimental.py,sha256=IK05AF4aZS15ke85hF3TWCqRIXU1AlD_XKzFz735Ua8,3640
17
+ kinemotion/core/filtering.py,sha256=GsC9BB71V07LJJHgS2lsaxUAtJsupcUiwtZFDgODh8c,11417
18
+ kinemotion/core/formatting.py,sha256=G_3eqgOtym9RFOZVEwCxye4A2cyrmgvtQ214vIshowU,2480
19
+ kinemotion/core/metadata.py,sha256=iz9YdkesHo-85TVBCoQVn7zkbrSde_fqjU79s_b-TZk,6829
20
+ kinemotion/core/pose.py,sha256=ztemdZ_ysVVK3gbXabm8qS_dr1VfJX9KZjmcO-Z-iNE,8532
21
+ kinemotion/core/quality.py,sha256=dPGQp08y8DdEUbUdjTThnUOUsALgF0D2sdz50cm6wLI,13098
22
+ kinemotion/core/smoothing.py,sha256=GAfC-jxu1eqNyDjsUXqUBicKx9um5hrk49wz1FxfRNM,15219
23
+ kinemotion/core/validation.py,sha256=LmKfSl4Ayw3DgwKD9IrhsPdzp5ia4drLsHA2UuU1SCM,6310
24
+ kinemotion/core/video_io.py,sha256=fDdyYVIKqUSgCjBJa8l_S0SrDPDAhrWYfsDBNRuz1oM,7549
25
+ kinemotion/dropjump/__init__.py,sha256=tC3H3BrCg8Oj-db-Vrtx4PH_llR1Ppkd5jwaOjhQcLg,862
26
+ kinemotion/dropjump/analysis.py,sha256=B_N_51WoChyQ8I7yaeKeqj3vw7NufgV_3QL-FBZEtW4,28752
27
+ kinemotion/dropjump/cli.py,sha256=n_Wfv3AC6YIgRPYhO3F2nTSai0NR7fh95nAoWjryQeY,16250
28
+ kinemotion/dropjump/debug_overlay.py,sha256=LkPw6ucb7beoYWS4L-Lvjs1KLCm5wAWDAfiznUeV2IQ,5668
29
+ kinemotion/dropjump/kinematics.py,sha256=yB4ws4VG59SUGcw1J-uXfDFfCMXBdzRh5C4jo0osXbs,17404
30
+ kinemotion/dropjump/metrics_validator.py,sha256=sx4RodHpeiW8_PRB0GUJvkUWto1Ard1Dvrc9z8eKk7M,9351
31
+ kinemotion/dropjump/validation_bounds.py,sha256=5b4I3CKPybuvrbn-nP5yCcGF_sH4Vtyw3a5AWWvWnBk,4645
32
+ kinemotion/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ kinemotion-0.35.1.dist-info/METADATA,sha256=n93tZOI22N9m3SF1OHG2HImIGP7_fru3jN22Jr8xKrw,26020
34
+ kinemotion-0.35.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
35
+ kinemotion-0.35.1.dist-info/entry_points.txt,sha256=zaqnAnjLvcdrk1Qvj5nvXZCZ2gp0prS7it1zTJygcIY,50
36
+ kinemotion-0.35.1.dist-info/licenses/LICENSE,sha256=KZajvqsHw0NoOHOi2q0FZ4NBe9HdV6oey-IPYAtHXfg,1088
37
+ kinemotion-0.35.1.dist-info/RECORD,,
@@ -1,35 +0,0 @@
1
- kinemotion/__init__.py,sha256=sxdDOekOrIgjxm842gy-6zfq7OWmGl9ShJtXCm4JI7c,723
2
- kinemotion/api.py,sha256=IcvtjK38Eoh5u53yu99NaeChLDNvv7NSnlDcK8fhVS8,39349
3
- kinemotion/cli.py,sha256=cqYV_7URH0JUDy1VQ_EDLv63FmNO4Ns20m6s1XAjiP4,464
4
- kinemotion/cmj/__init__.py,sha256=Ynv0-Oco4I3Y1Ubj25m3h9h2XFqeNwpAewXmAYOmwfU,127
5
- kinemotion/cmj/analysis.py,sha256=88zRNufM88Mc4p_YdnbXoSJM-pWAe4C9I7TqcH7QM9U,19815
6
- kinemotion/cmj/cli.py,sha256=Mj2h9It1jVjAauvtCxfLWTRijj7zbYhxZuebhw2Zz6w,10828
7
- kinemotion/cmj/debug_overlay.py,sha256=fXmWoHhqMLGo4vTtB6Ezs3yLUDOLw63zLIgU2gFlJQU,15892
8
- kinemotion/cmj/joint_angles.py,sha256=HmheIEiKcQz39cRezk4h-htorOhGNPsqKIR9RsAEKts,9960
9
- kinemotion/cmj/kinematics.py,sha256=qRBe87NkX-7HQTQ8RoF-EpvfcffgP5vycJJRrxpHboc,10307
10
- kinemotion/core/__init__.py,sha256=HsqolRa60cW3vrG8F9Lvr9WvWcs5hCmsTzSgo7imi-4,1278
11
- kinemotion/core/auto_tuning.py,sha256=j6cul_qC6k0XyryCG93C1AWH2MKPj3UBMzuX02xaqfI,11235
12
- kinemotion/core/cli_utils.py,sha256=8xQvTiZFAQULGdpB9g-Mvf0doSgoXi1ZZfVY2T2zWss,7278
13
- kinemotion/core/cmj_metrics_validator.py,sha256=VVRn56pG3GKna_EDv8jQ5msYNv4VOUR5fTcqpSUzdIA,31334
14
- kinemotion/core/cmj_validation_bounds.py,sha256=NXW0d4S8hDqSkzAiyX9UyWDnKsWf61ygEKTKE2a1uNc,14069
15
- kinemotion/core/debug_overlay_utils.py,sha256=TyUb5okv5qw8oeaX3jsUO_kpwf1NnaHEAOTm-8LwTno,4587
16
- kinemotion/core/dropjump_metrics_validator.py,sha256=zTkWslex9qGQuJSEZltUXYyFLBtisPGGxwqpYpE_Mx4,12040
17
- kinemotion/core/dropjump_validation_bounds.py,sha256=Ow7T-0IK_qy0k9UZdiCuT_tttxklWAkuXFalQkF8Jbs,6927
18
- kinemotion/core/filtering.py,sha256=f-m-aA59e4WqE6u-9MA51wssu7rI-Y_7n1cG8IWdeRQ,11241
19
- kinemotion/core/formatting.py,sha256=G_3eqgOtym9RFOZVEwCxye4A2cyrmgvtQ214vIshowU,2480
20
- kinemotion/core/metadata.py,sha256=iz9YdkesHo-85TVBCoQVn7zkbrSde_fqjU79s_b-TZk,6829
21
- kinemotion/core/pose.py,sha256=ztemdZ_ysVVK3gbXabm8qS_dr1VfJX9KZjmcO-Z-iNE,8532
22
- kinemotion/core/quality.py,sha256=dPGQp08y8DdEUbUdjTThnUOUsALgF0D2sdz50cm6wLI,13098
23
- kinemotion/core/smoothing.py,sha256=J-GjFP6xW9TO4teOqFd5523zTY2jYIY1Nd99cZvoM3s,14110
24
- kinemotion/core/video_io.py,sha256=fDdyYVIKqUSgCjBJa8l_S0SrDPDAhrWYfsDBNRuz1oM,7549
25
- kinemotion/dropjump/__init__.py,sha256=yc1XiZ9vfo5h_n7PKVSiX2TTgaIfGL7Y7SkQtiDZj_E,838
26
- kinemotion/dropjump/analysis.py,sha256=Sz1lnAsQSp9fzkDy8AuqQtE47LKBqNwiLkaKzz4W_Wk,29099
27
- kinemotion/dropjump/cli.py,sha256=n_Wfv3AC6YIgRPYhO3F2nTSai0NR7fh95nAoWjryQeY,16250
28
- kinemotion/dropjump/debug_overlay.py,sha256=LkPw6ucb7beoYWS4L-Lvjs1KLCm5wAWDAfiznUeV2IQ,5668
29
- kinemotion/dropjump/kinematics.py,sha256=IH6nCOwTuocQNX1VPS_am9vPpMRUUla0a0MjDhEiXnA,17129
30
- kinemotion/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- kinemotion-0.34.0.dist-info/METADATA,sha256=nXaUGuRRgIpqZt2D9iEhrsP9bmAAXRORt6WHb27RNAI,26020
32
- kinemotion-0.34.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
33
- kinemotion-0.34.0.dist-info/entry_points.txt,sha256=zaqnAnjLvcdrk1Qvj5nvXZCZ2gp0prS7it1zTJygcIY,50
34
- kinemotion-0.34.0.dist-info/licenses/LICENSE,sha256=KZajvqsHw0NoOHOi2q0FZ4NBe9HdV6oey-IPYAtHXfg,1088
35
- kinemotion-0.34.0.dist-info/RECORD,,