fix log entry tabs

This commit is contained in:
spinel 2021-11-25 19:25:13 +01:00
parent 13507d14c9
commit f0f8898627
15 changed files with 638 additions and 310 deletions

33
TODO
View File

@ -1,13 +1,15 @@
MAIN TASKS:
General/Framework:
☐ add active/deleted flag to all data models
☐ add deleted flag to all data models
☐ adjust remove and fetch methods accordingly
☐ find a general way to deal with duration fields
☐ implement tostring methods for all models
Accuracies:
☐ implement reordering
Basal/Bolus:
☐ create distinct visual mode for picking the active profile
☐ create distinct visual mode for picking the active profile (or remove active flag)
Log Overview:
☐ add active events view (as main menu item?)
@ -18,18 +20,24 @@ MAIN TASKS:
Log Entry:
☐ replace meal and glucose boli with logbolus entities
fix logmeals/logboli/logevents (probably use queries for associated items instead of onetomany)
check for multiple active events with temporary basal/bolus profiles
Event Types:
☐ add option to change bolus/basal profile for event duration
☐ add option to change bolus/basal profile for event duration (dropdown with detail option)
☐ add reminder option
☐ implement reminders as push notification
Settings:
☐ add objectbox class and use instead of shared preferences
☐ add fields for date and time formats
☐ add fields for glucose target
☐ add option to hide warning dialogs on cancel
☐ add option to hide extra customization options (ie. changing pre calculated values)
☐ add setting for decimal places
FUTURE TASKS:
General/Framework:
☐ clean up controllers (dispose method of each stateful widget)
☐ add explanations to each section
☐ account for deleted/disabled elements in dropdowns
☐ hide dropdown overlay on tapping anywhere else (especially menu)
@ -38,11 +46,12 @@ FUTURE TASKS:
☐ add pagination
ARCHIVE:
✔ add tab for bolus overview @done(21-11-24 22:05) @project(Log Entry)
✔ calculate bolus suggestions according to active profile @done(21-11-24 22:05) @project(Log Entry)
✔ place dropdown items right below their input @done(21-11-23 20:33) @project(General/Framework)
✔ add autocomplete function to dropdowns @done(21-11-23 20:33) @project(General/Framework)
✔ use local database instead of back4app @done(21-11-07 18:53) @project(General/Framework)
✔ use ids instead of passing entities around where possible @done(21-11-10 00:06) @project(General/Framework)
✔ add time picker for entry date/time @done(21-11-10 00:06) @project(Log Entry)
Archive:
✔ fix logmeals/logboli/logevents @done(21-11-25 17:10) @project(MAIN TASKS.Log Entry)
✔ add tab for bolus overview @done(21-11-24 22:05) @project(MAIN TASKS.Log Entry)
✔ calculate bolus suggestions according to active profile @done(21-11-24 22:05) @project(MAIN TASKS.Log Entry)
✔ place dropdown items right below their input @done(21-11-23 20:33) @project(MAIN TASKS.General/Framework)
✔ add autocomplete function to dropdowns @done(21-11-23 20:33) @project(MAIN TASKS.General/Framework)
✔ use local database instead of back4app @done(21-11-07 18:53) @project(MAIN TASKS.General/Framework)
✔ use ids instead of passing entities around where possible @done(21-11-10 00:06) @project(MAIN TASKS.General/Framework)
✔ add time picker for entry date/time @done(21-11-10 00:06) @project(MAIN TASKS.Log Entry)

View File

