DmitrMakeev commited on
Commit
bb865e0
·
verified ·
1 Parent(s): 242e355

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +164 -226
app.py CHANGED
@@ -718,40 +718,26 @@ NUTRIENT_CONTENT_IN_FERTILIZERS = {
718
  }
719
 
720
  class NutrientCalculator:
721
- def __init__(self, volume_liters=1.0):
722
  self.volume = volume_liters
723
  self.results = {}
724
- self.target_profile = BASE_PROFILE.copy()
725
- self.actual_profile = {k: 0.0 for k in BASE_PROFILE}
726
- self.fertilizers = NUTRIENT_CONTENT_IN_FERTILIZERS
727
  self.total_ec = 0.0
728
-
729
- # Расчет азота
730
- total_parts = NO3_RATIO + NH4_RATIO
731
- self.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / total_parts)
732
- self.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / total_parts)
733
- self.initial_n_profile = {
734
- "NO3-": self.target_profile['N (NO3-)'],
735
- "NH4+": self.target_profile['N (NH4+)']
736
- }
737
-
738
- # Настройки компенсации по умолчанию
739
  self.compensation_weights = {
740
- 'KNO3': 0.5, # Вес калийной селитры (0-1)
741
- 'CaNO3': 0.3, # Вес кальциевой селитры (0-1)
742
- 'K2SO4': 0.2 # Вес сульфата калия (0-1)
743
  }
744
 
745
- def _label(self, element):
746
- """Форматирование названий элементов для вывода"""
747
  labels = {
748
  'N (NO3-)': 'NO3',
749
  'N (NH4+)': 'NH4'
750
  }
751
  return labels.get(element, element)
752
 
753
- def set_compensation_weights(self, kno3_weight, cano3_weight, k2so4_weight):
754
- """Установка весов для компенсации элементов"""
755
  total = kno3_weight + cano3_weight + k2so4_weight
756
  self.compensation_weights = {
757
  'KNO3': kno3_weight / total,
@@ -759,174 +745,117 @@ class NutrientCalculator:
759
  'K2SO4': k2so4_weight / total
760
  }
761
 
762
- def calculate(self):
763
- """Основной метод расчета (исправленная версия)"""
764
- try:
765
- # 1. Вносим Mg и S
766
- self._apply("Сульфат магния", "Mg", self.target_profile['Mg'])
767
-
768
- # 2. Балансируем азот с учетом компенсации
769
- self._balance_nitrogen_with_compensation()
770
-
771
- # 3. Вносим Ca (остаток)
772
- ca_needed = self.target_profile['Ca'] - self.actual_profile['Ca']
773
- if ca_needed > 0.1:
774
- self._apply("Кальциевая селитра", "Ca", ca_needed)
775
-
776
- # 4. Вносим P
777
- p_needed = self.target_profile['P'] - self.actual_profile['P']
778
- if p_needed > 0.1:
779
- self._apply("Монофосфат калия", "P", p_needed)
780
-
781
- # 5. Корректируем K
782
- k_needed = self.target_profile['K'] - self.actual_profile['K']
783
- if k_needed > 0.1:
784
- self._apply("Калий сернокислый", "K", k_needed)
785
-
786
- return self.results
787
- except Exception as e:
788
- logging.error(f"Calculation error: {str(e)}")
789
- raise
790
-
791
-
792
-
793
- def _apply(self, fert_name, main_element, required_ppm):
794
  if required_ppm <= 0:
795
  return
796
-
797
  try:
798
  content = self.fertilizers[fert_name][main_element]
799
  grams = (required_ppm * self.volume) / (content * 1000)
800
 
801
  if fert_name not in self.results:
802
- result = {
803
  'граммы': 0.0,
804
  'миллиграммы': 0,
805
  'вклад в EC': 0.0
806
  }
807
  for element in self.fertilizers[fert_name]:
