DmitrMakeev commited on
Commit
ed1c345
·
verified ·
1 Parent(s): 9c604bc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +122 -297
app.py CHANGED
@@ -680,340 +680,165 @@ def view_image():
680
  def nutri_call():
681
  return render_template('nutri_call.html')
682
 
683
- # Константы
684
- TOTAL_NITROGEN = 125.000
685
- NO3_RATIO = 8.25
686
- NH4_RATIO = 1.00
687
- VOLUME_LITERS = 100
688
-
689
- # Коэффициенты электропроводности
690
- EC_COEFFICIENTS = {
691
- 'P': 0.0012, 'K': 0.0018, 'Mg': 0.0015,
692
- 'Ca': 0.0016, 'S': 0.0014,
693
- 'N (NO3-)': 0.0017, 'N (NH4+)': 0.0019
694
- }
695
-
696
- # Целевые значения
697
- BASE_PROFILE = {
698
- 'P': 31.000, 'K': 210.000, 'Mg': 24.000,
699
- 'Ca': 84.000, 'S': 56.439,
700
- 'N (NO3-)': 0, 'N (NH4+)': 0
701
- }
702
-
703
-
704
- # Содержание питательных элементов в удобрениях (в массовых долях)
705
- NUTRIENT_CONTENT_IN_FERTILIZERS = {
706
- "Кальциевая селитра": {"N (NO3-)": 0.11863, "Ca": 0.16972},
707
- "Калий азотнокислый": {"N (NO3-)": 0.136, "K": 0.382},
708
- "Калий сернокислый": {"K": 0.44874, "S": 0.18401},
709
- "Аммоний азотнокислый": {"N (NO3-)": 0.17499, "N (NH4+)": 0.17499},
710
- "Сульфат магния": {"Mg": 0.09861, "S": 0.13010},
711
- "Монофосфат калия": {"P": 0.218, "K": 0.275}
712
- }
713
 
714
 
715
  class NutrientCalculator:
716
  def __init__(self, volume_liters=1.0):
717
  self.volume = volume_liters
718
  self.results = {}
719
- self.target_profile = BASE_PROFILE.copy()
720
- self.actual_profile = {k: 0.0 for k in BASE_PROFILE}
721
- self.fertilizers = NUTRIENT_CONTENT_IN_FERTILIZERS
722
  self.total_ec = 0.0
723
 
724
- # Расчёт азота
725
- total_parts = NO3_RATIO + NH4_RATIO
726
- self.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / total_parts)
727
- self.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / total_parts)
728
- self.initial_n_profile = {
729
- "NO3-": self.target_profile['N (NO3-)'],
730
- "NH4+": self.target_profile['N (NH4+)']
731
- }
732
 
733
- def _label(self, element):
734
- """Форматирование названий элементов для вывода"""
735
- labels = {
736
- 'N (NO3-)': 'NO3',
737
- 'N (NH4+)': 'NH4'
738
- }
739
- return labels.get(element, element)
740
 
741
  def calculate(self):
 
742
  try:
743
- self._apply("Сульфат магния", "Mg", self.target_profile['Mg'])
744
- self._apply("Кальциевая селитра", "Ca", self.target_profile['Ca'])
745
- self._apply("Монофосфат калия", "P", self.target_profile['P'])
746
- self._apply("Аммоний азотнокислый", "N (NH4+)", self.target_profile['N (NH4+)'])
747
-
748
- current_no3 = self.actual_profile['N (NO3-)']
749
- no3_needed = self.target_profile['N (NO3-)'] - current_no3
750
 
 
 
751
  if no3_needed > 0.1:
752
- self._apply("Калий азотнокислый", "N (NO3-)", no3_needed)
753
 
754
- self._apply_k_sulfate()
 
 
 
755
 
756
- k_deficit = self.target_profile['K'] - self.actual_profile['K']
757
- if k_deficit > 0.1:
758
- self._apply("Калий азотнокислый", "K", k_deficit)
 
759
 
760
- return self.results
761
  except Exception as e:
762
- print(f"Ошибка при расчёте: {str(e)}")
763
- raise
764
 
765
- def _apply(self, fert_name, main_element, required_ppm):
766
- if required_ppm <= 0:
 
