main
1import 'dart:async';
2
3import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
4import 'package:blood_pressure_app/model/storage/storage.dart';
5import 'package:path/path.dart';
6import 'package:sqflite/sqflite.dart';
7
8/// Database for storing settings and internal app state.
9///
10/// The table names ensured by this class are stored as constant public strings ending in Table.
11@deprecated
12class ConfigDB {
13 ConfigDB._create();
14
15 /// Name of the settings table.
16 ///
17 /// It is used to store json representations of [Settings] objects. The json representation is chosen to allow editing
18 /// and editing fields without changing the database structure and because there is no use case where accessing
19 /// individual fields for SQLs logic and matching is needed and the complexity of maintaining different settings
20 /// formats (export) is not worth it. Disk space doesn't play a role, as in most cases there will be only one entry in
21 /// the table.
22 ///
23 /// Format:
24 /// `CREATE TABLE settings(profile_id INTEGER PRIMARY KEY, settings_json STRING)`
25 static const String settingsTable = 'settings';
26
27 /// Name of the table for storing [ExportSettings] objects.
28 ///
29 /// Data is saved the same way as with [settingsTable].
30 ///
31 /// Format:
32 /// `CREATE TABLE exportSettings(profile_id INTEGER PRIMARY KEY, json STRING)`
33 static const String exportSettingsTable = 'exportSettings';
34
35 /// Name of the table for storing [CsvExportSettings] objects.
36 ///
37 /// Data is saved the same way as with [settingsTable].
38 ///
39 /// Format:
40 /// `CREATE TABLE exportCsvSettings(profile_id INTEGER PRIMARY KEY, json STRING)`
41 static const String exportCsvSettingsTable = 'exportCsvSettings';
42
43 /// Name of the table for storing [PdfExportSettings] objects.
44 ///
45 /// Data is saved the same way as with [settingsTable].
46 ///
47 /// Format:
48 /// `CREATE TABLE exportPdfSettings(profile_id INTEGER PRIMARY KEY, json STRING)`
49 static const String exportPdfSettingsTable = 'exportPdfSettings';
50
51 /// Name of the table for storing time intervals to display.
52 ///
53 /// It is used to store json representations of [IntervalStorage] objects. Data is saved as fields, to save space
54 /// on the disk and because no changes in the data format are expected. The field names are made to match the variable
55 /// names in the class.
56 ///
57 /// Note that this table has 2 keys: profile_key to support multiple profiles and storage_id to support saving
58 /// multiple entries per profile (e.g. export screen and main page).
59 ///
60 /// Format: `CREATE TABLE selectedIntervallStorage(profile_id INTEGER, storage_id INTEGER, stepSize INTEGER,`
61 /// ` start INTEGER, end INTEGER, PRIMARY KEY(profile_id, storage_id))`
62 static const String selectedIntervalStorageTable = 'selectedIntervallStorage';
63
64 /// Name of the exportStrings table. It is used to to update old columns.
65 ///
66 /// Format:
67 /// `CREATE TABLE exportStrings(internalColumnName STRING PRIMARY KEY, columnTitle STRING, formatPattern STRING)`
68 @Deprecated('removed after all usages are replaced by export_columns_store')
69 static const String exportStringsTable = 'exportStrings';
70
71 /// Name of table of storing [ExportColumnsManager] objects
72 ///
73 /// Format:
74 /// `CREATE TABLE exportColumns(profile_id INTEGER PRIMARY KEY, json STRING)`
75 static const String exportColumnsTable = 'exportColumns';
76 static const String _exportColumnsTableCreationString =
77 'CREATE TABLE exportColumns(profile_id INTEGER PRIMARY KEY, json STRING)';
78
79 late final Database _database;
80
81 /// [dbPath] is the path to the folder the database is in. When [dbPath] is left empty the default database file is
82 /// used. The [isFullPath] option tells the constructor not to add the default filename at the end of [dbPath].
83 Future<bool> _asyncInit(String? dbPath, bool isFullPath) async {
84 dbPath ??= await getDatabasesPath();
85 if (dbPath != inMemoryDatabasePath && !isFullPath) {
86 dbPath = join(dbPath, 'config.db');
87 }
88 bool dbTooOld = false;
89 _database = await openDatabase(
90 dbPath,
91 onCreate: (_, __) => dbTooOld = true,
92 onUpgrade: (Database db, int oldVersion, int newVersion) async {
93 assert(newVersion == 3);
94 if (oldVersion == 1) {
95 dbTooOld = true;
96 } else if (oldVersion == 2) {
97 await db.execute(_exportColumnsTableCreationString);
98 } else {
99 assert(false, 'Unexpected version upgrade from $oldVersion to $newVersion.');
100 }
101 },
102 // When increasing the version an update procedure from every other possible version is needed
103 version: 3,
104 // In integration tests the file may be deleted which causes deadlocks.
105 singleInstance: false,
106 );
107 return dbTooOld;// TODO: test
108 }
109
110 /// Factory method that either opens an existing db file or creates a new one if no file is present.
111 ///
112 /// [dbPath] is the path to the folder the database is in. When [dbPath] is left empty the default database file is
113 /// used. The [isFullPath] option tells the constructor not to add the default filename at the end of [dbPath].
114 static Future<ConfigDB?> open({String? dbPath, bool isFullPath = false}) async {
115 final instance = ConfigDB._create();
116 if(await instance._asyncInit(dbPath, isFullPath)) {
117 return instance;
118 }
119 return null;
120 }
121
122 /// The database created by this class for getting and setting entries.
123 ///
124 /// Changes to the database structure should be done by altering the [_onDBCreate] and [_onDBUpgrade] methods of this
125 /// class to ensure smooth upgrades without the possibility of error.
126 Database get database => _database;
127}