Add saved analysis workspace and live dashboard statistics
This commit is contained in:
+54
-13
@@ -61,6 +61,16 @@ DEFAULT = {
|
||||
"derived": [],
|
||||
"aggregates": {},
|
||||
"filter_groups": [],
|
||||
"display": "separate",
|
||||
"baseline": None,
|
||||
"flag": None,
|
||||
"color": None,
|
||||
"size": None,
|
||||
"labels": {},
|
||||
"reference": None,
|
||||
"source": "runs",
|
||||
"cohorts": [],
|
||||
"comparison": "period",
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +131,7 @@ def query_text(q):
|
||||
period = f"during last {q['days']} days"
|
||||
else:
|
||||
period = f"during last {q['weeks']} weeks"
|
||||
lines = ["runs", " " + period]
|
||||
lines = [q["source"], " " + period]
|
||||
for spec in q["derived"]:
|
||||
lines.append(f" let {spec['name']} = {spec['expression']}")
|
||||
if q["end"] and not q["start"]:
|
||||
@@ -166,7 +176,11 @@ def query_text(q):
|
||||
)
|
||||
for condition in q["filters"]:
|
||||
lines.append(clause(condition))
|
||||
metrics = ", ".join(query_metrics(q))
|
||||
metrics = ", ".join(
|
||||
key
|
||||
+ (f" as {json.dumps(q['labels'][key], ensure_ascii=False)}" if key in q["labels"] else "")
|
||||
for key in query_metrics(q)
|
||||
)
|
||||
lines.extend(
|
||||
[
|
||||
f" summarize {metrics}",
|
||||
@@ -175,7 +189,14 @@ def query_text(q):
|
||||
if q["bin"]
|
||||
else f" by {q['group']}"
|
||||
),
|
||||
" compare " + ("previous period" if q["compare"] else "none"),
|
||||
" compare "
|
||||
+ (
|
||||
"previous year"
|
||||
if q["comparison"] == "year"
|
||||
else "previous period"
|
||||
if q["compare"]
|
||||
else "none"
|
||||
),
|
||||
]
|
||||
)
|
||||
if q["sort"] != ["date", "desc"]:
|
||||
@@ -188,7 +209,7 @@ def query_text(q):
|
||||
"runs"
|
||||
if spec and spec["function"] == "count"
|
||||
else "sec/km"
|
||||
if spec and field == "pace" and spec["function"] in {"sum", "stddev"}
|
||||
if spec and field in {"pace", "gap"} and spec["function"] in {"sum", "stddev"}
|
||||
else next(iter(units[field]))
|
||||
)
|
||||
lines.append(f" having {key} {condition['op']} {number_text(condition['value'])} {unit}")
|
||||
@@ -203,6 +224,25 @@ def query_text(q):
|
||||
lines.append(f" plot {q['plot'][0]} vs {q['plot'][1]}")
|
||||
if q["chart"] != "auto":
|
||||
lines.append(f" chart {q['chart']}")
|
||||
if q["display"] != "separate":
|
||||
lines.append(f" display {q['display']}")
|
||||
if q["baseline"]:
|
||||
lines.append(f" baseline previous {q['baseline']} days")
|
||||
if q["flag"]:
|
||||
flag = q["flag"]
|
||||
lines.append(
|
||||
f" flag {flag['direction']} baseline {number_text(flag['sd'])} sd min {flag['minimum']} runs sustained {flag['sustained']} buckets"
|
||||
)
|
||||
for key in ("color", "size"):
|
||||
if q[key]:
|
||||
lines.append(f" {key} by {q[key]}")
|
||||
if q["reference"]:
|
||||
ref = q["reference"]
|
||||
lines.append(f" reference {ref['field']} = {ref['raw']} {ref['unit']}")
|
||||
for cohort in q["cohorts"]:
|
||||
lines.append(
|
||||
f" cohort {json.dumps(cohort['name'], ensure_ascii=False)} where {cohort['source']}"
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@@ -254,16 +294,17 @@ def metric_value(row, key):
|
||||
if key == "heart_rate":
|
||||
return row.avg_hr
|
||||
if key in {"distance", "duration", "elevation"}:
|
||||
return (
|
||||
getattr(
|
||||
row,
|
||||
{"distance": "distance_m", "duration": "duration_s", "elevation": "elevation_m"}[
|
||||
key
|
||||
],
|
||||
)
|
||||
/ {"distance": 1000, "duration": 3600, "elevation": 1}[key]
|
||||
raw = getattr(
|
||||
row,
|
||||
{"distance": "distance_m", "duration": "duration_s", "elevation": "elevation_m"}[key],
|
||||
)
|
||||
return (row.analysis_summary or {}).get(key)
|
||||
return (
|
||||
raw / {"distance": 1000, "duration": 3600, "elevation": 1}[key]
|
||||
if isinstance(raw, (int, float)) and math.isfinite(raw)
|
||||
else None
|
||||
)
|
||||
summary = row.analysis_summary or {}
|
||||
return summary.get("summary", summary).get(key)
|
||||
|
||||
|
||||
def aggregate(rows, key):
|
||||
|
||||
Reference in New Issue
Block a user