808
- result[f'внесет {self._label(element)}'] = 0.0
809
- self.results[fert_name] = result
810
-
811
  self.results[fert_name]['граммы'] += grams
812
- self.results[fert_name]['миллиграммы'] += int(grams * 1000)
813
 
814
  fert_ec = 0.0
815
  for element, percent in self.fertilizers[fert_name].items():
816
  added_ppm = (grams * percent * 1000) / self.volume
817
  self.results[fert_name][f'внесет {self._label(element)}'] += added_ppm
818
  self.actual_profile[element] += added_ppm
819
- fert_ec += added_ppm * EC_COEFFICIENTS.get(element, 0.0015)
820
-
821
  self.results[fert_name]['вклад в EC'] += fert_ec
822
  self.total_ec += fert_ec
 
823
  except KeyError as e:
824
- print(f"Ошибка: отсутствует элемент {str(e)} в удобрении {fert_name}")
825
  raise
826
 
827
- def _apply_k_sulfate(self):
828
- fert = "Калий сернокислый"
829
- k_def = self.target_profile['K'] - self.actual_profile['K']
830
- s_def = self.target_profile['S'] - self.actual_profile['S']
831
-
832
- if k_def <= 0 and s_def <= 0:
833
- return
834
-
835
  try:
836
- if s_def > 0.1:
837
- s_content = self.fertilizers[fert]["S"]
838
- grams_s = (s_def * self.volume) / (s_content * 1000)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
839
 
840
- k_content = self.fertilizers[fert]["K"]
841
- k_from_s = (grams_s * k_content * 1000) / self.volume
 
 
 
 
 
 
 
842
 
843
- if k_from_s > k_def and k_def > 0.1:
844
- grams = (k_def * self.volume) / (k_content * 1000)
845
- else:
846
- grams = grams_s
847
-
848
- self._apply(fert, "S", s_def)
849
  except Exception as e:
850
- print(f"Ошибка при расчёте сульфата калия: {str(e)}")
851
  raise
852
 
853
- def calculate_ec(self):
854
- return round(self.total_ec, 2)
855
-
856
- def print_report(self):
857
  try:
858
- print("\n" + "="*60)
859
- print("ПРОФИЛЬ ПИТАТЕЛЬНОГО РАСТВОРА (ИТОГО):")
860
- print("="*60)
861
- table = [[el, round(self.actual_profile[el], 1)] for el in self.actual_profile]
862
- print(tabulate(table, headers=["Элемент", "ppm"]))
863
-
864
- print("\nИсходный расчёт азота:")
865
- for form, val in self.initial_n_profile.items():
866
- print(f" {form}: {round(val, 1)} ppm")
867
-
868
- print("\n" + "="*60)
869
- print(f"РАСЧЕТ ДЛЯ {self.volume} ЛИТРОВ РАСТВОРА")
870
- print("="*60)
871
- print(f"Общая концентрация: {round(sum(self.actual_profile.values()), 1)} ppm")
872
- print(f"EC: {self.calculate_ec()} mS/cm")
873
-
874
- print("\nРЕКОМЕНДУЕМЫЕ УДОБРЕНИЯ:")
875
- fert_table = []
876
- for fert, data in self.results.items():
877
- adds = [f"+{k}: {v:.1f} ppm" for k, v in data.items() if k.startswith('внесет')]
878
- fert_table.append([
879
- fert,
880
- round(data['граммы'], 3),
881
- data['миллиграммы'],
882
- round(data['вклад в EC'], 3),
883
- "\n".join(adds)
884
- ])
885
- print(tabulate(fert_table,
886
- headers=["Удобрение", "Граммы", "Миллиграммы", "EC (мСм/см)", "Добавит"]))
887
-
888
- print("\nОСТАТОЧНЫЙ ДЕФИЦИТ:")
889
- deficit = {
890
- k: round(self.target_profile[k] - self.actual_profile[k], 1)
891
- for k in self.target_profile
892
- if abs(self.target_profile[k] - self.actual_profile[k]) > 0.1
893
- }
894
- if deficit:
895
- for el, val in deficit.items():
896
- print(f" {el}: {val} ppm")
897
- else:
898
- print(" Все элементы покрыты полностью")
899
  except Exception as e:
