Commit bf546688 authored by Administrator's avatar Administrator

speed per heart rate on activity level

parent 3ca91ffc
......@@ -129,6 +129,17 @@ class Activity extends ChangeNotifier {
return db.maxPower;
}
Future<double> get avgSpeed async {
if (db.avgSpeed == null || db.avgSpeed == 0) {
List<Event> records = await this.records;
db.avgSpeed = Lap.calculateAverageSpeed(records: records);
await db.save();
notifyListeners();
}
return db.avgGroundTime;
}
Future<double> get avgGroundTime async {
if (db.avgGroundTime == null) {
List<Event> records = await this.records;
......
......@@ -248,6 +248,15 @@ class Lap {
return powers.max();
}
static double calculateAverageSpeed({List<Event> records}) {
var speeds =
records.map((record) => record.db.speed).nonZeroDoubles();
if (speeds.length > 0) {
return speeds.mean();
} else
return -1;
}
static double calculateAverageGroundTime({List<Event> records}) {
var groundTimes =
records.map((record) => record.db.groundTime).nonZeroDoubles();
......
import 'package:encrateia/widgets/activity_widgets/activity_metadata_widget.dart';
import 'package:encrateia/widgets/activity_widgets/activity_overview_widget.dart';
import 'package:encrateia/widgets/activity_widgets/speed_per_heart_rate_widget.dart';
import 'package:encrateia/widgets/laps_list_widget.dart';
import 'package:encrateia/widgets/activity_widgets/activity_heart_rate_widget.dart';
import 'package:encrateia/widgets/activity_widgets/activity_power_widget.dart';
......@@ -25,7 +26,7 @@ class ShowActivityScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 12,
length: 13,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
......@@ -55,6 +56,10 @@ class ShowActivityScreen extends StatelessWidget {
icon: MyIcon.power,
text: "Power/HR",
),
Tab(
icon: MyIcon.speed,
text: "speed/HR",
),
Tab(
icon: MyIcon.groundTime,
text: "Grnd.Time",
......@@ -93,6 +98,7 @@ class ShowActivityScreen extends StatelessWidget {
ActivityPowerWidget(activity: activity),
ActivityPowerDurationWidget(activity: activity),
ActivityPowerPerHeartRateWidget(activity: activity),
ActivitySpeedPerHeartRateWidget(activity: activity),
ActivityGroundTimeWidget(activity: activity),
ActivityLegSpringStiffnessWidget(activity: activity),
ActivityFormPowerWidget(activity: activity),
......
......@@ -12,10 +12,12 @@ class ActivityPowerPerHeartRateWidget extends StatefulWidget {
ActivityPowerPerHeartRateWidget({this.activity});
@override
_ActivityPowerPerHeartRateWidgetState createState() => _ActivityPowerPerHeartRateWidgetState();
_ActivityPowerPerHeartRateWidgetState createState() =>
_ActivityPowerPerHeartRateWidgetState();
}
class _ActivityPowerPerHeartRateWidgetState extends State<ActivityPowerPerHeartRateWidget> {
class _ActivityPowerPerHeartRateWidgetState
extends State<ActivityPowerPerHeartRateWidget> {
List<Event> records = [];
String avgPowerPerHeartRateString = "Loading ...";
......
import 'package:encrateia/widgets/charts/actitvity_charts/activity_speed_per_heart_rate_chart.dart';
import 'package:flutter/material.dart';
import 'package:encrateia/models/activity.dart';
import 'package:encrateia/models/event.dart';
import 'package:encrateia/utils/list_utils.dart';
import 'package:encrateia/utils/num_utils.dart';
import 'package:encrateia/utils/icon_utils.dart';
class ActivitySpeedPerHeartRateWidget extends StatefulWidget {
final Activity activity;
ActivitySpeedPerHeartRateWidget({this.activity});
@override
_ActivitySpeedPerHeartRateWidgetState createState() =>
_ActivitySpeedPerHeartRateWidgetState();
}
class _ActivitySpeedPerHeartRateWidgetState
extends State<ActivitySpeedPerHeartRateWidget> {
List<Event> records = [];
String avgSpeedPerHeartRateString = "Loading ...";
@override
void initState() {
getData();
super.initState();
}
@override
Widget build(context) {
if (records.length > 0) {
var heartRateValues = records.map((value) => value.db.heartRate).nonZeroInts();
if (heartRateValues.length > 0) {
return ListTileTheme(
iconColor: Colors.deepOrange,
child: ListView(
padding: EdgeInsets.only(left: 25),
children: <Widget>[
ActivitySpeedPerHeartRateChart(
records: records,
activity: widget.activity,
),
ListTile(
leading: MyIcon.average,
title: Text(avgSpeedPerHeartRateString),
subtitle: Text("average speed per heart rate"),
),
],
),
);
} else {
return Center(
child: Text("No speed per heart rate data available."),
);
}
} else {
return Center(
child: Text("Loading"),
);
}
}
getData() async {
Activity activity = widget.activity;
records = await activity.records;
double avg = await activity.avgSpeed / activity.db.avgHeartRate;
setState(() {
avgSpeedPerHeartRateString = avg.toStringOrDashes(1) + " km / h*bpm";
});
}
}
import 'package:charts_flutter/flutter.dart';
import 'package:flutter/material.dart';
import 'package:encrateia/models/activity.dart';
import 'package:encrateia/models/event.dart';
import 'package:encrateia/models/lap.dart';
import 'package:encrateia/models/plot_point.dart';
import 'package:encrateia/utils/graph_utils.dart';
import 'package:encrateia/utils/my_line_chart.dart';
class ActivitySpeedPerHeartRateChart extends StatelessWidget {
final List<Event> records;
final Activity activity;
ActivitySpeedPerHeartRateChart({this.records, @required this.activity});
@override
Widget build(BuildContext context) {
var nonZero = records
.where((value) => value.db.power > 100 && value.db.heartRate > 0);
var smoothedRecords = Event.toDoubleDataPoints(
attribute: "speedPerHeartRate",
records: nonZero,
amount: 30,
);
List<Series<dynamic, num>> data = [
Series<DoublePlotPoint, int>(
id: 'Speed per Heart Rate',
colorFn: (_, __) => MaterialPalette.green.shadeDefault,
domainFn: (DoublePlotPoint record, _) => record.domain,
measureFn: (DoublePlotPoint record, _) => record.measure,
data: smoothedRecords,
)
];
return FutureBuilder<List<Lap>>(
future: activity.laps,
builder: (BuildContext context, AsyncSnapshot<List<Lap>> snapshot) {
if (snapshot.hasData) {
var laps = snapshot.data;
return Container(
height: 300,
child: MyLineChart(
data: data,
maxDomain: nonZero.last.db.distance,
laps: laps,
domainTitle: 'Speed per Heart Rate (km / h*bpm)',
measureTickProviderSpec: BasicNumericTickProviderSpec(
zeroBound: false,
dataIsInWholeNumbers: false,
desiredTickCount: 6),
domainTickProviderSpec:
BasicNumericTickProviderSpec(desiredTickCount: 6),
),
);
} else
return GraphUtils.loadingContainer;
},
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment