diff options
Diffstat (limited to 'db')
-rw-r--r-- | db/data/dolibarr_plant_requirements.csv | 9 | ||||
-rw-r--r-- | db/data/nutrient_profiles.csv (renamed from db/data/nutrient_requirements.csv) | 0 | ||||
-rw-r--r-- | db/migrate/20250901112954_rename_crops_to_nutrient_profiles.rb | 10 | ||||
-rw-r--r-- | db/migrate/20250908181137_create_targets.rb | 9 | ||||
-rw-r--r-- | db/migrate/20250908181147_create_target_allocations.rb | 11 | ||||
-rw-r--r-- | db/schema.rb | 71 | ||||
-rw-r--r-- | db/seeds.rb | 14 | ||||
-rw-r--r-- | db/seeds/1_nutrients.rb | 24 | ||||
-rw-r--r-- | db/seeds/3_crops.rb | 125 | ||||
-rw-r--r-- | db/seeds/4_beds_and_rafts.rb | 24 | ||||
-rw-r--r-- | db/seeds/5_fertilizer_components.rb.bkp | 38 | ||||
-rw-r--r-- | db/seeds/6_fertilizer_products.rb | 143 | ||||
-rw-r--r-- | db/seeds/NutrientMeasurement.rb (renamed from db/seeds/2_nutrient_measurements.rb) | 2 | ||||
-rw-r--r-- | db/seeds/NutrientProfile.rb | 109 | ||||
-rw-r--r-- | db/seeds/Target.rb | 21 |
15 files changed, 225 insertions, 385 deletions
diff --git a/db/data/dolibarr_plant_requirements.csv b/db/data/dolibarr_plant_requirements.csv new file mode 100644 index 0000000..333ea38 --- /dev/null +++ b/db/data/dolibarr_plant_requirements.csv @@ -0,0 +1,9 @@ +Cycles et productions,NNO3,P,K,Ca,Mg,S,Fe,Zn,B,Mn,Cu,Mo +Formule moyenne générale,160.00,30.00,230.00,100.00,30.00,60.00,5.00,0.15,0.30,0.50,0.15,0.05 +Salades,130.00,60.00,300.00,100.00,30.00,60.00,2.00,0.10,0.50,0.50,0.05,0.05 +Développement floral,190.00,50.00,210.00,200.00,50.00,66.00,5.00,0.15,0.30,0.50,0.15,0.05 +Tomates général,140.00,50.00,352.00,180.00,50.00,168.00,5.00,0.10,0.30,0.80,0.07,0.03 +Jeunes tomates,100.00,40.00,200.00,100.00,20.00,53.00,3.00,0.10,0.30,0.80,0.07,0.03 +Tomate premiers fruits,130.00,55.00,300.00,150.00,33.00,109.00,3.00,0.10,0.30,0.80,0.07,0.03 +Tomate mûre,180.00,65.00,400.00,400.00,45.00,144.00,3.00,0.10,0.30,0.80,0.07,0.03 +Framboise - tous stades,70.00,12.00,88.00,90.00,24.00,48.00,0.56,0.33,0.11,0.11,0.03,0.01 diff --git a/db/data/nutrient_requirements.csv b/db/data/nutrient_profiles.csv index 61406b8..61406b8 100644 --- a/db/data/nutrient_requirements.csv +++ b/db/data/nutrient_profiles.csv diff --git a/db/migrate/20250901112954_rename_crops_to_nutrient_profiles.rb b/db/migrate/20250901112954_rename_crops_to_nutrient_profiles.rb new file mode 100644 index 0000000..5b46df1 --- /dev/null +++ b/db/migrate/20250901112954_rename_crops_to_nutrient_profiles.rb @@ -0,0 +1,10 @@ +class RenameCropsToNutrientProfiles < ActiveRecord::Migration[8.0] + def change + rename_table :crops, :nutrient_profiles + + rename_column :rafts, :crop_id, :crop_nutrient_need_id + add_index :rafts, :crop_nutrient_need_id unless index_exists?(:rafts, :crop_nutrient_need_id) + + remove_column :nutrient_profiles, :crop_type + end +end diff --git a/db/migrate/20250908181137_create_targets.rb b/db/migrate/20250908181137_create_targets.rb new file mode 100644 index 0000000..9144e50 --- /dev/null +++ b/db/migrate/20250908181137_create_targets.rb @@ -0,0 +1,9 @@ +class CreateTargets < ActiveRecord::Migration[8.0] + def change + create_table :targets do |t| + t.string :name + + t.timestamps + end + end +end diff --git a/db/migrate/20250908181147_create_target_allocations.rb b/db/migrate/20250908181147_create_target_allocations.rb new file mode 100644 index 0000000..9141bd3 --- /dev/null +++ b/db/migrate/20250908181147_create_target_allocations.rb @@ -0,0 +1,11 @@ +class CreateTargetAllocations < ActiveRecord::Migration[8.0] + def change + create_table :target_allocations do |t| + t.references :target, null: false, foreign_key: true + t.references :nutrient_profile, null: false, foreign_key: true + t.decimal :percentage + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index b5f2e03..d201f5a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_08_24_163257) do +ActiveRecord::Schema[8.0].define(version: 2025_09_08_181147) do create_table "beds", force: :cascade do |t| t.integer "location", null: false t.datetime "created_at", null: false @@ -18,29 +18,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_08_24_163257) do t.index ["location"], name: "index_beds_on_location", unique: true end - create_table "crops", force: :cascade do |t| - t.string "name" - t.integer "crop_type", default: 1, null: false - t.float "nno3" - t.float "p" - t.float "k" - t.float "ca" - t.float "mg" - t.float "s" - t.float "na" - t.float "cl" - t.float "si" - t.float "fe" - t.float "zn" - t.float "b" - t.float "mn" - t.float "cu" - t.float "mo" - t.float "nnh4" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - create_table "fertilizer_components", force: :cascade do |t| t.string "name" t.string "formula" @@ -105,6 +82,28 @@ ActiveRecord::Schema[8.0].define(version: 2025_08_24_163257) do t.index ["measured_on"], name: "index_nutrient_measurements_on_measured_on", unique: true end + create_table "nutrient_profiles", force: :cascade do |t| + t.string "name" + t.float "nno3" + t.float "p" + t.float "k" + t.float "ca" + t.float "mg" + t.float "s" + t.float "na" + t.float "cl" + t.float "si" + t.float "fe" + t.float "zn" + t.float "b" + t.float "mn" + t.float "cu" + t.float "mo" + t.float "nnh4" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "nutrients", force: :cascade do |t| t.string "formula" t.string "name" @@ -115,16 +114,34 @@ ActiveRecord::Schema[8.0].define(version: 2025_08_24_163257) do create_table "rafts", force: :cascade do |t| t.integer "bed_id", null: false t.integer "location", null: false - t.integer "crop_id" + t.integer "crop_nutrient_need_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["bed_id", "location"], name: "index_rafts_on_bed_id_and_location", unique: true t.index ["bed_id"], name: "index_rafts_on_bed_id" - t.index ["crop_id"], name: "index_rafts_on_crop_id" + t.index ["crop_nutrient_need_id"], name: "index_rafts_on_crop_nutrient_need_id" + end + + create_table "target_allocations", force: :cascade do |t| + t.integer "target_id", null: false + t.integer "nutrient_profile_id", null: false + t.decimal "percentage" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["nutrient_profile_id"], name: "index_target_allocations_on_nutrient_profile_id" + t.index ["target_id"], name: "index_target_allocations_on_target_id" + end + + create_table "targets", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_foreign_key "fertilizer_compositions", "fertilizer_components" add_foreign_key "fertilizer_compositions", "fertilizer_products" add_foreign_key "rafts", "beds" - add_foreign_key "rafts", "crops" + add_foreign_key "rafts", "nutrient_profiles", column: "crop_nutrient_need_id" + add_foreign_key "target_allocations", "nutrient_profiles" + add_foreign_key "target_allocations", "targets" end diff --git a/db/seeds.rb b/db/seeds.rb index 848db0d..d5f046d 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -8,6 +8,16 @@ # MovieGenre.find_or_create_by!(name: genre_name) # end -Dir[Rails.root.join("db/seeds/*.rb")].sort.each do |seed| - load seed +MODELS = [ NutrientMeasurement, + NutrientProfile, + Target + ].freeze + +def seed_file_for(model_class) + Rails.root.join("db", "seeds", "#{model_class.name}.rb") +end + +MODELS.each do |model| + load seed_file_for(model) + printf "%-22s %3s\n", model.name, model.count end diff --git a/db/seeds/1_nutrients.rb b/db/seeds/1_nutrients.rb deleted file mode 100644 index dab2249..0000000 --- a/db/seeds/1_nutrients.rb +++ /dev/null @@ -1,24 +0,0 @@ -NUTRIENTS = [ - [ "nno3", "nitrate" ], - [ "p", "phosphore" ], - [ "k", "potassium" ], - [ "ca", "calcium" ], - [ "mg", "magnésium" ], - [ "s", "soufre" ], - [ "na", "sodium" ], - [ "cl", "chlore" ], - [ "si", "silice" ], - [ "fe", "fer" ], - [ "zn", "zinc" ], - [ "b", "bore" ], - [ "mn", "manganèse" ], - [ "cu", "cuivre" ], - [ "mo", "molybdène" ], - [ "nnh4", "ammonium" ] -] - -NUTRIENTS.each do |formula, name| - Nutrient.find_or_create_by!(formula:) { |n| n.name = name } -end - -puts "Nutrients: #{Nutrient.count}" diff --git a/db/seeds/3_crops.rb b/db/seeds/3_crops.rb deleted file mode 100644 index 6a5c10f..0000000 --- a/db/seeds/3_crops.rb +++ /dev/null @@ -1,125 +0,0 @@ -# crop_type: 0=leafy greens, 1=fruits, 2=herbs - -LEAFY = { - nno3: 150, - p: 31, - k: 210, - ca: 90, - mg: 24, - s: 32, - fe: 1.0, - mn: 0.25, - zn: 0.13, - b: 0.16, - cu: 0.023, - mo: 0.024, - nnh4: 5, - # keep low for leafy - na: 10, - cl: 5, - si: 20 -} - -HERB = LEAFY # Herbs run well on the Cornell leafy recipe - -TOMATO = { - # CEAC Stage 3 "multi-crop" / mature tomato - nno3: 190, - p: 47, - k: 350, - ca: 200, - mg: 65, - s: 102, - fe: 2.0, - mn: 0.55, - zn: 0.33, - b: 0.28, - cu: 0.05, - mo: 0.05, - nnh4: 0, - # CEAC recipes are NO3-N; keep NH4 minimal - na: 20, - cl: 25, - si: 20 -} - -HOT_PEPPER = { - # Mature greenhouse pepper (HortAmericas summary of UA recipes) - nno3: 180, - p: 50, - k: 280, - ca: 200, - mg: 45, - s: 20, - fe: 1.0, - mn: 0.55, - zn: 0.33, - b: 0.30, - cu: 0.05, - mo: 0.05, - nnh4: 15, - na: 20, - cl: 10, - si: 20 -} - -STRAWBERRY = { - # UA strawberry (Yamazaki) emphasizes lower EC; use conservative macros - nno3: 120, - p: 30, - k: 200, - ca: 120, - mg: 35, - s: 50, - fe: 1.5, - mn: 0.50, - zn: 0.20, - b: 0.30, - cu: 0.05, - mo: 0.05, - nnh4: 5, - na: 10, - cl: 5, - si: 20 -} - -RASPBERRY = { - # Sparse data; align with strawberry but a touch higher vigor - nno3: 140, - p: 35, - k: 230, - ca: 150, - mg: 40, - s: 50, - fe: 1.5, - mn: 0.50, - zn: 0.20, - b: 0.30, - cu: 0.05, - mo: 0.05, - nnh4: 5, - na: 15, - cl: 5, - si: 20 -} - -[ - [ "lettuce", 0, LEAFY ], - [ "kale", 0, LEAFY ], - [ "cabbage, chinese", 0, LEAFY ], - [ "tomatoes", 1, TOMATO ], - [ "raspberries", 1, RASPBERRY ], - [ "strawberries", 1, STRAWBERRY ], - [ "hot peppers", 1, HOT_PEPPER ], - [ "parsley", 2, HERB ], - [ "chives", 2, HERB ], - [ "italian basil", 2, HERB ], - [ "dill", 2, HERB ] -].each do |name, type, nutrient_requirements| - Crop.find_or_create_by!(name: name) do |c| - c.crop_type = type - c.attributes = nutrient_requirements - end -end - -puts "Crops: #{Crop.count}" diff --git a/db/seeds/4_beds_and_rafts.rb b/db/seeds/4_beds_and_rafts.rb deleted file mode 100644 index 1cd95dc..0000000 --- a/db/seeds/4_beds_and_rafts.rb +++ /dev/null @@ -1,24 +0,0 @@ -BEDS = 14 -RAFTS = 10 - -1.upto(BEDS) do |b| - bed = Bed.find_or_create_by!(location: b) - - crop_name = case b - when 1..2 then "tomatoes" - when 3 then "hot peppers" - when 4 then "chives" - when 5 then "italian basil" - when 6..7 then "cabbage, chinese" - else "lettuce" - end - - 1.upto(RAFTS) do |r| - raft = bed.rafts.find_or_create_by!(location: r) do |raft| - raft.crop = Crop.find_by!(name: crop_name) - end - end -end - -puts "Beds: #{Bed.count}" -puts "Rafts: #{Raft.count}" diff --git a/db/seeds/5_fertilizer_components.rb.bkp b/db/seeds/5_fertilizer_components.rb.bkp deleted file mode 100644 index 8a1fc54..0000000 --- a/db/seeds/5_fertilizer_components.rb.bkp +++ /dev/null @@ -1,38 +0,0 @@ -FERTILIZER_COMPONENTS = [ - # Macros / bases - { name: "Potassium Nitrate", formula: "KNO3", nno3: 13.50, k: 38.60 }, - { name: "Calcium Nitrate", formula: "Ca(NO3)2·xH2O", nno3: 15.50, ca: 18.94 }, - { name: "Ammonium Nitrate", formula: "NH4NO3", nno3: 13.50, nnh4: 13.50 }, # total N ≈ 27 - { name: "Diammonium Phosphate", formula: "(NH4)2HPO4", p: 21.00 }, # NH4 can be added later if desired - - # Acids / buffers - { name: "Nitric Acid 53%", formula: "HNO3", nno3: 11.70 }, - { name: "Potassium Bicarbonate", formula: "KHCO3", k: 39.05 }, - { name: "Calcium Carbonate", formula: "CaCO3", ca: 38.00 }, - - # K–Mg–S complex (Patentkali-type blend) - { name: "Potassium Sulfate blend (K–Mg–S + NaCl)", formula: "K2SO4+MgSO4+NaCl", - p: 0.44, k: 22.66, mg: 6.16, s: 17.77, na: 2.16, cl: 3.34 }, - - # Sulfates (micros / secondary) - { name: "Magnesium Sulfate", formula: "MgSO4·7H2O", mg: 9.648, s: 13.016 }, - { name: "Manganese Sulfate", formula: "MnSO4", s: 7.17, mn: 12.00 }, - { name: "Zinc Sulfate (Fiza Zinc)", formula: "ZnSO4", zn: 12.00 }, - - # Chelates / traces - { name: "Iron DTPA 11.8%", formula: "Fe-DTPA", fe: 11.80 }, - { name: "HelioCopper (Cu chelate)", formula: "Cu-chelate", cu: 40.00 }, - - # Boron & Mo sources - { name: "Boron–Molybdenum (Boronia LS)", formula: "B+Mo", b: 13.50, mo: 0.028 }, - { name: "Boronia MO12 (10L)", formula: "B+Mn+Cu", b: 8.90, mn: 0.089, cu: 0.89 }, - { name: "Sodium Molybdate", formula: "Na2MoO4", mo: 39.50 } -] - -FERTILIZER_COMPONENTS.each do |attrs| - FertilizerComponent.find_or_create_by!(name: attrs[:name]) do |c| - c.attributes = attrs - end -end - -puts "Fertilizer components: #{FertilizerComponent.count}" diff --git a/db/seeds/6_fertilizer_products.rb b/db/seeds/6_fertilizer_products.rb deleted file mode 100644 index 6769bde..0000000 --- a/db/seeds/6_fertilizer_products.rb +++ /dev/null @@ -1,143 +0,0 @@ -FERTILIZER_RECIPES = [ - { - name: "Multi K Reci", - purity: 99.0, - composition: [ - { component: { - name: "Potassium nitrate", - formula: "KNO3", - nno3: 13.0, - k: 46.0 }, - percent_w: 100.0 } - ] - }, - { - name: "Fixa Mn", - purity: 98.0, - composition: [ - { component: { - name: "Manganese sulfate", - formula: "MnSO4·H2O", - mn: 32.0, - s: 18.0 }, - percent_w: 100.0 } - ] - }, - { - name: "Multi-Cal Haïfa", - purity: 99.0, - composition: [ - { component: { - name: "Calcium nitrate", - formula: "Ca(NO3)2·4H2O", - nno3: 15.5, - ca: 19.0 }, - percent_w: 100.0 } - ] - }, - { - name: "DAP 18/46/00", - purity: 100.0, - composition: [ - { component: { - name: "Diammonium phosphate", - formula: "(NH4)2HPO4", - nnh4: 18.0, - p: 20.0 }, - percent_w: 100.0 } - ] - }, - { - name: "Patenkali", - purity: 100.0, - composition: [ - { component: { - name: "Potassium sulfate", - formula: "K2SO4", - k: 50.0, - s: 18.0 - }, percent_w: 100.0 } - ] - }, - { - name: "Eso Top", - purity: 100.0, - composition: [ - { component: { - name: "Magnesium sulfate", - formula: "MgSO4·7H2O", - mg: 9.8, - s: 13.0 }, - percent_w: 100.0 } - ] - }, - { - name: "Ammonitrate 27", - purity: 100.0, - composition: [ - { component: { - name: "Ammonium nitrate", - formula: "NH4NO3", - nno3: 13.5, - nnh4: 13.5 }, - percent_w: 100.0 } - ] - }, - { - name: "Fer chélaté", - purity: 100.0, - composition: [ - { component: { - name: "Iron chelate (EDDHA)", - formula: "Fe-EDDHA", - fe: 6.0 }, - percent_w: 100.0 } - ] - }, - { - name: "Carbonate de calcium", - purity: 100.0, - composition: [ - { component: { - name: "Calcium carbonate", - formula: "CaCO3", - ca: 40.0 }, - percent_w: 100.0 } - ] - }, - { - name: "Héliocuivre", - purity: 100.0, - composition: [ - { component: { - name: "Copper chelate (EDTA)", - formula: "Cu-EDTA", - cu: 14.0 }, - percent_w: 100.0 } - ] - } -] - -FERTILIZER_RECIPES.each do |recipe| - product = FertilizerProduct.find_or_create_by!(name: recipe[:name]) do |fp| - fp.purity = recipe[:purity] - end - - recipe[:composition].each do |c| - comp_attrs = c[:component] - component = FertilizerComponent.find_or_create_by!(name: comp_attrs[:name]) do |fc| - fc.formula = comp_attrs[:formula] - end - # update nutrient fields if missing - component.update!(comp_attrs.except(:name, :formula)) - - FertilizerComposition.find_or_create_by!( - fertilizer_product: product, - fertilizer_component: component - ) do |fc| - fc.percent_w = c[:percent_w] - end - end -end - -puts "FertilizerProducts: #{FertilizerProduct.count}" diff --git a/db/seeds/2_nutrient_measurements.rb b/db/seeds/NutrientMeasurement.rb index 0ca14f1..fcf8cfa 100644 --- a/db/seeds/2_nutrient_measurements.rb +++ b/db/seeds/NutrientMeasurement.rb @@ -23,5 +23,3 @@ CSV.foreach(csv_path, headers: true) do |row| m.nnh4 = row["nnh4"] end end - -puts "NutrientMeasurements: #{NutrientMeasurement.count}" diff --git a/db/seeds/NutrientProfile.rb b/db/seeds/NutrientProfile.rb new file mode 100644 index 0000000..0c3c152 --- /dev/null +++ b/db/seeds/NutrientProfile.rb @@ -0,0 +1,109 @@ +[ { name: "formule moyenne générale", + nno3: 160.00, + p: 30.00, + k: 230.00, + ca: 100.00, + mg: 30.00, + s: 60.00, + fe: 5.00, + zn: 0.15, + b: 0.30, + mn: 0.50, + cu: 0.15, + mo: 0.05 }, + { name: "salades", + nno3: 130.00, + p: 60.00, + k: 300.00, + ca: 100.00, + mg: 30.00, + s: 60.00, + fe: 2.00, + zn: 0.10, + b: 0.50, + mn: 0.50, + cu: 0.05, + mo: 0.05 }, + { name: "développement floral", + nno3: 190.00, + p: 50.00, + k: 210.00, + ca: 200.00, + mg: 50.00, + s: 66.00, + fe: 5.00, + zn: 0.15, + b: 0.30, + mn: 0.50, + cu: 0.15, + mo: 0.05 }, + { name: "tomates général", + nno3: 140.00, + p: 50.00, + k: 352.00, + ca: 180.00, + mg: 50.00, + s: 168.00, + fe: 5.00, + zn: 0.10, + b: 0.30, + mn: 0.80, + cu: 0.07, + mo: 0.03 }, + { name: "jeunes tomates", + nno3: 100.00, + p: 40.00, + k: 200.00, + ca: 100.00, + mg: 20.00, + s: 53.00, + fe: 3.00, + zn: 0.10, + b: 0.30, + mn: 0.80, + cu: 0.07, + mo: 0.03 }, + { name: "tomate premiers fruits", + nno3: 130.00, + p: 55.00, + k: 300.00, + ca: 150.00, + mg: 33.00, + s: 109.00, + fe: 3.00, + zn: 0.10, + b: 0.30, + mn: 0.80, + cu: 0.07, + mo: 0.03 }, + { name: "tomate mûre", + nno3: 180.00, + p: 65.00, + k: 400.00, + ca: 400.00, + mg: 45.00, + s: 144.00, + fe: 3.00, + zn: 0.10, + b: 0.30, + mn: 0.80, + cu: 0.07, + mo: 0.03 }, + { name: "framboise - tous stades", + nno3: 70.00, + p: 12.00, + k: 88.00, + ca: 90.00, + mg: 24.00, + s: 48.00, + fe: 0.56, + zn: 0.33, + b: 0.11, + mn: 0.11, + cu: 0.03, + mo: 0.01 } +].each do |profile| + NutrientProfile.find_or_create_by!(name: profile[:name]) do |p| + p.attributes = profile + end +end diff --git a/db/seeds/Target.rb b/db/seeds/Target.rb new file mode 100644 index 0000000..790fef7 --- /dev/null +++ b/db/seeds/Target.rb @@ -0,0 +1,21 @@ +Target.transaction do + wanted = { + "salades" => 40, + "tomates général" => 40, + "développement floral" => 20 + } + + # Ensure all referenced profiles exist + profiles = NutrientProfile.where(name: wanted.keys).index_by(&:name) + missing = wanted.keys - profiles.keys + raise "Missing NutrientProfile(s): #{missing.join(', ')}" if missing.any? + + target = Target.find_or_create_by!(name: "Objectif par défaut") + + target.target_allocations.destroy_all + wanted.each do |name, pct| + target.target_allocations.build(nutrient_profile: profiles[name], percentage: pct) + end + + target.save! +end |