Нема описа

db.ts 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import * as SQLite from 'expo-sqlite';
  2. export const dbPromise = SQLite.openDatabaseAsync('smartfarming.db');
  3. type ColumnSpec = {
  4. name: string;
  5. type: string;
  6. };
  7. const userProfileColumns: ColumnSpec[] = [
  8. { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
  9. { name: 'name', type: 'TEXT' },
  10. { name: 'farm_name', type: 'TEXT' },
  11. { name: 'location', type: 'TEXT' },
  12. { name: 'photo_uri', type: 'TEXT' },
  13. { name: 'language', type: 'TEXT' },
  14. { name: 'currency', type: 'TEXT' },
  15. { name: 'created_at', type: 'TEXT' },
  16. { name: 'updated_at', type: 'TEXT' },
  17. ];
  18. const fieldColumns: ColumnSpec[] = [
  19. { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
  20. { name: 'name', type: 'TEXT' },
  21. { name: 'area_ha', type: 'REAL' },
  22. { name: 'boundary_geojson', type: 'TEXT' },
  23. { name: 'notes', type: 'TEXT' },
  24. { name: 'photo_uri', type: 'TEXT' },
  25. { name: 'created_at', type: 'TEXT' },
  26. { name: 'updated_at', type: 'TEXT' },
  27. ];
  28. const cropColumns: ColumnSpec[] = [
  29. { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
  30. { name: 'field_id', type: 'INTEGER' },
  31. { name: 'crop_name', type: 'TEXT' },
  32. { name: 'variety', type: 'TEXT' },
  33. { name: 'planting_date', type: 'TEXT' },
  34. { name: 'expected_harvest_date', type: 'TEXT' },
  35. { name: 'photo_uri', type: 'TEXT' },
  36. ];
  37. const harvestColumns: ColumnSpec[] = [
  38. { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
  39. { name: 'field_id', type: 'INTEGER' },
  40. { name: 'crop_id', type: 'INTEGER' },
  41. { name: 'harvested_at', type: 'TEXT' },
  42. { name: 'quantity', type: 'REAL' },
  43. { name: 'unit', type: 'TEXT' },
  44. { name: 'notes', type: 'TEXT' },
  45. { name: 'photo_uri', type: 'TEXT' },
  46. { name: 'created_at', type: 'TEXT' },
  47. ];
  48. const saleColumns: ColumnSpec[] = [
  49. { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
  50. { name: 'harvest_id', type: 'INTEGER' },
  51. { name: 'field_id', type: 'INTEGER' },
  52. { name: 'crop_id', type: 'INTEGER' },
  53. { name: 'sold_at', type: 'TEXT' },
  54. { name: 'quantity', type: 'REAL' },
  55. { name: 'unit', type: 'TEXT' },
  56. { name: 'price', type: 'REAL' },
  57. { name: 'buyer', type: 'TEXT' },
  58. { name: 'notes', type: 'TEXT' },
  59. { name: 'created_at', type: 'TEXT' },
  60. ];
  61. const costColumns: ColumnSpec[] = [
  62. { name: 'id', type: 'INTEGER PRIMARY KEY NOT NULL' },
  63. { name: 'field_id', type: 'INTEGER' },
  64. { name: 'crop_id', type: 'INTEGER' },
  65. { name: 'category', type: 'TEXT' },
  66. { name: 'amount', type: 'REAL' },
  67. { name: 'currency', type: 'TEXT' },
  68. { name: 'vendor', type: 'TEXT' },
  69. { name: 'notes', type: 'TEXT' },
  70. { name: 'spent_at', type: 'TEXT' },
  71. { name: 'photo_uri', type: 'TEXT' },
  72. { name: 'created_at', type: 'TEXT' },
  73. ];
  74. export async function initCoreTables() {
  75. const db = await dbPromise;
  76. await db.execAsync(
  77. [
  78. '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);',
  79. '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);',
  80. '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));',
  81. '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));',
  82. '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));',
  83. '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));',
  84. '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));',
  85. '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));',
  86. '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);',
  87. '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));',
  88. '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));',
  89. '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));',
  90. '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));',
  91. 'CREATE INDEX IF NOT EXISTS idx_observations_field_id ON observations(field_id);',
  92. 'CREATE INDEX IF NOT EXISTS idx_observations_crop_id ON observations(crop_id);',
  93. 'CREATE INDEX IF NOT EXISTS idx_images_observation_id ON images(observation_id);',
  94. 'CREATE INDEX IF NOT EXISTS idx_ml_inferences_image_id ON ml_inferences(image_id);',
  95. 'CREATE INDEX IF NOT EXISTS idx_sensors_field_id ON sensors(field_id);',
  96. 'CREATE INDEX IF NOT EXISTS idx_sensor_readings_sensor_id ON sensor_readings(sensor_id);',
  97. 'CREATE INDEX IF NOT EXISTS idx_daily_task_entries_task_id ON daily_task_entries(task_id);',
  98. 'CREATE INDEX IF NOT EXISTS idx_daily_task_entries_field_id ON daily_task_entries(field_id);',
  99. 'CREATE INDEX IF NOT EXISTS idx_harvests_field_id ON harvests(field_id);',
  100. 'CREATE INDEX IF NOT EXISTS idx_harvests_crop_id ON harvests(crop_id);',
  101. 'CREATE INDEX IF NOT EXISTS idx_sales_harvest_id ON sales(harvest_id);',
  102. 'CREATE INDEX IF NOT EXISTS idx_sales_crop_id ON sales(crop_id);',
  103. 'CREATE INDEX IF NOT EXISTS idx_costs_field_id ON costs(field_id);',
  104. 'CREATE INDEX IF NOT EXISTS idx_costs_crop_id ON costs(crop_id);',
  105. ].join('\n')
  106. );
  107. await ensureColumns(db, 'user_profile', userProfileColumns);
  108. await ensureColumns(db, 'fields', fieldColumns);
  109. await ensureColumns(db, 'crops', cropColumns);
  110. await ensureColumns(db, 'harvests', harvestColumns);
  111. await ensureColumns(db, 'sales', saleColumns);
  112. await ensureColumns(db, 'costs', costColumns);
  113. }
  114. async function ensureColumns(
  115. db: SQLite.SQLiteDatabase,
  116. table: string,
  117. columns: ColumnSpec[]
  118. ) {
  119. const existing = await db.getAllAsync<{ name: string }>(
  120. `PRAGMA table_info(${table});`
  121. );
  122. const names = new Set(existing.map((col) => col.name));
  123. for (const column of columns) {
  124. if (!names.has(column.name)) {
  125. await db.execAsync(
  126. `ALTER TABLE ${table} ADD COLUMN ${column.name} ${column.type};`
  127. );
  128. }
  129. }
  130. }