640 lines
28 KiB
Dart
640 lines
28 KiB
Dart
// lib/services/database_helper.dart
|
|
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:path/path.dart' as p;
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:sqflite/sqflite.dart';
|
|
|
|
// =======================================================================
|
|
// Part 3: Local Database Helper
|
|
// =======================================================================
|
|
|
|
class DatabaseHelper {
|
|
static Database? _database;
|
|
static const String _dbName = 'app_data.db';
|
|
// --- MODIFIED: Incremented DB version ---
|
|
static const int _dbVersion = 24; // Keep version updated if schema changes
|
|
// --- END MODIFIED ---
|
|
|
|
// compute-related static variables/methods REMOVED
|
|
|
|
static const String _profileTable = 'user_profile';
|
|
static const String _usersTable = 'all_users';
|
|
static const String _tarballStationsTable = 'marine_tarball_stations';
|
|
static const String _manualStationsTable = 'marine_manual_stations';
|
|
static const String _riverManualStationsTable = 'river_manual_stations';
|
|
static const String _riverTriennialStationsTable = 'river_triennial_stations';
|
|
// --- ADDED: River Investigative Stations Table Name ---
|
|
static const String _riverInvestigativeStationsTable = 'river_investigative_stations';
|
|
// --- END ADDED ---
|
|
static const String _tarballClassificationsTable = 'marine_tarball_classifications';
|
|
static const String _departmentsTable = 'departments';
|
|
static const String _companiesTable = 'companies';
|
|
static const String _positionsTable = 'positions';
|
|
static const String _alertQueueTable = 'alert_queue';
|
|
static const String _airManualStationsTable = 'air_manual_stations';
|
|
static const String _airClientsTable = 'air_clients';
|
|
static const String _statesTable = 'states';
|
|
static const String _appSettingsTable = 'app_settings';
|
|
// static const String _parameterLimitsTable = 'manual_parameter_limits'; // REMOVED
|
|
static const String _npeParameterLimitsTable = 'npe_parameter_limits';
|
|
static const String _marineParameterLimitsTable = 'marine_parameter_limits';
|
|
static const String _riverParameterLimitsTable = 'river_parameter_limits';
|
|
static const String _apiConfigsTable = 'api_configurations';
|
|
static const String _ftpConfigsTable = 'ftp_configurations';
|
|
static const String _retryQueueTable = 'retry_queue';
|
|
static const String _submissionLogTable = 'submission_log';
|
|
static const String _documentsTable = 'documents';
|
|
|
|
static const String _modulePreferencesTable = 'module_preferences';
|
|
static const String _moduleApiLinksTable = 'module_api_links';
|
|
static const String _moduleFtpLinksTable = 'module_ftp_links';
|
|
|
|
Future<Database> get database async {
|
|
if (_database != null) return _database!;
|
|
_database = await _initDB();
|
|
return _database!;
|
|
}
|
|
|
|
Future<Database> _initDB() async {
|
|
// Standard path retrieval
|
|
String dbPath = p.join(await getDatabasesPath(), _dbName);
|
|
|
|
return await openDatabase(dbPath, version: _dbVersion, onCreate: _onCreate, onUpgrade: _onUpgrade);
|
|
}
|
|
|
|
Future _onCreate(Database db, int version) async {
|
|
// Create all tables as defined in version 23
|
|
await db.execute('CREATE TABLE $_profileTable(user_id INTEGER PRIMARY KEY, profile_json TEXT)');
|
|
await db.execute('''
|
|
CREATE TABLE $_usersTable(
|
|
user_id INTEGER PRIMARY KEY,
|
|
email TEXT UNIQUE,
|
|
password_hash TEXT,
|
|
user_json TEXT
|
|
)
|
|
''');
|
|
await db.execute('CREATE TABLE $_tarballStationsTable(station_id INTEGER PRIMARY KEY, station_json TEXT)');
|
|
await db.execute('CREATE TABLE $_manualStationsTable(station_id INTEGER PRIMARY KEY, station_json TEXT)');
|
|
await db.execute('CREATE TABLE $_riverManualStationsTable(station_id INTEGER PRIMARY KEY, station_json TEXT)');
|
|
await db.execute('CREATE TABLE $_riverTriennialStationsTable(station_id INTEGER PRIMARY KEY, station_json TEXT)');
|
|
// --- ADDED: River Investigative Stations Table Create ---
|
|
await db.execute('CREATE TABLE $_riverInvestigativeStationsTable(station_id INTEGER PRIMARY KEY, station_json TEXT)');
|
|
// --- END ADDED ---
|
|
await db.execute('CREATE TABLE $_tarballClassificationsTable(classification_id INTEGER PRIMARY KEY, classification_json TEXT)');
|
|
await db.execute('CREATE TABLE $_departmentsTable(department_id INTEGER PRIMARY KEY, department_json TEXT)');
|
|
await db.execute('CREATE TABLE $_companiesTable(company_id INTEGER PRIMARY KEY, company_json TEXT)');
|
|
await db.execute('CREATE TABLE $_positionsTable(position_id INTEGER PRIMARY KEY, position_json TEXT)');
|
|
await db.execute('''CREATE TABLE $_alertQueueTable (id INTEGER PRIMARY KEY AUTOINCREMENT, chat_id TEXT NOT NULL, message TEXT NOT NULL, created_at TEXT NOT NULL)''');
|
|
await db.execute('CREATE TABLE $_airManualStationsTable(station_id INTEGER PRIMARY KEY, station_json TEXT)');
|
|
await db.execute('CREATE TABLE $_airClientsTable(client_id INTEGER PRIMARY KEY, client_json TEXT)');
|
|
await db.execute('CREATE TABLE $_statesTable(state_id INTEGER PRIMARY KEY, state_json TEXT)');
|
|
await db.execute('CREATE TABLE $_appSettingsTable(setting_id INTEGER PRIMARY KEY, setting_json TEXT)');
|
|
// No generic _parameterLimitsTable creation
|
|
await db.execute('CREATE TABLE $_npeParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
|
await db.execute('CREATE TABLE $_marineParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
|
await db.execute('CREATE TABLE $_riverParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
|
await db.execute('CREATE TABLE $_apiConfigsTable(api_config_id INTEGER PRIMARY KEY, config_json TEXT)');
|
|
await db.execute('CREATE TABLE $_ftpConfigsTable(ftp_config_id INTEGER PRIMARY KEY, config_json TEXT)');
|
|
await db.execute('''
|
|
CREATE TABLE $_retryQueueTable(
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
type TEXT NOT NULL,
|
|
endpoint_or_path TEXT NOT NULL,
|
|
payload TEXT,
|
|
timestamp TEXT NOT NULL,
|
|
status TEXT NOT NULL
|
|
)
|
|
''');
|
|
await db.execute('''
|
|
CREATE TABLE $_submissionLogTable (
|
|
submission_id TEXT PRIMARY KEY,
|
|
module TEXT NOT NULL,
|
|
type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
message TEXT,
|
|
report_id TEXT,
|
|
created_at TEXT NOT NULL,
|
|
form_data TEXT,
|
|
image_data TEXT,
|
|
server_name TEXT,
|
|
api_status TEXT,
|
|
ftp_status TEXT
|
|
)
|
|
''');
|
|
await db.execute('''
|
|
CREATE TABLE $_modulePreferencesTable (
|
|
module_name TEXT PRIMARY KEY,
|
|
is_api_enabled INTEGER NOT NULL DEFAULT 1,
|
|
is_ftp_enabled INTEGER NOT NULL DEFAULT 1
|
|
)
|
|
''');
|
|
await db.execute('''
|
|
CREATE TABLE $_moduleApiLinksTable (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
module_name TEXT NOT NULL,
|
|
api_config_id INTEGER NOT NULL,
|
|
is_enabled INTEGER NOT NULL DEFAULT 1
|
|
)
|
|
''');
|
|
await db.execute('''
|
|
CREATE TABLE $_moduleFtpLinksTable (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
module_name TEXT NOT NULL,
|
|
ftp_config_id INTEGER NOT NULL,
|
|
is_enabled INTEGER NOT NULL DEFAULT 1
|
|
)
|
|
''');
|
|
await db.execute('CREATE TABLE $_documentsTable(id INTEGER PRIMARY KEY, document_json TEXT)');
|
|
}
|
|
|
|
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
|
// Apply upgrades sequentially
|
|
if (oldVersion < 11) {
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_airManualStationsTable(station_id INTEGER PRIMARY KEY, station_json TEXT)');
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_airClientsTable(client_id INTEGER PRIMARY KEY, client_json TEXT)');
|
|
}
|
|
if (oldVersion < 12) {
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_statesTable(state_id INTEGER PRIMARY KEY, state_json TEXT)');
|
|
}
|
|
if (oldVersion < 13) {
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_appSettingsTable(setting_id INTEGER PRIMARY KEY, setting_json TEXT)');
|
|
}
|
|
if (oldVersion < 16) {
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_apiConfigsTable(api_config_id INTEGER PRIMARY KEY, config_json TEXT)');
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_ftpConfigsTable(ftp_config_id INTEGER PRIMARY KEY, config_json TEXT)');
|
|
}
|
|
if (oldVersion < 17) {
|
|
await db.execute('''
|
|
CREATE TABLE IF NOT EXISTS $_retryQueueTable(
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
type TEXT NOT NULL,
|
|
endpoint_or_path TEXT NOT NULL,
|
|
payload TEXT,
|
|
timestamp TEXT NOT NULL,
|
|
status TEXT NOT NULL
|
|
)
|
|
''');
|
|
}
|
|
if (oldVersion < 18) {
|
|
await db.execute('''
|
|
CREATE TABLE IF NOT EXISTS $_submissionLogTable (
|
|
submission_id TEXT PRIMARY KEY,
|
|
module TEXT NOT NULL,
|
|
type TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
message TEXT,
|
|
report_id TEXT,
|
|
created_at TEXT NOT NULL,
|
|
form_data TEXT,
|
|
image_data TEXT,
|
|
server_name TEXT
|
|
)
|
|
''');
|
|
}
|
|
if (oldVersion < 19) {
|
|
try {
|
|
await db.execute("ALTER TABLE $_submissionLogTable ADD COLUMN api_status TEXT");
|
|
await db.execute("ALTER TABLE $_submissionLogTable ADD COLUMN ftp_status TEXT");
|
|
} catch (_) {}
|
|
await db.execute('''
|
|
CREATE TABLE IF NOT EXISTS $_modulePreferencesTable (
|
|
module_name TEXT PRIMARY KEY,
|
|
is_api_enabled INTEGER NOT NULL DEFAULT 1,
|
|
is_ftp_enabled INTEGER NOT NULL DEFAULT 1
|
|
)
|
|
''');
|
|
await db.execute('''
|
|
CREATE TABLE IF NOT EXISTS $_moduleApiLinksTable (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
module_name TEXT NOT NULL,
|
|
api_config_id INTEGER NOT NULL,
|
|
is_enabled INTEGER NOT NULL DEFAULT 1
|
|
)
|
|
''');
|
|
await db.execute('''
|
|
CREATE TABLE IF NOT EXISTS $_moduleFtpLinksTable (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
module_name TEXT NOT NULL,
|
|
ftp_config_id INTEGER NOT NULL,
|
|
is_enabled INTEGER NOT NULL DEFAULT 1
|
|
)
|
|
''');
|
|
}
|
|
if (oldVersion < 20) {
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_documentsTable(id INTEGER PRIMARY KEY, document_json TEXT)');
|
|
}
|
|
|
|
if (oldVersion < 21) {
|
|
try {
|
|
await db.execute("ALTER TABLE $_usersTable ADD COLUMN email TEXT");
|
|
await db.execute("CREATE UNIQUE INDEX IF NOT EXISTS idx_user_email ON $_usersTable (email)");
|
|
} catch (e) {
|
|
debugPrint("Upgrade warning: Failed to add email column/index to users table (may already exist): $e");
|
|
}
|
|
try {
|
|
await db.execute("ALTER TABLE $_usersTable ADD COLUMN password_hash TEXT");
|
|
} catch (e) {
|
|
debugPrint("Upgrade warning: Failed to add password_hash column to users table (may already exist): $e");
|
|
}
|
|
}
|
|
if (oldVersion < 23) {
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_npeParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_marineParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_riverParameterLimitsTable(param_autoid INTEGER PRIMARY KEY, limit_json TEXT)');
|
|
try {
|
|
// await db.execute('DROP TABLE IF EXISTS $_parameterLimitsTable'); // Keep commented
|
|
debugPrint("Old generic parameter limits table check/drop logic executed (if applicable).");
|
|
} catch (e) {
|
|
debugPrint("Upgrade warning: Failed to drop old parameter limits table (may not exist): $e");
|
|
}
|
|
}
|
|
// --- ADDED: Upgrade step for new table ---
|
|
if (oldVersion < 24) {
|
|
await db.execute('CREATE TABLE IF NOT EXISTS $_riverInvestigativeStationsTable(station_id INTEGER PRIMARY KEY, station_json TEXT)');
|
|
}
|
|
// --- END ADDED ---
|
|
}
|
|
|
|
// --- Data Handling Methods ---
|
|
Future<void> _upsertData(String table, String idKeyName, List<Map<String, dynamic>> data, String jsonKeyName) async {
|
|
if (data.isEmpty) return;
|
|
final db = await database;
|
|
final batch = db.batch();
|
|
for (var item in data) {
|
|
if (item[idKeyName] != null) {
|
|
batch.insert(
|
|
table,
|
|
{idKeyName: item[idKeyName], '${jsonKeyName}_json': jsonEncode(item)},
|
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
);
|
|
} else {
|
|
debugPrint("Skipping upsert for item in $table due to null ID: $item");
|
|
}
|
|
}
|
|
await batch.commit(noResult: true);
|
|
debugPrint("Upserted items into $table (skipped items with null IDs if any)");
|
|
}
|
|
|
|
Future<void> _deleteData(String table, String idKeyName, List<dynamic> ids) async {
|
|
if (ids.isEmpty) return;
|
|
final db = await database;
|
|
final validIds = ids.where((id) => id != null).toList();
|
|
if (validIds.isEmpty) return;
|
|
final placeholders = List.filled(validIds.length, '?').join(', ');
|
|
await db.delete(
|
|
table,
|
|
where: '$idKeyName IN ($placeholders)',
|
|
whereArgs: validIds,
|
|
);
|
|
debugPrint("Deleted ${validIds.length} items from $table");
|
|
}
|
|
|
|
Future<List<Map<String, dynamic>>?> _loadData(String table, String jsonKey) async {
|
|
final db = await database;
|
|
final List<Map<String, dynamic>> maps = await db.query(table);
|
|
if (maps.isNotEmpty) {
|
|
try {
|
|
return maps.map((map) {
|
|
try {
|
|
return jsonDecode(map['${jsonKey}_json']) as Map<String, dynamic>;
|
|
} catch (e) {
|
|
final idKey = maps.first.keys.firstWhere((k) => k.endsWith('_id') || k == 'id' || k.endsWith('autoid'), orElse: () => 'unknown_id');
|
|
debugPrint("Error decoding JSON from $table, ID ${map[idKey]}: $e");
|
|
return <String, dynamic>{};
|
|
}
|
|
}).where((item) => item.isNotEmpty).toList();
|
|
} catch (e) {
|
|
debugPrint("General error loading data from $table: $e");
|
|
return null;
|
|
}
|
|
}
|
|
return null; // Return null if table is empty
|
|
}
|
|
|
|
Future<void> saveProfile(Map<String, dynamic> profile) async {
|
|
final db = await database;
|
|
await db.insert(_profileTable, {'user_id': profile['user_id'], 'profile_json': jsonEncode(profile)},
|
|
conflictAlgorithm: ConflictAlgorithm.replace);
|
|
}
|
|
|
|
Future<Map<String, dynamic>?> loadProfile() async {
|
|
final db = await database;
|
|
final List<Map<String, dynamic>> maps = await db.query(_profileTable);
|
|
if (maps.isNotEmpty) {
|
|
try {
|
|
return jsonDecode(maps.first['profile_json']);
|
|
} catch (e) {
|
|
debugPrint("Error decoding profile: $e");
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Future<Map<String, dynamic>?> loadProfileByEmail(String email) async {
|
|
final db = await database;
|
|
final List<Map<String, dynamic>> maps = await db.query(
|
|
_usersTable,
|
|
columns: ['user_json'],
|
|
where: 'email = ?',
|
|
whereArgs: [email],
|
|
);
|
|
if (maps.isNotEmpty) {
|
|
try {
|
|
return jsonDecode(maps.first['user_json']) as Map<String, dynamic>;
|
|
} catch (e) {
|
|
debugPrint("Error decoding profile for email $email: $e");
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Future<void> upsertUserWithCredentials({
|
|
required Map<String, dynamic> profile,
|
|
required String passwordHash,
|
|
}) async {
|
|
final db = await database;
|
|
await db.insert(
|
|
_usersTable,
|
|
{
|
|
'user_id': profile['user_id'],
|
|
'email': profile['email'],
|
|
'password_hash': passwordHash,
|
|
'user_json': jsonEncode(profile)
|
|
},
|
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
);
|
|
debugPrint("Upserted user credentials for ${profile['email']}");
|
|
}
|
|
|
|
Future<String?> getUserPasswordHashByEmail(String email) async {
|
|
final db = await database;
|
|
final List<Map<String, dynamic>> result = await db.query(
|
|
_usersTable,
|
|
columns: ['password_hash'],
|
|
where: 'email = ?',
|
|
whereArgs: [email],
|
|
);
|
|
if (result.isNotEmpty && result.first['password_hash'] != null) {
|
|
return result.first['password_hash'] as String;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Future<void> upsertUsers(List<Map<String, dynamic>> data) async {
|
|
if (data.isEmpty) return;
|
|
final db = await database;
|
|
final batch = db.batch();
|
|
for (var item in data) {
|
|
String email = item['email'] ?? 'missing_email_${item['user_id']}@placeholder.com';
|
|
if (item['email'] == null) {
|
|
debugPrint("Warning: User ID ${item['user_id']} is missing email during upsert.");
|
|
}
|
|
batch.insert(
|
|
_usersTable,
|
|
{
|
|
'user_id': item['user_id'],
|
|
'email': email,
|
|
'user_json': jsonEncode(item),
|
|
},
|
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
);
|
|
}
|
|
await batch.commit(noResult: true);
|
|
debugPrint("Upserted ${data.length} user items using batch.");
|
|
}
|
|
|
|
|
|
Future<void> deleteUsers(List<dynamic> ids) => _deleteData(_usersTable, 'user_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadUsers() => _loadData(_usersTable, 'user');
|
|
|
|
Future<void> upsertDocuments(List<Map<String, dynamic>> data) => _upsertData(_documentsTable, 'id', data, 'document');
|
|
Future<void> deleteDocuments(List<dynamic> ids) => _deleteData(_documentsTable, 'id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadDocuments() => _loadData(_documentsTable, 'document');
|
|
|
|
Future<void> upsertTarballStations(List<Map<String, dynamic>> data) =>
|
|
_upsertData(_tarballStationsTable, 'station_id', data, 'station');
|
|
Future<void> deleteTarballStations(List<dynamic> ids) => _deleteData(_tarballStationsTable, 'station_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadTarballStations() => _loadData(_tarballStationsTable, 'station');
|
|
|
|
Future<void> upsertManualStations(List<Map<String, dynamic>> data) =>
|
|
_upsertData(_manualStationsTable, 'station_id', data, 'station');
|
|
Future<void> deleteManualStations(List<dynamic> ids) => _deleteData(_manualStationsTable, 'station_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadManualStations() => _loadData(_manualStationsTable, 'station');
|
|
|
|
Future<void> upsertRiverManualStations(List<Map<String, dynamic>> data) =>
|
|
_upsertData(_riverManualStationsTable, 'station_id', data, 'station');
|
|
Future<void> deleteRiverManualStations(List<dynamic> ids) => _deleteData(_riverManualStationsTable, 'station_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadRiverManualStations() => _loadData(_riverManualStationsTable, 'station');
|
|
|
|
Future<void> upsertRiverTriennialStations(List<Map<String, dynamic>> data) =>
|
|
_upsertData(_riverTriennialStationsTable, 'station_id', data, 'station');
|
|
Future<void> deleteRiverTriennialStations(List<dynamic> ids) => _deleteData(_riverTriennialStationsTable, 'station_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadRiverTriennialStations() => _loadData(_riverTriennialStationsTable, 'station');
|
|
|
|
// --- ADDED: River Investigative Stations DB Methods ---
|
|
Future<void> upsertRiverInvestigativeStations(List<Map<String, dynamic>> data) =>
|
|
_upsertData(_riverInvestigativeStationsTable, 'station_id', data, 'station');
|
|
Future<void> deleteRiverInvestigativeStations(List<dynamic> ids) => _deleteData(_riverInvestigativeStationsTable, 'station_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadRiverInvestigativeStations() => _loadData(_riverInvestigativeStationsTable, 'station');
|
|
// --- END ADDED ---
|
|
|
|
Future<void> upsertTarballClassifications(List<Map<String, dynamic>> data) =>
|
|
_upsertData(_tarballClassificationsTable, 'classification_id', data, 'classification');
|
|
Future<void> deleteTarballClassifications(List<dynamic> ids) => _deleteData(_tarballClassificationsTable, 'classification_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadTarballClassifications() => _loadData(_tarballClassificationsTable, 'classification');
|
|
|
|
Future<void> upsertDepartments(List<Map<String, dynamic>> data) => _upsertData(_departmentsTable, 'department_id', data, 'department');
|
|
Future<void> deleteDepartments(List<dynamic> ids) => _deleteData(_departmentsTable, 'department_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadDepartments() => _loadData(_departmentsTable, 'department');
|
|
|
|
Future<void> upsertCompanies(List<Map<String, dynamic>> data) => _upsertData(_companiesTable, 'company_id', data, 'company');
|
|
Future<void> deleteCompanies(List<dynamic> ids) => _deleteData(_companiesTable, 'company_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadCompanies() => _loadData(_companiesTable, 'company');
|
|
|
|
Future<void> upsertPositions(List<Map<String, dynamic>> data) => _upsertData(_positionsTable, 'position_id', data, 'position');
|
|
Future<void> deletePositions(List<dynamic> ids) => _deleteData(_positionsTable, 'position_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadPositions() => _loadData(_positionsTable, 'position');
|
|
|
|
Future<void> upsertAirManualStations(List<Map<String, dynamic>> data) =>
|
|
_upsertData(_airManualStationsTable, 'station_id', data, 'station');
|
|
Future<void> deleteAirManualStations(List<dynamic> ids) => _deleteData(_airManualStationsTable, 'station_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadAirManualStations() => _loadData(_airManualStationsTable, 'station');
|
|
|
|
Future<void> upsertAirClients(List<Map<String, dynamic>> data) => _upsertData(_airClientsTable, 'client_id', data, 'client');
|
|
Future<void> deleteAirClients(List<dynamic> ids) => _deleteData(_airClientsTable, 'client_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadAirClients() => _loadData(_airClientsTable, 'client');
|
|
|
|
Future<void> upsertStates(List<Map<String, dynamic>> data) => _upsertData(_statesTable, 'state_id', data, 'state');
|
|
Future<void> deleteStates(List<dynamic> ids) => _deleteData(_statesTable, 'state_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadStates() => _loadData(_statesTable, 'state');
|
|
|
|
Future<void> upsertAppSettings(List<Map<String, dynamic>> data) => _upsertData(_appSettingsTable, 'setting_id', data, 'setting');
|
|
Future<void> deleteAppSettings(List<dynamic> ids) => _deleteData(_appSettingsTable, 'setting_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadAppSettings() => _loadData(_appSettingsTable, 'setting');
|
|
|
|
Future<void> upsertNpeParameterLimits(List<Map<String, dynamic>> data) => _upsertData(_npeParameterLimitsTable, 'param_autoid', data, 'limit');
|
|
Future<void> deleteNpeParameterLimits(List<dynamic> ids) => _deleteData(_npeParameterLimitsTable, 'param_autoid', ids);
|
|
Future<List<Map<String, dynamic>>?> loadNpeParameterLimits() => _loadData(_npeParameterLimitsTable, 'limit');
|
|
|
|
Future<void> upsertMarineParameterLimits(List<Map<String, dynamic>> data) => _upsertData(_marineParameterLimitsTable, 'param_autoid', data, 'limit');
|
|
Future<void> deleteMarineParameterLimits(List<dynamic> ids) => _deleteData(_marineParameterLimitsTable, 'param_autoid', ids);
|
|
Future<List<Map<String, dynamic>>?> loadMarineParameterLimits() => _loadData(_marineParameterLimitsTable, 'limit');
|
|
|
|
Future<void> upsertRiverParameterLimits(List<Map<String, dynamic>> data) => _upsertData(_riverParameterLimitsTable, 'param_autoid', data, 'limit');
|
|
Future<void> deleteRiverParameterLimits(List<dynamic> ids) => _deleteData(_riverParameterLimitsTable, 'param_autoid', ids);
|
|
Future<List<Map<String, dynamic>>?> loadRiverParameterLimits() => _loadData(_riverParameterLimitsTable, 'limit');
|
|
|
|
Future<void> upsertApiConfigs(List<Map<String, dynamic>> data) => _upsertData(_apiConfigsTable, 'api_config_id', data, 'config');
|
|
Future<void> deleteApiConfigs(List<dynamic> ids) => _deleteData(_apiConfigsTable, 'api_config_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadApiConfigs() => _loadData(_apiConfigsTable, 'config');
|
|
|
|
Future<void> upsertFtpConfigs(List<Map<String, dynamic>> data) => _upsertData(_ftpConfigsTable, 'ftp_config_id', data, 'config');
|
|
Future<void> deleteFtpConfigs(List<dynamic> ids) => _deleteData(_ftpConfigsTable, 'ftp_config_id', ids);
|
|
Future<List<Map<String, dynamic>>?> loadFtpConfigs() => _loadData(_ftpConfigsTable, 'config');
|
|
|
|
Future<int> queueFailedRequest(Map<String, dynamic> data) async {
|
|
final db = await database;
|
|
return await db.insert(_retryQueueTable, data, conflictAlgorithm: ConflictAlgorithm.replace);
|
|
}
|
|
|
|
Future<List<Map<String, dynamic>>> getPendingRequests() async {
|
|
final db = await database;
|
|
return await db.query(_retryQueueTable, where: 'status = ?', whereArgs: ['pending'], orderBy: 'timestamp ASC'); // Order by timestamp
|
|
}
|
|
|
|
Future<Map<String, dynamic>?> getRequestById(int id) async {
|
|
final db = await database;
|
|
final results = await db.query(_retryQueueTable, where: 'id = ?', whereArgs: [id]);
|
|
return results.isNotEmpty ? results.first : null;
|
|
}
|
|
|
|
Future<void> deleteRequestFromQueue(int id) async {
|
|
final db = await database;
|
|
await db.delete(_retryQueueTable, where: 'id = ?', whereArgs: [id]);
|
|
}
|
|
|
|
Future<void> saveSubmissionLog(Map<String, dynamic> data) async {
|
|
final db = await database;
|
|
await db.insert(
|
|
_submissionLogTable,
|
|
data,
|
|
conflictAlgorithm: ConflictAlgorithm.replace, // Replace if same ID exists
|
|
);
|
|
}
|
|
|
|
Future<List<Map<String, dynamic>>?> loadSubmissionLogs({String? module}) async {
|
|
final db = await database;
|
|
List<Map<String, dynamic>> maps;
|
|
|
|
try { // Add try-catch for robustness
|
|
if (module != null && module.isNotEmpty) {
|
|
maps = await db.query(
|
|
_submissionLogTable,
|
|
where: 'module = ?',
|
|
whereArgs: [module],
|
|
orderBy: 'created_at DESC',
|
|
);
|
|
} else {
|
|
maps = await db.query(
|
|
_submissionLogTable,
|
|
orderBy: 'created_at DESC',
|
|
);
|
|
}
|
|
return maps.isNotEmpty ? maps : null; // Return null if empty
|
|
} catch (e) {
|
|
debugPrint("Error loading submission logs: $e");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<void> saveModulePreference({
|
|
required String moduleName,
|
|
required bool isApiEnabled,
|
|
required bool isFtpEnabled,
|
|
}) async {
|
|
final db = await database;
|
|
await db.insert(
|
|
_modulePreferencesTable,
|
|
{
|
|
'module_name': moduleName,
|
|
'is_api_enabled': isApiEnabled ? 1 : 0,
|
|
'is_ftp_enabled': isFtpEnabled ? 1 : 0,
|
|
},
|
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
);
|
|
}
|
|
|
|
Future<Map<String, dynamic>?> getModulePreference(String moduleName) async {
|
|
final db = await database;
|
|
final result = await db.query(
|
|
_modulePreferencesTable,
|
|
where: 'module_name = ?',
|
|
whereArgs: [moduleName],
|
|
);
|
|
if (result.isNotEmpty) {
|
|
final row = result.first;
|
|
return {
|
|
'module_name': row['module_name'],
|
|
'is_api_enabled': (row['is_api_enabled'] as int) == 1,
|
|
'is_ftp_enabled': (row['is_ftp_enabled'] as int) == 1,
|
|
};
|
|
}
|
|
// Return default values if no preference found
|
|
return {'module_name': moduleName, 'is_api_enabled': true, 'is_ftp_enabled': true};
|
|
}
|
|
|
|
Future<void> saveApiLinksForModule(String moduleName, List<Map<String, dynamic>> links) async {
|
|
final db = await database;
|
|
await db.transaction((txn) async {
|
|
await txn.delete(_moduleApiLinksTable, where: 'module_name = ?', whereArgs: [moduleName]);
|
|
for (final link in links) {
|
|
if (link['api_config_id'] != null) { // Ensure ID is not null
|
|
await txn.insert(_moduleApiLinksTable, {
|
|
'module_name': moduleName,
|
|
'api_config_id': link['api_config_id'],
|
|
'is_enabled': (link['is_enabled'] as bool? ?? true) ? 1 : 0,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> saveFtpLinksForModule(String moduleName, List<Map<String, dynamic>> links) async {
|
|
final db = await database;
|
|
await db.transaction((txn) async {
|
|
await txn.delete(_moduleFtpLinksTable, where: 'module_name = ?', whereArgs: [moduleName]);
|
|
for (final link in links) {
|
|
if (link['ftp_config_id'] != null) { // Ensure ID is not null
|
|
await txn.insert(_moduleFtpLinksTable, {
|
|
'module_name': moduleName,
|
|
'ftp_config_id': link['ftp_config_id'],
|
|
'is_enabled': (link['is_enabled'] as bool? ?? true) ? 1 : 0,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<List<Map<String, dynamic>>> getAllApiLinksForModule(String moduleName) async {
|
|
final db = await database;
|
|
final result = await db.query(_moduleApiLinksTable, where: 'module_name = ?', whereArgs: [moduleName]);
|
|
return result.map((row) => {
|
|
'api_config_id': row['api_config_id'],
|
|
'is_enabled': (row['is_enabled'] as int) == 1,
|
|
}).toList();
|
|
}
|
|
|
|
Future<List<Map<String, dynamic>>> getAllFtpLinksForModule(String moduleName) async {
|
|
final db = await database;
|
|
final result = await db.query(_moduleFtpLinksTable, where: 'module_name = ?', whereArgs: [moduleName]);
|
|
return result.map((row) => {
|
|
'ftp_config_id': row['ftp_config_id'],
|
|
'is_enabled': (row['is_enabled'] as int) == 1,
|
|
}).toList();
|
|
}
|
|
} |