DmitrMakeev commited on
Commit
14165cc
·
verified ·
1 Parent(s): 8470169

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -159
app.py CHANGED
@@ -802,131 +802,109 @@ class NutrientCalculator:
802
  self.total_ec = 0.0
803
  self.fertilizers = {}
804
 
805
- # Веса для компенсации (приоритеты)
806
- self.compensation_weights = {
807
- 'POTASSIUM_NITRATE': {'weight': 0.5, 'fert': 'Калий азотнокислый', 'main_element': 'K'},
808
- 'CALCIUM_NITRATE': {'weight': 0.3, 'fert': 'Кальциевая селитра', 'main_element': 'Ca'},
809
- 'POTASSIUM_SULFATE': {'weight': 0.2, 'fert': 'Калий сернокислый', 'main_element': 'K'}
810
  }
811
 
812
  def _apply_fertilizer(self, fert_name: str, element: str, ppm: float):
813
- """Основной метод внесения удобрений"""
814
  try:
815
  if ppm <= 0:
816
  return
817
 
818
  content = self.fertilizers[fert_name][element]
819
  grams = (ppm * self.volume) / (content * 1000)
 
820
 
821
  if fert_name not in self.results:
822
  self.results[fert_name] = {
823
- 'grams': 0.0,
824
- 'ppm_added': {}
 
 
825
  }
826
 
827
- self.results[fert_name]['grams'] += grams
 
828
 
829
- # Обновляем все элементы из удобрения
 
830
  for el, percent in self.fertilizers[fert_name].items():
831
  added_ppm = (grams * percent * 1000) / self.volume
832
  self.actual_profile[el] = self.actual_profile.get(el, 0) + added_ppm
833
- self.results[fert_name]['ppm_added'][el] = self.results[fert_name]['ppm_added'].get(el, 0) + added_ppm
 
 
 
 
834
 
835
  except KeyError as e:
836
  raise ValueError(f"Неверный элемент или удобрение: {str(e)}")
837
 
838
- def _compensate_with_weights(self, element: str, needed_ppm: float):
839
- """Компенсация с учётом весов"""
840
- candidates = []
841
-
842
- # Ищем все удобрения, которые содержат нужный элемент
843
- for fert_name, fert_data in self.fertilizers.items():
844
- if element in fert_data:
845
- # Находим соответствующий вес в compensation_weights
846
- weight = 0.0
847
- for comp_key, comp_data in self.compensation_weights.items():
848
- if comp_data['fert'] == fert_name and comp_data['main_element'] == element:
849
- weight = comp_data['weight']
850
- break
851
-
852
- candidates.append({
853
- 'fert_name': fert_name,
854
- 'element': element,
855
- 'weight': weight,
856
- 'content': fert_data[element]
857
- })
858
-
859
- if not candidates:
860
- raise ValueError(f"No fertilizers found to compensate {element}")
861
-
862
- # Сортируем по весу (больший вес = выше приоритет)
863
- candidates.sort(key=lambda x: x['weight'], reverse=True)
864
-
865
- # Применяем удобрения по порядку, пока не компенсируем дефицит
866
- remaining_ppm = needed_ppm
867
- for candidate in candidates:
868
- if remaining_ppm <= 0:
869
- break
870
-
871
- # Рассчитываем сколько можем внести этим удобрением
872
- max_possible = remaining_ppm / candidate['weight'] if candidate['weight'] > 0 else remaining_ppm
873
- to_apply = min(max_possible, remaining_ppm)
874
-
875
- self._apply_fertilizer(candidate['fert_name'], candidate['element'], to_apply)
876
- remaining_ppm -= to_apply
877
 
878
- def calculate(self):
879
- """Основная логика расчёта с компенсационными весами"""
880
  try:
881
- # 1. Вносим магний и серу (без компенсации)
882
  self._apply_fertilizer("Сульфат магния", "Mg", self.target_profile['Mg'])
883
 
884
- # 2. Вносим аммонийный азот (без компенсации)
885
  nh4_needed = self.target_profile['N (NH4+)'] - self.actual_profile['N (NH4+)']
886
  if nh4_needed > 0:
887
  self._apply_fertilizer("Аммоний азотнокислый", "N (NH4+)", nh4_needed)
888
 
