This repository has been archived on 2024-03-27. You can view files and clone it, but cannot push or open issues/pull-requests.
encrateia/lib/models/interval.dart

359 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:sqfentity_gen/sqfentity_gen.dart';
import '../model/model.dart'
show DbInterval, DbActivity, DbPowerZone, DbHeartRateZone, DbEvent;
import '../models/ftp.dart' as ftp_lib;
import '../models/tag.dart';
import 'activity.dart';
import 'athlete.dart';
import 'event.dart';
import 'heart_rate_zone.dart';
import 'heart_rate_zone_schema.dart';
import 'interval_tagging.dart';
import 'lap.dart';
import 'power_zone.dart';
import 'power_zone_schema.dart';
import 'record_list.dart';
class Interval {
Interval() {
_db = DbInterval();
}
Interval._fromDb(this._db);
DbInterval _db;
Activity activity;
List<Event> _records = <Event>[];
List<Tag> cachedTags = <Tag>[];
double firstDistance = 0;
double lastDistance = 0;
int index;
double weight;
PowerZoneSchema _powerZoneSchema;
PowerZone _powerZone;
HeartRateZone _heartRateZone;
HeartRateZoneSchema _heartRateZoneSchema;
int get id => _db?.id;
DateTime get timeStamp => _db.timeStamp;
double get avgCadence => _db.avgCadence;
double get avgFormPower => _db.avgFormPower;
double get avgGroundTime => _db.avgGroundTime;
double get avgLegSpringStiffness => _db.avgLegSpringStiffness;
double get avgPower => _db.avgPower;
double get avgSpeed => _db.avgSpeed;
double get avgSpeedByMeasurements => _db.avgSpeedByMeasurements;
double get avgSpeedByDistance => _db.avgSpeedByDistance;
double get avgSpeedBySpeed => _db.avgSpeedBySpeed;
double get avgStrydCadence => _db.avgStrydCadence;
double get avgVerticalOscillation => _db.avgVerticalOscillation;
double get cp => _db.cp;
double get ftp => _db.ftp;
double get maxCadence => _db.maxCadence;
double get maxGroundTime => _db.maxGroundTime;
double get maxLegSpringStiffness => _db.maxLegSpringStiffness;
double get maxSpeed => _db.maxSpeed;
double get maxStrydCadence => _db.maxStrydCadence;
double get maxVerticalOscillation => _db.maxVerticalOscillation;
double get minCadence => _db.minCadence;
double get minGroundTime => _db.minGroundTime;
double get minLegSpringStiffness => _db.minLegSpringStiffness;
double get minSpeed => _db.minSpeed;
double get minStrydCadence => _db.minStrydCadence;
double get minVerticalOscillation => _db.minVerticalOscillation;
double get sdevCadence => _db.sdevCadence;
double get sdevFormPower => _db.sdevFormPower;
double get sdevGroundTime => _db.sdevGroundTime;
double get sdevHeartRate => _db.sdevHeartRate;
double get sdevLegSpringStiffness => _db.sdevLegSpringStiffness;
double get sdevPace => _db.sdevPace;
double get sdevPower => _db.sdevPower;
double get sdevSpeed => _db.sdevSpeed;
double get sdevStrydCadence => _db.sdevStrydCadence;
double get sdevVerticalOscillation => _db.sdevVerticalOscillation;
int get activitiesId => _db.activitiesId;
int get athletesId => _db.athletesId;
int get firstRecordId => _db.firstRecordId;
int get lastRecordId => _db.lastRecordId;
int get avgHeartRate => _db.avgHeartRate;
int get distance => _db.distance;
Duration get duration => Duration(seconds: _db.duration ?? 0);
int get maxFormPower => _db.maxFormPower;
int get maxHeartRate => _db.maxHeartRate;
int get maxPower => _db.maxPower;
int get minFormPower => _db.minFormPower;
int get minHeartRate => _db.minHeartRate;
int get minPower => _db.minPower;
int get movingTime => _db.movingTime;
int get totalAscent => _db.totalAscent;
int get totalDescent => _db.totalDescent;
set timeStamp(DateTime value) => _db.timeStamp = value;
set firstRecordId(int value) => _db.firstRecordId = value;
set lastRecordId(int value) => _db.lastRecordId = value;
set athletesId(int value) => _db.athletesId = value;
set activitiesId(int value) => _db.activitiesId = value;
set distance(int value) => _db.distance = value;
set duration(Duration value) => _db.duration = value.inSeconds;
set ftp(double value) => _db.ftp = value;
Future<BoolResult> delete() async => await _db.delete();
Future<int> save() async => await _db.save();
Future<void> setValues() async {
final RecordList<Event> recordList = RecordList<Event>(await records);
_db
..avgPower = recordList.avgPower()
..sdevPower = recordList.sdevPower()
..minPower = recordList.minPower()
..maxPower = recordList.maxPower()
..avgHeartRate = recordList.avgHeartRate()
..sdevHeartRate = recordList.sdevHeartRate()
..minHeartRate = recordList.minHeartRate()
..maxHeartRate = recordList.maxHeartRate()
..avgSpeedByMeasurements = recordList.avgSpeedByMeasurements()
..avgSpeedBySpeed = recordList.avgSpeedBySpeed()
..avgSpeedByDistance = recordList.avgSpeedByDistance()
..sdevSpeed = recordList.sdevSpeed()
..sdevPace = recordList.sdevPace()
..minSpeed = recordList.minSpeed()
..maxSpeed = recordList.maxSpeed()
..avgGroundTime = recordList.avgGroundTime()
..sdevGroundTime = recordList.sdevGroundTime()
..minGroundTime = recordList.minGroundTime()
..maxGroundTime = recordList.maxGroundTime()
..avgStrydCadence = recordList.avgStrydCadence()
..sdevStrydCadence = recordList.sdevStrydCadence()
..minStrydCadence = recordList.minStrydCadence()
..maxStrydCadence = recordList.maxStrydCadence()
..avgCadence = recordList.avgStrydCadence()
..sdevCadence = recordList.sdevStrydCadence()
..minCadence = recordList.minCadence()
..maxCadence = recordList.maxCadence()
..avgLegSpringStiffness = recordList.avgLegSpringStiffness()
..sdevLegSpringStiffness = recordList.sdevLegSpringStiffness()
..minLegSpringStiffness = recordList.minLegSpringStiffness()
..maxLegSpringStiffness = recordList.maxLegSpringStiffness()
..avgVerticalOscillation = recordList.avgVerticalOscillation()
..sdevVerticalOscillation = recordList.sdevVerticalOscillation()
..minVerticalOscillation = recordList.minVerticalOscillation()
..maxVerticalOscillation = recordList.maxVerticalOscillation()
..avgFormPower = recordList.avgFormPower()
..sdevFormPower = recordList.sdevFormPower()
..minFormPower = recordList.minFormPower()
..maxFormPower = recordList.maxFormPower()
..totalAscent = recordList.totalAscent().round()
..totalDescent = recordList.totalDescent().round()
..movingTime = recordList.movingTime();
await save();
}
Future<List<Event>> get records async {
final List<DbEvent> dbEvents = await DbEvent()
.select()
.id
.greaterThanOrEquals(firstRecordId)
.and
.id
.lessThanOrEquals(lastRecordId)
.toList();
final List<Event> events = dbEvents.map(Event.exDb).toList();
_records = events.where((Event event) => event.event == 'record').toList();
return _records;
}
// calculated from other attributes:
double get ecor {
if (avgPower != null &&
avgSpeed != null &&
avgSpeed > 0 &&
weight != null &&
weight > 0) {
return avgPower / avgSpeed / weight;
} else {
return null;
}
}
double get avgPace {
if (avgSpeedByDistance != null && avgSpeedByDistance != 0) {
return 50 / 3 / avgSpeedByDistance;
} else {
return null;
}
}
double get avgSpeedPerHeartRate {
if (avgSpeed != null && avgHeartRate != null && avgHeartRate != 0) {
return 100 * (avgSpeed / avgHeartRate);
} else {
return null;
}
}
double get avgPowerPerHeartRate {
if (avgPower != null &&
avgPower != -1 &&
avgHeartRate != null &&
avgHeartRate != null) {
return avgPower / avgHeartRate;
} else {
return null;
}
}
int get elevationDifference {
if (totalAscent != null && totalDescent != null) {
return totalAscent - totalDescent;
} else {
return null;
}
}
double get avgDoubleStrydCadence {
if (avgStrydCadence != null) {
return avgStrydCadence * 2;
} else {
return null;
}
}
// easier check for data availability
bool get powerAvailable => !<num>[null, -1].contains(avgPower);
bool get heartRateAvailable => !<num>[null, -1].contains(avgHeartRate);
bool get ascentAvailable => totalAscent != null && totalDescent != null;
bool get cadenceAvailable => !<num>[null, -1].contains(avgStrydCadence);
bool get speedAvailable => !<num>[null, 0, -1].contains(avgSpeedByDistance);
bool get weightAvailable => !<num>[null, 0].contains(weight);
bool get paceAvailable => !<num>[null, -1].contains(avgPace);
bool get ecorAvailable => !<num>[null, -1].contains(ecor);
bool get groundTimeAvailable => !<num>[null, -1].contains(avgGroundTime);
bool get formPowerAvailable => !<num>[null, -1].contains(avgFormPower);
bool get verticalOscillationAvailable =>
!<num>[null, -1].contains(avgVerticalOscillation);
bool get strideCadenceAvailable =>
!<num>[null, -1].contains(avgDoubleStrydCadence);
bool get legSpringStiffnessAvailable =>
!<num>[null, -1].contains(avgLegSpringStiffness);
Future<PowerZone> get powerZone async {
if (_powerZone == null) {
final DbPowerZone dbPowerZone = await DbPowerZone()
.select()
.powerZoneSchemataId
.equals((await powerZoneSchema).id)
.and
.lowerLimit
.lessThanOrEquals(avgPower)
.and
.upperLimit
.greaterThanOrEquals(avgPower)
.toSingle();
_powerZone = PowerZone.exDb(dbPowerZone);
}
return _powerZone;
}
Future<HeartRateZone> get heartRateZone async {
if (_heartRateZone == null) {
final DbHeartRateZone dbHeartRateZone = await DbHeartRateZone()
.select()
.heartRateZoneSchemataId
.equals((await heartRateZoneSchema).id)
.and
.lowerLimit
.lessThanOrEquals(avgHeartRate)
.and
.upperLimit
.greaterThanOrEquals(avgHeartRate)
.toSingle();
_heartRateZone = HeartRateZone.exDb(dbHeartRateZone);
}
return _heartRateZone;
}
Future<HeartRateZoneSchema> get heartRateZoneSchema async {
if (_heartRateZoneSchema == null) {
final DbActivity dbActivity = await DbActivity().getById(activitiesId);
_heartRateZoneSchema = await HeartRateZoneSchema.getBy(
athletesId: dbActivity.athletesId,
date: dbActivity.timeCreated,
);
}
return _heartRateZoneSchema;
}
Future<PowerZoneSchema> get powerZoneSchema async {
if (_powerZoneSchema == null) {
final DbActivity dbActivity = await DbActivity().getById(activitiesId);
_powerZoneSchema = await PowerZoneSchema.getBy(
athletesId: dbActivity.athletesId,
date: dbActivity.timeCreated,
);
}
return _powerZoneSchema;
}
Future<void> autoTagger({@required Athlete athlete}) async {
final PowerZone powerZone = await this.powerZone;
if (powerZone.id != null) {
final Tag powerTag = await Tag.autoPowerTag(
athlete: athlete,
sortOrder: powerZone.lowerLimit,
color: powerZone.color,
name: powerZone.name,
);
await IntervalTagging.createBy(
interval: this,
tag: powerTag,
system: true,
);
}
final HeartRateZone heartRateZone = await this.heartRateZone;
if (heartRateZone.id != null) {
final Tag heartRateTag = await Tag.autoHeartRateTag(
athlete: athlete,
sortOrder: heartRateZone.lowerLimit,
color: heartRateZone.color,
name: heartRateZone.name,
);
await IntervalTagging.createBy(
interval: this,
tag: heartRateTag,
system: true,
);
}
}
Future<void> calculateAndSave({RecordList<Event> records}) async {
distance = (records.last.distance - records.first.distance).round();
duration = records.last.timeStamp.difference(records.first.timeStamp);
ftp = ftp_lib.calculate(records: records);
timeStamp = records.first.timeStamp;
await setValues();
}
Future<void> copyTaggings({Lap lap}) async {
final List<Tag> tags = await Tag.allByLap(lap: lap);
for (final Tag tag in tags) {
await IntervalTagging(tag: tag, interval: this).save();
}
}
Future<List<Tag>> get tags async {
if (cachedTags.isEmpty) {
cachedTags = await Tag.allByInterval(interval: this);
}
return cachedTags;
}
static Interval exDb(DbInterval db) => Interval._fromDb(db);
}