@ -1,7 +1,9 @@
import 'package:diameter/main.dart';
import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 3095978685310268382)
@Entity(uid: 291512798403320400)
class Accuracy {
static final Box<Accuracy> box = objectBox.store.box<Accuracy>();

View File

@ -1,6 +1,8 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 1467758525778521891)
class Basal {

View File

@ -1,5 +1,7 @@
import 'package:diameter/main.dart';
import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 3613736032926903785)
class BasalProfile {

View File

@ -1,8 +1,11 @@
import 'package:diameter/config.dart';
import 'package:diameter/main.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/objectbox.g.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 3417770529060202389)
class Bolus {
@ -45,13 +48,18 @@ class Bolus {
// ignore: todo
// TODO: check if an event is active that would change the active profile
final bolusProfile = BolusProfile.getActive();
final time = DateTimeUtils.convertTimeOfDayToDateTime(TimeOfDay.fromDateTime(dateTime));
final time = DateTimeUtils.convertTimeOfDayToDateTime(
TimeOfDay.fromDateTime(dateTime));
if (bolusProfile != null) {
final rates = Bolus.getAllForProfile(bolusProfile.id);
final result = rates.where((rate) =>
(time.isAfter(rate.startTime) ||
time.isAtSameMomentAs(rate.startTime)) &&
time.isBefore(rate.endTime));
final result = rates.where((rate) {
DateTime endTime = rate.endTime == dummyDate
? rate.endTime.add(const Duration(days: 1))
: rate.endTime;
return (time.isAfter(rate.startTime) ||
time.isAtSameMomentAs(rate.startTime)) &&
time.isBefore(endTime);
});
return result.length != 1 ? null : result.single;
}
return null;

View File

@ -1,5 +1,7 @@
import 'package:diameter/main.dart';
import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 8812452529027052317)
class BolusProfile {

View File

@ -3,8 +3,9 @@ import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/objectbox.g.dart';
import 'package:objectbox/objectbox.dart';
@Entity(uid: 0)
@Entity(uid: 8033487006694871160)
class LogBolus {
static final Box<LogBolus> box = objectBox.store.box<LogBolus>();
@ -14,7 +15,7 @@ class LogBolus {
int? delay;
int? mgPerDl;
double? mmolPerL;
bool manuallyAdjusted;
bool setManually;
String? notes;
final logEntry = ToOne<LogEntry>();
@ -28,7 +29,7 @@ class LogBolus {
this.delay,
this.mgPerDl,
this.mmolPerL,
this.manuallyAdjusted = false,
this.setManually = false,
this.notes,
});

View File

@ -3,6 +3,8 @@ import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 752131069307970560)
class LogEntry {

View File

@ -2,6 +2,8 @@ import 'package:diameter/main.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 4303325892753185970)
class LogEvent {

View File

@ -3,45 +3,6 @@
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [
{
"id": "1:3095978685310268382",
"lastPropertyId": "6:5471636804765937328",
"name": "Accuracy",
"properties": [
{
"id": "1:3455702077061719523",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:1048198814030724077",
"name": "value",
"type": 9
},
{
"id": "3:9003780003858349085",
"name": "forCarbsRatio",
"type": 1
},
{
"id": "4:5421422436108145565",
"name": "forPortionSize",
"type": 1
},
{
"id": "5:7741631874181070179",
"name": "confidenceRating",
"type": 6
},
{
"id": "6:5471636804765937328",
"name": "notes",
"type": 9
}
],
"relations": []
},
{
"id": "2:1467758525778521891",
"lastPropertyId": "5:3908367275335317130",
@ -632,17 +593,138 @@
}
],
"relations": []
},
{
"id": "14:8033487006694871160",
"lastPropertyId": "11:4818762109001810295",
"name": "LogBolus",
"properties": [
{
"id": "1:8254237730262024662",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:7669701519569266656",
"name": "units",
"type": 8
},
{
"id": "3:1967840431906109999",
"name": "carbs",
"type": 8
},
{
"id": "4:5520321978435312625",
"name": "delay",
"type": 6
},
{
"id": "5:6855574218883169324",
"name": "mgPerDl",
"type": 6
},
{
"id": "6:5313708456544000157",
"name": "mmolPerL",
"type": 8
},
{
"id": "7:3065420032567707091",
"name": "setManually",
"type": 1
},
{
"id": "8:2967613978873295525",
"name": "notes",
"type": 9
},
{
"id": "9:5454965717985089938",
"name": "logEntryId",
"type": 11,
"flags": 520,
"indexId": "22:5852072074740543047",
"relationTarget": "LogEntry"
},
{
"id": "10:4105009806564072037",
"name": "rateId",
"type": 11,
"flags": 520,
"indexId": "23:1594553054621930876",
"relationTarget": "Bolus"
},
{
"id": "11:4818762109001810295",
"name": "mealId",
"type": 11,
"flags": 520,
"indexId": "24:4224983816051843140",
"relationTarget": "LogMeal"
}
],
"relations": []
},
{
"id": "15:291512798403320400",
"lastPropertyId": "6:6625101003527710274",
"name": "Accuracy",
"properties": [
{
"id": "1:8405388350474524599",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:1919049381880760479",
"name": "value",
"type": 9
},
{
"id": "3:7181081526218678274",
"name": "forCarbsRatio",
"type": 1
},
{
"id": "4:3576006369067328383",
"name": "forPortionSize",
"type": 1
},
{
"id": "5:7027546512578846894",
"name": "confidenceRating",
"type": 6
},
{
"id": "6:6625101003527710274",
"name": "notes",
"type": 9
}
],
"relations": []
}
],
"lastEntityId": "13:1283034494527412242",
"lastIndexId": "21:1931330716440762729",
"lastEntityId": "15:291512798403320400",
"lastIndexId": "24:4224983816051843140",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 5,
"modelVersionParserMinimum": 5,
"retiredEntityUids": [],
"retiredEntityUids": [
3095978685310268382
],
"retiredIndexUids": [],
"retiredPropertyUids": [],
"retiredPropertyUids": [
3455702077061719523,
1048198814030724077,
9003780003858349085,
5421422436108145565,
7741631874181070179,
5471636804765937328
],
"retiredRelationUids": [],
"version": 1
}

File diff suppressed because it is too large Load Diff

View File

@ -74,8 +74,8 @@ class _LogScreenState extends State<LogScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: SingleChildScrollView(
child: _logEntryDailyMap.isNotEmpty ? ListView.builder(
child: _logEntryDailyMap.isNotEmpty ? SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(10.0),
itemCount: _logEntryDailyMap.length,
@ -134,10 +134,10 @@ class _LogScreenState extends State<LogScreen> {
],
);
},
) : const Center(
),
) : const Center(
child: Text('You have not created any Log Entries yet!'),
),
),
),
],
),

