99 lines
3.4 KiB
Python
99 lines
3.4 KiB
Python
import copy
|
|
import math
|
|
from types import SimpleNamespace
|
|
|
|
from app.analytics import analyse
|
|
from app.display import (
|
|
ANALYSIS_VERSION,
|
|
CHART_LIMIT,
|
|
compact_routes,
|
|
persist_analysis,
|
|
stored_analysis,
|
|
)
|
|
from app.services import activity_data, route_data
|
|
|
|
|
|
def long_run():
|
|
return SimpleNamespace(
|
|
sport="running",
|
|
distance_m=21000,
|
|
samples=[
|
|
{
|
|
"point": [-33 + i / 1000000, 151 + math.sin(i / 100) / 1000],
|
|
"distance": i * 3,
|
|
"time": i,
|
|
"speed": 3,
|
|
"elevation": 100 + math.sin(i / 100),
|
|
"heart_rate": 150,
|
|
}
|
|
for i in range(7001)
|
|
],
|
|
laps=[{"distance": 21000, "duration": 7000}],
|
|
)
|
|
|
|
|
|
def test_persisted_summaries_equal_full_resolution_and_raw_data_is_untouched():
|
|
row = long_run()
|
|
original = copy.deepcopy(row.samples)
|
|
expected = analyse(row)
|
|
persist_analysis(row)
|
|
assert row.analysis_version == ANALYSIS_VERSION
|
|
for key in ("terrain", "laps"):
|
|
assert row.analysis_summary[key] == expected[key]
|
|
for key, value in expected["summary"].items():
|
|
assert row.analysis_summary["summary"][key] == value
|
|
assert row.analysis_summary["segments"]
|
|
assert "heart_rate_drift" in row.analysis_summary["summary"]
|
|
assert row.samples == original
|
|
assert len(row.display_data["chart"]) == CHART_LIMIT
|
|
assert row.display_data["chart"][0]["distance"] == 0
|
|
assert row.display_data["chart"][-1]["distance"] == 21000
|
|
assert sum(map(len, row.route_geometry)) <= 1400
|
|
assert row.route_geometry[0][0] == [round(v, 6) for v in original[0]["point"]]
|
|
assert row.route_geometry[-1][-1] == [round(v, 6) for v in original[-1]["point"]]
|
|
assert set(route_data(row)) == {"segments"}
|
|
assert set(row.display_data["route_segments"][0][0]) == {
|
|
"point",
|
|
"distance",
|
|
"speed",
|
|
"grade",
|
|
"heart_rate",
|
|
}
|
|
assert "point" not in row.display_data["chart"][0]
|
|
|
|
|
|
def test_simplification_retains_sharp_turns_and_does_not_bridge_gps_gaps():
|
|
points = [[0, i / 10000] for i in range(100)]
|
|
points += [[i / 10000, 0.0099] for i in range(1, 100)]
|
|
samples = [{"point": point} for point in points]
|
|
samples += [{"point": None}, {"point": [1, 1]}, {"point": [1, 1.01]}]
|
|
routes = compact_routes(samples)
|
|
assert len(routes) == 2
|
|
assert [0, 0.0099] in [row["point"] for row in routes[0]]
|
|
assert routes[0][0]["point"] == points[0]
|
|
assert routes[0][-1]["point"] == points[-1]
|
|
assert routes[1][0]["point"] == [1, 1]
|
|
assert routes[1][-1]["point"] == [1, 1.01]
|
|
|
|
|
|
def test_incomplete_and_non_gps_data_and_refresh_clear_stored_routes():
|
|
row = long_run()
|
|
persist_analysis(row)
|
|
row.samples = [
|
|
{"point": None, "distance": 0, "time": 0},
|
|
{"point": None, "distance": 100, "time": 30},
|
|
]
|
|
persist_analysis(row)
|
|
assert row.route_geometry == []
|
|
assert activity_data(row)["route_segments"] == []
|
|
assert len(activity_data(row)["chart"]) == 2
|
|
assert stored_analysis(row)["summary"]["samples"] == 2
|
|
row.analysis_version = 0
|
|
assert route_data(row) == {"segments": []}
|
|
assert activity_data(row) == {"segments": [], "route_segments": [], "chart": []}
|
|
|
|
|
|
def test_fragmented_routes_preserve_each_singleton():
|
|
samples = [{"point": [i / 1000, 0]} if i % 2 else {"point": None} for i in range(20)]
|
|
assert len(compact_routes(samples)) == 10
|