DmitrMakeev commited on
Commit
01ed6ad
·
verified ·
1 Parent(s): 0d3a2e1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -83
app.py CHANGED
@@ -689,52 +689,49 @@ def nutri_call():
689
  class NutrientCalculator:
690
  def __init__(self, volume_liters=1.0):
691
  self.volume = volume_liters
692
- self.results = {}
693
- self.target_profile = {
694
  'P': 0, 'K': 0, 'Mg': 0, 'Ca': 0, 'S': 0,
695
- 'NO3': 0, 'NH4': 0 # Изменил ключи на NO3/NH4 без скобок
696
- }
697
- self.actual_profile = {
698
- 'P': 0.0, 'K': 0.0, 'Mg': 0.0, 'Ca': 0.0, 'S': 0.0,
699
- 'NO3': 0.0, 'NH4': 0.0
700
  }
 
701
  self.fertilizers = {}
702
  self.total_ec = 0.0
703
 
704
  def set_target_profile(self, profile_data):
705
  """Устанавливаем целевые значения с преобразованием ключей"""
706
- self.target_profile.update({
707
  'P': float(profile_data.get('P', 0)),
708
  'K': float(profile_data.get('K', 0)),
709
  'Mg': float(profile_data.get('Mg', 0)),
710
  'Ca': float(profile_data.get('Ca', 0)),
711
  'S': float(profile_data.get('S', 0)),
712
- 'NO3': float(profile_data.get('N (NO3-)', 0)), # Преобразуем ключ
713
- 'NH4': float(profile_data.get('N (NH4+)', 0)) # Преобразуем ключ
714
  })
715
 
716
  def set_fertilizers(self, fertilizers_data):
