fix low level issues
This commit is contained in:
+1
-1
@@ -64,7 +64,7 @@ def _resolve_data_dir(explicit: Optional[str], config_path: Optional[str]) -> Pa
|
||||
|
||||
if config_path and Path(config_path).exists():
|
||||
import yaml
|
||||
raw = yaml.safe_load(Path(config_path).read_text())
|
||||
raw = yaml.safe_load(Path(config_path).read_text()) or {}
|
||||
out = raw.get("output", {}).get("dir")
|
||||
if out:
|
||||
return Path(out).expanduser().resolve()
|
||||
|
||||
@@ -506,12 +506,7 @@ async def save_athlete(payload: dict[str, Any]) -> JSONResponse:
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
# Patch athlete.json in-place (preserves power_curve, updated_at, etc.)
|
||||
data = json.loads(athlete_path.read_text(encoding="utf-8"))
|
||||
data.update(overrides)
|
||||
athlete_path.write_text(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
|
||||
# Re-merge so _merged/athlete.json symlink stays valid
|
||||
# Re-merge — merge_all() applies edits/athlete.yaml on top of athlete.json
|
||||
from bincio.render.merge import merge_all
|
||||
merge_all(dd)
|
||||
|
||||
|
||||
@@ -346,5 +346,6 @@ def _patch_duplicate_of(output_dir: Path, activity_id: str, canonical_id: str) -
|
||||
data = json.loads(p.read_text())
|
||||
data["duplicate_of"] = canonical_id
|
||||
p.write_text(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
import logging
|
||||
logging.getLogger(__name__).warning("_patch_duplicate_of failed for %s: %s", activity_id, e)
|
||||
|
||||
@@ -237,19 +237,29 @@ def _fastest_time_for_distance(speed_1hz: list[float], target_km: float) -> Opti
|
||||
def _best_climb(ele_1hz: list[Optional[float]]) -> Optional[float]:
|
||||
"""Maximum net elevation gain over any contiguous window (Kadane's on deltas).
|
||||
|
||||
Ignores samples where elevation is None. Returns None if fewer than two
|
||||
valid elevation samples exist.
|
||||
None samples are treated as breaks between segments — the Kadane window is
|
||||
reset to 0 at each gap so non-contiguous elevation data is never joined.
|
||||
Returns None if fewer than two non-None samples exist.
|
||||
"""
|
||||
valid = [e for e in ele_1hz if e is not None]
|
||||
if len(valid) < 2:
|
||||
non_null = sum(1 for e in ele_1hz if e is not None)
|
||||
if non_null < 2:
|
||||
return None
|
||||
|
||||
max_gain = 0.0
|
||||
current = 0.0
|
||||
for a, b in zip(valid, valid[1:]):
|
||||
current = max(0.0, current + (b - a))
|
||||
if current > max_gain:
|
||||
max_gain = current
|
||||
prev: Optional[float] = None
|
||||
|
||||
for e in ele_1hz:
|
||||
if e is None:
|
||||
# Gap — reset window so we don't bridge the discontinuity
|
||||
current = 0.0
|
||||
prev = None
|
||||
continue
|
||||
if prev is not None:
|
||||
current = max(0.0, current + (e - prev))
|
||||
if current > max_gain:
|
||||
max_gain = current
|
||||
prev = e
|
||||
|
||||
return round(max_gain, 1) if max_gain > 0 else None
|
||||
|
||||
|
||||
@@ -43,11 +43,11 @@ def preview_coords(
|
||||
mask = rdp(coords, epsilon=0.001, return_mask=True)
|
||||
reduced = [gps[i] for i, keep in enumerate(mask) if keep]
|
||||
|
||||
# Subsample if still too many
|
||||
# Subsample if still too many — always include last point without exceeding max_points
|
||||
if len(reduced) > max_points:
|
||||
step = len(reduced) / max_points
|
||||
reduced = [reduced[int(i * step)] for i in range(max_points)]
|
||||
reduced.append(gps[-1]) # always include the last point
|
||||
step = len(reduced) / (max_points - 1)
|
||||
reduced = [reduced[int(i * step)] for i in range(max_points - 1)]
|
||||
reduced.append(gps[-1])
|
||||
|
||||
return [[round(lat, 5), round(lon, 5)] for lat, lon in reduced]
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ def build_timeseries(
|
||||
t = int((p.timestamp - started_at).total_seconds())
|
||||
if t < 0:
|
||||
continue
|
||||
if last_t is not None and t == last_t:
|
||||
continue # skip sub-second duplicates
|
||||
if last_t is not None and t <= last_t:
|
||||
continue # skip sub-second duplicates and non-monotonic points
|
||||
sampled.append(p)
|
||||
last_t = t
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ def _find_data_dir(explicit: Optional[str], config_path: Optional[str]) -> Path:
|
||||
|
||||
if config_path and Path(config_path).exists():
|
||||
import yaml
|
||||
raw = yaml.safe_load(Path(config_path).read_text())
|
||||
raw = yaml.safe_load(Path(config_path).read_text()) or {}
|
||||
out = raw.get("output", {}).get("dir")
|
||||
if out:
|
||||
return Path(out).expanduser().resolve()
|
||||
@@ -47,7 +47,7 @@ def _find_data_dir(explicit: Optional[str], config_path: Optional[str]) -> Path:
|
||||
auto_config = Path.cwd() / "extract_config.yaml"
|
||||
if auto_config.exists():
|
||||
import yaml
|
||||
raw = yaml.safe_load(auto_config.read_text())
|
||||
raw = yaml.safe_load(auto_config.read_text()) or {}
|
||||
out = raw.get("output", {}).get("dir")
|
||||
if out:
|
||||
return Path(out).expanduser().resolve()
|
||||
|
||||
+16
-2
@@ -140,13 +140,27 @@ def merge_all(data_dir: Path) -> int:
|
||||
if not dest_img.exists():
|
||||
dest_img.symlink_to(img_dir.resolve())
|
||||
|
||||
# Symlink athlete.json if present
|
||||
# Produce merged athlete.json — base from extract overlaid with edits/athlete.yaml
|
||||
athlete_src = data_dir / "athlete.json"
|
||||
athlete_dest = merged_dir / "athlete.json"
|
||||
if athlete_dest.exists() or athlete_dest.is_symlink():
|
||||
athlete_dest.unlink()
|
||||
if athlete_src.exists():
|
||||
athlete_dest.symlink_to(athlete_src.resolve())
|
||||
athlete_edits_path = data_dir / "edits" / "athlete.yaml"
|
||||
if athlete_edits_path.exists():
|
||||
try:
|
||||
import yaml as _yaml
|
||||
edits = _yaml.safe_load(athlete_edits_path.read_text(encoding="utf-8")) or {}
|
||||
except Exception:
|
||||
edits = {}
|
||||
else:
|
||||
edits = {}
|
||||
if edits:
|
||||
athlete_data = json.loads(athlete_src.read_text(encoding="utf-8"))
|
||||
athlete_data.update(edits)
|
||||
athlete_dest.write_text(json.dumps(athlete_data, indent=2, ensure_ascii=False))
|
||||
else:
|
||||
athlete_dest.symlink_to(athlete_src.resolve())
|
||||
|
||||
# Write merged index.json (private filtered, highlight sorted)
|
||||
index_path = data_dir / "index.json"
|
||||
|
||||
Reference in New Issue
Block a user