"""Expand persisted compact laps or moving segments without reading raw recordings.""" from copy import copy from .stats_analysis import evaluate, expression_tree, valid from .stats_dsl import QueryError def expand(history, q, catalog): rows = [] trees = [ (spec["name"], expression_tree(spec["expression"], catalog)[0]) for spec in q["derived"] ] for activity in history: source = q["source"] units = (activity.analysis_summary or {}).get(source, []) if source == "efforts": units = [{"distance": e["distance"], "duration": e["seconds"], "effort_time": e["seconds"]} for e in activity.best_efforts or []] for index, unit in enumerate(units): if ( not isinstance(unit, dict) or not valid(unit.get("distance")) or not valid(unit.get("duration")) or unit["duration"] <= 0 or unit["distance"] <= 0 ): continue row = copy(activity) row.activity_id = activity.id row.parent_title = activity.title row.recording = unit row.id = activity.id * 1000000 + index row.title = ( f"{activity.title} ยท {'lap' if q['source'] == 'laps' else 'segment'} {index + 1}" ) row.distance_m, row.duration_s = unit["distance"], unit["duration"] row.elevation_m = unit.get("elevation") row.avg_hr = ( unit.get("heart_rate") if valid(unit.get("heart_rate")) and unit["heart_rate"] > 0 else None ) row.values = {key: None for key in catalog} row.values.update( count=1, distance=row.distance_m / 1000, duration=row.duration_s / 3600, pace=row.duration_s * 1000 / row.distance_m, speed=row.distance_m / row.duration_s * 3.6, heart_rate=row.avg_hr, matched_minutes=row.duration_s / 60, matched_windows=1, elevation=row.elevation_m, elevation_per_km=row.elevation_m / (row.distance_m / 1000) if valid(row.elevation_m) else None, ) for key in catalog: if key in {"distance", "duration", "pace", "count", "speed", "heart_rate", "elevation", "elevation_per_km", "matched_minutes", "matched_windows"}: continue row.values[key] = unit.get(key) row.analysis_summary = row.values row.previous = None for name, tree in trees: row.values[name] = evaluate(tree, row.values) rows.append(row) if len(rows) > 100000: raise QueryError( "This expands over 100,000 recording units. Use a shorter date range." ) return rows