galleria_photo_face_rknn_op.../db/init.js.ok
2026-10-03 12:16:58 +02:00

1296 lines
32 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// db/init.js
const db = require('./knex');
const {
ensureFaceScanAutomation,
} = require('./faceScanAutomation');
/* ============================================================
FOTO
============================================================ */
async function ensurePhotos() {
const exists = await db.schema.hasTable('photos');
if (!exists) {
await db.schema.createTable('photos', (t) => {
t.string('id').primary();
t.string('user');
t.string('cartella');
t.string('name');
t.string('path');
t.text('extPath').notNullable().defaultTo('');
t.text('extMedia').notNullable().defaultTo('');
t.integer('hybrid').notNullable().defaultTo(0);
t.string('thub1');
t.string('thub2');
t.string('cast');
t.string('mime_type');
t.integer('width');
t.integer('height');
t.integer('rotation');
t.integer('size_bytes');
t.integer('mtimeMs');
t.integer('duration_ms');
t.string('taken_at');
t.string('data');
t.float('lat');
t.float('lon');
t.float('alt');
t.text('location');
t.string('_indexHash');
t.string('fast_hash');
t.datetime('created_at').defaultTo(db.fn.now());
t.datetime('updated_at').defaultTo(db.fn.now());
t.datetime('deleted_at').nullable();
});
console.log("✔ photos creata");
} else {
const cols = [
'thub1','thub2','fast_hash','created_at','updated_at','deleted_at'
];
for (const col of cols) {
const existsCol = await db.schema.hasColumn('photos', col);
if (!existsCol) {
await db.schema.alterTable('photos', (t) => {
if (col === 'created_at' || col === 'updated_at')
t.datetime(col).defaultTo(db.fn.now());
else if (col === 'deleted_at')
t.datetime(col).nullable();
else
t.string(col);
});
console.log(`✔ aggiunta colonna photos.${col}`);
}
}
}
}
/* ============================================================
FOTO — deleted_hard
============================================================ */
async function ensureDeletedHard() {
const exists = await db.schema.hasTable('deleted_hard');
if (!exists) {
await db.schema.createTable('deleted_hard', (t) => {
t.string('id').primary();
t.string('user');
t.datetime('deleted_at').defaultTo(db.fn.now());
});
console.log("✔ deleted_hard creata");
}
}
/* ============================================================
WS
============================================================ */
async function ensureWsSessions() {
const exists = await db.schema.hasTable('ws_sessions');
if (!exists) {
await db.schema.createTable('ws_sessions', (t) => {
t.string('session_id').notNullable();
t.string('device_id').notNullable();
t.string('user').notNullable();
t.integer('connected_at').notNullable();
t.integer('last_ack').notNullable();
t.integer('last_sync').nullable();
t.boolean('need_full_sync').notNullable().defaultTo(false);
t.primary(['session_id', 'device_id']);
});
console.log("✔ ws_sessions creata");
} else {
const cols = [
{ name: 'device_id', type: 'string' },
{ name: 'last_sync', type: 'integer' }
];
for (const c of cols) {
const existsCol = await db.schema.hasColumn('ws_sessions', c.name);
if (!existsCol) {
await db.schema.alterTable('ws_sessions', (t) => {
if (c.type === 'string') t.string(c.name).notNullable().defaultTo('default');
if (c.type === 'integer') t.integer(c.name).nullable();
});
console.log(`✔ aggiunta colonna ws_sessions.${c.name}`);
}
}
}
}
async function ensureWsPending() {
const exists = await db.schema.hasTable('ws_pending_events');
if (!exists) {
await db.schema.createTable('ws_pending_events', (t) => {
t.string('event_id').primary();
t.string('session_id').notNullable();
t.text('payload').notNullable();
t.integer('sent_at').notNullable();
t.integer('retries').notNullable().defaultTo(0);
});
console.log("✔ ws_pending_events creata");
}
}
async function ensurePendingEvents() {
const exists = await db.schema.hasTable('pending_events');
if (!exists) {
await db.schema.createTable('pending_events', (t) => {
t.increments('id').primary();
t.string('user_id').notNullable();
t.string('session_id').notNullable();
t.string('device_id').notNullable();
t.string('event_id').notNullable();
t.text('payload').notNullable();
t.integer('created_at').notNullable();
t.unique(['user_id', 'session_id', 'device_id', 'event_id']);
});
console.log("✔ pending_events creata");
}
}
/* ============================================================
🏃 ATTIVITÀ — nuova tabella
============================================================ */
async function ensureActivities() {
const exists = await db.schema.hasTable('activities');
if (!exists) {
await db.schema.createTable('activities', (t) => {
t.string('id').primary();
t.string('user');
t.string('cartella');
t.string('file_name');
t.string('file_extension');
t.string('format');
t.string('display_name');
t.string('route_name');
t.string('title');
t.string('description');
t.string('notes');
t.string('sport');
t.string('activity_type');
t.string('device_manufacturer');
t.string('device_model');
t.string('source_app');
t.bigInteger('start_time_millis');
t.bigInteger('end_time_millis');
t.bigInteger('duration_millis');
t.float('distance_meters');
t.float('elevation_gain_meters');
t.float('elevation_loss_meters');
t.float('min_elevation_meters');
t.float('max_elevation_meters');
t.float('file_distance_meters');
t.float('file_elevation_gain_meters');
t.float('computed_distance_meters');
t.float('computed_elevation_gain_meters');
t.float('average_speed_mps');
t.float('max_speed_mps');
t.float('average_pace_sec_per_km');
t.integer('average_heart_rate');
t.integer('max_heart_rate');
t.integer('average_cadence');
t.integer('max_cadence');
t.integer('calories');
t.integer('power_avg');
t.integer('power_max');
t.integer('point_count');
t.integer('segment_count');
t.integer('lap_count');
t.float('min_latitude');
t.float('max_latitude');
t.float('min_longitude');
t.float('max_longitude');
t.float('center_latitude');
t.float('center_longitude');
t.bigInteger('size_bytes');
t.bigInteger('last_modified_millis');
t.bigInteger('date_added_millis');
t.bigInteger('date_modified_millis');
t.bigInteger('last_scan_millis');
t.boolean('is_favorite');
t.boolean('is_hidden');
t.integer('rating');
t.text('tags');
t.text('track_preview');
t.string('scan_status');
t.text('scan_error');
t.string('path');
t.text('extPath').notNullable().defaultTo('');
t.integer('hybrid').notNullable().defaultTo(0);
t.text('diagnostics');
t.text('location');
t.string('fast_hash');
t.datetime('deleted_at').nullable();
t.datetime('created_at').defaultTo(db.fn.now());
t.datetime('updated_at').defaultTo(db.fn.now());
});
console.log("✔ activities creata");
}
}
/* ============================================================
GEOCODING — coda asincrona
============================================================ */
async function ensureGeocodeJobs() {
const exists = await db.schema.hasTable('geocode_jobs');
if (!exists) {
await db.schema.createTable('geocode_jobs', (t) => {
t.string('id').primary();
t.string('entity_type').notNullable();
t.string('entity_id').notNullable();
t.float('lat').notNullable();
t.float('lon').notNullable();
t.string('status')
.notNullable()
.defaultTo('pending');
t.integer('attempts')
.notNullable()
.defaultTo(0);
t.text('last_error');
t.datetime('started_at');
t.datetime('completed_at');
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
t.index(
['status'],
'idx_geocode_jobs_status'
);
t.index(
['entity_type', 'entity_id'],
'idx_geocode_jobs_entity'
);
});
console.log("✔ geocode_jobs creata");
}
}
/* ============================================================
GEOCODING — cache condivisa
============================================================ */
async function ensureGeocodeCache() {
const exists = await db.schema.hasTable('geocode_cache');
if (!exists) {
await db.schema.createTable('geocode_cache', (t) => {
t.string('cache_key').primary();
t.float('lat').notNullable();
t.float('lon').notNullable();
t.float('alt');
t.text('location');
t.integer('hits')
.notNullable()
.defaultTo(0);
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
});
console.log("✔ geocode_cache creata");
}
}
/* ============================================================
ATTIVITÀ — deleted_hard
============================================================ */
async function ensureDeletedHardActivities() {
const exists = await db.schema.hasTable('deleted_hard_activities');
if (!exists) {
await db.schema.createTable('deleted_hard_activities', (t) => {
t.string('id').primary();
t.string('user');
t.datetime('deleted_at').defaultTo(db.fn.now());
});
console.log("✔ deleted_hard_activities creata");
}
}
/* ============================================================
ATTIVITÀ — sync_done
============================================================ */
async function ensureSyncDoneActivities() {
const exists = await db.schema.hasTable('sync_done_activities');
if (!exists) {
await db.schema.createTable('sync_done_activities', (t) => {
t.string('session_id');
t.string('device_id');
t.bigInteger('last_sync');
});
console.log("✔ sync_done_activities creata");
}
}
/* ============================================================
VOLTI
============================================================ */
async function ensureFaces() {
const exists = await db.schema.hasTable('faces');
if (!exists) {
await db.schema.createTable('faces', (t) => {
// ID univoco del volto
t.string('id').primary();
// Foto da cui è stato estratto
t.string('photo_id').notNullable();
t.integer('face_index').notNullable();
// Bounding box nel frame originale
t.float('x').notNullable();
t.float('y').notNullable();
t.float('width').notNullable();
t.float('height').notNullable();
// Confidence del detector SCRFD
t.float('score').notNullable();
// 5 landmark × 2 coordinate
t.float('landmark_0_x');
t.float('landmark_0_y');
t.float('landmark_1_x');
t.float('landmark_1_y');
t.float('landmark_2_x');
t.float('landmark_2_y');
t.float('landmark_3_x');
t.float('landmark_3_y');
t.float('landmark_4_x');
t.float('landmark_4_y');
// 128 × float32 = 512 byte
t.binary('embedding').notNullable();
// Risultato HDBSCAN.
// NULL = non ancora clusterizzato.
t.string('cluster_id').nullable();
// Persona assegnata dall'utente.
// NULL = identità non ancora assegnata.
t.string('person_id').nullable();
// Stato applicativo del volto.
// detected / clustered / assigned
t.string('status').notNullable().defaultTo('detected');
t.datetime('created_at').defaultTo(db.fn.now());
t.datetime('updated_at').defaultTo(db.fn.now());
// Indici principali
t.index(['photo_id']);
t.index(['cluster_id']);
t.index(['person_id']);
t.index(['status']);
t.unique(['photo_id', 'face_index']);
// Evita due record per lo stesso volto nella stessa foto
// solo se il nostro scanner assegnerà un face_index.
});
console.log("✔ faces creata");
}
}
/* ============================================================
CODA SCANSIONE VOLTI
============================================================ */
async function ensureFaceScanJobs() {
const exists =
await db.schema.hasTable('face_scan_jobs');
if (!exists) {
await db.schema.createTable(
'face_scan_jobs',
(t) => {
t.string('photo_id').primary();
// pending / processing / done / error
t.string('status')
.notNullable()
.defaultTo('pending');
t.integer('attempts')
.notNullable()
.defaultTo(0);
t.integer('rescan_requested')
.notNullable()
.defaultTo(0);
t.integer('faces_detected').nullable();
t.integer('execution_ms').nullable();
t.integer('applied_rotation').nullable();
t.text('last_error').nullable();
t.datetime('started_at').nullable();
t.datetime('completed_at').nullable();
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
t.index(['status']);
t.index(['updated_at']);
},
);
console.log("✔ face_scan_jobs creata");
} else {
const hasRescanRequested =
await db.schema.hasColumn(
'face_scan_jobs',
'rescan_requested',
);
if (!hasRescanRequested) {
await db.schema.alterTable(
'face_scan_jobs',
(t) => {
t.integer('rescan_requested')
.notNullable()
.defaultTo(0);
},
);
console.log(
"✔ aggiunta face_scan_jobs.rescan_requested",
);
}
}
}
/* ============================================================
CLUSTER HDBSCAN
============================================================ */
async function ensureFaceClusters() {
const exists = await db.schema.hasTable('face_clusters');
if (!exists) {
await db.schema.createTable('face_clusters', (t) => {
// ID persistente del cluster
t.string('id').primary();
// Identifica una specifica esecuzione di HDBSCAN.
// Esempio: "initial-2026-09-23T04:20:00"
t.string('run_id').notNullable();
// Label numerica prodotta da HDBSCAN.
// Può essere -1 per noise.
t.integer('label').notNullable();
// Numero di facce nel cluster
t.integer('size').notNullable().defaultTo(0);
// Parametri principali usati per generare il cluster
t.integer('min_samples');
t.integer('min_cluster_size');
// NULL se il cluster non è ancora stato
// associato ad una persona.
t.string('person_id').nullable();
t.datetime('created_at').defaultTo(db.fn.now());
t.datetime('updated_at').defaultTo(db.fn.now());
t.index(['run_id']);
t.index(['person_id']);
// Un label può comparire una sola volta
// all'interno dello stesso run.
t.unique(['run_id', 'label']);
});
console.log("✔ face_clusters creata");
}
}
/* ============================================================
RUN CLUSTERING VOLTI
============================================================ */
async function ensureFaceClusterRuns() {
const exists =
await db.schema.hasTable(
'face_cluster_runs',
);
if (!exists) {
await db.schema.createTable(
'face_cluster_runs',
(t) => {
// Identificativo stabile della run.
t.string('id').primary();
// Proprietario della libreria fotografica.
t.string('user')
.notNullable();
// pending / processing / done / error
t.string('status')
.notNullable()
.defaultTo('pending');
// all / unclustered / unknown
t.string('scope')
.notNullable()
.defaultTo('all');
t.integer('embedding_count')
.notNullable()
.defaultTo(0);
t.integer('min_cluster_size')
.notNullable();
t.integer('min_samples')
.notNullable();
t.string('metric')
.notNullable()
.defaultTo('euclidean');
// eom / leaf
t.string('selection_method')
.notNullable()
.defaultTo('eom');
// sklearn / native / opencl-native
t.string('engine')
.notNullable()
.defaultTo('sklearn');
t.string('engine_version')
.nullable();
t.integer('cluster_count')
.nullable();
t.integer('clustered_faces')
.nullable();
t.integer('noise_faces')
.nullable();
t.float('noise_percent')
.nullable();
t.text('last_error')
.nullable();
t.datetime('started_at')
.nullable();
t.datetime('completed_at')
.nullable();
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
t.index(['user']);
t.index(['status']);
t.index(['user', 'created_at']);
},
);
console.log(
"✔ face_cluster_runs creata",
);
}
}
/* ============================================================
APPARTENENZE DEI VOLTI AI CLUSTER
============================================================ */
async function ensureFaceClusterMemberships() {
const exists =
await db.schema.hasTable(
'face_cluster_memberships',
);
if (!exists) {
await db.schema.createTable(
'face_cluster_memberships',
(t) => {
// Identificativo stabile della membership.
t.string('id').primary();
// Run HDBSCAN che ha prodotto il risultato.
t.string('run_id')
.notNullable();
// NULL per i volti classificati come noise.
t.string('cluster_id')
.nullable();
t.string('face_id')
.notNullable();
// Label originale HDBSCAN.
// -1 indica noise.
t.integer('label')
.notNullable();
// Probabilità di appartenenza HDBSCAN.
t.float('probability')
.notNullable()
.defaultTo(0);
t.integer('noise_flag')
.notNullable()
.defaultTo(0);
// suggested / accepted / excluded
t.string('review_status')
.notNullable()
.defaultTo('suggested');
t.string('reviewed_by')
.nullable();
t.datetime('reviewed_at')
.nullable();
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
// Un volto compare una sola volta per run.
t.unique([
'run_id',
'face_id',
]);
t.index(['run_id']);
t.index(['cluster_id']);
t.index(['face_id']);
t.index(['review_status']);
t.index([
'run_id',
'label',
]);
},
);
console.log(
"✔ face_cluster_memberships creata",
);
}
}
/* ============================================================
ESCLUSIONI MANUALI PERSONA-VOLTO
============================================================ */
async function ensurePersonFaceExclusions() {
const exists =
await db.schema.hasTable(
'person_face_exclusions',
);
if (!exists) {
await db.schema.createTable(
'person_face_exclusions',
(t) => {
// Persona dalla quale il volto è stato escluso.
t.string('person_id')
.notNullable();
t.string('face_id')
.notNullable();
// Proprietario della libreria.
t.string('user')
.notNullable();
t.text('reason')
.nullable();
t.string('created_by')
.nullable();
t.datetime('created_at')
.defaultTo(db.fn.now());
t.primary([
'person_id',
'face_id',
]);
t.index(['user']);
t.index(['face_id']);
},
);
console.log(
"✔ person_face_exclusions creata",
);
}
}
/* ============================================================
PERSONE
============================================================ */
async function ensurePersons() {
const exists = await db.schema.hasTable('persons');
if (!exists) {
await db.schema.createTable('persons', (t) => {
// ID stabile dell'identità applicativa
t.string('id').primary();
// Utente proprietario della libreria
t.string('user').nullable();
// Nome assegnato dall'utente
t.string('name').notNullable();
t.datetime('created_at').defaultTo(db.fn.now());
t.datetime('updated_at').defaultTo(db.fn.now());
t.index(['user']);
t.index(['name']);
});
console.log("✔ persons creata");
}
}
/* ============================================================
PROTOTYPE ATTIVI DELLE PERSONE
============================================================ */
async function ensurePersonFacePrototypes() {
const exists =
await db.schema.hasTable(
'person_face_prototypes',
);
if (!exists) {
await db.schema.createTable(
'person_face_prototypes',
(t) => {
// Persona rappresentata dal prototype.
t.string('person_id')
.notNullable();
// Volto reale usato come riferimento.
t.string('face_id')
.notNullable();
// Proprietario della libreria.
t.string('user')
.notNullable();
// cluster / noise / manual
t.string('source_type')
.notNullable();
// cluster_medoid /
// greedy_leave_one_out_gain /
// manual
t.string('selection_method')
.notNullable();
// Ordine nel set dei prototype.
t.integer('selection_order')
.nullable();
t.float('detector_score')
.nullable();
t.float('quality_score')
.nullable();
// 1 = usato nell'indice corrente.
t.integer('active')
.notNullable()
.defaultTo(1);
// 1 = prototype protetto manualmente.
// Il ricalcolo automatico non lo disattiva.
t.integer('locked')
.notNullable()
.defaultTo(0);
// Nome o versione dell'esperimento.
t.string('experiment_name')
.nullable();
// Metriche e metadati in formato JSON.
t.text('metrics_json')
.nullable();
t.string('created_by')
.nullable();
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
t.primary([
'person_id',
'face_id',
]);
t.index(['user']);
t.index(['person_id']);
t.index(['face_id']);
t.index(['active']);
t.index([
'user',
'active',
]);
},
);
console.log(
"✔ person_face_prototypes creata",
);
} else {
const hasLocked =
await db.schema.hasColumn(
'person_face_prototypes',
'locked',
);
if (!hasLocked) {
await db.schema.alterTable(
'person_face_prototypes',
(t) => {
t.integer('locked')
.notNullable()
.defaultTo(0);
},
);
console.log(
"✔ aggiunta person_face_prototypes.locked",
);
}
}
}
/* ============================================================
VOLTI CONFERMATI NON ADATTI COME RIFERIMENTO
============================================================ */
async function ensurePersonFaceReferenceExclusions() {
const exists =
await db.schema.hasTable(
'person_face_reference_exclusions',
);
if (!exists) {
await db.schema.createTable(
'person_face_reference_exclusions',
(t) => {
// La persona rimane confermata.
t.string('person_id')
.notNullable();
// Il volto non deve entrare
// nell'indice di riconoscimento.
t.string('face_id')
.notNullable();
// Proprietario della libreria.
t.string('user')
.notNullable();
// Esempi:
// occlusion_extreme_pose
// blur
// face_too_small
// bad_crop
// manual
t.string('reason_code')
.notNullable();
t.text('note')
.nullable();
t.string('created_by')
.nullable();
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
t.primary([
'person_id',
'face_id',
]);
t.index(['user']);
t.index(['person_id']);
t.index(['face_id']);
t.index(['reason_code']);
},
);
console.log(
"✔ person_face_reference_exclusions creata",
);
}
}
/* ============================================================
STATO DELL'INDICE DI RICONOSCIMENTO
============================================================ */
async function ensureFaceRecognitionIndexState() {
const exists =
await db.schema.hasTable(
'face_recognition_index_state',
);
if (!exists) {
await db.schema.createTable(
'face_recognition_index_state',
(t) => {
// Una riga per proprietario
// della libreria.
t.string('user')
.primary();
// missing / dirty / building /
// ready / error
t.string('status')
.notNullable()
.defaultTo('missing');
// 1 se l'indice deve essere
// ricostruito.
t.integer('dirty')
.notNullable()
.defaultTo(1);
t.integer('version')
.notNullable()
.defaultTo(0);
t.integer('prototype_count')
.notNullable()
.defaultTo(0);
t.string('index_path')
.nullable();
t.string('metadata_path')
.nullable();
t.datetime('built_at')
.nullable();
t.text('last_error')
.nullable();
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
t.index(['status']);
t.index(['dirty']);
},
);
console.log(
"✔ face_recognition_index_state creata",
);
}
}
/* ============================================================
JOB DI RICALCOLO DEI PROTOTYPE
============================================================ */
async function ensureFacePrototypeJobs() {
const exists =
await db.schema.hasTable(
'face_prototype_jobs',
);
if (!exists) {
await db.schema.createTable(
'face_prototype_jobs',
(t) => {
t.string('id')
.primary();
t.string('user')
.notNullable();
t.string('person_id')
.notNullable();
// pending / processing / done /
// error / superseded
t.string('status')
.notNullable()
.defaultTo('pending');
// Motivo dell'ultimo aggiornamento.
t.string('reason')
.notNullable();
t.string('requested_by')
.nullable();
t.datetime('requested_at')
.notNullable()
.defaultTo(db.fn.now());
t.datetime('started_at')
.nullable();
t.datetime('completed_at')
.nullable();
t.integer('attempts')
.notNullable()
.defaultTo(0);
t.text('last_error')
.nullable();
t.text('result_json')
.nullable();
// Firma dello stato dei volti
// all'inizio dell'elaborazione.
t.string('input_signature')
.nullable();
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
t.index(['user']);
t.index(['person_id']);
t.index(['status']);
t.index([
'user',
'status',
'requested_at',
]);
},
);
console.log(
"✔ face_prototype_jobs creata",
);
}
}
/* ============================================================
VALUTAZIONI DI COPERTURA DEI PROTOTYPE
============================================================ */
async function ensurePersonFaceRecognitionEvaluations() {
const exists =
await db.schema.hasTable(
'person_face_recognition_evaluations',
);
if (!exists) {
await db.schema.createTable(
'person_face_recognition_evaluations',
(t) => {
// Persona rispetto alla quale
// viene valutato il volto.
t.string('person_id')
.notNullable();
t.string('face_id')
.notNullable();
t.string('user')
.notNullable();
// Job prototype che ha prodotto
// questa valutazione.
t.string('prototype_job_id')
.notNullable();
t.integer('prototype_count')
.notNullable();
// Migliore similarità verso
// i prototype attivi, in modalità
// leave-one-out.
t.float('best_similarity')
.nullable();
t.float('top3_mean')
.nullable();
t.string('best_prototype_face_id')
.nullable();
// Soglia visualizzata in galleria.
t.float('recognition_threshold')
.notNullable()
.defaultTo(0.50);
t.integer('recognized')
.notNullable()
.defaultTo(0);
t.datetime('evaluated_at')
.notNullable()
.defaultTo(db.fn.now());
t.datetime('created_at')
.defaultTo(db.fn.now());
t.datetime('updated_at')
.defaultTo(db.fn.now());
t.primary([
'person_id',
'face_id',
]);
t.index(['user']);
t.index(['person_id']);
t.index(['face_id']);
t.index(['prototype_job_id']);
t.index(['recognized']);
t.index([
'person_id',
'recognized',
]);
},
);
console.log(
"✔ person_face_recognition_evaluations creata",
);
}
}
/* ============================================================
INIT
============================================================ */
/* async function init() {
try {
console.log("🔧 init DB...");
await ensurePhotos();
await ensureDeletedHard();
await ensureActivities();
await ensureDeletedHardActivities();
await ensureSyncDoneActivities();
await ensureWsSessions();
await ensureWsPending();
await ensurePendingEvents();
console.log("✅ init completato");
process.exit(0);
} catch (err) {
console.error("❌ errore init:", err);
process.exit(1);
}
}
*/
async function init() {
try {
console.log("🔧 init DB...");
await ensurePhotos();
await ensureDeletedHard();
await ensureActivities();
await ensureGeocodeJobs();
await ensureGeocodeCache();
await ensureDeletedHardActivities();
await ensureSyncDoneActivities();
await ensureWsSessions();
await ensureWsPending();
await ensurePendingEvents();
// Face recognition
await ensureFaces();
await ensureFaceScanJobs();
await ensureFaceScanAutomation(db);
await ensureFaceClusters();
await ensureFaceClusterRuns();
await ensureFaceClusterMemberships();
await ensurePersons();
await ensurePersonFaceExclusions();
await ensurePersonFacePrototypes();
await ensurePersonFaceReferenceExclusions();
await ensureFaceRecognitionIndexState();
await ensureFacePrototypeJobs();
await ensurePersonFaceRecognitionEvaluations();
console.log("✅ init completato");
process.exit(0);
} catch (err) {
console.error("❌ errore init:", err);
process.exit(1);
}
}
init();