900
- print(f"Ошибка при выводе отчёта: {str(e)}")
901
  raise
902
 
903
- if __name__ == "__main__":
904
- try:
905
- calculator = NutrientCalculator(volume_liters=VOLUME_LITERS)
906
- calculator.calculate()
907
- calculator.print_report() # Правильный вызов метода класса
908
- except Exception as e:
909
- print(f"Критическая ошибка: {str(e)}")
910
-
911
-
912
-
913
-
914
-
915
-
916
-
917
-
918
-
919
-
920
-
921
- from flask import request, jsonify
922
 
923
- def round_floats(obj, ndigits=3):
924
- """Рекурсивно округляет все float значения в структуре данных"""
925
  if isinstance(obj, float):
926
  return round(obj, ndigits)
927
  elif isinstance(obj, dict):
928
  return {k: round_floats(v, ndigits) for k, v in obj.items()}
929
- elif isinstance(obj, (list, tuple)):
930
  return [round_floats(x, ndigits) for x in obj]
931
  return obj
932
 
@@ -934,104 +863,113 @@ def round_floats(obj, ndigits=3):
934
  def handle_calculation():
935
  try:
936
  data = request.get_json()
 
937
 
938
- # Получаем параметр точности округления (по умолчанию 3)
939
- rounding_precision = int(data['profileSettings'].get('rounding_precision', 3))
 
 
940
 
941
- # Проверка обязательных полей
942
- if not data or 'fertilizerConstants' not in data or 'profileSettings' not in data:
943
- return jsonify({'error': 'Неверный формат данных'}), 400
944
-
945
- # Извлекаем данные из запроса
946
- fertilizer_data = data['fertilizerConstants']
947
- profile_data = data['profileSettings']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
948
 
949
- # Устанавливаем константы из запроса
950
- TOTAL_NITROGEN = float(profile_data.get('TOTAL_NITROG', 125.0))
951
- NO3_RATIO = float(profile_data.get('NO3_RAT', 8.25))
952
- VOLUME_LITERS = float(profile_data.get('liters', 100))
953
- NH4_RATIO = 1.00 # Фиксированное значение
 
 
 
 
 
 
 
954
 
955
- # Формируем целевой профиль
956
  target_profile = {
957
- 'P': float(profile_data.get('P', 31.0)),
958
- 'K': float(profile_data.get('K', 210.0)),
959
- 'Mg': float(profile_data.get('Mg', 24.0)),
960
- 'Ca': float(profile_data.get('Ca', 84.0)),
961
- 'S': float(profile_data.get('S', 56.439)),
962
- 'N (NO3-)': 0, # Будет рассчитано в калькуляторе
963
- 'N (NH4+)': 0 # Будет рассчитано в калькуляторе
964
  }