767
  return
768
-
769
- try:
770
- content = self.fertilizers[fert_name][main_element]
771
- grams = (required_ppm * self.volume) / (content * 1000)
772
-
773
- if fert_name not in self.results:
774
- result = {
775
- 'граммы': 0.0,
776
- 'миллиграммы': 0,
777
- 'вклад в EC': 0.0
778
- }
779
- for element in self.fertilizers[fert_name]:
780
- result[f'внесет {self._label(element)}'] = 0.0
781
- self.results[fert_name] = result
782
-
783
- self.results[fert_name]['граммы'] += grams
784
- self.results[fert_name]['миллиграммы'] += int(grams * 1000)
785
-
786
- fert_ec = 0.0
787
- for element, percent in self.fertilizers[fert_name].items():
788
- added_ppm = (grams * percent * 1000) / self.volume
789
- self.results[fert_name][f'внесет {self._label(element)}'] += added_ppm
790
- self.actual_profile[element] += added_ppm
791
- fert_ec += added_ppm * EC_COEFFICIENTS.get(element, 0.0015)
792
-
793
- self.results[fert_name]['вклад в EC'] += fert_ec
794
- self.total_ec += fert_ec
795
- except KeyError as e:
796
- print(f"Ошибка: отсутствует элемент {str(e)} в удобрении {fert_name}")
797
- raise
798
-
799
- def _apply_k_sulfate(self):
800
- fert = "Калий сернокислый"
801
- k_def = self.target_profile['K'] - self.actual_profile['K']
802
- s_def = self.target_profile['S'] - self.actual_profile['S']
803
 
804
- if k_def <= 0 and s_def <= 0:
805
  return
806
-
807
- try:
808
- if s_def > 0.1:
809
- s_content = self.fertilizers[fert]["S"]
810
- grams_s = (s_def * self.volume) / (s_content * 1000)
811
-
812
- k_content = self.fertilizers[fert]["K"]
813
- k_from_s = (grams_s * k_content * 1000) / self.volume
814
-
815
- if k_from_s > k_def and k_def > 0.1:
816
- grams = (k_def * self.volume) / (k_content * 1000)
817
- else:
818
- grams = grams_s
819
-
820
- self._apply(fert, "S", s_def)
821
- except Exception as e:
822
- print(f"Ошибка при расчёте сульфата калия: {str(e)}")
823
- raise
824
 
825
- def calculate_ec(self):
826
- return round(self.total_ec, 2)
827
-
828
- def print_report(self):
829
- try:
830
- print("\n" + "="*60)
831
- print("ПРОФИЛЬ ПИТАТЕЛЬНОГО РАСТВОРА (ИТОГО):")
832
- print("="*60)
833
- table = [[el, round(self.actual_profile[el], 1)] for el in self.actual_profile]
834
- print(tabulate(table, headers=["Элемент", "ppm"]))
835
-
836
- print("\nИсходный расчёт азота:")
837
- for form, val in self.initial_n_profile.items():
838
- print(f" {form}: {round(val, 1)} ppm")
839
-
840
- print("\n" + "="*60)
841
- print(f"РАСЧЕТ ДЛЯ {self.volume} ЛИТРОВ РАСТВОРА")
842
- print("="*60)
843
- print(f"Общая концентрация: {round(sum(self.actual_profile.values()), 1)} ppm")
844
- print(f"EC: {self.calculate_ec()} mS/cm")
845
-
846
- print("\nРЕКОМЕНДУЕМЫЕ УДОБРЕНИЯ:")
847
- fert_table = []
848
- for fert, data in self.results.items():
849
- adds = [f"+{k}: {v:.1f} ppm" for k, v in data.items() if k.startswith('внесет')]
850
- fert_table.append([
851
- fert,
852
- round(data['граммы'], 3),
853
- data['миллиграммы'],
854
- round(data['вклад в EC'], 3),
855
- "\n".join(adds)
856
- ])
857
- print(tabulate(fert_table,
858
- headers=["Удобрение", "Граммы", "Миллиграммы", "EC (мСм/см)", "Добавит"]))
859
-
860
- print("\nОСТАТОЧНЫЙ ДЕФИЦИТ:")
861
- deficit = {
862
- k: round(self.target_profile[k] - self.actual_profile[k], 1)
863
- for k in self.target_profile
864
- if abs(self.target_profile[k] - self.actual_profile[k]) > 0.1
865
- }
866
- if deficit:
867
- for el, val in deficit.items():
868
- print(f" {el}: {val} ppm")
869
- else:
870
- print(" Все элементы покрыты полностью")
871
- except Exception as e:
872
- print(f"Ошибка при выводе отчёта: {str(e)}")
873
- raise
874
 