View File

@ -40,7 +40,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
final _delayController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
bool _manuallyAdjusted = false;
bool _setManually = false;
LogMeal? _meal;
Bolus? _rate;
@ -61,10 +61,12 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_mmolPerLController.text = (_logBolus!.mmolPerL ?? '').toString();
_delayController.text = (_logBolus!.delay ?? '').toString();
_notesController.text = _logBolus!.notes ?? '';
_manuallyAdjusted = _logBolus!.manuallyAdjusted;
_setManually = _logBolus!.setManually;
_meal = _logBolus!.meal.target;
_rate = _logBolus!.rate.target ?? Bolus.getRateForTime(_logEntry?.time);
_rate = _logBolus!.rate.target;
}
_rate ??= Bolus.getRateForTime(_logEntry?.time);
}
void reload() {
@ -76,17 +78,22 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_isNew = _logBolus == null;
}
Future<void> onSelectMeal(LogMeal meal) async {
void onSelectMeal(LogMeal meal) {
setState(() {
_meal = meal;
if (meal.carbsPerPortion != null) {
_carbsController.text = meal.carbsPerPortion.toString();
onChangeCarbs();
}
});
}
if (_rate != null) {
_unitsController.text =
(meal.carbsPerPortion ?? 0 / (_rate!.carbs / _rate!.units))
.toString();
}
void onChangeCarbs() {
setState(() {
if (_rate != null && !_setManually) {
_unitsController.text =
((double.tryParse(_carbsController.text) ?? 0) / (_rate!.carbs / _rate!.units))
.toString();
}
});
}
@ -103,7 +110,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
mgPerDl: int.tryParse(_mgPerDlController.text),
mmolPerL: double.tryParse(_mmolPerLController.text),
delay: int.tryParse(_delayController.text),
manuallyAdjusted: _manuallyAdjusted,
setManually: _setManually,
notes: _notesController.text,
);
logBolus.logEntry.target = _logEntry;
@ -125,15 +132,18 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_mgPerDlController.text != '' ||
_mmolPerLController.text != '' ||
_delayController.text != '' ||
_manuallyAdjusted ||
_setManually ||
_notesController.text != '')) ||
(!_isNew &&
(double.tryParse(_unitsController.text) != _logBolus!.units ||
double.tryParse(_carbsController.text) != _logBolus!.carbs ||
int.tryParse(_mgPerDlController.text) != _logBolus!.mgPerDl ||
double.tryParse(_mmolPerLController.text) != _logBolus!.mmolPerL ||
double.tryParse(_carbsController.text) !=
_logBolus!.carbs ||
int.tryParse(_mgPerDlController.text) !=
_logBolus!.mgPerDl ||
double.tryParse(_mmolPerLController.text) !=
_logBolus!.mmolPerL ||
int.tryParse(_delayController.text) != _logBolus!.delay ||
_manuallyAdjusted != _logBolus!.manuallyAdjusted ||
_setManually != _logBolus!.setManually ||
_notesController.text != (_logBolus!.notes ?? ''))))) {
Dialogs.showCancelConfirmationDialog(
context: context,
@ -165,9 +175,23 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
suffixText: ' U',
),
controller: _unitsController,
onChanged: (_) {
setState(() {
_setManually = true;
});
},
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
),
BooleanFormField(
value: _setManually,
label: 'set Bolus manually',
onChanged: (value) {
setState(() {
_setManually = value;
});
},
),
AutoCompleteDropdownButton<LogMeal>(
selectedItem: _meal,
label: 'Meal',
@ -179,23 +203,21 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
}
},
),
Expanded(
child: TextFormField(
decoration: InputDecoration(
labelText: 'Carbs',
suffixText:
nutritionMeasurement == NutritionMeasurement.grams
? 'g'
: nutritionMeasurement ==
NutritionMeasurement.ounces
? 'oz'
: '',
),
controller: _carbsController,
keyboardType: const TextInputType.numberWithOptions(
decimal: true),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Carbs',
suffixText: nutritionMeasurement ==
NutritionMeasurement.grams
? 'g'
: nutritionMeasurement == NutritionMeasurement.ounces
? 'oz'
: '',
),
controller: _carbsController,
onChanged: (_) => onChangeCarbs(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Delayed Bolus Duration',

View File

@ -71,6 +71,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
hasEndTime: _hasEndTime,
notes: _notesController.text,
);
event.logEntry.targetId = widget.logEntryId;
event.eventType.target = _eventType;
LogEvent.put(event);
Navigator.pop(context, '${_isNew ? 'New' : ''} Event Saved');

View File

@ -148,6 +148,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
delayedBolusRate: double.tryParse(_delayedBolusRateController.text),
notes: _notesController.text,
);
logMeal.logEntry.targetId = widget.logEntryId;
logMeal.meal.target = _meal;
logMeal.mealSource.target = _mealSource;
logMeal.mealCategory.target = _mealCategory;
@ -197,8 +198,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_logMeal!.carbsRatioAccuracy.target ||
_portionSizeAccuracy !=
_logMeal!.portionSizeAccuracy.target ||
double.tryParse(_bolusController.text) !=
_logMeal!.bolus ||
double.tryParse(_bolusController.text) != _logMeal!.bolus ||
int.tryParse(_delayedBolusDurationController.text) !=
_logMeal!.delayedBolusDuration ||
double.tryParse(_delayedBolusRateController.text) !=