889
- # 3. Вносим нитратный азот с компенсацией
890
  no3_needed = self.target_profile['N (NO3-)'] - self.actual_profile['N (NO3-)']
891
  if no3_needed > 0:
892
- self._compensate_with_weights("N (NO3-)", no3_needed)
 
 
 
 
 
 
 
 
893
 
894
- # 4. Вносим фосфор (без компенсации)
895
  self._apply_fertilizer("Монофосфат калия", "P", self.target_profile['P'])
896
 
897
- # 5. Корректируем калий с компенсацией
898
  k_needed = self.target_profile['K'] - self.actual_profile['K']
899
  if k_needed > 0:
900
- self._compensate_with_weights("K", k_needed)
901
 
902
  return self._prepare_results()
903
 
904
  except Exception as e:
905
  raise RuntimeError(f"Ошибка расчёта: {str(e)}")
906
 
907
- def _prepare_results(self):
908
- """Форматируем результаты для ответа"""
909
  return {
910
- 'profile': self.actual_profile,
911
- 'fertilizers': self.results,
912
- 'stats': {
913
- 'total_ppm': sum(self.actual_profile.values()),
914
- 'nitrogen_ratio': {
915
- 'NO3': self.target_profile['N (NO3-)'],
916
- 'NH4': self.target_profile['N (NH4+)']
917
- },
918
- 'compensation_weights_used': self.compensation_weights
 
 
 
 
 
 
919
  }
920
  }
921
- if __name__ == "__main__":
922
- try:
923
- calculator = NutrientCalculator(volume_liters=VOLUME_LITERS)
924
- calculator.calculate()
925
- calculator.print_report() # Правильный вызов метода класса
926
- except Exception as e:
927
- print(f"Критическая ошибка: {str(e)}")
928
-
929
-
930
 
931
 
932
 
@@ -936,117 +914,84 @@ if __name__ == "__main__":
936
 
937
 
938
 
939
- from flask import request, jsonify
940
-
941
- def round_floats(obj, ndigits=3):
942
- """Рекурсивно округляет все float значения в структуре данных"""
943
- if isinstance(obj, float):
944
- return round(obj, ndigits)
945
- elif isinstance(obj, dict):
946
- return {k: round_floats(v, ndigits) for k, v in obj.items()}
947
- elif isinstance(obj, (list, tuple)):
948
- return [round_floats(x, ndigits) for x in obj]
949
- return obj
950
-
951
  @app.route('/calculation', methods=['POST'])
952
  def handle_calculation():
953
  try:
954
  data = request.get_json()
955
 
956
- # Получаем параметр точности округления (по умолчанию 3)
957
- rounding_precision = int(data['profileSettings'].get('rounding_precision', 3))
958
-
959
  # Проверка обязательных полей
960
  if not data or 'fertilizerConstants' not in data or 'profileSettings' not in data:
961
  return jsonify({'error': 'Неверный формат данных'}), 400
962
 
963
- # Извлекаем данные из запроса
964
- fertilizer_data = data['fertilizerConstants']
965
- profile_data = data['profileSettings']
966
-
967
- # Устанавливаем константы из запроса
968
- TOTAL_NITROGEN = float(profile_data.get('TOTAL_NITROG', 125.0))
969
- NO3_RATIO = float(profile_data.get('NO3_RAT', 8.25))
970
- VOLUME_LITERS = float(profile_data.get('liters', 100))
971
- NH4_RATIO = 1.00 # Фиксированное значение
972
-
973
- # Формируем целевой профиль
974
- target_profile = {
975
- 'P': float(profile_data.get('P', 31.0)),
976
- 'K': float(profile_data.get('K', 210.0)),
977
- 'Mg': float(profile_data.get('Mg', 24.0)),
978
- 'Ca': float(profile_data.get('Ca', 84.0)),
979
- 'S': float(profile_data.get('S', 56.439)),
980
- 'N (NO3-)': 0, # Будет рассчитано в калькуляторе
981
- 'N (NH4+)': 0 # Будет рассчитано в калькуляторе
982
- }
983
 