875
- # Данные на страницу
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876
 
877
- def get_web_results(self):
878
- """Возвращает данные в формате для web-интерфейса"""
879
  return {
880
- 'fertilizers': self._format_fertilizers(),
881
- 'profile': self._format_profile(),
882
- 'ec': self.calculate_ec(),
883
- 'deficits': self.calculate_deficits()
884
- }
885
-
886
- def _format_fertilizers(self):
887
- formatted = []
888
- for name, data in self.results.items():
889
- fert = {
890
- 'name': name,
891
- 'grams': round(data['граммы'], 3),
892
- 'adds': {}
 
 
 
 
 
 
893
  }
894
- for k, v in data.items():
895
- if k.startswith('внесет'):
896
- fert['adds'][k.replace('внесет ', '')] = round(v, 1)
897
- formatted.append(fert)
898
- return formatted
899
-
900
- def _format_profile(self):
901
- return [{'element': k, 'ppm': round(v, 1)}
902
- for k, v in self.actual_profile.items()]
903
- def calculate_deficits(self):
904
- """Вычисляет остаточные дефициты элементов"""
905
- return {
906
- k: max(self.target_profile[k] - self.actual_profile[k], 0)
907
- for k in self.target_profile
908
- }
909
-
910
- def get_web_results(self):
911
- """Возвращает данные в формате для web-интерфейса"""
912
- return {
913
- 'fertilizers': self._format_fertilizers(),
914
- 'profile': self._format_profile(),
915
- 'ec': self.calculate_ec(),
916
- 'deficits': self.calculate_deficits(),
917
- 'volume': self.volume
918
  }
919
 
920
- def _format_fertilizers(self):
921
- formatted = []
922
- for name, data in self.results.items():
923
- fert = {
924
- 'name': name,
925
- 'grams': round(data['граммы'], 3),
926
- 'adds': {}
927
- }
928
- for k, v in data.items():
929
- if k.startswith('внесет'):
930
- fert['adds'][k.replace('внесет ', '')] = round(v, 1)
931
- formatted.append(fert)
932
- return formatted
933
-
934
- def _format_profile(self):
935
- return [{'element': k.replace(' (NO3-)', '-NO3').replace(' (NH4+)', '-NH4'),
936
- 'ppm': round(v, 1)}
937
- for k, v in self.actual_profile.items()]
938
-
939
- # Функции для преобразования данных от клиента
940
- FERTILIZER_NAME_MAP = {
941
- "CaN2O6": "Кальциевая селитра",
942
- "KNO3": "Калий азотнокислый",
943
- "K2SO4": "Калий сернокислый",
944
- "NH4NO3": "Аммоний азотнокислый",
945
- "MgSO4": "Сульфат магния",
946
- "KH2PO4": "Монофосфат калия"
947
- }
948
-
949
- NUTRIENT_MAP = {
950
- "NO3": "N (NO3-)",
951
- "NH4": "N (NH4+)"
952
- }
953
-
954
- def convert_client_data(client_data):
955
- """Преобразует данные от клиента в серверный формат"""
956
- server_format = {}
957
- for chem_form, nutrients in client_data.items():
958
- fert_name = FERTILIZER_NAME_MAP.get(chem_form, chem_form)
959
- server_format[fert_name] = {}
960
-
961
- for element, value in nutrients.items():
962
- server_element = NUTRIENT_MAP.get(element, element)
963
- server_format[fert_name][server_element] = value
964
-
965
- return server_format
966
-
967
- if __name__ == "__main__":
968
- try:
969
- calculator = NutrientCalculator(volume_liters=VOLUME_LITERS)
970
- calculator.calculate()
971
- calculator.print_report() # Правильный вызов метода класса
972
- except Exception as e:
973
- print(f"Критическая ошибка: {str(e)}")
974
-
975
-
976
-
977
-
978
-
979
-
980
-
981
-
982
  @app.route('/calculation', methods=['POST'])
