Replace fixed DSL fields with parameterized metric expressions
This commit is contained in:
+12
-38
@@ -68,26 +68,6 @@ EXTRA_METRICS = {
|
||||
"bpm",
|
||||
"Average heart rate minus the previous run's average, before filtering.",
|
||||
),
|
||||
"rolling_7d_distance": (
|
||||
"7-day distance",
|
||||
"km",
|
||||
"Distance in the preceding 168 hours, including this run. Calculated before filters.",
|
||||
),
|
||||
"rolling_28d_distance": (
|
||||
"28-day distance",
|
||||
"km",
|
||||
"Distance in the preceding 672 hours, including this run. Calculated before filters.",
|
||||
),
|
||||
"rolling_7d_count": (
|
||||
"7-day run count",
|
||||
"runs",
|
||||
"Number of runs in the preceding 168 hours, including this run, before filters.",
|
||||
),
|
||||
"rolling_28d_count": (
|
||||
"28-day run count",
|
||||
"runs",
|
||||
"Number of runs in the preceding 672 hours, including this run, before filters.",
|
||||
),
|
||||
"run_day_streak": (
|
||||
"Consecutive running days",
|
||||
"days",
|
||||
@@ -122,10 +102,6 @@ EXTRA_UNITS = {
|
||||
"distance_change": {"km": 1, "m": 0.001},
|
||||
"pace_change": {"sec/km": 1},
|
||||
"heart_rate_change": {"bpm": 1},
|
||||
"rolling_7d_distance": {"km": 1, "m": 0.001},
|
||||
"rolling_28d_distance": {"km": 1, "m": 0.001},
|
||||
"rolling_7d_count": {"runs": 1},
|
||||
"rolling_28d_count": {"runs": 1},
|
||||
"run_day_streak": {"days": 1},
|
||||
"elevation_per_km": {"m/km": 1},
|
||||
"max_heart_rate": {"bpm": 1},
|
||||
@@ -231,8 +207,10 @@ def evaluate(tree, values):
|
||||
def parse_analysis(text, metrics, defaults):
|
||||
if not isinstance(text, str) or len(text) > 4000:
|
||||
raise QueryError("Keep the query under 4,000 characters.")
|
||||
from .stats_expressions import calculation, lower
|
||||
from .stats_features import extract, validate
|
||||
|
||||
text, expressions = lower(text)
|
||||
text, features = extract(text)
|
||||
catalog, units = dict(metrics), {**UNITS, **EXTRA_UNITS}
|
||||
lines = text.splitlines()
|
||||
@@ -280,6 +258,13 @@ def parse_analysis(text, metrics, defaults):
|
||||
|
||||
for index, source in enumerate(lines):
|
||||
line, number = source.strip(), index + 1
|
||||
for name, expression in expressions.get(number, []):
|
||||
spec, conversions = calculation(name, expression, catalog, units, defaults, number)
|
||||
if len(derived) >= 32:
|
||||
raise QueryError("Use at most 32 metric expressions.", number)
|
||||
catalog[name] = (expression, spec["unit"], f"Per-input expression: {expression}.")
|
||||
units[name] = conversions
|
||||
derived.append(spec)
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
if match := re.fullmatch(r"let (\w+) = (.+)", line):
|
||||
@@ -288,30 +273,19 @@ def parse_analysis(text, metrics, defaults):
|
||||
not re.fullmatch(r"[a-z][a-z0-9_]{0,39}", name)
|
||||
or name.startswith("analysis_")
|
||||
or name in catalog
|
||||
or len(derived) >= 8
|
||||
or sum(not s.get("inline") for s in derived) >= 8
|
||||
):
|
||||
raise QueryError(
|
||||
"Choose a new lowercase metric name; at most eight calculations.", number
|
||||
)
|
||||
window = re.fullmatch(r"rolling_(mean|sum|count)\((\w+), (\d+) days\)", expression)
|
||||
if window:
|
||||
function, field, days = window.groups()
|
||||
if field not in catalog or not 1 <= int(days) <= 365:
|
||||
raise QueryError(
|
||||
"Rolling functions need an earlier metric and 1–365 days.", number
|
||||
)
|
||||
unit = "runs" if function == "count" else catalog[field][1]
|
||||
else:
|
||||
_, unit = expression_tree(expression, catalog, number)
|
||||
_, unit = expression_tree(expression, catalog, number)
|
||||
catalog[name] = (
|
||||
name.replace("_", " ").capitalize(),
|
||||
unit,
|
||||
f"Calculated per run: {expression}. Missing inputs or division by zero give missing data.",
|
||||
)
|
||||
units[name] = {unit: 1}
|
||||
units[name] = {"s": 1, "min": 60} if unit == "time" else {unit: 1}
|
||||
spec = {"name": name, "expression": expression, "unit": unit}
|
||||
if window:
|
||||
spec["window"] = {"function": function, "field": field, "days": int(days)}
|
||||
derived.append(spec)
|
||||
lines[index] = ""
|
||||
elif line == "during all time":
|
||||
|
||||
Reference in New Issue
Block a user