717
- """Устанавливаем состав удобрений с преобразованием ключей"""
718
  self.fertilizers = {
 
 
 
 
719
  "Кальциевая селитра": {
720
  "NO3": float(fertilizers_data["Кальциевая селитра"]["N (NO3-)"]),
721
  "Ca": float(fertilizers_data["Кальциевая селитра"]["Ca"])
722
  },
723
- "Калий азотнокислый": {
724
- "NO3": float(fertilizers_data["Калий азотнокислый"]["N (NO3-)"]),
725
- "K": float(fertilizers_data["Калий азотнокислый"]["K"])
726
  },
727
  "Аммоний азотнокислый": {
728
  "NO3": float(fertilizers_data["Аммоний азотнокислый"]["N (NO3-)"]),
729
  "NH4": float(fertilizers_data["Аммоний азотнокислый"]["N (NH4+)"])
730
  },
731
- "Сульфат магния": {
732
- "Mg": float(fertilizers_data["Сульфат магния"]["Mg"]),
733
- "S": float(fertilizers_data["Сульфат магния"]["S"])
734
- },
735
- "Монофосфат калия": {
736
- "P": float(fertilizers_data["Монофосфат калия"]["P"]),
737
- "K": float(fertilizers_data["Монофосфат калия"]["K"])
738
  },
739
  "Калий сернокислый": {
740
  "K": float(fertilizers_data["Калий сернокислый"]["K"]),
@@ -743,86 +740,84 @@ class NutrientCalculator:
743
  }
744
 
745
  def calculate(self):
746
- """Логика расчёта с приоритетом по нитратам"""
747
  try:
748
- # 1. Вносим сульфат магния (Mg + S)
749
  self._apply_fertilizer("Сульфат магния", "Mg")
750
-
751
- # 2. Вносим монофосфат калия (P + K)
752
  self._apply_fertilizer("Монофосфат калия", "P")
753
 
754
- # 3. Вносим аммоний азотнокислый (NH4 + NO3)
755
  self._apply_fertilizer("Аммоний азотнокислый", "NH4")
756
 
757
- # 4. Жёстко контролируем NO3:
758
- # - Если нужно меньше, чем уже внесено, пропускаем кальциевую селитру
759
- if self.target_profile['NO3'] > self.actual_profile['NO3']:
760
  self._apply_fertilizer("Кальциевая селитра", "Ca")
761
 
762
- # 5. Довносим K через калийную селитру или сульфат калия
763
- k_deficit = self.target_profile['K'] - self.actual_profile['K']
764
  if k_deficit > 0.1:
765
- # Выбираем удобрение с минимальным NO3
766
- if self.actual_profile['NO3'] < self.target_profile['NO3']:
767
- self._apply_fertilizer("Калий азотнокислый", "K", k_deficit)
768
  else:
769
- self._apply_fertilizer("Калий сернокислый", "K", k_deficit)
770
 
771
- # 6. Довносим серу при необходимости
772
- s_deficit = self.target_profile['S'] - self.actual_profile['S']
773
  if s_deficit > 0.1:
774
- self._apply_fertilizer("Калий сернокислый", "S", s_deficit)
775
 
776
  return True
777
  except Exception as e:
778
- print(f"Ошибка расчёта: {str(e)}")
779
  return False
780
 
781
  def _apply_fertilizer(self, name, main_element, required_ppm=None):
782
- """Метод внесения удобрений с контролем NO3"""
 
 
 
783
  if required_ppm is None:
784
- required_ppm = self.target_profile[main_element] - self.actual_profile[main_element]
785
 
786
- if required_ppm <= 0 or name not in self.fertilizers:
787
  return
788
 
789
  content = self.fertilizers[name].get(main_element, 0)
790
  if content <= 0:
791
  return
792
 
793
- # Корректируем граммы, если NO3 превышает целевое значение
794
  if 'NO3' in self.fertilizers[name]:
795
- no3_added = (required_ppm / content) * self.fertilizers[name]['NO3']
796
- if self.actual_profile['NO3'] + no3_added > self.target_profile['NO3']:
797
- required_ppm = max(0, (self.target_profile['NO3'] - self.actual_profile['NO3']) * content / self.fertilizers[name]['NO3'])
 
 
 
 
 
798
 
799
- grams = (required_ppm * self.volume) / (content * 1000)
800
-
801
- # Запись результатов
802
- if name not in self.results:
803
- self.results[name] = {'grams': 0.0, 'adds': {}}
804
-
805
  self.results[name]['grams'] += grams
806
-
807
- # Обновление профиля
808
  for element, percent in self.fertilizers[name].items():
809
  added_ppm = (grams * percent * 1000) / self.volume
810
- self.actual_profile[element] += added_ppm
811
- self.results[name]['adds'][element] = self.results[name]['adds'].get(element, 0) + added_ppm
812
-
813
  # Расчёт EC
814
- ec_coefficients = {
815
  'P': 0.0012, 'K': 0.0018, 'Mg': 0.0015,
816
  'Ca': 0.0016, 'S': 0.0014,
817
  'NO3': 0.0017, 'NH4': 0.0019
818
  }
819
  self.total_ec += sum(
820
- added_ppm * ec_coefficients.get(element, 0.0015)
821
- for element, added_ppm in self.results[name]['adds'].items()
822
  )
823
 
824
  def get_results(self):
825
- """Форматирование результатов с обратным преобразованием ключей"""
826
  return {
827
  'fertilizers': [
828
  {
@@ -832,42 +827,40 @@ class NutrientCalculator:
832
  } for name, data in self.results.items()
833
  ],
834
  'profile': [
835
- {
836
- 'element': 'N-NO3' if k == 'NO3' else 'N-NH4' if k == 'NH4' else k,
837
- 'ppm': round(v, 3)
838
- } for k, v in self.actual_profile.items()
 
 
 
839
  ],
840
  'ec': round(self.total_ec, 2),
841
  'deficits': {
842
- 'N (NO3-)' if k == 'NO3' else 'N (NH4+)' if k == 'NH4' else k:
843
- max(self.target_profile[k] - self.actual_profile.get(k, 0), 0)
844
- for k in self.target_profile
 
 
 
 
845
  }
846
  }
847
 
848
- @app.route('/calculation', methods=['POST'])
849
  def handle_calculation():
850
  try:
851
  data = request.json
852
 
853
- # Валидация входных данных
854
- required_fields = ['P', 'K', 'Mg', 'Ca', 'S', 'N (NO3-)', 'N (NH4+)', 'liters']
855
- if not all(field in data['profileSettings'] for field in required_fields):
856
- return jsonify({'status': 'error', 'message': 'Missing required profile fields'}), 400
857
-
858
- required_fertilizers = [
859
- "Кальциевая селитра", "Калий азотнокислый", "Аммоний азотнокислый",
860
- "Сульфат магния", "Монофосфат калия", "Калий сернокислый"
861
- ]
862
- if not all(fert in data['fertilizerConstants'] for fert in required_fertilizers):
863
- return jsonify({'status': 'error', 'message': 'Missing required fertilizers'}), 400
864
-
865
- # Инициализация калькулятора
866
  calculator = NutrientCalculator(volume_liters=data['profileSettings']['liters'])
867
  calculator.set_target_profile(data['profileSettings'])
868
  calculator.set_fertilizers(data['fertilizerConstants'])
869
 
870
- # Расчёт
871
  if calculator.calculate():
872
  return jsonify({
873
  'status': 'success',
 
689
  class NutrientCalculator:
690
  def __init__(self, volume_liters=1.0):
691
  self.volume = volume_liters
692
+ self.results = defaultdict(lambda: {'grams': 0.0, 'adds': defaultdict(float)})
693
+ self.target = {
694
  'P': 0, 'K': 0, 'Mg': 0, 'Ca': 0, 'S': 0,
695
+ 'NO3': 0, 'NH4': 0 # Упрощенные ключи
 
 
 
 
696
  }
697
+ self.actual = defaultdict(float)
698
  self.fertilizers = {}
699
  self.total_ec = 0.0
700
 
701
  def set_target_profile(self, profile_data):
702
  """Устанавливаем целевые значения с преобразованием ключей"""
703
+ self.target.update({
704
  'P': float(profile_data.get('P', 0)),
705
  'K': float(profile_data.get('K', 0)),
706
  'Mg': float(profile_data.get('Mg', 0)),
707
  'Ca': float(profile_data.get('Ca', 0)),
708
  'S': float(profile_data.get('S', 0)),
709
+ 'NO3': float(profile_data.get('N (NO3-)', 0)),
710
+ 'NH4': float(profile_data.get('N (NH4+)', 0))
711
  })
712
 
713
  def set_fertilizers(self, fertilizers_data):
714
+ """Преобразуем входные данные в единый формат"""
715
  self.fertilizers = {
716
+ "Сульфат магния": {
717
+ "Mg": float(fertilizers_data["Сульфат магния"]["Mg"]),
718
+ "S": float(fertilizers_data["Сульфат магния"]["S"])
719
+ },
720
  "Кальциевая селитра": {
721
  "NO3": float(fertilizers_data["Кальциевая селитра"]["N (NO3-)"]),
722
  "Ca": float(fertilizers_data["Кальциевая селитра"]["Ca"])
723
  },
724
+ "Монофосфат калия": {
725
+ "P": float(fertilizers_data["Монофосфат калия"]["P"]),
726
+ "K": float(fertilizers_data["Монофосфат калия"]["K"])
727
  },
728
  "Аммоний азотнокислый": {
729
  "NO3": float(fertilizers_data["Аммоний азотнокислый"]["N (NO3-)"]),
730
  "NH4": float(fertilizers_data["Аммоний азотнокислый"]["N (NH4+)"])
731
  },
732
+ "Калий азотнокислый": {
733
+ "NO3": float(fertilizers_data["Калий азотнокислый"]["N (NO3-)"]),
734
+ "K": float(fertilizers_data["Калий азотнокислый"]["K"])
 
 
 
 
735
  },
736
  "Калий сернокислый": {
737
  "K": float(fertilizers_data["Калий сернокислый"]["K"]),
 
740
  }
741
 
742
  def calculate(self):
743
+ """Новая логика расчёта с приоритетом по азоту"""
744
  try:
745
+ # 1. Вносим обязательные компоненты
746
  self._apply_fertilizer("Сульфат магния", "Mg")
 
 
747
  self._apply_fertilizer("Монофосфат калия", "P")
748
 
749
+ # 2. Контроль азота
750
  self._apply_fertilizer("Аммоний азотнокислый", "NH4")
751
 
752
+ # 3. Кальций с контролем NO3
753
+ remaining_no3 = max(self.target['NO3'] - self.actual['NO3'], 0)
754
+ if remaining_no3 > 0.1:
755
  self._apply_fertilizer("Кальциевая селитра", "Ca")
756
 
757
+ # 4. Калий с минимальным NO3
758
+ k_deficit = self.target['K'] - self.actual['K']
759
  if k_deficit > 0.1:
760
+ if self.actual['NO3'] < self.target['NO3']:
761
+ self._apply_fertilizer("Калий азотнокислый", "K")
 
762
  else:
763
+ self._apply_fertilizer("Калий сернокислый", "K")
764
 
765
+ # 5. Сера
766
+ s_deficit = self.target['S'] - self.actual['S']
767
  if s_deficit > 0.1:
768
+ self._apply_fertilizer("Калий сернокислый", "S")
769
 
770
  return True
771
  except Exception as e:
772
+ print(f"Calculation error: {str(e)}")
773
  return False
774
 
775
  def _apply_fertilizer(self, name, main_element, required_ppm=None):
776
+ """Умное внесение удобрений с контролем NO3"""
777
+ if name not in self.fertilizers:
778
+ return
779
+
780
  if required_ppm is None:
781
+ required_ppm = self.target[main_element] - self.actual[main_element]
782
 
783
+ if required_ppm <= 0:
784
  return
785
 
786
  content = self.fertilizers[name].get(main_element, 0)
787
  if content <= 0:
788
  return
789
 
790
+ # Корректировка для NO3
791
  if 'NO3' in self.fertilizers[name]:
792
+ no3_limit = max(self.target['NO3'] - self.actual['NO3'], 0)
793
+ if no3_limit <= 0:
794
+ return # Превышение NO3
795
+ max_grams = (no3_limit * self.volume) / (self.fertilizers[name]['NO3'] * 1000)
796
+ required_grams = (required_ppm * self.volume) / (content * 1000)
797
+ grams = min(required_grams, max_grams)
798
+ else:
799
+ grams = (required_ppm * self.volume) / (content * 1000)
800
 
801
+ # Обновляем профиль
 
 
 
 
 
802
  self.results[name]['grams'] += grams
 
 
803
  for element, percent in self.fertilizers[name].items():
804
  added_ppm = (grams * percent * 1000) / self.volume
805
+ self.actual[element] += added_ppm
806
+ self.results[name]['adds'][element] += added_ppm
807
+
808
  # Расчёт EC
809
+ ec_coeff = {
810
  'P': 0.0012, 'K': 0.0018, 'Mg': 0.0015,
811
  'Ca': 0.0016, 'S': 0.0014,
812
  'NO3': 0.0017, 'NH4': 0.0019
813
  }
814
  self.total_ec += sum(
815
+ added_ppm * ec_coeff.get(elem, 0.0015)
816
+ for elem, added_ppm in self.results[name]['adds'].items()
817
  )
818
 
819
  def get_results(self):
820
+ """Форматируем выходные данные"""
821
  return {
822
  'fertilizers': [
823
  {
 
827
  } for name, data in self.results.items()
828
  ],
829
  'profile': [
830
+ {'element': 'P', 'ppm': round(self.actual['P'], 3)},
831
+ {'element': 'K', 'ppm': round(self.actual['K'], 3)},
832
+ {'element': 'Mg', 'ppm': round(self.actual['Mg'], 3)},
833
+ {'element': 'Ca', 'ppm': round(self.actual['Ca'], 3)},
834
+ {'element': 'S', 'ppm': round(self.actual['S'], 3)},
835
+ {'element': 'N-NO3', 'ppm': round(self.actual['NO3'], 3)},
836
+ {'element': 'N-NH4', 'ppm': round(self.actual['NH4'], 3)}
837
  ],
838
  'ec': round(self.total_ec, 2),
839
  'deficits': {
840
+ 'Ca': max(self.target['Ca'] - self.actual['Ca'], 0),
841
+ 'K': max(self.target['K'] - self.actual['K'], 0),
842
+ 'Mg': max(self.target['Mg'] - self.actual['Mg'], 0),
843
+ 'N (NH4+)': max(self.target['NH4'] - self.actual['NH4'], 0),
844
+ 'N (NO3-)': max(self.target['NO3'] - self.actual['NO3'], 0),
845
+ 'P': max(self.target['P'] - self.actual['P'], 0),
846
+ 'S': max(self.target['S'] - self.actual['S'], 0)
847
  }
848
  }
849
 
850
+ @app.route('/calculate', methods=['POST'])
851
  def handle_calculation():
852
  try:
853
  data = request.json
854
 
855
+ # Валидация
856
+ required = ['P', 'K', 'Mg', 'Ca', 'S', 'N (NO3-)', 'N (NH4+)', 'liters']
857
+ if not all(k in data['profileSettings'] for k in required):
858
+ return jsonify({'status': 'error', 'message': 'Missing required fields'}), 400
859
+
 
 
 
 
 
 
 
 
860
  calculator = NutrientCalculator(volume_liters=data['profileSettings']['liters'])
861
  calculator.set_target_profile(data['profileSettings'])
862
  calculator.set_fertilizers(data['fertilizerConstants'])
863
 
 
864
  if calculator.calculate():
865
  return jsonify({
866
  'status': 'success',