965
-
966
- # Обновляем константы удобрений
967
- NUTRIENT_CONTENT_IN_FERTILIZERS = {
968
- "Кальциевая селитра": {
969
- "N (NO3-)": float(fertilizer_data["Кальциевая селитра"].get("N (NO3-)", 0.11863)),
970
- "Ca": float(fertilizer_data["Кальциевая селитра"].get("Ca", 0.16972))
971
- },
972
- "Калий азотнокислый": {
973
- "N (NO3-)": float(fertilizer_data["Калий азотнокислый"].get("N (NO3-)", 0.13854)),
974
- "K": float(fertilizer_data["Калий азотнокислый"].get("K", 0.36672))
975
- },
976
- "Аммоний азотнокислый": {
977
- "N (NO3-)": float(fertilizer_data["Аммоний азотнокислый"].get("N (NO3-)", 0.17499)),
978
- "N (NH4+)": float(fertilizer_data["Аммоний азотнокислый"].get("N (NH4+)", 0.17499))
979
- },
980
- "Сульфат магния": {
981
- "Mg": float(fertilizer_data["Сульфат магния"].get("Mg", 0.1022)),
982
- "S": float(fertilizer_data["Сульфат магния"].get("S", 0.13483))
983
- },
984
- "Монофосфат калия": {
985
- "P": float(fertilizer_data["Монофосфат калия"].get("P", 0.22761)),
986
- "K": float(fertilizer_data["Монофосфат калия"].get("K", 0.28731))
987
- },
988
- "Калий сернокислый": {
989
- "K": float(fertilizer_data["Калий сернокислый"].get("K", 0.44874)),
990
- "S": float(fertilizer_data["Калий сернокислый"].get("S", 0.18401))
991
- }
992
- }
993
-
994
- # Создаем и настраиваем калькулятор
995
- calculator = NutrientCalculator(volume_liters=VOLUME_LITERS)
996
  calculator.target_profile = target_profile
997
- calculator.fertilizers = NUTRIENT_CONTENT_IN_FERTILIZERS
998
-
999
- # Устанавливаем параметры азота
1000
- calculator.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / (NO3_RATIO + NH4_RATIO))
1001
- calculator.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / (NO3_RATIO + NH4_RATIO))
1002
-
1003
- # Выполняем расчет
1004
- results = calculator.calculate()
1005
-
1006
- # Формируем ответ
 
 
 
 
 
 
 
 
 
 
 
1007
  response = {
1008
  'actual_profile': calculator.actual_profile,
1009
  'fertilizers': results,
1010
  'total_ec': calculator.calculate_ec(),
1011
  'total_ppm': sum(calculator.actual_profile.values()),
1012
  'nitrogen_ratios': {
1013
- 'NO3_RATIO': NO3_RATIO,
1014
- 'NH4_RATIO': NH4_RATIO,
1015
- 'TOTAL_NITROGEN': TOTAL_NITROGEN
1016
  }
1017
  }
1018
-
1019
- # Округляем все числовые значения
1020
  rounded_response = round_floats(response, rounding_precision)
1021
 
1022
- # Для миллиграммов применяем целочисленное округление
1023
  if 'fertilizers' in rounded_response:
1024
  for fert in rounded_response['fertilizers'].values():
1025
  if 'миллиграммы' in fert:
1026
- fert['миллиграммы'] = int(round(fert['миллиграммы']))
1027
-
 
1028
  return jsonify(rounded_response)
1029
 
1030
  except Exception as e:
1031
- return jsonify({'error': str(e)}), 500
1032
-
1033
-
1034
-
1035
 
1036
  if __name__ == '__main__':
1037
  app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))
 
718
  }
719
 
720
  class NutrientCalculator:
721
+ def __init__(self, volume_liters: float = 1.0):
722
  self.volume = volume_liters
723
  self.results = {}
724
+ self.target_profile = DEFAULT_VALUES['BASE_PROFILE'].copy()
725
+ self.actual_profile = {k: 0.0 for k in DEFAULT_VALUES['BASE_PROFILE']}
 
726
  self.total_ec = 0.0
 
 
 
 
 
 
 
 
 
 
 
727
  self.compensation_weights = {
728
+ 'KNO3': 0.5,
729
+ 'CaNO3': 0.3,
730
+ 'K2SO4': 0.2
731
  }
732
 
733
+ def _label(self, element: str) -> str:
 
734
  labels = {
735
  'N (NO3-)': 'NO3',
736
  'N (NH4+)': 'NH4'
737
  }
738
  return labels.get(element, element)
739
 
740
+ def set_compensation_weights(self, kno3_weight: float, cano3_weight: float, k2so4_weight: float):
 
741
  total = kno3_weight + cano3_weight + k2so4_weight
742
  self.compensation_weights = {
743
  'KNO3': kno3_weight / total,
 
745
  'K2SO4': k2so4_weight / total
746
  }
