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: MAIN TASKS:
General/Framework: 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 ☐ find a general way to deal with duration fields
☐ implement tostring methods for all models
Accuracies: Accuracies:
☐ implement reordering ☐ implement reordering
Basal/Bolus: 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: Log Overview:
☐ add active events view (as main menu item?) ☐ add active events view (as main menu item?)
@ -18,18 +20,24 @@ MAIN TASKS:
Log Entry: Log Entry:
☐ replace meal and glucose boli with logbolus entities ☐ 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: 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: Settings:
☐ add objectbox class and use instead of shared preferences ☐ add objectbox class and use instead of shared preferences
☐ add fields for date and time formats ☐ add fields for date and time formats
☐ add fields for glucose target ☐ 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: FUTURE TASKS:
General/Framework: General/Framework:
☐ clean up controllers (dispose method of each stateful widget)
☐ add explanations to each section ☐ add explanations to each section
☐ account for deleted/disabled elements in dropdowns ☐ account for deleted/disabled elements in dropdowns
☐ hide dropdown overlay on tapping anywhere else (especially menu) ☐ hide dropdown overlay on tapping anywhere else (especially menu)
@ -38,11 +46,12 @@ FUTURE TASKS:
☐ add pagination ☐ add pagination
ARCHIVE: Archive:
✔ add tab for bolus overview @done(21-11-24 22:05) @project(Log Entry) ✔ fix logmeals/logboli/logevents @done(21-11-25 17:10) @project(MAIN TASKS.Log Entry)
✔ calculate bolus suggestions according to active profile @done(21-11-24 22:05) @project(Log Entry) ✔ add tab for bolus overview @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(General/Framework) ✔ calculate bolus suggestions according to active profile @done(21-11-24 22:05) @project(MAIN TASKS.Log Entry)
✔ add autocomplete function to dropdowns @done(21-11-23 20:33) @project(General/Framework) ✔ place dropdown items right below their input @done(21-11-23 20:33) @project(MAIN TASKS.General/Framework)
✔ use local database instead of back4app @done(21-11-07 18:53) @project(General/Framework) ✔ add autocomplete function to dropdowns @done(21-11-23 20:33) @project(MAIN TASKS.General/Framework)
✔ use ids instead of passing entities around where possible @done(21-11-10 00:06) @project(General/Framework) ✔ use local database instead of back4app @done(21-11-07 18:53) @project(MAIN TASKS.General/Framework)
✔ add time picker for entry date/time @done(21-11-10 00:06) @project(Log Entry) ✔ 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/main.dart';
import 'package:diameter/objectbox.g.dart'; import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 3095978685310268382) @Entity(uid: 291512798403320400)
class Accuracy { class Accuracy {
static final Box<Accuracy> box = objectBox.store.box<Accuracy>(); static final Box<Accuracy> box = objectBox.store.box<Accuracy>();

View File

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

View File

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

View File

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

View File

@ -1,5 +1,7 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/objectbox.g.dart'; import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 8812452529027052317) @Entity(uid: 8812452529027052317)
class BolusProfile { 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_entry.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/objectbox.g.dart'; import 'package:diameter/objectbox.g.dart';
import 'package:objectbox/objectbox.dart';
@Entity(uid: 0) @Entity(uid: 8033487006694871160)
class LogBolus { class LogBolus {
static final Box<LogBolus> box = objectBox.store.box<LogBolus>(); static final Box<LogBolus> box = objectBox.store.box<LogBolus>();
@ -14,7 +15,7 @@ class LogBolus {
int? delay; int? delay;
int? mgPerDl; int? mgPerDl;
double? mmolPerL; double? mmolPerL;
bool manuallyAdjusted; bool setManually;
String? notes; String? notes;
final logEntry = ToOne<LogEntry>(); final logEntry = ToOne<LogEntry>();
@ -28,7 +29,7 @@ class LogBolus {
this.delay, this.delay,
this.mgPerDl, this.mgPerDl,
this.mmolPerL, this.mmolPerL,
this.manuallyAdjusted = false, this.setManually = false,
this.notes, 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_event.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/objectbox.g.dart'; import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 752131069307970560) @Entity(uid: 752131069307970560)
class LogEntry { 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_entry.dart';
import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/objectbox.g.dart'; import 'package:diameter/objectbox.g.dart';
// ignore: unnecessary_import
import 'package:objectbox/objectbox.dart';
@Entity(uid: 4303325892753185970) @Entity(uid: 4303325892753185970)
class LogEvent { class LogEvent {

View File

@ -3,45 +3,6 @@
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", "_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.", "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [ "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", "id": "2:1467758525778521891",
"lastPropertyId": "5:3908367275335317130", "lastPropertyId": "5:3908367275335317130",
@ -632,17 +593,138 @@
} }
], ],
"relations": [] "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"
} }
], ],
"lastEntityId": "13:1283034494527412242", "relations": []
"lastIndexId": "21:1931330716440762729", },
{
"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": "15:291512798403320400",
"lastIndexId": "24:4224983816051843140",
"lastRelationId": "0:0", "lastRelationId": "0:0",
"lastSequenceId": "0:0", "lastSequenceId": "0:0",
"modelVersion": 5, "modelVersion": 5,
"modelVersionParserMinimum": 5, "modelVersionParserMinimum": 5,
"retiredEntityUids": [], "retiredEntityUids": [
3095978685310268382
],
"retiredIndexUids": [], "retiredIndexUids": [],
"retiredPropertyUids": [], "retiredPropertyUids": [
3455702077061719523,
1048198814030724077,
9003780003858349085,
5421422436108145565,
7741631874181070179,
5471636804765937328
],
"retiredRelationUids": [], "retiredRelationUids": [],
"version": 1 "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, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: SingleChildScrollView( child: _logEntryDailyMap.isNotEmpty ? SingleChildScrollView(
child: _logEntryDailyMap.isNotEmpty ? ListView.builder( child: ListView.builder(
shrinkWrap: true, shrinkWrap: true,
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
itemCount: _logEntryDailyMap.length, itemCount: _logEntryDailyMap.length,
@ -134,11 +134,11 @@ class _LogScreenState extends State<LogScreen> {
], ],
); );
}, },
),
) : const Center( ) : const Center(
child: Text('You have not created any Log Entries yet!'), child: Text('You have not created any Log Entries yet!'),
), ),
), ),
),
], ],
), ),
// ignore: todo // ignore: todo

View File

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

View File

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

View File

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