984
- # Обновляем константы удобрений
985
- NUTRIENT_CONTENT_IN_FERTILIZERS = {
986
  "Кальциевая селитра": {
987
- "N (NO3-)": float(fertilizer_data["Кальциевая селитра"].get("N (NO3-)", 0.11863)),
988
- "Ca": float(fertilizer_data["Кальциевая селитра"].get("Ca", 0.16972))
989
  },
990
  "Калий азотнокислый": {
991
- "N (NO3-)": float(fertilizer_data["Калий азотнокислый"].get("N (NO3-)", 0.13854)),
992
- "K": float(fertilizer_data["Калий азотнокислый"].get("K", 0.36672))
993
  },
994
  "Аммоний азотнокислый": {
995
- "N (NO3-)": float(fertilizer_data["Аммоний азотнокислый"].get("N (NO3-)", 0.17499)),
996
- "N (NH4+)": float(fertilizer_data["Аммоний азотнокислый"].get("N (NH4+)", 0.17499))
997
  },
998
  "Сульфат магния": {
999
- "Mg": float(fertilizer_data["Сульфат магния"].get("Mg", 0.1022)),
1000
- "S": float(fertilizer_data["Сульфат магния"].get("S", 0.13483))
1001
  },
1002
  "Монофосфат калия": {
1003
- "P": float(fertilizer_data["Монофосфат калия"].get("P", 0.22761)),
1004
- "K": float(fertilizer_data["Монофосфат калия"].get("K", 0.28731))
1005
  },
1006
  "Калий сернокислый": {
1007
- "K": float(fertilizer_data["Калий сернокислый"].get("K", 0.44874)),
1008
- "S": float(fertilizer_data["Калий сернокислый"].get("S", 0.18401))
1009
  }
1010
  }
1011
 
1012
- # Создаем и настраиваем калькулятор
1013
- calculator = NutrientCalculator(volume_liters=VOLUME_LITERS)
1014
- calculator.target_profile = target_profile
1015
- calculator.fertilizers = NUTRIENT_CONTENT_IN_FERTILIZERS
1016
-
1017
- # Устанавливаем параметры азота
1018
- calculator.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / (NO3_RATIO + NH4_RATIO))
1019
- calculator.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / (NO3_RATIO + NH4_RATIO))
1020
-
1021
- # Выполняем расчет
1022
- results = calculator.calculate()
1023
-
1024
- # Формируем ответ
1025
- response = {
1026
- 'actual_profile': calculator.actual_profile,
1027
- 'fertilizers': results,
1028
- 'total_ec': calculator.calculate_ec(),
1029
- 'total_ppm': sum(calculator.actual_profile.values()),
1030
- 'nitrogen_ratios': {
1031
- 'NO3_RATIO': NO3_RATIO,
1032
- 'NH4_RATIO': NH4_RATIO,
1033
- 'TOTAL_NITROGEN': TOTAL_NITROGEN
1034
- }
1035
  }
1036
 
1037
- # Округляем все числовые значения
1038
- rounded_response = round_floats(response, rounding_precision)
 
 
1039
 
1040
- # Для миллиграммов применяем целочисленное округление
1041
- if 'fertilizers' in rounded_response:
1042
- for fert in rounded_response['fertilizers'].values():
1043
- if 'миллиграммы' in fert:
1044
- fert['миллиграммы'] = int(round(fert['миллиграммы']))
1045
 
1046
- return jsonify(rounded_response)
 
 
1047
 
1048
  except Exception as e:
1049
  return jsonify({'error': str(e)}), 500
1050
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1051
  if __name__ == '__main__':
1052
  app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))
 
802
  self.total_ec = 0.0
803
  self.fertilizers = {}
804
 
805
+ # Коэффициенты для расчета EC (мСм/см на ppm)
806
+ self.ec_coefficients = {
807
+ 'P': 0.0012, 'K': 0.0018, 'Mg': 0.0015,
808
+ 'Ca': 0.0016, 'S': 0.0014,
809
+ 'N (NO3-)': 0.0017, 'N (NH4+)': 0.0019
810
  }
811
 
812
  def _apply_fertilizer(self, fert_name: str, element: str, ppm: float):
813
+ """Основной метод внесения удобрений с расчетом EC"""
814
  try:
815
  if ppm <= 0:
816
  return
817
 
818
  content = self.fertilizers[fert_name][element]
819
  grams = (ppm * self.volume) / (content * 1000)
820
+ milligrams = grams * 1000
821
 
822
  if fert_name not in self.results:
