did more of the knex -> sqlite migration.

This commit is contained in:
Jordan Hewitt 2025-03-10 10:32:27 -07:00
parent 3616592896
commit 8f67d0421b
5 changed files with 67 additions and 59 deletions

View File

@ -1,18 +1,18 @@
import { Settings } from '@/app/lib/settings';
import { getDb } from '@/app/lib/db';
import { Knex } from 'knex';
import { getDb, migrateDb } from '@/app/lib/db';
import { SQLiteDatabase } from 'expo-sqlite';
describe('Settings', () => {
let settings : Settings;
let db : Knex;
let settings: Settings;
let db: SQLiteDatabase;
beforeEach(async () => {
db = await getDb("development");
settings = new Settings(db)
settings = new Settings(db);
});
afterEach(async () => {
await db.migrate.down();
await migrateDb("development");
});
it('should set the host language in the database', async () => {

View File

@ -1,14 +1,16 @@
import * as SQLite from "expo-sqlite";
import { MIGRATE_UP, MIGRATE_DOWN } from "./migrations";
export async function getDb() {
return await SQLite.openDatabaseAsync("translation_terrace");
export type db_mode = "development" | "staging" | "production";
export async function getDb(mode : db_mode = "production") {
return await SQLite.openDatabaseAsync(`translation_terrace_${mode}`);
}
export async function migrateDb(direction: "up" | "down" = "up") {
export async function migrateDb(mode : db_mode = "production", direction: "up" | "down" = "up") {
const db = await getDb();
const db = await getDb(mode);
const m = direction === "up" ? MIGRATE_UP : MIGRATE_DOWN;

View File

@ -1,5 +1,5 @@
import { SQLiteDatabase } from "expo-sqlite";
import { getDb } from "./db";
import { Knex } from "knex";
export class Settings {
@ -10,7 +10,7 @@ export class Settings {
"whisper_model",
]
constructor(public db: Knex) {
constructor(public db: SQLiteDatabase) {
}
@ -20,7 +20,8 @@ export class Settings {
throw new Error(`Invalid setting: '${key}'`)
}
const row = await this.db("settings").select(key).limit(1).first();
const row: { [key: string]: string } | null = this.db.getFirstSync(`SELECT ${key} from settings LIMIT 1`)
if (!(row && row[key])) return undefined;
return row[key];
}
@ -32,17 +33,13 @@ export class Settings {
}
// Check if the key already exists
const [exists] = await this.db("settings").select(1).whereNotNull(key).limit(1);
if (exists) {
// Update the existing column
await this.db("settings").update({ [key]: value });
} else {
// Insert a new value into the specified column
const insertData: { [key: string]: any } = {};
insertData[key] = value;
await this.db("settings").insert(insertData);
}
this.db.runSync(`INSERT INTO OR UPDATE
settings
(${key})
VALUES
(?)
WHERE
${key} IS NOT NULL`, value);
}
async setHostLanguage(value: string) {
@ -53,7 +50,7 @@ export class Settings {
return await this.getValue("host_language")
}
async setLibretranslateBaseUrl(value : string) {
async setLibretranslateBaseUrl(value: string) {
await this.setValue("libretranslate_base_url", value)
}
@ -61,7 +58,7 @@ export class Settings {
return await this.getValue("libretranslate_base_url")
}
async setWhisperModel(value : string) {
async setWhisperModel(value: string) {
await this.setValue("whisper_model", value);
}

View File

@ -75,7 +75,7 @@ export const WHISPER_MODELS = {
size: 6173629930,
},
} as {
[key:whisper_model_tag_t]: {
[key: whisper_model_tag_t]: {
source: string;
target: string;
label: string;
@ -145,25 +145,26 @@ export class WhisperFile {
if (!(await this.doesTargetExist())) {
console.debug("%s does not exist", this.targetPath);
}
if (!this.label) {
throw new Error("No label");
}
const digest1Str = await this.getActualTargetHash();
if (!digest1Str) {
return;
}
const db = await getDb();
await db("whisper_models")
.upsert({
model: this.tag,
last_hash: digest1Str,
})
.where({ model: this.tag });
db.runSync(`INSERT OR UPDATE
INTO whisper_models
(model, last_hash)
VALUES (?, ?)
WHERE
model = ?`, this.label, digest1Str, this.label);
}
public async getRecordedTargetHash(): Promise<string> {
const db = await getDb();
const row = await db("whisper_models").select("last_hash").where({
model: this.tag,
}).first();
return row["last_hash"]
const row = db.getFirstSync("SELECT last_hash FROM whisper_models WHERE model = ?", this.tag);
return (row as {last_hash: string}).last_hash
}
public async getActualTargetHash(): Promise<string | undefined> {
@ -269,20 +270,24 @@ export class WhisperFile {
async addToDatabase() {
const db = await getDb();
await db("whisper_models").upsert({
model: this.tag,
expected_size: this.size,
}).where({
model: this.tag,
});
if (!(this.size && this.tag)) {
throw new Error();
}
db.runSync(`INSERT OR UPDATE
INTO whisper_models
(model, expected_size)
VALUES
(?, ?)
WHERE
model = ?`, this.tag, this.size.valueOf(), this.tag);
}
async createDownloadResumable(
options: {
onData?: DownloadCallback | undefined;
} = {
onData: undefined,
}
onData: undefined,
}
) {
const existingData = (await this.doesTargetExist())
? this.targetFile.text()
@ -297,10 +302,14 @@ export class WhisperFile {
{},
async (data: FileSystem.DownloadProgressData) => {
const db = await getDb();
await db.upsert({
model: this.tag,
download_status: "active",
})
db.runAsync(`INSERT INTO OR UPDATE
whisper_models
(model, download_status)
VALUES
(?, ?)
WHERE
model = ?
`, this.tag, "active", this.tag);
await this.recordLatestTargetHash();
if (options.onData) await options.onData(data);
},
@ -308,7 +317,7 @@ export class WhisperFile {
);
}
async getDownloadStatus() : Promise<download_status_t> {
async getDownloadStatus(): Promise<download_status_t> {
const doesTargetExist = await this.doesTargetExist();
const isDownloadComplete = await this.isDownloadComplete();
const hasDownloadStarted = doesTargetExist && !isDownloadComplete;
@ -328,11 +337,11 @@ export class WhisperFile {
const progress = hasDownloadStarted
? {
current: this.targetFile.size || 0,
total: this.size,
remaining: this.size - (this.targetFile.size as number),
percentRemaining: (remaining / this.size) * 100.0,
}
current: this.targetFile.size || 0,
total: this.size,
remaining: this.size - (this.targetFile.size as number),
percentRemaining: (remaining / this.size) * 100.0,
}
: undefined;
return {

View File

@ -3,9 +3,10 @@ import { render, screen, fireEvent, act } from "@testing-library/react-native";
import SettingsComponent from "@/components/Settings";
import { language_matrix } from "@/app/i18n/api";
import { Settings } from "@/app/lib/settings";
import { getDb } from "@/app/lib/db";
import { getDb, migrateDb } from "@/app/lib/db";
import { Knex } from "knex";
import { WhisperFile } from "@/app/lib/whisper";
import { SQLiteDatabase } from "expo-sqlite";
const RENDER_TIME = 1000;
@ -77,7 +78,7 @@ jest.mock("@/app/i18n/api", () => {
describe("SettingsComponent", () => {
let db: Knex;
let db: SQLiteDatabase;
let settings: Settings;
beforeEach(async () => {
@ -90,8 +91,7 @@ describe("SettingsComponent", () => {
afterEach(async () => {
jest.restoreAllMocks();
await db.migrate.down();
await db.destroy();
await migrateDb("development", "down");
});
beforeAll(async () => {