removed utterance system atm
This commit is contained in:
@@ -1,13 +0,0 @@
|
|||||||
create table "user" ("id" text not null primary key, "name" text not null, "email" text not null unique, "emailVerified" integer not null, "image" text, "createdAt" date not null, "updatedAt" date not null);
|
|
||||||
|
|
||||||
create table "session" ("id" text not null primary key, "expiresAt" date not null, "token" text not null unique, "createdAt" date not null, "updatedAt" date not null, "ipAddress" text, "userAgent" text, "userId" text not null references "user" ("id") on delete cascade);
|
|
||||||
|
|
||||||
create table "account" ("id" text not null primary key, "accountId" text not null, "providerId" text not null, "userId" text not null references "user" ("id") on delete cascade, "accessToken" text, "refreshToken" text, "idToken" text, "accessTokenExpiresAt" date, "refreshTokenExpiresAt" date, "scope" text, "password" text, "createdAt" date not null, "updatedAt" date not null);
|
|
||||||
|
|
||||||
create table "verification" ("id" text not null primary key, "identifier" text not null, "value" text not null, "expiresAt" date not null, "createdAt" date not null, "updatedAt" date not null);
|
|
||||||
|
|
||||||
create index "session_userId_idx" on "session" ("userId");
|
|
||||||
|
|
||||||
create index "account_userId_idx" on "account" ("userId");
|
|
||||||
|
|
||||||
create index "verification_identifier_idx" on "verification" ("identifier");
|
|
||||||
160
package-lock.json
generated
160
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "ba-tts-dialect-annotation-plattform",
|
"name": "ba-tts-dialect-annotation-plattform",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@better-auth/drizzle-adapter": "^1.5.4",
|
||||||
"@libsql/client": "^0.17.0",
|
"@libsql/client": "^0.17.0",
|
||||||
"@tailwindcss/postcss": "^4.2.1",
|
"@tailwindcss/postcss": "^4.2.1",
|
||||||
"auth": "^1.5.3",
|
"auth": "^1.5.3",
|
||||||
@@ -558,38 +559,45 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@better-auth/core": {
|
"node_modules/@better-auth/core": {
|
||||||
"version": "1.4.19",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.5.4.tgz",
|
||||||
"integrity": "sha512-uADLHG1jc5BnEJi7f6ijUN5DmPPRSj++7m/G19z3UqA3MVCo4Y4t1MMa4IIxLCqGDFv22drdfxescgW+HnIowA==",
|
"integrity": "sha512-k5AdwPRQETZn0vdB60EB9CDxxfllpJXKqVxTjyXIUSRz7delNGlU0cR/iRP3VfVJwvYR1NbekphBDNo+KGoEzQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standard-schema/spec": "^1.0.0",
|
"@standard-schema/spec": "^1.1.0",
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@better-auth/utils": "0.3.0",
|
"@better-auth/utils": "0.3.1",
|
||||||
"@better-fetch/fetch": "1.1.21",
|
"@better-fetch/fetch": "1.1.21",
|
||||||
"better-call": "1.1.8",
|
"@cloudflare/workers-types": ">=4",
|
||||||
|
"better-call": "1.3.2",
|
||||||
"jose": "^6.1.0",
|
"jose": "^6.1.0",
|
||||||
"kysely": "^0.28.5",
|
"kysely": "^0.28.5",
|
||||||
"nanostores": "^1.0.1"
|
"nanostores": "^1.0.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@cloudflare/workers-types": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@better-auth/telemetry": {
|
"node_modules/@better-auth/drizzle-adapter": {
|
||||||
"version": "1.4.19",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@better-auth/telemetry/-/telemetry-1.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@better-auth/drizzle-adapter/-/drizzle-adapter-1.5.4.tgz",
|
||||||
"integrity": "sha512-ApGNS7olCTtDpKF8Ow3Z+jvFAirOj7c4RyFUpu8axklh3mH57ndpfUAUjhgA8UVoaaH/mnm/Tl884BlqiewLyw==",
|
"integrity": "sha512-4M4nMAWrDd3TmpV6dONkJjybBVKRZghe5Oj0NNyDEoXubxastQdO7Sb5B54I1rTx5yoMgsqaB+kbJnu/9UgjQg==",
|
||||||
"dependencies": {
|
"license": "MIT",
|
||||||
"@better-auth/utils": "0.3.0",
|
|
||||||
"@better-fetch/fetch": "1.1.21"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@better-auth/core": "1.4.19"
|
"@better-auth/core": "1.5.4",
|
||||||
|
"@better-auth/utils": "^0.3.0",
|
||||||
|
"drizzle-orm": ">=0.41.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@better-auth/utils": {
|
"node_modules/@better-auth/utils": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.1.tgz",
|
||||||
"integrity": "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==",
|
"integrity": "sha512-+CGp4UmZSUrHHnpHhLPYu6cV+wSUSvVbZbNykxhUDocpVNTo9uFFxw/NqJlh1iC4wQ9HKKWGCKuZ5wUgS0v6Kg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@better-fetch/fetch": {
|
"node_modules/@better-fetch/fetch": {
|
||||||
@@ -2814,6 +2822,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/auth/node_modules/@better-auth/drizzle-adapter": {
|
||||||
|
"version": "1.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@better-auth/drizzle-adapter/-/drizzle-adapter-1.5.3.tgz",
|
||||||
|
"integrity": "sha512-dib9V1vpwDu+TKLC+L+8Q5bLNS0uE3JCT4pGotw52pnpiQF8msoMK4eEfri19f8DtNltpb2F2yzyIsTugBBYNQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"@better-auth/core": "1.5.3",
|
||||||
|
"@better-auth/utils": "^0.3.0",
|
||||||
|
"drizzle-orm": ">=0.41.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/auth/node_modules/@better-auth/kysely-adapter": {
|
"node_modules/auth/node_modules/@better-auth/kysely-adapter": {
|
||||||
"version": "1.5.3",
|
"version": "1.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/@better-auth/kysely-adapter/-/kysely-adapter-1.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/@better-auth/kysely-adapter/-/kysely-adapter-1.5.3.tgz",
|
||||||
@@ -2848,12 +2869,6 @@
|
|||||||
"@better-auth/core": "1.5.3"
|
"@better-auth/core": "1.5.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/auth/node_modules/@better-auth/utils": {
|
|
||||||
"version": "0.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.1.tgz",
|
|
||||||
"integrity": "sha512-+CGp4UmZSUrHHnpHhLPYu6cV+wSUSvVbZbNykxhUDocpVNTo9uFFxw/NqJlh1iC4wQ9HKKWGCKuZ5wUgS0v6Kg==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/auth/node_modules/better-auth": {
|
"node_modules/auth/node_modules/better-auth": {
|
||||||
"version": "1.5.3",
|
"version": "1.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/better-auth/-/better-auth-1.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/better-auth/-/better-auth-1.5.3.tgz",
|
||||||
@@ -2968,26 +2983,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/auth/node_modules/better-call": {
|
|
||||||
"version": "1.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/better-call/-/better-call-1.3.2.tgz",
|
|
||||||
"integrity": "sha512-4cZIfrerDsNTn3cm+MhLbUePN0gdwkhSXEuG7r/zuQ8c/H7iU0/jSK5TD3FW7U0MgKHce/8jGpPYNO4Ve+4NBw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@better-auth/utils": "^0.3.1",
|
|
||||||
"@better-fetch/fetch": "^1.1.21",
|
|
||||||
"rou3": "^0.7.12",
|
|
||||||
"set-cookie-parser": "^3.0.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"zod": "^4.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"zod": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/auth/node_modules/drizzle-orm": {
|
"node_modules/auth/node_modules/drizzle-orm": {
|
||||||
"version": "0.41.0",
|
"version": "0.41.0",
|
||||||
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.41.0.tgz",
|
||||||
@@ -3109,12 +3104,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/auth/node_modules/set-cookie-parser": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@@ -3256,7 +3245,42 @@
|
|||||||
"better-auth": "^1.4.19"
|
"better-auth": "^1.4.19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/better-call": {
|
"node_modules/better-auth/node_modules/@better-auth/core": {
|
||||||
|
"version": "1.4.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.4.19.tgz",
|
||||||
|
"integrity": "sha512-uADLHG1jc5BnEJi7f6ijUN5DmPPRSj++7m/G19z3UqA3MVCo4Y4t1MMa4IIxLCqGDFv22drdfxescgW+HnIowA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@standard-schema/spec": "^1.0.0",
|
||||||
|
"zod": "^4.3.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@better-auth/utils": "0.3.0",
|
||||||
|
"@better-fetch/fetch": "1.1.21",
|
||||||
|
"better-call": "1.1.8",
|
||||||
|
"jose": "^6.1.0",
|
||||||
|
"kysely": "^0.28.5",
|
||||||
|
"nanostores": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/better-auth/node_modules/@better-auth/telemetry": {
|
||||||
|
"version": "1.4.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@better-auth/telemetry/-/telemetry-1.4.19.tgz",
|
||||||
|
"integrity": "sha512-ApGNS7olCTtDpKF8Ow3Z+jvFAirOj7c4RyFUpu8axklh3mH57ndpfUAUjhgA8UVoaaH/mnm/Tl884BlqiewLyw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@better-auth/utils": "0.3.0",
|
||||||
|
"@better-fetch/fetch": "1.1.21"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@better-auth/core": "1.4.19"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/better-auth/node_modules/@better-auth/utils": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/better-auth/node_modules/better-call": {
|
||||||
"version": "1.1.8",
|
"version": "1.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/better-call/-/better-call-1.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/better-call/-/better-call-1.1.8.tgz",
|
||||||
"integrity": "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==",
|
"integrity": "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==",
|
||||||
@@ -3276,6 +3300,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/better-auth/node_modules/set-cookie-parser": {
|
||||||
|
"version": "2.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
|
||||||
|
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/better-call": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/better-call/-/better-call-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-4cZIfrerDsNTn3cm+MhLbUePN0gdwkhSXEuG7r/zuQ8c/H7iU0/jSK5TD3FW7U0MgKHce/8jGpPYNO4Ve+4NBw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@better-auth/utils": "^0.3.1",
|
||||||
|
"@better-fetch/fetch": "^1.1.21",
|
||||||
|
"rou3": "^0.7.12",
|
||||||
|
"set-cookie-parser": "^3.0.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/better-sqlite3": {
|
"node_modules/better-sqlite3": {
|
||||||
"version": "12.6.2",
|
"version": "12.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz",
|
||||||
@@ -5244,9 +5294,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/set-cookie-parser": {
|
"node_modules/set-cookie-parser": {
|
||||||
"version": "2.7.2",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.0.1.tgz",
|
||||||
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
|
"integrity": "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/sharp": {
|
"node_modules/sharp": {
|
||||||
|
|||||||
@@ -7,9 +7,10 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"db:push": "drizzle-kit push",
|
"db:push": "drizzle-kit push",
|
||||||
"auth:push": "auth migrate"
|
"auth:push": "auth generate"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@better-auth/drizzle-adapter": "^1.5.4",
|
||||||
"@libsql/client": "^0.17.0",
|
"@libsql/client": "^0.17.0",
|
||||||
"@tailwindcss/postcss": "^4.2.1",
|
"@tailwindcss/postcss": "^4.2.1",
|
||||||
"auth": "^1.5.3",
|
"auth": "^1.5.3",
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ interface FilterParams {
|
|||||||
modelName?: string;
|
modelName?: string;
|
||||||
dialect?: string;
|
dialect?: string;
|
||||||
iteration?: number;
|
iteration?: number;
|
||||||
utteranceId?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getDatasetEntries(
|
export async function getDatasetEntries(
|
||||||
@@ -40,10 +39,6 @@ export async function getDatasetEntries(
|
|||||||
conditions.push(eq(dataset_entry.iteration, filters.iteration));
|
conditions.push(eq(dataset_entry.iteration, filters.iteration));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filters?.utteranceId) {
|
|
||||||
conditions.push(eq(dataset_entry.utteranceId, filters.utteranceId));
|
|
||||||
}
|
|
||||||
|
|
||||||
const whereClause = and(...conditions);
|
const whereClause = and(...conditions);
|
||||||
|
|
||||||
const entries = await db
|
const entries = await db
|
||||||
|
|||||||
@@ -15,13 +15,11 @@ export async function getFilterOptions(datasetId: number) {
|
|||||||
const modelNames = [...new Set(entries.map(e => e.modelName))].sort();
|
const modelNames = [...new Set(entries.map(e => e.modelName))].sort();
|
||||||
const dialects = [...new Set(entries.map(e => e.dialect))].sort();
|
const dialects = [...new Set(entries.map(e => e.dialect))].sort();
|
||||||
const iterations = [...new Set(entries.map(e => e.iteration))].sort((a, b) => a - b);
|
const iterations = [...new Set(entries.map(e => e.iteration))].sort((a, b) => a - b);
|
||||||
const utteranceIds = [...new Set(entries.map(e => e.utteranceId).filter(Boolean))].sort();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
speakerIds,
|
speakerIds,
|
||||||
modelNames,
|
modelNames,
|
||||||
dialects,
|
dialects,
|
||||||
iterations,
|
iterations,
|
||||||
utteranceIds,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
'use server';
|
|
||||||
|
|
||||||
import db from '@/lib/db';
|
|
||||||
import { dataset_utterance } from '@/lib/model/utterance';
|
|
||||||
import { eq, and } from 'drizzle-orm';
|
|
||||||
import { revalidatePath } from 'next/cache';
|
|
||||||
|
|
||||||
export async function uploadDatasetUtterances(
|
|
||||||
datasetId: number,
|
|
||||||
formData: FormData
|
|
||||||
) {
|
|
||||||
const file = formData.get('file') as File;
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
throw new Error('No file provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.name.endsWith('.csv')) {
|
|
||||||
throw new Error('File must be a CSV file');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const csvContent = await file.text();
|
|
||||||
const lines = csvContent.split('\n').filter((line) => line.trim());
|
|
||||||
|
|
||||||
if (lines.length < 2) {
|
|
||||||
throw new Error('CSV is empty or invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse CSV header
|
|
||||||
const headers = lines[0].split(',').map((h) => h.trim());
|
|
||||||
|
|
||||||
if (!headers.includes('id') || !headers.includes('text')) {
|
|
||||||
throw new Error('CSV must have "id" and "text" columns');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get indices of id and text columns
|
|
||||||
const idIndex = headers.indexOf('id');
|
|
||||||
const textIndex = headers.indexOf('text');
|
|
||||||
|
|
||||||
// Parse utterances
|
|
||||||
const utterances: typeof dataset_utterance.$inferInsert[] = [];
|
|
||||||
|
|
||||||
for (let i = 1; i < lines.length; i++) {
|
|
||||||
// Handle CSV parsing with quotes
|
|
||||||
const line = lines[i];
|
|
||||||
const parts: string[] = [];
|
|
||||||
let current = '';
|
|
||||||
let inQuotes = false;
|
|
||||||
|
|
||||||
for (let j = 0; j < line.length; j++) {
|
|
||||||
const char = line[j];
|
|
||||||
const nextChar = line[j + 1];
|
|
||||||
|
|
||||||
if (char === '"') {
|
|
||||||
if (inQuotes && nextChar === '"') {
|
|
||||||
current += '"';
|
|
||||||
j++; // Skip next quote
|
|
||||||
} else {
|
|
||||||
inQuotes = !inQuotes;
|
|
||||||
}
|
|
||||||
} else if (char === ',' && !inQuotes) {
|
|
||||||
parts.push(current.trim());
|
|
||||||
current = '';
|
|
||||||
} else {
|
|
||||||
current += char;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parts.push(current.trim());
|
|
||||||
|
|
||||||
if (parts.length > Math.max(idIndex, textIndex)) {
|
|
||||||
const id = parts[idIndex]?.trim();
|
|
||||||
const text = parts[textIndex]?.trim();
|
|
||||||
|
|
||||||
if (id && text) {
|
|
||||||
utterances.push({
|
|
||||||
datasetId,
|
|
||||||
id,
|
|
||||||
text,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (utterances.length === 0) {
|
|
||||||
throw new Error('No valid utterances found in CSV');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for existing utterances to avoid duplicates
|
|
||||||
const existingUtterances = await db
|
|
||||||
.select({ id: dataset_utterance.id })
|
|
||||||
.from(dataset_utterance)
|
|
||||||
.where(eq(dataset_utterance.datasetId, datasetId));
|
|
||||||
|
|
||||||
const existingIds = new Set(existingUtterances.map((u) => u.id));
|
|
||||||
|
|
||||||
// Filter out duplicates
|
|
||||||
const newUtterances = utterances.filter((u) => !existingIds.has(u.id));
|
|
||||||
|
|
||||||
if (newUtterances.length === 0) {
|
|
||||||
throw new Error('All utterances already exist in the dataset');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert new utterances into database
|
|
||||||
await db.insert(dataset_utterance).values(newUtterances);
|
|
||||||
|
|
||||||
revalidatePath(`/admin/datasets/${datasetId}`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
utterancesCreated: newUtterances.length,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error uploading utterances:', error);
|
|
||||||
throw new Error(
|
|
||||||
error instanceof Error ? error.message : 'Failed to upload utterances'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import db from '@/lib/db';
|
import db from '@/lib/db';
|
||||||
import { dataset_entry } from '@/lib/model/dataset_entry';
|
import { dataset_entry } from '@/lib/model/dataset_entry';
|
||||||
import { dataset_utterance } from '@/lib/model/utterance';
|
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { writeFile, mkdir, readFile, rm, readdir } from 'fs/promises';
|
import { writeFile, mkdir, readFile, rm, readdir } from 'fs/promises';
|
||||||
@@ -14,7 +13,6 @@ interface DatasetEntryInput {
|
|||||||
id: string;
|
id: string;
|
||||||
audio_file: string;
|
audio_file: string;
|
||||||
duration_ms: string;
|
duration_ms: string;
|
||||||
utt_id: string;
|
|
||||||
speaker: string;
|
speaker: string;
|
||||||
model: string;
|
model: string;
|
||||||
dialect: string;
|
dialect: string;
|
||||||
@@ -104,7 +102,6 @@ export async function uploadDatasetEntries(
|
|||||||
speakerId: row.speaker,
|
speakerId: row.speaker,
|
||||||
modelName: row.model,
|
modelName: row.model,
|
||||||
fileName: relativePath,
|
fileName: relativePath,
|
||||||
utteranceId: row.utt_id || undefined,
|
|
||||||
dialect: row.dialect,
|
dialect: row.dialect,
|
||||||
iteration: parseInt(row.iteration, 10),
|
iteration: parseInt(row.iteration, 10),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import db from '@/lib/db';
|
import db from '@/lib/db';
|
||||||
import { dataset_entry } from '@/lib/model/dataset_entry';
|
import { dataset_entry } from '@/lib/model/dataset_entry';
|
||||||
import { dataset_utterance } from '@/lib/model/utterance';
|
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import AudioPlayer from '@/components/AudioPlayer';
|
import AudioPlayer from '@/components/AudioPlayer';
|
||||||
|
|
||||||
@@ -42,17 +41,6 @@ export default async function DatasetEntryPage({ params }: DatasetEntryPageProps
|
|||||||
|
|
||||||
const entry = entries[0];
|
const entry = entries[0];
|
||||||
|
|
||||||
// Fetch utterance if utteranceId is present
|
|
||||||
let utterance = null;
|
|
||||||
if (entry.utteranceId) {
|
|
||||||
const utterances = await db
|
|
||||||
.select()
|
|
||||||
.from(dataset_utterance)
|
|
||||||
.where(eq(dataset_utterance.id, entry.utteranceId));
|
|
||||||
|
|
||||||
utterance = utterances.length > 0 ? utterances[0] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
@@ -107,13 +95,6 @@ export default async function DatasetEntryPage({ params }: DatasetEntryPageProps
|
|||||||
<AudioPlayer datasetId={datasetIdNum} fileName={entry.fileName} externalId={entry.externalId} />
|
<AudioPlayer datasetId={datasetIdNum} fileName={entry.fileName} externalId={entry.externalId} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{utterance && (
|
|
||||||
<div className="col-span-2 bg-blue-50 p-4 rounded-lg border border-blue-200">
|
|
||||||
<p className="text-sm font-semibold text-gray-700 mb-2">Utterance {entry.utteranceId}</p>
|
|
||||||
<p className="text-lg text-gray-800 leading-relaxed">{utterance.text}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="bg-white border border-gray-200 p-4 rounded-lg">
|
<div className="bg-white border border-gray-200 p-4 rounded-lg">
|
||||||
<p className="text-sm text-gray-600 mb-1">Created</p>
|
<p className="text-sm text-gray-600 mb-1">Created</p>
|
||||||
<p className="text-lg font-semibold">
|
<p className="text-lg font-semibold">
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { eq } from 'drizzle-orm';
|
|||||||
import EditableDatasetHeader from '@/components/EditableDatasetHeader';
|
import EditableDatasetHeader from '@/components/EditableDatasetHeader';
|
||||||
import DatasetEntriesList from '@/components/DatasetEntriesList';
|
import DatasetEntriesList from '@/components/DatasetEntriesList';
|
||||||
import UploadDatasetEntriesModal from '@/components/UploadDatasetEntriesModal';
|
import UploadDatasetEntriesModal from '@/components/UploadDatasetEntriesModal';
|
||||||
import UploadUtterancesModal from '@/components/UploadUtterancesModal';
|
|
||||||
import RemoveAllEntriesButton from '@/components/RemoveAllEntriesButton';
|
import RemoveAllEntriesButton from '@/components/RemoveAllEntriesButton';
|
||||||
|
|
||||||
interface DatasetPageProps {
|
interface DatasetPageProps {
|
||||||
@@ -57,7 +56,6 @@ export default async function DatasetPage({ params }: DatasetPageProps) {
|
|||||||
|
|
||||||
<div className="flex gap-3 mb-6">
|
<div className="flex gap-3 mb-6">
|
||||||
<UploadDatasetEntriesModal datasetId={datasetId} />
|
<UploadDatasetEntriesModal datasetId={datasetId} />
|
||||||
<UploadUtterancesModal datasetId={datasetId} />
|
|
||||||
<RemoveAllEntriesButton datasetId={datasetId} />
|
<RemoveAllEntriesButton datasetId={datasetId} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ interface DatasetEntry {
|
|||||||
speakerId: string;
|
speakerId: string;
|
||||||
modelName: string;
|
modelName: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
utteranceId: string | null;
|
|
||||||
dialect: string;
|
dialect: string;
|
||||||
iteration: number;
|
iteration: number;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
@@ -29,7 +28,6 @@ interface FilterOptions {
|
|||||||
modelNames: string[];
|
modelNames: string[];
|
||||||
dialects: string[];
|
dialects: string[];
|
||||||
iterations: number[];
|
iterations: number[];
|
||||||
utteranceIds: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DatasetEntriesList({
|
export default function DatasetEntriesList({
|
||||||
@@ -47,7 +45,6 @@ export default function DatasetEntriesList({
|
|||||||
modelName: '',
|
modelName: '',
|
||||||
dialect: '',
|
dialect: '',
|
||||||
iteration: '',
|
iteration: '',
|
||||||
utteranceId: '',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter options
|
// Filter options
|
||||||
@@ -56,7 +53,6 @@ export default function DatasetEntriesList({
|
|||||||
modelNames: [],
|
modelNames: [],
|
||||||
dialects: [],
|
dialects: [],
|
||||||
iterations: [],
|
iterations: [],
|
||||||
utteranceIds: [],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load filter options on mount
|
// Load filter options on mount
|
||||||
@@ -85,7 +81,6 @@ export default function DatasetEntriesList({
|
|||||||
modelName: filters.modelName || undefined,
|
modelName: filters.modelName || undefined,
|
||||||
dialect: filters.dialect || undefined,
|
dialect: filters.dialect || undefined,
|
||||||
iteration: filters.iteration ? parseInt(filters.iteration, 10) : undefined,
|
iteration: filters.iteration ? parseInt(filters.iteration, 10) : undefined,
|
||||||
utteranceId: filters.utteranceId || undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await getDatasetEntries(datasetId, pageNum, filterParams);
|
const result = await getDatasetEntries(datasetId, pageNum, filterParams);
|
||||||
@@ -112,7 +107,6 @@ export default function DatasetEntriesList({
|
|||||||
modelName: filters.modelName || undefined,
|
modelName: filters.modelName || undefined,
|
||||||
dialect: filters.dialect || undefined,
|
dialect: filters.dialect || undefined,
|
||||||
iteration: filters.iteration ? parseInt(filters.iteration, 10) : undefined,
|
iteration: filters.iteration ? parseInt(filters.iteration, 10) : undefined,
|
||||||
utteranceId: filters.utteranceId || undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await getDatasetEntries(datasetId, page + 1, filterParams);
|
const result = await getDatasetEntries(datasetId, page + 1, filterParams);
|
||||||
@@ -140,7 +134,6 @@ export default function DatasetEntriesList({
|
|||||||
modelName: '',
|
modelName: '',
|
||||||
dialect: '',
|
dialect: '',
|
||||||
iteration: '',
|
iteration: '',
|
||||||
utteranceId: '',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,23 +222,6 @@ export default function DatasetEntriesList({
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Utterance ID
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
value={filters.utteranceId}
|
|
||||||
onChange={(e) => handleFilterChange('utteranceId', e.target.value)}
|
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
>
|
|
||||||
<option value="">All utterances</option>
|
|
||||||
{filterOptions.utteranceIds.map((id) => (
|
|
||||||
<option key={id} value={id}>
|
|
||||||
{id}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -338,23 +314,6 @@ export default function DatasetEntriesList({
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Utterance ID
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
value={filters.utteranceId}
|
|
||||||
onChange={(e) => handleFilterChange('utteranceId', e.target.value)}
|
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
>
|
|
||||||
<option value="">All utterances</option>
|
|
||||||
{filterOptions.utteranceIds.map((id) => (
|
|
||||||
<option key={id} value={id}>
|
|
||||||
{id}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -373,7 +332,6 @@ export default function DatasetEntriesList({
|
|||||||
<th className="text-left py-3 px-4 font-semibold text-gray-700">Speaker ID</th>
|
<th className="text-left py-3 px-4 font-semibold text-gray-700">Speaker ID</th>
|
||||||
<th className="text-left py-3 px-4 font-semibold text-gray-700">Model Name</th>
|
<th className="text-left py-3 px-4 font-semibold text-gray-700">Model Name</th>
|
||||||
<th className="text-left py-3 px-4 font-semibold text-gray-700">Dialect</th>
|
<th className="text-left py-3 px-4 font-semibold text-gray-700">Dialect</th>
|
||||||
<th className="text-left py-3 px-4 font-semibold text-gray-700">Utterance ID</th>
|
|
||||||
<th className="text-left py-3 px-4 font-semibold text-gray-700">File Name</th>
|
<th className="text-left py-3 px-4 font-semibold text-gray-700">File Name</th>
|
||||||
<th className="text-left py-3 px-4 font-semibold text-gray-700">Iteration</th>
|
<th className="text-left py-3 px-4 font-semibold text-gray-700">Iteration</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -395,7 +353,6 @@ export default function DatasetEntriesList({
|
|||||||
<td className="py-3 px-4">{entry.speakerId}</td>
|
<td className="py-3 px-4">{entry.speakerId}</td>
|
||||||
<td className="py-3 px-4">{entry.modelName}</td>
|
<td className="py-3 px-4">{entry.modelName}</td>
|
||||||
<td className="py-3 px-4">{entry.dialect}</td>
|
<td className="py-3 px-4">{entry.dialect}</td>
|
||||||
<td className="py-3 px-4">{entry.utteranceId || '-'}</td>
|
|
||||||
<td className="py-3 px-4 truncate">{entry.fileName}</td>
|
<td className="py-3 px-4 truncate">{entry.fileName}</td>
|
||||||
<td className="py-3 px-4">{entry.iteration}</td>
|
<td className="py-3 px-4">{entry.iteration}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,215 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { uploadDatasetUtterances } from '@/app/actions/upload-utterances';
|
|
||||||
import Modal from '@/components/Modal';
|
|
||||||
|
|
||||||
interface UploadUtterancesModalProps {
|
|
||||||
datasetId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function UploadUtterancesModal({
|
|
||||||
datasetId,
|
|
||||||
}: UploadUtterancesModalProps) {
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [file, setFile] = useState<File | null>(null);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
const [success, setSuccess] = useState(false);
|
|
||||||
const [utterancesCreated, setUtterancesCreated] = useState(0);
|
|
||||||
const [status, setStatus] = useState<string>('');
|
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
|
||||||
|
|
||||||
function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
|
|
||||||
const selectedFile = e.target.files?.[0];
|
|
||||||
if (selectedFile) {
|
|
||||||
setFile(selectedFile);
|
|
||||||
setError(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragOver(e: React.DragEvent<HTMLDivElement>) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
setIsDragging(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragLeave(e: React.DragEvent<HTMLDivElement>) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
setIsDragging(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDrop(e: React.DragEvent<HTMLDivElement>) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
setIsDragging(false);
|
|
||||||
|
|
||||||
const droppedFiles = e.dataTransfer.files;
|
|
||||||
if (droppedFiles.length > 0) {
|
|
||||||
const droppedFile = droppedFiles[0];
|
|
||||||
if (droppedFile.name.endsWith('.csv')) {
|
|
||||||
setFile(droppedFile);
|
|
||||||
setError(null);
|
|
||||||
} else {
|
|
||||||
setError('Please upload a CSV file');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
setError(null);
|
|
||||||
setSuccess(false);
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
setError('Please select a CSV file');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
setStatus('Preparing upload...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file', file);
|
|
||||||
|
|
||||||
const result = await uploadDatasetUtterances(datasetId, formData);
|
|
||||||
|
|
||||||
setSuccess(true);
|
|
||||||
setUtterancesCreated(result.utterancesCreated);
|
|
||||||
setStatus(`Successfully created ${result.utterancesCreated} utterances!`);
|
|
||||||
setFile(null);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
setSuccess(false);
|
|
||||||
setIsOpen(false);
|
|
||||||
setStatus('');
|
|
||||||
window.location.reload();
|
|
||||||
}, 2500);
|
|
||||||
} catch (err) {
|
|
||||||
const errorMsg =
|
|
||||||
err instanceof Error ? err.message : 'Failed to upload utterances';
|
|
||||||
setError(errorMsg);
|
|
||||||
setStatus('');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClose() {
|
|
||||||
if (!loading) {
|
|
||||||
setIsOpen(false);
|
|
||||||
setFile(null);
|
|
||||||
setError(null);
|
|
||||||
setSuccess(false);
|
|
||||||
setStatus('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
onClick={() => setIsOpen(true)}
|
|
||||||
className="px-4 py-2 bg-purple-500 text-white rounded-md hover:bg-purple-600 transition-colors"
|
|
||||||
>
|
|
||||||
📝 Upload Utterances
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={handleClose}
|
|
||||||
title="Upload Utterances CSV"
|
|
||||||
actions={[
|
|
||||||
{
|
|
||||||
label: 'Cancel',
|
|
||||||
onClick: handleClose,
|
|
||||||
variant: 'secondary',
|
|
||||||
disabled: loading,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: loading ? 'Uploading...' : 'Upload',
|
|
||||||
onClick: () => {
|
|
||||||
const form = document.getElementById(
|
|
||||||
'uploadUtterancesForm'
|
|
||||||
) as HTMLFormElement;
|
|
||||||
form?.dispatchEvent(new Event('submit', { bubbles: true }));
|
|
||||||
},
|
|
||||||
variant: 'primary',
|
|
||||||
disabled: loading || !file,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<form id="uploadUtterancesForm" onSubmit={handleSubmit} className="space-y-4">
|
|
||||||
<div>
|
|
||||||
<label htmlFor="utterances-file" className="block text-sm font-medium mb-2">
|
|
||||||
CSV File
|
|
||||||
</label>
|
|
||||||
<div
|
|
||||||
className={`border-2 border-dashed rounded-lg p-6 text-center transition-colors ${
|
|
||||||
isDragging ? 'border-blue-400 bg-blue-50' : 'border-gray-300'
|
|
||||||
}`}
|
|
||||||
onDragOver={handleDragOver}
|
|
||||||
onDragLeave={handleDragLeave}
|
|
||||||
onDrop={handleDrop}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
id="utterances-file"
|
|
||||||
type="file"
|
|
||||||
accept=".csv"
|
|
||||||
onChange={handleFileChange}
|
|
||||||
disabled={loading}
|
|
||||||
className="hidden"
|
|
||||||
/>
|
|
||||||
<label htmlFor="utterances-file" className="cursor-pointer block">
|
|
||||||
{file ? (
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-blue-600">
|
|
||||||
{file.name}
|
|
||||||
</p>
|
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
|
||||||
{(file.size / 1024).toFixed(2)} KB
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-gray-700">
|
|
||||||
Click to select or drag and drop
|
|
||||||
</p>
|
|
||||||
<p className="text-xs text-gray-500 mt-1">CSV file only</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 text-sm text-blue-800">
|
|
||||||
<p className="font-semibold mb-2">CSV file should contain:</p>
|
|
||||||
<ul className="list-disc list-inside space-y-1 text-xs">
|
|
||||||
<li>
|
|
||||||
<code className="bg-blue-100 px-1 rounded">id</code> column with
|
|
||||||
utterance IDs (e.g., utt_001)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<code className="bg-blue-100 px-1 rounded">text</code> column with the
|
|
||||||
utterance text
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{error && (
|
|
||||||
<div className="p-3 bg-red-100 border border-red-400 text-red-700 rounded text-sm">
|
|
||||||
{error}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{success && (
|
|
||||||
<div className="p-3 bg-green-100 border border-green-400 text-green-700 rounded text-sm">
|
|
||||||
{status}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
import { betterAuth } from "better-auth";
|
import { betterAuth } from "better-auth";
|
||||||
import Database from "better-sqlite3";
|
|
||||||
import { localization } from "better-auth-localization";
|
import { localization } from "better-auth-localization";
|
||||||
|
import { drizzleAdapter } from "@better-auth/drizzle-adapter";
|
||||||
|
import db from "./db";
|
||||||
|
|
||||||
|
|
||||||
export const auth = betterAuth({
|
export const auth = betterAuth({
|
||||||
database: new Database("./sqlite_users.db"),
|
database: drizzleAdapter(db, {
|
||||||
|
provider: "sqlite",
|
||||||
|
}),
|
||||||
emailAndPassword: {
|
emailAndPassword: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
|
|||||||
107
src/lib/model/auth-schema.ts
Normal file
107
src/lib/model/auth-schema.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { relations, sql } from "drizzle-orm";
|
||||||
|
import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
|
||||||
|
|
||||||
|
export const user = sqliteTable("user", {
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
email: text("email").notNull().unique(),
|
||||||
|
emailVerified: integer("email_verified", { mode: "boolean" })
|
||||||
|
.default(false)
|
||||||
|
.notNull(),
|
||||||
|
image: text("image"),
|
||||||
|
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
||||||
|
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
||||||
|
.notNull(),
|
||||||
|
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
|
||||||
|
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
||||||
|
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||||
|
.notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const session = sqliteTable(
|
||||||
|
"session",
|
||||||
|
{
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
|
||||||
|
token: text("token").notNull().unique(),
|
||||||
|
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
||||||
|
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
||||||
|
.notNull(),
|
||||||
|
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
|
||||||
|
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||||
|
.notNull(),
|
||||||
|
ipAddress: text("ip_address"),
|
||||||
|
userAgent: text("user_agent"),
|
||||||
|
userId: text("user_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => user.id, { onDelete: "cascade" }),
|
||||||
|
},
|
||||||
|
(table) => [index("session_userId_idx").on(table.userId)],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const account = sqliteTable(
|
||||||
|
"account",
|
||||||
|
{
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
accountId: text("account_id").notNull(),
|
||||||
|
providerId: text("provider_id").notNull(),
|
||||||
|
userId: text("user_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => user.id, { onDelete: "cascade" }),
|
||||||
|
accessToken: text("access_token"),
|
||||||
|
refreshToken: text("refresh_token"),
|
||||||
|
idToken: text("id_token"),
|
||||||
|
accessTokenExpiresAt: integer("access_token_expires_at", {
|
||||||
|
mode: "timestamp_ms",
|
||||||
|
}),
|
||||||
|
refreshTokenExpiresAt: integer("refresh_token_expires_at", {
|
||||||
|
mode: "timestamp_ms",
|
||||||
|
}),
|
||||||
|
scope: text("scope"),
|
||||||
|
password: text("password"),
|
||||||
|
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
||||||
|
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
||||||
|
.notNull(),
|
||||||
|
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
|
||||||
|
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(table) => [index("account_userId_idx").on(table.userId)],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const verification = sqliteTable(
|
||||||
|
"verification",
|
||||||
|
{
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
identifier: text("identifier").notNull(),
|
||||||
|
value: text("value").notNull(),
|
||||||
|
expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
|
||||||
|
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
||||||
|
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
||||||
|
.notNull(),
|
||||||
|
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
|
||||||
|
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
||||||
|
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(table) => [index("verification_identifier_idx").on(table.identifier)],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const userRelations = relations(user, ({ many }) => ({
|
||||||
|
sessions: many(session),
|
||||||
|
accounts: many(account),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const sessionRelations = relations(session, ({ one }) => ({
|
||||||
|
user: one(user, {
|
||||||
|
fields: [session.userId],
|
||||||
|
references: [user.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const accountRelations = relations(account, ({ one }) => ({
|
||||||
|
user: one(user, {
|
||||||
|
fields: [account.userId],
|
||||||
|
references: [user.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import { sqliteTable, text, integer, real } from 'drizzle-orm/sqlite-core';
|
import { sqliteTable, text, integer, real } from 'drizzle-orm/sqlite-core';
|
||||||
import { relations } from 'drizzle-orm';
|
import { relations } from 'drizzle-orm';
|
||||||
import { dataset } from './dataset';
|
import { dataset } from './dataset';
|
||||||
import { dataset_utterance } from './utterance';
|
|
||||||
|
|
||||||
// Define the dataset table schema
|
|
||||||
export const dataset_entry = sqliteTable('dataset_entry', {
|
export const dataset_entry = sqliteTable('dataset_entry', {
|
||||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
externalId: text('external_id').notNull(),
|
externalId: text('external_id').notNull(),
|
||||||
@@ -12,7 +10,6 @@ export const dataset_entry = sqliteTable('dataset_entry', {
|
|||||||
datasetId: integer('dataset_id')
|
datasetId: integer('dataset_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => dataset.id),
|
.references(() => dataset.id),
|
||||||
utteranceId: text('utterance_id').references(() => dataset_utterance.id),
|
|
||||||
fileName: text('file_name').notNull(),
|
fileName: text('file_name').notNull(),
|
||||||
dialect: text('dialect').notNull(),
|
dialect: text('dialect').notNull(),
|
||||||
iteration: integer('iteration').notNull(),
|
iteration: integer('iteration').notNull(),
|
||||||
@@ -28,10 +25,6 @@ export const dataset_entryRelations = relations(dataset_entry, ({ one }) => ({
|
|||||||
dataset: one(dataset, {
|
dataset: one(dataset, {
|
||||||
fields: [dataset_entry.datasetId],
|
fields: [dataset_entry.datasetId],
|
||||||
references: [dataset.id]
|
references: [dataset.id]
|
||||||
}),
|
|
||||||
utterance: one(dataset_utterance, {
|
|
||||||
fields: [dataset_entry.utteranceId],
|
|
||||||
references: [dataset_utterance.id]
|
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export * from "./dataset";
|
export * from "./dataset";
|
||||||
export * from "./dataset_entry";
|
export * from "./dataset_entry";
|
||||||
export * from "./utterance";
|
export * from "./auth-schema";
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
|
|
||||||
import { relations } from 'drizzle-orm';
|
|
||||||
import { dataset } from './dataset';
|
|
||||||
|
|
||||||
// Define the dataset_utterance table schema
|
|
||||||
export const dataset_utterance = sqliteTable('dataset_utterance', {
|
|
||||||
id: text('id').primaryKey(),
|
|
||||||
datasetId: integer('dataset_id')
|
|
||||||
.notNull()
|
|
||||||
.references(() => dataset.id),
|
|
||||||
text: text('text').notNull(),
|
|
||||||
createdAt: integer('created_at', { mode: 'timestamp' })
|
|
||||||
.notNull()
|
|
||||||
.default(new Date()),
|
|
||||||
updatedAt: integer('updated_at', { mode: 'timestamp' })
|
|
||||||
.notNull()
|
|
||||||
.default(new Date()),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const dataset_utteranceRelations = relations(dataset_utterance, ({ one }) => ({
|
|
||||||
dataset: one(dataset, {
|
|
||||||
fields: [dataset_utterance.datasetId],
|
|
||||||
references: [dataset.id],
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Create types for dataset_utterance records based on the schema
|
|
||||||
export type DatasetUtterance = typeof dataset_utterance.$inferSelect;
|
|
||||||
export type NewDatasetUtterance = typeof dataset_utterance.$inferInsert;
|
|
||||||
Reference in New Issue
Block a user