983
  def handle_calculation():
984
  try:
985
- raw_data = request.json
986
- print("\n=== ПРИНЯТЫЕ ДАННЫЕ ===")
987
- print(raw_data)
988
-
989
- # 1. Извлекаем значения азота из запроса
990
- no3 = float(raw_data['profileSettings'].get('N (NO3-)', 0))
991
- nh4 = float(raw_data['profileSettings'].get('N (NH4+)', 0))
992
- liters = int(raw_data['profileSettings'].get('liters', VOLUME_LITERS))
993
-
994
- # 2. Создаём калькулятор с РУЧНОЙ установкой азота
995
- calculator = NutrientCalculator(volume_liters=liters)
996
 
997
- # 3. ПЕРЕЗАПИСЫВАЕМ расчёт азота (главное исправление!)
998
- calculator.target_profile.update({
999
- 'N (NO3-)': no3,
1000
- 'N (NH4+)': nh4
 
 
 
 
 
1001
  })
1002
-
1003
- # 4. Устанавливаем кастомные удобрения
1004
- calculator.fertilizers = convert_client_data(raw_data['fertilizerConstants'])
1005
 
1006
- # 5. Запускаем расчёт
1007
- calculator.calculate()
 
 
 
 
 
 
1008
 
1009
- return jsonify(calculator.get_web_results())
1010
-
 
 
 
 
 
 
 
 
 
 
1011
  except Exception as e:
1012
- print(f"Ошибка: {str(e)}")
1013
- return jsonify({"error": str(e)}), 500
1014
-
1015
-
1016
-
1017
 
1018
 
1019
 
 
680
  def nutri_call():
681
  return render_template('nutri_call.html')
682
 
683
+
684
+
685
+
686
+
687
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
 
689
 
690
  class NutrientCalculator:
691
  def __init__(self, volume_liters=1.0):
692
  self.volume = volume_liters
693
  self.results = {}
694
+ self.target_profile = {}
695
+ self.actual_profile = {}
696
+ self.fertilizers = {}
697
  self.total_ec = 0.0
698
 
699
+ def set_target_profile(self, profile):
700
+ """Устанавливаем целевой профиль извне"""
701
+ self.target_profile = profile.copy()
702
+ self.actual_profile = {k: 0.0 for k in profile}
 
 
 
 
703
 
704
+ def set_fertilizers(self, fertilizers):
705
+ """Устанавливаем состав удобрений извне"""
706
+ self.fertilizers = fertilizers
 
 
 
 
707
 
708
  def calculate(self):
709
+ """Основной расчёт"""
710
  try:
711
+ # Расчёт по порядку внесения удобрений
712
+ self._apply_fertilizer("Сульфат магния", "Mg")
713
+ self._apply_fertilizer("Кальциевая селитра", "Ca")
714
+ self._apply_fertilizer("Монофосфат калия", "P")
715
+ self._apply_fertilizer("Аммоний азотнокислый", "N (NH4+)")
 
 
716
 
717
+ # Довносим NO3 если нужно
718
+ no3_needed = self.target_profile['N (NO3-)'] - self.actual_profile['N (NO3-)']
719
  if no3_needed > 0.1:
720
+ self._apply_fertilizer("Калий азотнокислый", "N (NO3-)", no3_needed)
721
 
722
+ # Довносим калий если нужно
723
+ k_needed = self.target_profile['K'] - self.actual_profile['K']
724
+ if k_needed > 0.1:
725
+ self._apply_fertilizer("Калий азотнокислый", "K", k_needed)
726
 
727
+ # Довносим серу если нужно
728
+ s_needed = self.target_profile['S'] - self.actual_profile['S']
729
+ if s_needed > 0.1:
730
+ self._apply_fertilizer("Калий сернокислый", "S", s_needed)
731
 
732
+ return True
733
  except Exception as e:
734
+ print(f"Calculation error: {str(e)}")
735
+ return False
736
 