747
 
748
+ def _apply(self, fert_name: str, main_element: str, required_ppm: float):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
749
  if required_ppm <= 0:
750
  return
751
+
752
  try:
753
  content = self.fertilizers[fert_name][main_element]
754
  grams = (required_ppm * self.volume) / (content * 1000)
755
 
756
  if fert_name not in self.results:
757
+ self.results[fert_name] = {
758
  'граммы': 0.0,
759
  'миллиграммы': 0,
760
  'вклад в EC': 0.0
761
  }
762
  for element in self.fertilizers[fert_name]:
763
+ self.results[fert_name][f'внесет {self._label(element)}'] = 0.0
764
+
 
765
  self.results[fert_name]['граммы'] += grams
766
+ self.results[fert_name]['миллиграммы'] = int(self.results[fert_name]['граммы'] * 1000)
767
 
768
  fert_ec = 0.0
769
  for element, percent in self.fertilizers[fert_name].items():
770
  added_ppm = (grams * percent * 1000) / self.volume
771
  self.results[fert_name][f'внесет {self._label(element)}'] += added_ppm
772
  self.actual_profile[element] += added_ppm
773
+ fert_ec += added_ppm * DEFAULT_VALUES['EC_COEFFICIENTS'].get(element, 0.0015)
774
+
775
  self.results[fert_name]['вклад в EC'] += fert_ec
776
  self.total_ec += fert_ec
777
+
778
  except KeyError as e:
779
+ logger.error(f"Key error in _apply: {str(e)}")
780
  raise
781
 
782
+ def _balance_nitrogen_with_compensation(self):
 
 
 
 
 
 
 
783
  try:
784
+ # Вносим NH4+
785
+ nh4_needed = self.target_profile['N (NH4+)'] - self.actual_profile['N (NH4+)']
786
+ if nh4_needed > 0.1:
787
+ self._apply("Аммоний азотнокислый", "N (NH4+)", nh4_needed)
788
+
789
+ # Вносим NO3- с компенсацией
790
+ no3_needed = self.target_profile['N (NO3-)'] - self.actual_profile['N (NO3-)']
791
+ if no3_needed > 0.1:
792
+ # Через CaNO3 (если нужен Ca)
793
+ ca_part = self.compensation_weights['CaNO3'] * no3_needed
794
+ ca_needed = self.target_profile['Ca'] - self.actual_profile['Ca']
795
+ if ca_needed > 0.1:
796
+ max_ca_no3 = (ca_needed / self.fertilizers["Кальциевая селитра"]["Ca"]) * \
797
+ self.fertilizers["Кальциевая селитра"]["N (NO3-)"]
798
+ ca_no3_part = min(ca_part, max_ca_no3)
799
+ self._apply("Кальциевая селитра", "N (NO3-)", ca_no3_part)
800
+ no3_needed -= ca_no3_part
801
 
802
+ # Через KNO3 (если нужен K)
803
+ kno3_part = self.compensation_weights['KNO3'] * no3_needed
804
+ k_remaining = self.target_profile['K'] - self.actual_profile['K']
805
+ if k_remaining > 0.1:
806
+ max_k_no3 = (k_remaining / self.fertilizers["Калий азотнокислый"]["K"]) * \
807
+ self.fertilizers["Калий азотнокислый"]["N (NO3-)"]
808
+ k_no3_part = min(kno3_part, max_k_no3)
809
+ self._apply("Калий азотнокислый", "N (NO3-)", k_no3_part)
810
+ no3_needed -= k_no3_part
811
 
812
+ # Остаток через K2SO4 (если разрешено весами)
813
+ if no3_needed > 0.1 and self.compensation_weights['K2SO4'] > 0:
814
+ pass # Логика компенсации через K2SO4
815
+
 
 
816
  except Exception as e:
817
+ logger.error(f"Error in _balance_nitrogen_with_compensation: {str(e)}")
818
  raise
