Added sqlite

This commit is contained in:
Courtney Arnold 2026-08-08 16:55:40 -05:00
parent 73a7c93e64
commit f01186df79
14 changed files with 784 additions and 382 deletions

View file

@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:io';
import 'package:googleapis/drive/v3.dart' as drive;
@ -6,7 +5,7 @@ import 'package:http/http.dart' as http;
const appFolderName = 'MO-Fuel-Tax-Back';
const receiptsFolderName = 'receipts';
const dataFileName = 'fuel_tax_data.json';
const dataFileName = 'fuel_tax_tracker.db';
const _folderMimeType = 'application/vnd.google-apps.folder';
@ -17,6 +16,16 @@ class DriveFolder {
DriveFolder({required this.id, required this.name});
}
class DriveDataFileInfo {
final String id;
/// Cheap change-detection signal: compare against the last value seen to
/// decide whether a pull is actually needed, without downloading content.
final String? md5Checksum;
DriveDataFileInfo({required this.id, required this.md5Checksum});
}
class DriveLockFile {
final String id;
final String username;
@ -87,23 +96,21 @@ class DriveService {
return created.id!;
}
Future<String?> findDataFileId(String appFolderId) async {
/// Looks up the shared data file's ID and md5 checksum without
/// downloading its content, so sync can cheaply decide whether a pull is
/// actually needed.
Future<DriveDataFileInfo?> findDataFile(String appFolderId) async {
final result = await _api.files.list(
q: "'$appFolderId' in parents and trashed=false and name='$dataFileName'",
orderBy: 'modifiedTime desc',
$fields: 'files(id,name)',
$fields: 'files(id,name,md5Checksum)',
spaces: 'drive',
);
return (result.files ?? []).firstOrNullWithId();
}
Future<String> downloadTextFile(String fileId) async {
final media = await _api.files.get(
fileId,
downloadOptions: drive.DownloadOptions.fullMedia,
) as drive.Media;
final bytes = await _collectBytes(media.stream);
return utf8.decode(bytes);
final files = result.files ?? <drive.File>[];
if (files.isEmpty) return null;
final first = files.first;
if (first.id == null) return null;
return DriveDataFileInfo(id: first.id!, md5Checksum: first.md5Checksum);
}
Future<List<int>> downloadFileBytes(String fileId) async {
@ -115,17 +122,20 @@ class DriveService {
}
/// Creates the data file if [existingFileId] is null, otherwise
/// overwrites its content. Returns the (possibly new) file ID.
Future<String> uploadDataFile({
/// overwrites its content with the bytes of the local sqlite database
/// file (see [DriveSyncService]). Returns the (possibly new) file ID and
/// its fresh md5 checksum, so the caller can remember it for
/// change-detection on the next sync without an extra round-trip.
Future<DriveDataFileInfo> uploadDataFile({
required String appFolderId,
required String? existingFileId,
required String jsonContent,
required File localSnapshotFile,
}) async {
final bytes = utf8.encode(jsonContent);
final length = await localSnapshotFile.length();
final media = drive.Media(
Stream.value(bytes),
bytes.length,
contentType: 'application/json',
localSnapshotFile.openRead(),
length,
contentType: 'application/x-sqlite3',
);
if (existingFileId != null) {
@ -133,8 +143,9 @@ class DriveService {
drive.File(),
existingFileId,
uploadMedia: media,
$fields: 'id,md5Checksum',
);
return updated.id ?? existingFileId;
return DriveDataFileInfo(id: updated.id ?? existingFileId, md5Checksum: updated.md5Checksum);
}
final created = await _api.files.create(
@ -142,8 +153,9 @@ class DriveService {
..name = dataFileName
..parents = [appFolderId],
uploadMedia: media,
$fields: 'id,md5Checksum',
);
return created.id!;
return DriveDataFileInfo(id: created.id!, md5Checksum: created.md5Checksum);
}
Future<String> uploadReceiptImage({