Commit d3c031cf authored by Administrator's avatar Administrator

consistently caching min, max, sdev and averages for laps and activities

parent 23a0275c
......@@ -58,6 +58,8 @@ const SqfEntityTable tableActivity = SqfEntityTable(
SqfEntityField('totalStrides', DbType.integer),
SqfEntityField('totalCalories', DbType.integer),
SqfEntityField('avgSpeed', DbType.real),
SqfEntityField('sdevSpeed', DbType.real),
SqfEntityField('minSpeed', DbType.real),
SqfEntityField('maxSpeed', DbType.real),
SqfEntityField('totalAscent', DbType.integer),
SqfEntityField('totalDescent', DbType.integer),
......@@ -87,9 +89,11 @@ const SqfEntityTable tableActivity = SqfEntityTable(
SqfEntityField('localTimestamp', DbType.datetime),
// Cached calculated values:
SqfEntityField('avgPower', DbType.real),
SqfEntityField('sdevPower', DbType.real),
SqfEntityField('minPower', DbType.integer),
SqfEntityField('maxPower', DbType.integer),
SqfEntityField('sdevPower', DbType.real),
SqfEntityField('minHeartRate', DbType.integer),
SqfEntityField('sdevHeartRate', DbType.real),
SqfEntityField('avgGroundTime', DbType.real),
SqfEntityField('sdevGroundTime', DbType.real),
SqfEntityField('avgLegSpringStiffness', DbType.real),
......@@ -179,6 +183,8 @@ const SqfEntityTable tableLap = SqfEntityTable(
SqfEntityField('totalStrides', DbType.integer),
SqfEntityField('totalCalories', DbType.integer),
SqfEntityField('avgSpeed', DbType.real),
SqfEntityField('sdevSpeed', DbType.real),
SqfEntityField('minSpeed', DbType.real),
SqfEntityField('maxSpeed', DbType.real),
SqfEntityField('totalAscent', DbType.integer),
SqfEntityField('totalDescent', DbType.integer),
......@@ -197,6 +203,8 @@ const SqfEntityTable tableLap = SqfEntityTable(
SqfEntityField('minPower', DbType.integer),
SqfEntityField('maxPower', DbType.integer),
SqfEntityField('sdevPower', DbType.real),
SqfEntityField('minHeartRate', DbType.integer),
SqfEntityField('sdevHeartRate', DbType.real),
SqfEntityField('avgGroundTime', DbType.real),
SqfEntityField('sdevGroundTime', DbType.real),
SqfEntityField('avgLegSpringStiffness', DbType.real),
......
This diff is collapsed.
......@@ -126,6 +126,10 @@ class Activity {
double get avgGroundTime => _db.avgGroundTime;
double get sdevGroundTime => _db.sdevGroundTime;
double get sdevVerticalOscillation => _db.sdevVerticalOscillation;
double get sdevHeartRate => _db.sdevHeartRate;
int get minHeartRate => _db.minHeartRate;
double get sdevSpeed => _db.sdevSpeed;
double get minSpeed => _db.minSpeed;
set name(String value) => _db.name = value;
set state(String value) => _db.state = value;
......@@ -225,12 +229,6 @@ class Activity {
: avgHeartRate.toString() + ' bpm';
}
String averagePowerString() {
return (avgPower == null || avgPower == -1)
? '-'
: avgPower.toStringAsFixed(1) + ' W';
}
String timeString() {
return timeCreated == null
? '- - -'
......@@ -271,25 +269,32 @@ class Activity {
Future<bool> setAverages() async {
final RecordList<Event> recordList = RecordList<Event>(await records);
_db
..avgPower = recordList.averagePower()
..avgPower = recordList.avgPower()
..sdevPower = recordList.sdevPower()
..minPower = recordList.minPower()
..maxPower = recordList.maxPower()
..avgSpeed = recordList.averageSpeed()
..avgGroundTime = recordList.averageGroundTime()
..avgHeartRate = recordList.avgHeartRate()
..sdevHeartRate = recordList.sdevHeartRate()
..minHeartRate = recordList.minHeartRate()
..maxHeartRate = recordList.maxHeartRate()
..avgSpeed = recordList.avgSpeed()
..sdevSpeed = recordList.sdevSpeed()
..minSpeed = recordList.minSpeed()
..maxSpeed = recordList.maxSpeed()
..avgGroundTime = recordList.avgGroundTime()
..sdevGroundTime = recordList.sdevGroundTime()
..avgVerticalOscillation =
recordList.averageVerticalOscillation()
..sdevVerticalOscillation = recordList.sdevVerticalOscillation()
..avgStrydCadence = recordList.averageStrydCadence()
..avgStrydCadence = recordList.avgStrydCadence()
..sdevStrydCadence = recordList.sdevStrydCadence()
..avgLegSpringStiffness = recordList.averageLegSpringStiffness()
..avgLegSpringStiffness = recordList.avgLegSpringStiffness()
..sdevLegSpringStiffness = recordList.sdevLegSpringStiffness()
..avgFormPower = recordList.averageFormPower()
..avgVerticalOscillation =
recordList.avgVerticalOscillation()
..sdevVerticalOscillation = recordList.sdevVerticalOscillation()
..avgFormPower = recordList.avgFormPower()
..sdevFormPower = recordList.sdevFormPower()
..avgPowerRatio = recordList.averagePowerRatio()
..avgPowerRatio = recordList.avgPowerRatio()
..sdevPowerRatio = recordList.sdevPowerRatio()
..avgStrideRatio = recordList.averageStrideRatio()
..avgStrideRatio = recordList.avgStrideRatio()
..sdevStrideRatio = recordList.sdevStrideRatio();
final List<Lap> laps = await this.laps;
......
......@@ -76,6 +76,18 @@ class Lap {
double get avgStrydCadence => _db.avgStrydCadence;
double get avgLegSpringStiffness => _db.avgLegSpringStiffness;
double get avgFormPower => _db.avgFormPower;
double get sdevHeartRate => _db.sdevHeartRate;
int get minHeartRate => _db.minHeartRate;
double get sdevSpeed => _db.sdevSpeed;
double get minSpeed => _db.minSpeed;
double get sdevFormPower => _db.sdevFormPower;
double get sdevPower => _db.sdevPower;
int get minPower => _db.minPower;
int get maxPower => _db.maxPower;
double get sdevGroundTime => _db.sdevGroundTime;
double get sdevVerticalOscillation => _db.sdevVerticalOscillation;
double get sdevStrydCadence => _db.sdevStrydCadence;
double get sdevLegSpringStiffness => _db.sdevLegSpringStiffness;
Future<BoolResult> delete() async => await _db.delete();
Future<int> save() async => await _db.save();
......@@ -138,75 +150,6 @@ class Lap {
return _records;
}
Future<double> get sdevPower async {
if (_db.sdevPower == null) {
_db.sdevPower = RecordList<Event>(await records).sdevPower();
await save();
}
return _db.sdevPower;
}
Future<int> get minPower async {
if (_db.minPower == null) {
_db.minPower = RecordList<Event>(await records).minPower();
await save();
}
return _db.minPower;
}
Future<int> get maxPower async {
if (_db.maxPower == null) {
_db.maxPower = RecordList<Event>(await records).maxPower();
await save();
}
return _db.maxPower;
}
Future<double> get sdevGroundTime async {
if (_db.sdevGroundTime == null) {
_db.sdevGroundTime =
RecordList<Event>(await records).sdevGroundTime();
await save();
}
return _db.sdevGroundTime;
}
Future<double> get sdevVerticalOscillation async {
if (_db.sdevVerticalOscillation == null) {
_db.sdevVerticalOscillation =
RecordList<Event>(await records).sdevVerticalOscillation();
await save();
}
return _db.sdevVerticalOscillation;
}
Future<double> get sdevStrydCadence async {
if (_db.sdevStrydCadence == null) {
_db.sdevStrydCadence =
RecordList<Event>(await records).sdevStrydCadence();
await save();
}
return _db.sdevStrydCadence;
}
Future<double> get sdevLegSpringStiffness async {
if (_db.sdevLegSpringStiffness == null) {
_db.sdevLegSpringStiffness =
RecordList<Event>(await records).sdevLegSpringStiffness();
await save();
}
return _db.sdevLegSpringStiffness;
}
Future<double> get sdevFormPower async {
if (_db.sdevFormPower == null) {
_db.sdevFormPower =
RecordList<Event>(await records).sdevFormPower();
await save();
}
return _db.sdevFormPower;
}
Future<PowerZoneSchema> get powerZoneSchema async {
if (_powerZoneSchema == null) {
final DbActivity dbActivity = await DbActivity().getById(activitiesId);
......@@ -234,17 +177,34 @@ class Lap {
Future<void> setAverages() async {
final RecordList<Event> recordList = RecordList<Event>(await records);
_db
..avgPower = recordList.averagePower()
..avgFormPower = recordList.averageFormPower()
..avgHeartRate = recordList.averageHeartRate()
..avgSpeed = recordList.averageSpeed()
..avgGroundTime = recordList.averageGroundTime()
..avgStrydCadence = recordList.averageStrydCadence()
..avgLegSpringStiffness = recordList.averageLegSpringStiffness()
..avgStrideRatio = recordList.averageStrideRatio()
..avgPowerRatio = recordList.averageStrideRatio()
..avgPower = recordList.avgPower()
..sdevPower = recordList.sdevPower()
..minPower = recordList.minPower()
..maxPower = recordList.maxPower()
..avgHeartRate = recordList.avgHeartRate()
..sdevHeartRate = recordList.sdevHeartRate()
..minHeartRate = recordList.minHeartRate()
..maxHeartRate = recordList.maxHeartRate()
..avgSpeed = recordList.avgSpeed()
..sdevSpeed = recordList.sdevSpeed()
..minSpeed = recordList.minSpeed()
..maxSpeed = recordList.maxSpeed()
..avgGroundTime = recordList.avgGroundTime()
..sdevGroundTime = recordList.sdevGroundTime()
..avgStrydCadence = recordList.avgStrydCadence()
..sdevStrydCadence = recordList.sdevStrydCadence()
..avgLegSpringStiffness = recordList.avgLegSpringStiffness()
..sdevLegSpringStiffness = recordList.sdevLegSpringStiffness()
..avgVerticalOscillation =
recordList.averageVerticalOscillation();
recordList.avgVerticalOscillation()
..sdevVerticalOscillation = recordList.sdevVerticalOscillation()
..avgFormPower = recordList.avgFormPower()
..sdevFormPower = recordList.sdevFormPower()
..avgPowerRatio = recordList.avgPowerRatio()
..sdevPowerRatio = recordList.sdevPowerRatio()
..avgStrideRatio = recordList.avgStrideRatio()
..sdevStrideRatio = recordList.sdevStrideRatio();
await save();
}
......
......@@ -2,7 +2,6 @@ import 'package:encrateia/models/event.dart';
import 'package:encrateia/models/power_zone.dart';
import 'package:encrateia/models/power_zone_schema.dart';
import 'package:encrateia/utils/list_utils.dart';
import 'package:encrateia/utils/num_utils.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:encrateia/models/plot_point.dart';
......@@ -18,31 +17,9 @@ class RecordList<E> extends DelegatingList<E> {
final List<Event> _records;
String get sdevHeartRateString => _records
.map((Event record) => record.heartRate)
.nonZeroInts()
.sdev()
.toStringAsFixed(2);
String get minHeartRateString => _records
.map((Event record) => record.heartRate)
.nonZeroInts()
.min()
.toString();
String get avgHeartRateString => _records
.map((Event record) => record.heartRate)
.nonZeroInts()
.mean()
.toStringOrDashes(1);
String get maxHeartRateString => _records
.map((Event record) => record.heartRate)
.nonZeroInts()
.max()
.toString();
double averagePower() {
// AVERAGES:
// Power
double avgPower() {
final Iterable<int> powers = _records
.where((Event record) =>
record.power != null && record.power > 0 && record.power < 2000)
......@@ -58,17 +35,18 @@ class RecordList<E> extends DelegatingList<E> {
int minPower() {
final List<int> powers =
_records.map((Event record) => record.power).nonZeroInts();
return powers.isNotEmpty ? powers.min() : 0;
_records.map((Event record) => record.power).nonZeros() as List<int>;
return powers.isNotEmpty ? powers.min() as int : 0;
}
int maxPower() {
final List<int> powers =
_records.map((Event record) => record.power).nonZeroInts();
return powers.isNotEmpty ? powers.max() : 0;
_records.map((Event record) => record.power).nonZeros() as List<int>;
return powers.isNotEmpty ? powers.max() as int : 0;
}
int averageHeartRate() {
// Heart Rate
int avgHeartRate() {
final Iterable<int> heartRates = _records
.where((Event record) =>
record.heartRate != null &&
......@@ -89,70 +67,93 @@ class RecordList<E> extends DelegatingList<E> {
int minHeartRate() {
final List<int> heartRates =
_records.map((Event record) => record.heartRate).nonZeroInts();
return heartRates.isNotEmpty ? heartRates.min() : 0;
_records.map((Event record) => record.heartRate).nonZeros() as List<int>;
return heartRates.isNotEmpty ? heartRates.min() as int : 0;
}
int maxHeartRate() {
final List<int> heartRates =
_records.map((Event record) => record.heartRate).nonZeroInts();
return heartRates.isNotEmpty ? heartRates.max() : 0;
_records.map((Event record) => record.heartRate).nonZeros() as List<int>;
return heartRates.isNotEmpty ? heartRates.max() as int : 0;
}
double averageSpeed() {
// Speed
double avgSpeed() {
final List<double> speeds =
_records.map((Event record) => record.speed).nonZeroDoubles();
_records.map((Event record) => record.speed).nonZeros() as List<double>;
return speeds.isNotEmpty ? speeds.mean() : -1;
}
double averageGroundTime() {
double sdevSpeed() => _records
.where((Event record) => record.speed != null)
.map((Event record) => record.speed)
.sdev();
double minSpeed() {
final List<double> speeds =
_records.map((Event record) => record.speed).nonZeros() as List<double>;
return speeds.isNotEmpty ? speeds.min() as double : 0;
}
double maxSpeed() {
final List<double> speeds =
_records.map((Event record) => record.speed).nonZeros() as List<double>;
return speeds.isNotEmpty ? speeds.max() as double : 0;
}
// Ground Time
double avgGroundTime() {
final List<double> groundTimes =
_records.map((Event record) => record.groundTime).nonZeroDoubles();
_records.map((Event record) => record.groundTime).nonZeros() as List<double>;
return groundTimes.isNotEmpty ? groundTimes.mean() : -1;
}
double sdevGroundTime() =>
_records.map((Event record) => record.groundTime).nonZeroDoubles().sdev();
_records.map((Event record) => record.groundTime).nonZeros().sdev();
double averageStrydCadence() {
// Stryd Cadence
double avgStrydCadence() {
final List<double> strydCadences = _records
.map((Event record) => record.strydCadence ?? 0.0 * 2)
.nonZeroDoubles();
.nonZeros() as List<double>;
return strydCadences.isNotEmpty ? strydCadences.mean() : -1;
}
double sdevStrydCadence() => _records
.map((Event record) => record.strydCadence ?? 0.0 * 2)
.nonZeroDoubles()
.nonZeros()
.sdev();
double averageLegSpringStiffness() {
// Leg Spring Stiffness
double avgLegSpringStiffness() {
final List<double> legSpringStiffnesses = _records
.map((Event record) => record.legSpringStiffness)
.nonZeroDoubles();
.nonZeros() as List<double>;
return legSpringStiffnesses.isNotEmpty ? legSpringStiffnesses.mean() : -1;
}
double sdevLegSpringStiffness() => _records
.map((Event record) => record.legSpringStiffness)
.nonZeroDoubles()
.nonZeros()
.sdev();
double averageVerticalOscillation() {
// Vertical Oscillation
double avgVerticalOscillation() {
final List<double> verticalOscillation = _records
.map((Event record) => record.verticalOscillation)
.nonZeroDoubles();
.nonZeros() as List<double>;
return verticalOscillation.isNotEmpty ? verticalOscillation.mean() : -1;
}
double sdevVerticalOscillation() => _records
.map((Event record) => record.verticalOscillation)
.nonZeroDoubles()
.nonZeros()
.sdev();
double averageFormPower() {
// Form Power
double avgFormPower() {
final Iterable<int> formPowers = _records
.where((Event record) =>
record.formPower != null && record.formPower < 200)
......@@ -166,7 +167,8 @@ class RecordList<E> extends DelegatingList<E> {
.map((Event record) => record.formPower)
.sdev();
double averagePowerRatio() {
// Power Ratio
double avgPowerRatio() {
final Iterable<double> powerRatios = _records
.where((Event record) =>
record.power != null &&
......@@ -189,7 +191,8 @@ class RecordList<E> extends DelegatingList<E> {
(record.power - record.formPower) / record.power * 100)
.sdev();
double averageStrideRatio() {
// Stride Ratio
double avgStrideRatio() {
final Iterable<double> powerRatios = _records
.where((Event record) =>
record.speed != null &&
......@@ -221,6 +224,7 @@ class RecordList<E> extends DelegatingList<E> {
record.strydCadence /
record.verticalOscillation)
.sdev();
// END OF AVERAGES
List<IntPlotPoint> toIntDataPoints({
int amount,
......@@ -233,13 +237,13 @@ class RecordList<E> extends DelegatingList<E> {
for (final Event record in _records) {
switch (attribute) {
case LapIntAttr.power:
sum = sum + record.power;
sum += record.power;
break;
case LapIntAttr.formPower:
sum = sum + record.formPower;
sum += record.formPower;
break;
case LapIntAttr.heartRate:
sum = sum + record.heartRate;
sum += record.heartRate;
}
if (index++ % amount == amount - 1) {
......
......@@ -16,27 +16,20 @@ extension StatisticFunctions on Iterable<dynamic> {
return math.sqrt(variance);
}
int min() {
final List<int> values = toList().cast<int>();
num min() {
final List<num> values = toList().cast<num>();
return values.reduce(math.min);
}
int max() {
final List<int> values = toList().cast<int>();
num max() {
final List<num> values = toList().cast<num>();
return values.reduce(math.max);
}
List<int> nonZeroInts() {
final List<int> values = toList().cast<int>();
final Iterable<int> nonZeroValues =
values.where((int value) => value != null && value != 0);
return nonZeroValues.toList();
}
List<double> nonZeroDoubles() {
final List<double> values = toList().cast<double>();
final Iterable<double> nonZeroValues =
values.where((double value) => value != null && value != 0);
List<num> nonZeros() {
final List<num> values = toList().cast<num>();
final Iterable<num> nonZeroValues =
values.where((num value) => value != null && value != 0);
return nonZeroValues.toList();
}
}
......@@ -43,8 +43,8 @@ class _ActivitiesFeedWidgetState extends State<ActivitiesFeedWidget> {
itemCount: activities.length,
itemBuilder: (BuildContext context, int index) {
final Activity activity = activities[index];
return Column(crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: <
Widget>[
ListTile(
leading: sportsIcon(sport: activity.sport),
title: Text(activity.name ?? 'Activity'),
......@@ -55,7 +55,9 @@ class _ActivitiesFeedWidgetState extends State<ActivitiesFeedWidget> {
const SizedBox(width: 20),
Text(activity.paceString() + '\n' + activity.heartRateString()),
const SizedBox(width: 20),
Text(activity.averagePowerString() + '\n'),
Text((activity.avgPower == null || activity.avgPower == -1)
? '-'
: activity.avgPower.toStringAsFixed(1) + ' W' + '\n'),
],
),
onTap: () async {
......
......@@ -64,7 +64,7 @@ class _ActivityHeartRateWidgetState extends State<ActivityHeartRateWidget> {
),
ListTile(
leading: MyIcon.minimum,
title: Text(records.minHeartRateString),
title: Text(widget.activity.minHeartRate.toString()),
subtitle: const Text('minimum heart rate'),
),
ListTile(
......@@ -74,7 +74,7 @@ class _ActivityHeartRateWidgetState extends State<ActivityHeartRateWidget> {
),
ListTile(
leading: MyIcon.standardDeviation,
title: Text(records.sdevHeartRateString),
title: Text(widget.activity.sdevHeartRate.toStringAsFixed(2)),
subtitle: const Text('standard deviation heart rate'),
),
ListTile(
......
......@@ -83,9 +83,8 @@ class _LapFormPowerWidgetState extends State<LapFormPowerWidget> {
final Lap lap = widget.lap;
records = RecordList<Event>(await lap.records);
final double sdev = await lap.sdevFormPower;
setState(() {
sdevFormPowerString = sdev.toStringOrDashes(2) + ' W';
sdevFormPowerString = lap.sdevFormPower.toStringOrDashes(2) + ' W';
});
}
}
......@@ -83,9 +83,8 @@ class _LapGroundTimeWidgetState extends State<LapGroundTimeWidget> {
final Lap lap = widget.lap;
records = RecordList<Event>(await lap.records);
final double sdev = await lap.sdevGroundTime;
setState(() {
sdevGroundTimeString = sdev.toStringOrDashes(2) + ' ms';
sdevGroundTimeString = lap.sdevGroundTime.toStringOrDashes(2) + ' ms';
});
}
}
......@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:encrateia/models/lap.dart';
import 'package:encrateia/widgets/charts/lap_charts/lap_heart_rate_chart.dart';
import 'package:encrateia/utils/icon_utils.dart';
import 'package:encrateia/utils/num_utils.dart';
class LapHeartRateWidget extends StatefulWidget {
const LapHeartRateWidget({this.lap});
......@@ -58,22 +59,22 @@ class _LapHeartRateWidgetState extends State<LapHeartRateWidget> {
const Divider(),
ListTile(
leading: MyIcon.average,
title: Text(records.avgHeartRateString),
title: Text(widget.lap.avgHeartRate.toStringOrDashes(1)),
subtitle: const Text('average heart rate'),
),
ListTile(
leading: MyIcon.minimum,
title: Text(records.minHeartRateString),
title: Text(widget.lap.minHeartRate.toString()),
subtitle: const Text('minimum heart rate'),
),
ListTile(
leading: MyIcon.maximum,
title: Text(records.maxHeartRateString),
title: Text(widget.lap.maxHeartRate.toString()),
subtitle: const Text('maximum heart rate'),
),
ListTile(
leading: MyIcon.standardDeviation,
title: Text(records.sdevHeartRateString),
title: Text(widget.lap.sdevHeartRate.toStringAsFixed(2)),
subtitle: const Text('standard deviation heart rate'),
),
ListTile(
......
......@@ -89,9 +89,9 @@ class _LapLegSpringStiffnessWidgetState
Future<void> getData() async {
final Lap lap = widget.lap;
records = RecordList<Event>(await lap.records);
final double sdev = await lap.sdevLegSpringStiffness;
setState(() {
sdevLegSpringStiffnessString = sdev.toStringOrDashes(2) + ' ms';
sdevLegSpringStiffnessString =
lap.sdevLegSpringStiffness.toStringOrDashes(2) + ' ms';
});
}
}
......@@ -103,15 +103,9 @@ class _LapPowerWidgetState extends State<LapPowerWidget> {