737
+ def _apply_fertilizer(self, name, element, required_ppm=None):
738
+ """Универсальный метод внесения удобрений"""
739
+ if name not in self.fertilizers:
740
  return
741
+
742
+ if required_ppm is None:
743
+ required_ppm = self.target_profile[element] - self.actual_profile[element]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
744
 
745
+ if required_ppm <= 0:
746
  return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747
 
748
+ content = self.fertilizers[name].get(element, 0)
749
+ if content == 0:
750
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
751
 
752
+ grams = (required_ppm * self.volume) / (content * 1000)
753
+
754
+ # Записываем результаты
755
+ if name not in self.results:
756
+ self.results[name] = {'grams': 0.0, 'adds': {}}
757
+
758
+ self.results[name]['grams'] += grams
759
+
760
+ # Обновляем фактические концентрации
761
+ for nutrient, percent in self.fertilizers[name].items():
762
+ added_ppm = (grams * percent * 1000) / self.volume
763
+ self.actual_profile[nutrient] = self.actual_profile.get(nutrient, 0) + added_ppm
764
+ self.results[name]['adds'][nutrient] = self.results[name]['adds'].get(nutrient, 0) + added_ppm
765
+
766
+ # Расчёт EC
767
+ self.total_ec += sum(
768
+ added_ppm * 0.0015 # Упрощённый коэффициент
769
+ for nutrient, added_ppm in self.results[name]['adds'].items()
770
+ )
771
 
772
+ def get_results(self):
773
+ """Возвращаем результаты в стандартном формате"""
774
  return {
775
+ 'fertilizers': [
776
+ {
777
+ 'name': name,
778
+ 'grams': round(data['grams'], 3),
779
+ 'adds': {k: round(v, 3) for k, v in data['adds'].items()}
780
+ }
781
+ for name, data in self.results.items()
782
+ ],
783
+ 'profile': [
784
+ {
785
+ 'element': k.replace(' (NO3-)', '-NO3').replace(' (NH4+)', '-NH4'),
786
+ 'ppm': round(v, 3)
787
+ }
788
+ for k, v in self.actual_profile.items()
789
+ ],
790
+ 'ec': round(self.total_ec, 2),
791
+ 'deficits': {
792
+ k: max(self.target_profile[k] - self.actual_profile.get(k, 0), 0)
793
+ for k in self.target_profile
794
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
795
  }
796
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
797
  @app.route('/calculation', methods=['POST'])
798
  def handle_calculation():
799
  try:
800
+ data = request.json
801
+
802
+ # Создаём калькулятор
803
+ calculator = NutrientCalculator(volume_liters=data['profileSettings']['liters'])
 
 
 
 
 
 
 
804
 
805
+ # Настраиваем профиль
806
+ calculator.set_target_profile({
807
+ 'P': float(data['profileSettings']['P']),
808
+ 'K': float(data['profileSettings']['K']),
809
+ 'Mg': float(data['profileSettings']['Mg']),
810
+ 'Ca': float(data['profileSettings']['Ca']),
811
+ 'S': float(data['profileSettings']['S']),
812
+ 'N (NO3-)': float(data['profileSettings']['N (NO3-)']),
813
+ 'N (NH4+)': float(data['profileSettings']['N (NH4+)'])
814
  })
 
 
 
815
 
816
+ # Настраиваем удобрения
817
+ calculator.set_fertilizers({
818
+ "Кальциевая селитра": {
819
+ "N (NO3-)": float(data['fertilizerConstants']['CaN2O6']['NO3']) / 100,
820
+ "Ca": float(data['fertilizerConstants']['CaN2O6']['Ca']) / 100
821
+ },
822
+ # ... остальные удобрения аналогично
823
+ })
824
 
825
+ # Выполняем расчёт
826
+ if calculator.calculate():
827
+ return jsonify({
828
+ 'status': 'success',
829
+ 'data': calculator.get_results()
830
+ })
831
+ else:
832
+ return jsonify({
833
+ 'status': 'error',
834
+ 'message': 'Calculation failed'
835
+ }), 500
836
+
837
  except Exception as e:
838
+ return jsonify({
839
+ 'status': 'error',
840
+ 'message': str(e)
841
+ }), 400
 
842
 
843
 
844