| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- import * as SQLite from 'expo-sqlite';
- export const dbPromise = SQLite.openDatabaseAsync('smartfarming.db');
- type ColumnSpec = {
- name: string;
- type: string;
- };
- const userProfileColumns: ColumnSpec[] = [
- { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
- { name: 'name', type: 'TEXT' },
- { name: 'farm_name', type: 'TEXT' },
- { name: 'location', type: 'TEXT' },
- { name: 'photo_uri', type: 'TEXT' },
- { name: 'language', type: 'TEXT' },
- { name: 'currency', type: 'TEXT' },
- { name: 'created_at', type: 'TEXT' },
- { name: 'updated_at', type: 'TEXT' },
- ];
- const fieldColumns: ColumnSpec[] = [
- { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
- { name: 'name', type: 'TEXT' },
- { name: 'area_ha', type: 'REAL' },
- { name: 'boundary_geojson', type: 'TEXT' },
- { name: 'notes', type: 'TEXT' },
- { name: 'photo_uri', type: 'TEXT' },
- { name: 'created_at', type: 'TEXT' },
- { name: 'updated_at', type: 'TEXT' },
- ];
- const cropColumns: ColumnSpec[] = [
- { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
- { name: 'field_id', type: 'INTEGER' },
- { name: 'crop_name', type: 'TEXT' },
- { name: 'variety', type: 'TEXT' },
- { name: 'planting_date', type: 'TEXT' },
- { name: 'expected_harvest_date', type: 'TEXT' },
- { name: 'photo_uri', type: 'TEXT' },
- ];
- const harvestColumns: ColumnSpec[] = [
- { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
- { name: 'field_id', type: 'INTEGER' },
- { name: 'crop_id', type: 'INTEGER' },
- { name: 'harvested_at', type: 'TEXT' },
- { name: 'quantity', type: 'REAL' },
- { name: 'unit', type: 'TEXT' },
- { name: 'notes', type: 'TEXT' },
- { name: 'photo_uri', type: 'TEXT' },
- { name: 'created_at', type: 'TEXT' },
- ];
- const saleColumns: ColumnSpec[] = [
- { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
- { name: 'harvest_id', type: 'INTEGER' },
- { name: 'field_id', type: 'INTEGER' },
- { name: 'crop_id', type: 'INTEGER' },
- { name: 'sold_at', type: 'TEXT' },
- { name: 'quantity', type: 'REAL' },
- { name: 'unit', type: 'TEXT' },
- { name: 'price', type: 'REAL' },
- { name: 'buyer', type: 'TEXT' },
- { name: 'notes', type: 'TEXT' },
- { name: 'created_at', type: 'TEXT' },
- ];
- const costColumns: ColumnSpec[] = [
- { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
- { name: 'field_id', type: 'INTEGER' },
- { name: 'crop_id', type: 'INTEGER' },
- { name: 'category', type: 'TEXT' },
- { name: 'amount', type: 'REAL' },
- { name: 'currency', type: 'TEXT' },
- { name: 'vendor', type: 'TEXT' },
- { name: 'notes', type: 'TEXT' },
- { name: 'spent_at', type: 'TEXT' },
- { name: 'photo_uri', type: 'TEXT' },
- { name: 'created_at', type: 'TEXT' },
- ];
- export async function initCoreTables() {
- const db = await dbPromise;
- await db.execAsync(
- [
- 'CREATE TABLE IF NOT EXISTS user_profile (id INTEGER PRIMARY KEY NOT NULL, name TEXT, farm_name TEXT, location TEXT, photo_uri TEXT, language TEXT, currency TEXT, created_at TEXT, updated_at TEXT);',
- 'CREATE TABLE IF NOT EXISTS fields (id INTEGER PRIMARY KEY NOT NULL, name TEXT, area_ha REAL, boundary_geojson TEXT, notes TEXT, photo_uri TEXT, created_at TEXT, updated_at TEXT);',
- 'CREATE TABLE IF NOT EXISTS crops (id INTEGER PRIMARY KEY NOT NULL, field_id INTEGER, crop_name TEXT, variety TEXT, planting_date TEXT, expected_harvest_date TEXT, photo_uri TEXT, FOREIGN KEY(field_id) REFERENCES fields(id));',
- 'CREATE TABLE IF NOT EXISTS observations (id INTEGER PRIMARY KEY NOT NULL, field_id INTEGER, crop_id INTEGER, obs_type TEXT, note TEXT, severity INTEGER, lat REAL, lng REAL, observed_at TEXT, FOREIGN KEY(field_id) REFERENCES fields(id), FOREIGN KEY(crop_id) REFERENCES crops(id));',
- 'CREATE TABLE IF NOT EXISTS images (id INTEGER PRIMARY KEY NOT NULL, observation_id INTEGER, uri TEXT, thumbnail_uri TEXT, width INTEGER, height INTEGER, created_at TEXT, FOREIGN KEY(observation_id) REFERENCES observations(id));',
- 'CREATE TABLE IF NOT EXISTS ml_inferences (id INTEGER PRIMARY KEY NOT NULL, image_id INTEGER, model_name TEXT, model_version TEXT, top_label TEXT, top_score REAL, scores_json TEXT, created_at TEXT, FOREIGN KEY(image_id) REFERENCES images(id));',
- 'CREATE TABLE IF NOT EXISTS sensors (id INTEGER PRIMARY KEY NOT NULL, name TEXT, type TEXT, unit TEXT, field_id INTEGER, location TEXT, created_at TEXT, FOREIGN KEY(field_id) REFERENCES fields(id));',
- 'CREATE TABLE IF NOT EXISTS sensor_readings (id INTEGER PRIMARY KEY NOT NULL, sensor_id INTEGER, value REAL, observed_at TEXT, FOREIGN KEY(sensor_id) REFERENCES sensors(id));',
- 'CREATE TABLE IF NOT EXISTS daily_tasks (id INTEGER PRIMARY KEY NOT NULL, name TEXT, description TEXT, default_time TEXT, is_active INTEGER DEFAULT 1, created_at TEXT);',
- 'CREATE TABLE IF NOT EXISTS daily_task_entries (id INTEGER PRIMARY KEY NOT NULL, task_id INTEGER, field_id INTEGER, notes TEXT, status TEXT, completed_at TEXT, created_at TEXT, meta_json TEXT, FOREIGN KEY(task_id) REFERENCES daily_tasks(id), FOREIGN KEY(field_id) REFERENCES fields(id));',
- 'CREATE TABLE IF NOT EXISTS harvests (id INTEGER PRIMARY KEY NOT NULL, field_id INTEGER, crop_id INTEGER, harvested_at TEXT, quantity REAL, unit TEXT, notes TEXT, photo_uri TEXT, created_at TEXT, FOREIGN KEY(field_id) REFERENCES fields(id), FOREIGN KEY(crop_id) REFERENCES crops(id));',
- 'CREATE TABLE IF NOT EXISTS sales (id INTEGER PRIMARY KEY NOT NULL, harvest_id INTEGER, field_id INTEGER, crop_id INTEGER, sold_at TEXT, quantity REAL, unit TEXT, price REAL, buyer TEXT, notes TEXT, created_at TEXT, FOREIGN KEY(harvest_id) REFERENCES harvests(id), FOREIGN KEY(field_id) REFERENCES fields(id), FOREIGN KEY(crop_id) REFERENCES crops(id));',
- 'CREATE TABLE IF NOT EXISTS costs (id INTEGER PRIMARY KEY NOT NULL, field_id INTEGER, crop_id INTEGER, category TEXT, amount REAL, currency TEXT, vendor TEXT, notes TEXT, spent_at TEXT, photo_uri TEXT, created_at TEXT, FOREIGN KEY(field_id) REFERENCES fields(id), FOREIGN KEY(crop_id) REFERENCES crops(id));',
- 'CREATE TABLE IF NOT EXISTS field_media (id INTEGER PRIMARY KEY NOT NULL, field_id INTEGER, uri TEXT, media_type TEXT, created_at TEXT, FOREIGN KEY(field_id) REFERENCES fields(id));',
- 'CREATE TABLE IF NOT EXISTS crop_media (id INTEGER PRIMARY KEY NOT NULL, crop_id INTEGER, uri TEXT, media_type TEXT, created_at TEXT, FOREIGN KEY(crop_id) REFERENCES crops(id));',
- 'CREATE TABLE IF NOT EXISTS harvest_media (id INTEGER PRIMARY KEY NOT NULL, harvest_id INTEGER, uri TEXT, media_type TEXT, created_at TEXT, FOREIGN KEY(harvest_id) REFERENCES harvests(id));',
- 'CREATE TABLE IF NOT EXISTS sale_media (id INTEGER PRIMARY KEY NOT NULL, sale_id INTEGER, uri TEXT, media_type TEXT, created_at TEXT, FOREIGN KEY(sale_id) REFERENCES sales(id));',
- 'CREATE TABLE IF NOT EXISTS cost_media (id INTEGER PRIMARY KEY NOT NULL, cost_id INTEGER, uri TEXT, media_type TEXT, created_at TEXT, FOREIGN KEY(cost_id) REFERENCES costs(id));',
- 'CREATE TABLE IF NOT EXISTS task_entry_media (id INTEGER PRIMARY KEY NOT NULL, entry_id INTEGER, uri TEXT, media_type TEXT, created_at TEXT, FOREIGN KEY(entry_id) REFERENCES daily_task_entries(id));',
- 'CREATE INDEX IF NOT EXISTS idx_observations_field_id ON observations(field_id);',
- 'CREATE INDEX IF NOT EXISTS idx_observations_crop_id ON observations(crop_id);',
- 'CREATE INDEX IF NOT EXISTS idx_images_observation_id ON images(observation_id);',
- 'CREATE INDEX IF NOT EXISTS idx_ml_inferences_image_id ON ml_inferences(image_id);',
- 'CREATE INDEX IF NOT EXISTS idx_sensors_field_id ON sensors(field_id);',
- 'CREATE INDEX IF NOT EXISTS idx_sensor_readings_sensor_id ON sensor_readings(sensor_id);',
- 'CREATE INDEX IF NOT EXISTS idx_daily_task_entries_task_id ON daily_task_entries(task_id);',
- 'CREATE INDEX IF NOT EXISTS idx_daily_task_entries_field_id ON daily_task_entries(field_id);',
- 'CREATE INDEX IF NOT EXISTS idx_harvests_field_id ON harvests(field_id);',
- 'CREATE INDEX IF NOT EXISTS idx_harvests_crop_id ON harvests(crop_id);',
- 'CREATE INDEX IF NOT EXISTS idx_sales_harvest_id ON sales(harvest_id);',
- 'CREATE INDEX IF NOT EXISTS idx_sales_crop_id ON sales(crop_id);',
- 'CREATE INDEX IF NOT EXISTS idx_costs_field_id ON costs(field_id);',
- 'CREATE INDEX IF NOT EXISTS idx_costs_crop_id ON costs(crop_id);',
- 'CREATE INDEX IF NOT EXISTS idx_field_media_field_id ON field_media(field_id);',
- 'CREATE INDEX IF NOT EXISTS idx_crop_media_crop_id ON crop_media(crop_id);',
- 'CREATE INDEX IF NOT EXISTS idx_harvest_media_harvest_id ON harvest_media(harvest_id);',
- 'CREATE INDEX IF NOT EXISTS idx_sale_media_sale_id ON sale_media(sale_id);',
- 'CREATE INDEX IF NOT EXISTS idx_cost_media_cost_id ON cost_media(cost_id);',
- 'CREATE INDEX IF NOT EXISTS idx_task_entry_media_entry_id ON task_entry_media(entry_id);',
- ].join('\n')
- );
- await ensureColumns(db, 'user_profile', userProfileColumns);
- await ensureColumns(db, 'fields', fieldColumns);
- await ensureColumns(db, 'crops', cropColumns);
- await ensureColumns(db, 'harvests', harvestColumns);
- await ensureColumns(db, 'sales', saleColumns);
- await ensureColumns(db, 'costs', costColumns);
- await db.execAsync(
- `INSERT INTO field_media (field_id, uri, media_type, created_at)
- SELECT f.id, f.photo_uri, 'image', f.created_at
- FROM fields f
- WHERE f.photo_uri IS NOT NULL AND TRIM(f.photo_uri) <> ''
- AND NOT EXISTS (
- SELECT 1 FROM field_media fm WHERE fm.field_id = f.id
- );`
- );
- await db.execAsync(
- `INSERT INTO crop_media (crop_id, uri, media_type, created_at)
- SELECT c.id, c.photo_uri, 'image', datetime('now')
- FROM crops c
- WHERE c.photo_uri IS NOT NULL AND TRIM(c.photo_uri) <> ''
- AND NOT EXISTS (
- SELECT 1 FROM crop_media cm WHERE cm.crop_id = c.id
- );`
- );
- await db.execAsync(
- `INSERT INTO harvest_media (harvest_id, uri, media_type, created_at)
- SELECT h.id, h.photo_uri, 'image', h.created_at
- FROM harvests h
- WHERE h.photo_uri IS NOT NULL AND TRIM(h.photo_uri) <> ''
- AND NOT EXISTS (
- SELECT 1 FROM harvest_media hm WHERE hm.harvest_id = h.id
- );`
- );
- await db.execAsync(
- `INSERT INTO cost_media (cost_id, uri, media_type, created_at)
- SELECT c.id, c.photo_uri, 'image', c.created_at
- FROM costs c
- WHERE c.photo_uri IS NOT NULL AND TRIM(c.photo_uri) <> ''
- AND NOT EXISTS (
- SELECT 1 FROM cost_media cm WHERE cm.cost_id = c.id
- );`
- );
- }
- async function ensureColumns(
- db: SQLite.SQLiteDatabase,
- table: string,
- columns: ColumnSpec[]
- ) {
- const existing = await db.getAllAsync<{ name: string }>(
- `PRAGMA table_info(${table});`
- );
- const names = new Set(existing.map((col) => col.name));
- for (const column of columns) {
- if (!names.has(column.name)) {
- await db.execAsync(
- `ALTER TABLE ${table} ADD COLUMN ${column.name} ${column.type};`
- );
- }
- }
- }
|