Commit e500b11b authored by Administrator's avatar Administrator

powerZone and heartRateZoneDistribution

parent fc4a2462
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_secure_storage-3.3.3/","dependencies":[]},{"name":"path_provider","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.8/","dependencies":[]},{"name":"shared_preferences","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.5.7+2/","dependencies":[]},{"name":"sqflite","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-1.3.0+1/","dependencies":[]},{"name":"uni_links","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/uni_links-0.4.0/","dependencies":[]},{"name":"url_launcher","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-5.4.7/","dependencies":[]}],"android":[{"name":"flutter_secure_storage","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_secure_storage-3.3.3/","dependencies":[]},{"name":"path_provider","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.8/","dependencies":[]},{"name":"shared_preferences","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.5.7+2/","dependencies":[]},{"name":"sqflite","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-1.3.0+1/","dependencies":[]},{"name":"uni_links","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/uni_links-0.4.0/","dependencies":[]},{"name":"url_launcher","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-5.4.7/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+2/","dependencies":[]},{"name":"shared_preferences_macos","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_macos-0.0.1+8/","dependencies":[]},{"name":"sqflite","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-1.3.0+1/","dependencies":[]},{"name":"url_launcher_macos","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.0.1+5/","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_web-0.1.2+5/","dependencies":[]},{"name":"url_launcher_web","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-0.1.1+5/","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]},{"name":"uni_links","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_web","url_launcher_macos"]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]}],"date_created":"2020-05-29 09:43:01.135624","version":"1.17.1"}
\ No newline at end of file
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_secure_storage-3.3.3/","dependencies":[]},{"name":"path_provider","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.8/","dependencies":[]},{"name":"shared_preferences","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.5.7+2/","dependencies":[]},{"name":"sqflite","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-1.3.0+1/","dependencies":[]},{"name":"uni_links","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/uni_links-0.4.0/","dependencies":[]},{"name":"url_launcher","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-5.4.7/","dependencies":[]}],"android":[{"name":"flutter_secure_storage","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_secure_storage-3.3.3/","dependencies":[]},{"name":"path_provider","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.8/","dependencies":[]},{"name":"shared_preferences","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.5.7+2/","dependencies":[]},{"name":"sqflite","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-1.3.0+1/","dependencies":[]},{"name":"uni_links","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/uni_links-0.4.0/","dependencies":[]},{"name":"url_launcher","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-5.4.7/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+2/","dependencies":[]},{"name":"shared_preferences_macos","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_macos-0.0.1+8/","dependencies":[]},{"name":"sqflite","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-1.3.0+1/","dependencies":[]},{"name":"url_launcher_macos","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.0.1+5/","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_web-0.1.2+5/","dependencies":[]},{"name":"url_launcher_web","path":"/home/stefan/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-0.1.1+5/","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]},{"name":"uni_links","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_web","url_launcher_macos"]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]}],"date_created":"2020-05-29 12:49:03.729159","version":"1.17.1"}
\ No newline at end of file
......@@ -22,6 +22,7 @@ import 'package:encrateia/utils/date_time_utils.dart';
import 'package:intl/intl.dart';
import 'package:encrateia/utils/enums.dart';
import 'activity_tagging.dart';
import 'bar_zone.dart';
import 'heart_rate_zone.dart';
import 'heart_rate_zone_schema.dart';
......@@ -597,4 +598,24 @@ class Activity extends ChangeNotifier {
await lap.autoTagger(athlete: athlete);
}
}
Future<List<BarZone>> powerZoneCounts() async {
final PowerZoneSchema powerZoneSchema = await this.powerZoneSchema;
final List<Event> records = await this.records;
final List<Event> powerRecords =
records.where((Event record) => record.db.power != null).toList();
final List<BarZone> powerZoneCounts = await RecordList<Event>(powerRecords)
.powerZoneCounts(powerZoneSchema: powerZoneSchema);
return powerZoneCounts;
}
Future<List<BarZone>> heartRateZoneCounts() async {
final HeartRateZoneSchema heartRateZoneSchema = await this.heartRateZoneSchema;
final List<Event> records = await this.records;
final List<Event> heartRateRecords =
records.where((Event record) => record.db.heartRate != null).toList();
final List<BarZone> heartRateZoneCounts = await RecordList<Event>(heartRateRecords)
.heartRateZoneCounts(heartRateZoneSchema: heartRateZoneSchema);
return heartRateZoneCounts;
}
}
......@@ -13,6 +13,8 @@ import 'package:flutter/material.dart';
import 'package:encrateia/models/athlete.dart';
import 'package:encrateia/models/heart_rate_zone_schema.dart';
import 'bar_zone.dart';
class Lap {
Lap() {
db = DbLap();
......@@ -28,6 +30,8 @@ class Lap {
PowerZone _powerZone;
HeartRateZone _heartRateZone;
HeartRateZoneSchema _heartRateZoneSchema;
List<BarZone> powerDistributions;
List<BarZone> heartRateDistributions;
static Lap fromLap({
DataMessage dataMessage,
......@@ -335,4 +339,24 @@ class Lap {
);
}
}
Future<List<BarZone>> powerZoneCounts() async {
final PowerZoneSchema powerZoneSchema = await this.powerZoneSchema;
final List<Event> records = await this.records;
final List<Event> powerRecords =
records.where((Event record) => record.db.power != null).toList();
final List<BarZone> powerZoneCounts = await RecordList<Event>(powerRecords)
.powerZoneCounts(powerZoneSchema: powerZoneSchema);
return powerZoneCounts;
}
Future<List<BarZone>> heartRateZoneCounts() async {
final HeartRateZoneSchema heartRateZoneSchema = await this.heartRateZoneSchema;
final List<Event> records = await this.records;
final List<Event> heartRateRecords =
records.where((Event record) => record.db.heartRate != null).toList();
final List<BarZone> heartRateZoneCounts = await RecordList<Event>(heartRateRecords)
.heartRateZoneCounts(heartRateZoneSchema: heartRateZoneSchema);
return heartRateZoneCounts;
}
}
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';
import 'package:encrateia/utils/enums.dart';
import 'bar_zone.dart';
import 'heart_rate_zone.dart';
import 'heart_rate_zone_schema.dart';
class RecordList<E> extends DelegatingList<E> {
RecordList(List<E> records)
......@@ -360,4 +365,51 @@ class RecordList<E> extends DelegatingList<E> {
return plotPoints;
}
Future<List<BarZone>> powerZoneCounts(
{PowerZoneSchema powerZoneSchema}) async {
final List<BarZone> distributions = <BarZone>[];
double counter = 0.0;
final List<PowerZone> powerZones = await powerZoneSchema.powerZones;
for (final PowerZone powerZone in powerZones.reversed) {
final int numberInZone = _records
.where((Event event) =>
(event.db.power >= powerZone.db.lowerLimit) &&
(event.db.power <= powerZone.db.upperLimit))
.length;
distributions.add(BarZone(
lower: counter,
upper: counter + numberInZone,
color: powerZone.db.color,
));
counter = counter + numberInZone;
}
return distributions;
}
Future<List<BarZone>> heartRateZoneCounts(
{HeartRateZoneSchema heartRateZoneSchema}) async {
final List<BarZone> distributions = <BarZone>[];
double counter = 0.0;
final List<HeartRateZone> heartRateZones =
await heartRateZoneSchema.heartRateZones;
for (final HeartRateZone heartRateZone in heartRateZones.reversed) {
final int numberInZone = _records
.where((Event event) =>
(event.db.heartRate >= heartRateZone.db.lowerLimit) &&
(event.db.heartRate <= heartRateZone.db.upperLimit))
.length;
distributions.add(BarZone(
lower: counter,
upper: counter + numberInZone,
color: heartRateZone.db.color,
));
counter = counter + numberInZone;
}
return distributions;
}
}
......@@ -17,7 +17,7 @@ class BarChartPainter extends CustomPainter {
final double value;
final double maximum;
final double minimum;
final double strokeWidth = 2;
final double strokeWidth = 1;
final List<BarZone> barZones;
@override
......@@ -42,7 +42,7 @@ class BarChartPainter extends CustomPainter {
Offset(barWidth, height - strokeWidth),
),
Paint()
..color = MyColor.grapeFruit
..color = MyColor.lightGray
..strokeWidth = strokeWidth
..style = PaintingStyle.fill);
} else {
......@@ -50,7 +50,8 @@ class BarChartPainter extends CustomPainter {
double lowerInPixel;
double upperInPixel;
if (value < barZone.lower) continue;
if (value < barZone.lower)
continue;
if (value >= barZone.upper) {
lowerInPixel = (width - 2 * strokeWidth) /
(maximum - minimum) *
......
......@@ -43,4 +43,10 @@ extension DegreeFormatters on double {
return '- - - ';
}
}
double toPaceDouble() {
if (this != null)
return 1000 / this;
return 0;
}
}
......@@ -15,7 +15,7 @@ class MyBarChart extends StatelessWidget {
num minimum,
List<PowerZone> powerZones,
List<HeartRateZone> heartRateZones,
}) : _width = width?.toDouble() ?? 300.0,
}) : _width = width?.toDouble() ?? 200.0,
_height = height?.toDouble() ?? 30.0,
_value = value.toDouble(),
_maximum = maxFromZones(
......@@ -33,6 +33,15 @@ class MyBarChart extends StatelessWidget {
heartRateZones: heartRateZones,
);
MyBarChart.visualizeDistributions(
{int width, int height, List<BarZone> distributions})
: _width = width?.toDouble() ?? 200.0,
_height = height?.toDouble() ?? 30.0,
_minimum = 0,
_maximum = distributions.last.upper.toDouble(),
_value = distributions.last.upper.toDouble(),
_barZones = distributions;
final double _width;
final double _height;
final double _value;
......
......@@ -2,12 +2,13 @@ import 'package:encrateia/models/heart_rate_zone.dart';
import 'package:encrateia/models/heart_rate_zone_schema.dart';
import 'package:encrateia/models/athlete.dart';
import 'package:encrateia/models/lap.dart';
import 'package:encrateia/models/bar_zone.dart';
import 'package:encrateia/models/power_zone.dart';
import 'package:encrateia/models/power_zone_schema.dart';
import 'package:encrateia/utils/my_bar_chart.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:encrateia/models/activity.dart';
import 'package:encrateia/utils/date_time_utils.dart';
class ActivityBarGraphWidget extends StatefulWidget {
const ActivityBarGraphWidget({
......@@ -28,6 +29,9 @@ class _ActivityBarGraphWidgetState extends State<ActivityBarGraphWidget> {
List<PowerZone> _powerZones = <PowerZone>[];
List<HeartRateZone> _heartRateZones = <HeartRateZone>[];
List<Lap> _laps = <Lap>[];
List<BarZone> _heartRateDistributions = <BarZone>[];
List<BarZone> _powerDistributions = <BarZone>[];
@override
void initState() {
......@@ -38,77 +42,95 @@ class _ActivityBarGraphWidgetState extends State<ActivityBarGraphWidget> {
@override
Widget build(BuildContext context) {
if (_powerZones.isNotEmpty && _laps.isNotEmpty) {
return DataTable(
showCheckboxColumn: false,
onSelectAll: (_) {},
columns: const <DataColumn>[
DataColumn(label: Text('Title'), numeric: false),
DataColumn(label: Text('Value'), numeric: true),
DataColumn(label: Text('Bar'), numeric: false),
],
rows: <DataRow>[
const DataRow(cells: <DataCell>[
DataCell(Text('Power')),
DataCell(Text('')),
DataCell(Text('')),
]),
DataRow(cells: <DataCell>[
const DataCell(Text('Activity')),
DataCell(
Text(widget.activity.db.avgPower.toStringAsFixed(1)),
),
DataCell(
MyBarChart(
value: widget.activity.db.avgPower,
powerZones: _powerZones,
),
),
]),
for (Lap lap in _laps)
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
showCheckboxColumn: false,
columnSpacing: 20,
onSelectAll: (_) {},
columns: const <DataColumn>[
DataColumn(label: Text('Title'), numeric: false),
DataColumn(label: Text('Average Power'), numeric: false),
DataColumn(label: Text('Watts'), numeric: true),
DataColumn(label: Text('Power Zone Distribution'), numeric: false),
DataColumn(label: Text('Average Heart Rate'), numeric: false),
DataColumn(label: Text('bpm'), numeric: true),
DataColumn(label: Text('Heart Rate Zone Distribution'), numeric: false),
DataColumn(label: Text('Average Pace'), numeric: false),
DataColumn(label: Text('min/km'), numeric: true),
],
rows: <DataRow>[
DataRow(cells: <DataCell>[
DataCell(Text('Lap ' + lap.index.toString())),
DataCell(
Text(lap.db.avgPower.toStringAsFixed(1)),
),
const DataCell(Text('Activity')),
DataCell(
MyBarChart(
value: lap.db.avgPower,
value: widget.activity.db.avgPower,
powerZones: _powerZones,
),
),
]),
const DataRow(cells: <DataCell>[
DataCell(Text('Heart Rate')),
DataCell(Text('')),
DataCell(Text('')),
]),
DataRow(cells: <DataCell>[
const DataCell(Text('Activity')),
DataCell(Text(
widget.activity.db.avgHeartRate.toString(),
)),
DataCell(
MyBarChart(
value: widget.activity.db.avgHeartRate,
heartRateZones: _heartRateZones,
DataCell(
Text(widget.activity.db.avgPower.toStringAsFixed(1)),
),
)
]),
for (Lap lap in _laps)
DataRow(cells: <DataCell>[
DataCell(Text('Lap ' + lap.index.toString())),
DataCell(
Text(lap.db.avgHeartRate.toString()),
MyBarChart.visualizeDistributions(distributions: _heartRateDistributions),
),
DataCell(
MyBarChart(
value: lap.db.avgHeartRate,
value: widget.activity.db.avgHeartRate,
heartRateZones: _heartRateZones,
),
),
DataCell(Text(
widget.activity.db.avgHeartRate.toString(),
)),
DataCell(
MyBarChart.visualizeDistributions(distributions: _powerDistributions),
),
DataCell(MyBarChart(
value: widget.activity.db.avgSpeed.toPaceDouble(),
maximum: 700,
)),
DataCell(Text(
widget.activity.db.avgSpeed.toPace(),
)),
]),
]);
for (Lap lap in _laps)
DataRow(cells: <DataCell>[
DataCell(Text('Lap ' + lap.index.toString())),
DataCell(
MyBarChart(
value: lap.db.avgPower,
powerZones: _powerZones,
),
),
DataCell(
Text(lap.db.avgPower.toStringAsFixed(1)),
),
DataCell(
MyBarChart.visualizeDistributions(distributions: lap.powerDistributions),
),
DataCell(
MyBarChart(
value: lap.db.avgHeartRate,
heartRateZones: _heartRateZones,
),
),
DataCell(
Text(lap.db.avgHeartRate.toString()),
),
DataCell(
MyBarChart.visualizeDistributions(distributions: lap.heartRateDistributions),
),
DataCell(MyBarChart(
value: lap.db.avgSpeed.toPaceDouble(),
maximum: 700,
)),
DataCell(Text(
lap.db.avgSpeed.toPace(),
)),
]),
]),
);
} else {
return const Center(
child: Text('Loading'),
......@@ -119,11 +141,18 @@ class _ActivityBarGraphWidgetState extends State<ActivityBarGraphWidget> {
Future<void> getData() async {
final Activity activity = widget.activity;
_laps = await activity.laps;
for(final Lap _lap in _laps) {
_lap.powerDistributions = await _lap.powerZoneCounts();
_lap.heartRateDistributions = await _lap.heartRateZoneCounts();
}
_powerZoneSchema = await activity.powerZoneSchema;
if (_powerZoneSchema != null)
_powerZones = await _powerZoneSchema.powerZones;
_heartRateDistributions = await activity.powerZoneCounts();
_powerDistributions = await activity.heartRateZoneCounts();
_heartRateZoneSchema = await activity.heartRateZoneSchema;
if (_heartRateZoneSchema != null)
_heartRateZones = await _heartRateZoneSchema.heartRateZones;
......
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