823
  self.results[fert_name] = {
824
+ 'граммы': 0.0,
825
+ 'миллиграммы': 0,
826
+ 'вклад в EC': 0.0,
827
+ 'внесенные ppm': {}
828
  }
829
 
830
+ self.results[fert_name]['граммы'] += grams
831
+ self.results[fert_name]['миллиграммы'] = int(self.results[fert_name]['граммы'] * 1000)
832
 
833
+ # Обновляем все элементы из удобрения и рассчитываем EC
834
+ fert_ec = 0.0
835
  for el, percent in self.fertilizers[fert_name].items():
836
  added_ppm = (grams * percent * 1000) / self.volume
837
  self.actual_profile[el] = self.actual_profile.get(el, 0) + added_ppm
838
+ self.results[fert_name]['внесенные ppm'][el] = self.results[fert_name]['внесенные ppm'].get(el, 0) + added_ppm
839
+ fert_ec += added_ppm * self.ec_coefficients.get(el, 0.0015)
840
+
841
+ self.results[fert_name]['вклад в EC'] += fert_ec
842
+ self.total_ec += fert_ec
843
 
844
  except KeyError as e:
845
  raise ValueError(f"Неверный элемент или удобрение: {str(e)}")
846
 
847
+ def calculate_ec(self) -> float:
848
+ """Расчет общей электропроводности"""
849
+ return round(self.total_ec, 2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
850
 
851
+ def calculate(self) -> Dict[str, Any]:
852
+ """Основная логика расчёта"""
853
  try:
854
+ # 1. Вносим магний и серу
855
  self._apply_fertilizer("Сульфат магния", "Mg", self.target_profile['Mg'])
856
 
857
+ # 2. Вносим аммонийный азот
858
  nh4_needed = self.target_profile['N (NH4+)'] - self.actual_profile['N (NH4+)']
859
  if nh4_needed > 0:
860
  self._apply_fertilizer("Аммоний азотнокислый", "N (NH4+)", nh4_needed)
861
 
862
+ # 3. Вносим нитратный азот
863
  no3_needed = self.target_profile['N (NO3-)'] - self.actual_profile['N (NO3-)']
864
  if no3_needed > 0:
865
+ # Через кальциевую селитру (если нужен кальций)
866
+ ca_needed = self.target_profile['Ca'] - self.actual_profile['Ca']
867
+ if ca_needed > 0:
868
+ self._apply_fertilizer("Кальциевая селитра", "N (NO3-)", min(no3_needed, ca_needed/0.16972*0.11863))
869
+
870
+ # Через калийную селитру (остаток)
871
+ remaining_no3 = self.target_profile['N (NO3-)'] - self.actual_profile['N (NO3-)']
872
+ if remaining_no3 > 0:
873
+ self._apply_fertilizer("Калий азотнокислый", "N (NO3-)", remaining_no3)
874
 
875
+ # 4. Вносим фосфор
876
  self._apply_fertilizer("Монофосфат калия", "P", self.target_profile['P'])
877
 
878
+ # 5. Корректируем калий
879
  k_needed = self.target_profile['K'] - self.actual_profile['K']
880
  if k_needed > 0:
881
+ self._apply_fertilizer("Калий сернокислый", "K", k_needed)
882
 
883
  return self._prepare_results()
884
 
885
  except Exception as e:
886
  raise RuntimeError(f"Ошибка расчёта: {str(e)}")
887
 
888
+ def _prepare_results(self) -> Dict[str, Any]:
889
+ """Форматируем результаты для ответа в Flask"""
890
  return {
891
+ 'actual_profile': {k: round(v, 2) for k, v in self.actual_profile.items()},
892
+ 'fertilizers': {
893
+ fert: {
894
+ 'граммы': round(data['граммы'], 3),
895
+ 'миллиграммы': data['миллиграммы'],
896
+ 'вклад в EC': round(data['вклад в EC'], 3),
897
+ 'внесенные ppm': {k: round(v, 2) for k, v in data['внесенные ppm'].items()}
898
+ }
899
+ for fert, data in self.results.items()
900
+ },
901
+ 'total_ec': self.calculate_ec(),
902
+ 'total_ppm': round(sum(self.actual_profile.values()), 2),
903
+ 'nitrogen_ratios': {
904
+ 'NO3': round(self.target_profile['N (NO3-)'], 2),
905
+ 'NH4': round(self.target_profile['N (NH4+)'], 2)
906
  }
907
  }
 
 
 
 
 
 
 
 
 
908
 
909
 
910
 
 
914
 
915
 
916
 
 
 
 
 
 
 
 
 
 
 
 
 
917
  @app.route('/calculation', methods=['POST'])
918
  def handle_calculation():
919
  try:
920
  data = request.get_json()
921
 
 
 
 
922
  # Проверка обязательных полей
923
  if not data or 'fertilizerConstants' not in data or 'profileSettings' not in data:
924
  return jsonify({'error': 'Неверный формат данных'}), 400
925
 
926
+ # Инициализация калькулятора
927
+ calculator = NutrientCalculator(volume_liters=float(data['profileSettings'].get('liters', 100)))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
928
 
929
+ # Настройка удобрений из запроса
930
+ calculator.fertilizers = {
931
  "Кальциевая селитра": {
932
+ "N (NO3-)": float(data['fertilizerConstants']["Кальциевая селитра"].get("N (NO3-)", 0.11863)),
933
+ "Ca": float(data['fertilizerConstants']["Кальциевая селитра"].get("Ca", 0.16972))
934
  },
935
  "Калий азотнокислый": {
936
+ "N (NO3-)": float(data['fertilizerConstants']["Калий азотнокислый"].get("N (NO3-)", 0.136)),
937
+ "K": float(data['fertilizerConstants']["Калий азотнокислый"].get("K", 0.382))
938
  },
939
  "Аммоний азотнокислый": {
940
+ "N (NO3-)": float(data['fertilizerConstants']["Аммоний азотнокислый"].get("N (NO3-)", 0.17499)),
941
+ "N (NH4+)": float(data['fertilizerConstants']["Аммоний азотнокислый"].get("N (NH4+)", 0.17499))
942
  },
943
  "Сульфат магния": {
944
+ "Mg": float(data['fertilizerConstants']["Сульфат магния"].get("Mg", 0.09861)),
945
+ "S": float(data['fertilizerConstants']["Сульфат магния"].get("S", 0.13010))
946
  },
947
  "Монофосфат калия": {
948
+ "P": float(data['fertilizerConstants']["Монофосфат калия"].get("P", 0.218)),
949
+ "K": float(data['fertilizerConstants']["Монофосфат калия"].get("K", 0.275))
950
  },
951
  "Калий сернокислый": {
952
+ "K": float(data['fertilizerConstants']["Калий сернокислый"].get("K", 0.44874)),
953
+ "S": float(data['fertilizerConstants']["Калий сернокислый"].get("S", 0.18401))
954
  }
955
  }
956
 
957
+ # Настройка целевого профиля
958
+ calculator.target_profile = {
959
+ 'P': float(data['profileSettings'].get('P', 31.0)),
960
+ 'K': float(data['profileSettings'].get('K', 210.0)),
961
+ 'Mg': float(data['profileSettings'].get('Mg', 24.0)),
962
+ 'Ca': float(data['profileSettings'].get('Ca', 84.0)),
963
+ 'S': float(data['profileSettings'].get('S', 56.439)),
964
+ 'N (NO3-)': 0.0,
965
+ 'N (NH4+)': 0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
966
  }
967
 
968
+ # Установка параметров азота
969
+ total_n = float(data['profileSettings'].get('TOTAL_NITROGEN', 125.0))
970
+ no3_ratio = float(data['profileSettings'].get('NO3_RATIO', 8.25))
971
+ nh4_ratio = 1.0
972
 
973
+ calculator.target_profile['N (NO3-)'] = total_n * (no3_ratio / (no3_ratio + nh4_ratio))
974
+ calculator.target_profile['N (NH4+)'] = total_n * (nh4_ratio / (no3_ratio + nh4_ratio))
 
 
 
975
 
976
+ # Выполнение расчета
977
+ results = calculator.calculate()
978
+ return jsonify(results)
979
 
980
  except Exception as e:
981
  return jsonify({'error': str(e)}), 500
982
 
983
+
984
+
985
+
986
+
987
+
988
+
989
+
990
+
991
+
992
+
993
+
994
+
995
+
996
  if __name__ == '__main__':
997
  app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))