819
 
820
+ def calculate(self) -> Dict[str, Any]:
 
 
 
821
  try:
822
+ # 1. Вносим Mg и S
823
+ self._apply("Сульфат магния", "Mg", self.target_profile['Mg'])
824
+
825
+ # 2. Балансируем азот
826
+ self._balance_nitrogen_with_compensation()
827
+
828
+ # 3. Вносим Ca (остаток)
829
+ ca_needed = self.target_profile['Ca'] - self.actual_profile['Ca']
830
+ if ca_needed > 0.1:
831
+ self._apply("Кальциевая селитра", "Ca", ca_needed)
832
+
833
+ # 4. Вносим P
834
+ p_needed = self.target_profile['P'] - self.actual_profile['P']
835
+ if p_needed > 0.1:
836
+ self._apply("Монофосфат калия", "P", p_needed)
837
+
838
+ # 5. Корректируем K
839
+ k_needed = self.target_profile['K'] - self.actual_profile['K']
840
+ if k_needed > 0.1:
841
+ self._apply("Калий сернокислый", "K", k_needed)
842
+
843
+ return self.results
844
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
845
  except Exception as e:
846
+ logger.error(f"Calculation error: {str(e)}")
847
  raise
848
 
849
+ def calculate_ec(self) -> float:
850
+ return round(self.total_ec, 2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
851
 
852
+ def round_floats(obj: Union[float, Dict, List], ndigits: int = 3) -> Union[float, Dict, List]:
853
+ """Рекурсивно округляет float значения"""
854
  if isinstance(obj, float):
855
  return round(obj, ndigits)
856
  elif isinstance(obj, dict):
857
  return {k: round_floats(v, ndigits) for k, v in obj.items()}
858
+ elif isinstance(obj, list):
859
  return [round_floats(x, ndigits) for x in obj]
860
  return obj
861
 
 
863
  def handle_calculation():
864
  try:
865
  data = request.get_json()
866
+ logger.info(f"Received request data: {data}")
867
 
868
+ # Валидация базовой структуры
869
+ if not data or not isinstance(data, dict):
870
+ logger.error("Invalid JSON format")
871
+ return jsonify({'error': 'Invalid JSON format'}), 400
872
 
873
+ # Проверка обязательных разделов
874
+ required_sections = ['fertilizerConstants', 'profileSettings']
875
+ for section in required_sections:
876
+ if section not in data or not isinstance(data[section], dict):
877
+ logger.error(f"Missing or invalid section: {section}")
878
+ return jsonify({'error': f'Missing or invalid {section}'}), 400
879
+
880
+ # Извлечение данных
881
+ try:
882
+ rounding_precision = int(data['profileSettings'].get('rounding_precision', 3))
883
+ volume_liters = float(data['profileSettings'].get('liters', DEFAULT_VALUES['VOLUME_LITERS']))
884
+ total_nitrogen = float(data['profileSettings'].get('TOTAL_NITROG', DEFAULT_VALUES['TOTAL_NITROGEN']))
885
+ no3_ratio = float(data['profileSettings'].get('NO3_RAT', DEFAULT_VALUES['NO3_RATIO']))
886
+ nh4_ratio = DEFAULT_VALUES['NH4_RATIO']
887
+ except (ValueError, TypeError) as e:
888
+ logger.error(f"Invalid numeric value: {str(e)}")
889
+ return jsonify({'error': f'Invalid numeric value: {str(e)}'}), 400
890
+
891
+ # Проверка удобрений
892
+ required_fertilizers = {
893
+ "Кальциевая селитра": ["N (NO3-)", "Ca"],
894
+ "Калий азотнокислый": ["N (NO3-)", "K"],
895
+ "Аммоний азотнокислый": ["N (NO3-)", "N (NH4+)"],
896
+ "Сульфат магния": ["Mg", "S"],
897
+ "Монофосфат калия": ["P", "K"],
898
+ "Калий сернокислый": ["K", "S"]
899
+ }
900
 
901
+ for fert, elements in required_fertilizers.items():
902
+ if fert not in data['fertilizerConstants']:
903
+ logger.error(f"Missing fertilizer: {fert}")
904
+ return jsonify({'error': f'Missing fertilizer: {fert}'}), 400
905
+ for element in elements:
906
+ if element not in data['fertilizerConstants'][fert]:
907
+ logger.error(f"Missing element {element} in {fert}")
908
+ return jsonify({'error': f'Missing element {element} in {fert}'}), 400
909
+
910
+ # Создание калькулятора
911
+ calculator = NutrientCalculator(volume_liters=volume_liters)
912
+ calculator.fertilizers = data['fertilizerConstants']
913
 
914
+ # Установка целевого профиля
915
  target_profile = {
916
+ 'P': float(data['profileSettings'].get('P', DEFAULT_VALUES['BASE_PROFILE']['P'])),
917
+ 'K': float(data['profileSettings'].get('K', DEFAULT_VALUES['BASE_PROFILE']['K'])),
918
+ 'Mg': float(data['profileSettings'].get('Mg', DEFAULT_VALUES['BASE_PROFILE']['Mg'])),
919
+ 'Ca': float(data['profileSettings'].get('Ca', DEFAULT_VALUES['BASE_PROFILE']['Ca'])),
920
+ 'S': float(data['profileSettings'].get('S', DEFAULT_VALUES['BASE_PROFILE']['S'])),
921
+ 'N (NO3-)': total_nitrogen * (no3_ratio / (no3_ratio + nh4_ratio)),
922
+ 'N (NH4+)': total_nitrogen * (nh4_ratio / (no3_ratio + nh4_ratio))
923
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
924
  calculator.target_profile = target_profile
925
+
926
+ # Установка весов компенсации (если есть)
927
+ if 'compensationWeights' in data['profileSettings']:
928
+ try:
929
+ weights = data['profileSettings']['compensationWeights']
930
+ calculator.set_compensation_weights(
931
+ float(weights.get('KNO3', 0.5)),
932
+ float(weights.get('CaNO3', 0.3)),
933
+ float(weights.get('K2SO4', 0.2))
934
+ )
935
+ except Exception as e:
936
+ logger.warning(f"Invalid compensation weights: {str(e)}")
937
+
938
+ # Выполнение расчета
939
+ try:
940
+ results = calculator.calculate()
941
+ except Exception as e:
942
+ logger.error(f"Calculation failed: {str(e)}\n{traceback.format_exc()}")
943
+ return jsonify({'error': 'Calculation failed'}), 500
944
+
945
+ # Формирование ответа
946
  response = {
947
  'actual_profile': calculator.actual_profile,
948
  'fertilizers': results,
949
  'total_ec': calculator.calculate_ec(),
950
  'total_ppm': sum(calculator.actual_profile.values()),
951
  'nitrogen_ratios': {
952
+ 'NO3_RATIO': no3_ratio,
953
+ 'NH4_RATIO': nh4_ratio,
954
+ 'TOTAL_NITROGEN': total_nitrogen
955
  }
956
  }
957
+
958
+ # Округление значений
959
  rounded_response = round_floats(response, rounding_precision)
960
 
961
+ # Коррекция миллиграммов
962
  if 'fertilizers' in rounded_response:
963
  for fert in rounded_response['fertilizers'].values():
964
  if 'миллиграммы' in fert:
965
+ fert['миллиграммы'] = int(round(fert['граммы'] * 1000))
966
+
967
+ logger.info("Calculation completed successfully")
968
  return jsonify(rounded_response)
969
 
970
  except Exception as e:
971
+ logger.error(f"Server error: {str(e)}\n{traceback.format_exc()}")
972
+ return jsonify({'error': 'Internal server error'}), 500
 
 
973
 
974
  if __name__ == '__main__':
975
  app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))