calorimeter/lib/storage/storage.dart
2024-12-07 13:39:11 +01:00

224 lines
5.1 KiB
Dart

/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright (C) 2024 Marco Groß <mgross@sw-gross.de> */
import 'dart:io';
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
import 'package:calorimeter/utils/date_time_helper.dart';
import 'package:path_provider/path_provider.dart';
import 'package:universal_platform/universal_platform.dart';
class FoodStorage {
static late FoodStorage _instance;
late String path;
final Map<String, int> _foodLookupDatabase = {};
FoodStorage._create();
static Future<FoodStorage> create() async {
var storage = FoodStorage._create();
Directory dir = Directory('');
if (UniversalPlatform.isDesktop) {
dir = await getApplicationCacheDirectory();
} else if (UniversalPlatform.isAndroid) {
dir = await getApplicationDocumentsDirectory();
}
storage.path = dir.path;
_instance = storage;
return _instance;
}
static FoodStorage getInstance() => _instance;
Future<List<FoodEntryState>> getEntriesForDate(DateTime date) async {
List<FoodEntryState> entries = [];
var filePath = '$path/${date.year}/${date.month}/${date.day}';
var file = File(filePath);
var exists = await file.exists();
if (!exists) return [];
var lines = await file.readAsLines();
for (var line in lines) {
var fields = line.splitWithIgnore(',', ignoreIn: '"');
int mass = 0;
int kcalPerMass = 0;
try {
mass = int.parse(fields[2]);
} catch (e) {
mass = double.parse(fields[2]).toInt();
}
try {
kcalPerMass = int.parse(fields[3]);
} catch (e) {
kcalPerMass = double.parse(fields[3]).toInt();
}
var entry = FoodEntryState(
name: fields[1].replaceAll('"', ""),
mass: mass,
kcalPer100: kcalPerMass,
waitingForNetwork: false,
isSelected: false,
);
entries.add(entry);
}
return entries;
}
Future<void> writeEntriesForDate(
DateTime date, List<FoodEntryState> foodEntries) async {
var filePath = '$path/${date.year}/${date.month}/${date.day}';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
await file.create(recursive: true);
}
String fullString = '';
for (var entry in foodEntries) {
fullString += '${entry.toString()}\n';
}
await file.writeAsString(fullString);
}
Future<void> updateLimit(double limit) async {
var filePath = '$path/limit';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
await file.create();
}
await file.writeAsString(limit.toString());
}
Future<double> readLimit() async {
var filePath = '$path/limit';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
return 2000;
}
var line = await file.readAsLines();
double limit;
try {
limit = double.parse(line[0]);
} catch (e) {
limit = 2000;
}
return limit;
}
Future<String> readBrightness() async {
var filePath = '$path/brightness';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
return 'dark';
}
var line = await file.readAsLines();
if (line.isEmpty || (line[0] != 'dark' && line[0] != 'light')) {
return 'dark';
}
return line[0];
}
Future<void> writeBrightness(String brightness) async {
var filePath = '$path/brightness';
var file = File(filePath);
var exists = await file.exists();
if (!exists) {
file.create();
}
await file.writeAsString(brightness);
}
Future<void> buildFoodLookupDatabase() async {
// get a list of dates of the last 365 days
var dates = List<DateTime>.generate(365, (idx) {
var durationToPast = Duration(days: idx);
return DateTimeHelper.now().subtract(durationToPast);
});
for (var date in dates.reversed) {
addFoodEntryToLookupDatabaseFor(date);
}
}
Future<void> addFoodEntryToLookupDatabaseFor(DateTime date) async {
var entriesForDate = await getEntriesForDate(date);
for (var entry in entriesForDate) {
_foodLookupDatabase[entry.name] = entry.kcalPer100;
}
}
void addFoodEntryToLookupDatabase(FoodEntryState entry) {
_foodLookupDatabase[entry.name] = entry.kcalPer100;
}
Map<String, int> get getFoodEntryLookupDatabase => _foodLookupDatabase;
}
extension SplitWithIgnore on String {
List<String> splitWithIgnore(String delimiter, {String? ignoreIn}) {
List<String> parts = [];
if (ignoreIn == null) {
return split(delimiter);
}
int index = -1;
int indexCharAfterDelimiter = 0;
bool inIgnore = false;
for (var rune in runes) {
var char = String.fromCharCode(rune);
index += 1;
if (char == ignoreIn) {
inIgnore = !inIgnore;
continue;
}
if (inIgnore) {
continue;
}
if (char == delimiter) {
parts.add(substring(indexCharAfterDelimiter, index));
indexCharAfterDelimiter = index + 1;
}
if (index + 1 == length) {
parts.add(substring(indexCharAfterDelimiter, length));
}
}
return parts;
}
}